2012-08-27 11 views
41

assertAlmostEqual(x, y)Python's unit testing framework में विधि x और y परीक्षण करती है कि वे लगभग समान हैं कि वे फ़्लोट हैं।assertAlmostEqual फ्लोट्स के संग्रह के लिए पायथन यूनिट-टेस्ट में

assertAlmostEqual() के साथ समस्या यह है कि यह केवल फ्लोट पर काम करता है। मैं assertAlmostEqual() जैसी एक विधि की तलाश में हूं जो फ्लोट्स, फ्लोट्स के सेट, फ्लोट्स के ट्यूट्स, फ्लोट्स के टुपल्स, फ्लोट्स के टुपल्स, फ्लोट्स की सूचियों के सेट इत्यादि की सूची,

उदाहरण के लिए, चलो x = 0.1234567890, y = 0.1234567891x और y लगभग बराबर हैं क्योंकि वे अंतिम अंक को छोड़कर प्रत्येक अंक पर सहमत हैं। इसलिए self.assertAlmostEqual(x, y)True है क्योंकि assertAlmostEqual() फ्लोट के लिए काम करता है।

मैं एक अधिक सामान्य assertAlmostEquals() जो भी True के लिए निम्न कॉल का मूल्यांकन करता है के लिए देख रहा हूँ:

  • self.assertAlmostEqual_generic([x, x, x], [y, y, y])
  • self.assertAlmostEqual_generic({1: x, 2: x, 3: x}, {1: y, 2: y, 3: y})
  • self.assertAlmostEqual_generic([(x,x)], [(y,y)])

क्या ऐसी कोई विधि है या क्या मुझे इसे स्वयं लागू करना है?

स्पष्टीकरण:

  • assertAlmostEquals()places नामक एक वैकल्पिक पैरामीटर है और संख्या अंतर दशमलव places की संख्या को गोल कंप्यूटिंग से तुलना की जाती है। डिफ़ॉल्ट रूप से places=7, इसलिए self.assertAlmostEqual(0.5, 0.4) गलत है जबकि self.assertAlmostEqual(0.12345678, 0.12345679) सत्य है। मेरा सट्टा assertAlmostEqual_generic() समान कार्यक्षमता होना चाहिए।

  • दो सूचियों को लगभग बराबर माना जाता है यदि उनके पास बिल्कुल उसी क्रम में लगभग बराबर संख्याएं हैं। औपचारिक रूप से, for i in range(n): self.assertAlmostEqual(list1[i], list2[i])

  • इसी प्रकार, दो सेट लगभग बराबर माना जाता है अगर उन्हें लगभग बराबर सूचियों में परिवर्तित किया जा सकता है (प्रत्येक सेट को ऑर्डर सौंपकर)।

  • इसी तरह, दो शब्दकोशों को लगभग बराबर माना जाता है यदि प्रत्येक शब्दकोश का मुख्य सेट अन्य शब्दकोश के कुंजी सेट के बराबर होता है, और प्रत्येक ऐसी लगभग समान कुंजी जोड़ी के लिए लगभग समान मूल्य होता है।

  • सामान्य रूप से: मैं दो संग्रहों को लगभग बराबर मानता हूं यदि वे समान हैं जो कुछ समान फ्लोट्स को छोड़कर बराबर हैं जो लगभग एक दूसरे के बराबर हैं। दूसरे शब्दों में, मैं वास्तव में वस्तुओं की तुलना करना चाहता हूं लेकिन रास्ते में फ्लोट की तुलना करते समय कम (अनुकूलित) परिशुद्धता के साथ।

उत्तर

6

यहां बताया गया है मैं एक सामान्य is_almost_equal(first, second) समारोह इसलिए लगाए गए हैं टी एक सटीक प्रतिलिपि बनाओ: ऑब्जेक्ट के अंदर आने वाली किसी भी फ्लोट के महत्वहीन दशमलव अंकों को काट लें।

अब आप first और second जिसके लिए तुच्छ दशमलव अंक चले गए हैं की प्रतियां है, बस == ऑपरेटर का उपयोग first और second तुलना कि।

मान लेते हैं कि हम एक cut_insignificant_digits_recursively(obj, places) समारोह जो obj डुप्लिकेट लेकिन केवल places सबसे महत्वपूर्ण दशमलव प्रत्येक नाव के मूल obj में अंकों को छोड़ देता है करते हैं। यहाँ is_almost_equals(first, second, places) की एक काम कार्यान्वयन है:

from insignificant_digit_cutter import cut_insignificant_digits_recursively 

def is_almost_equal(first, second, places): 
    '''returns True if first and second equal. 
    returns true if first and second aren't equal but have exactly the same 
    structure and values except for a bunch of floats which are just almost 
    equal (floats are almost equal if they're equal when we consider only the 
    [places] most significant digits of each).''' 
    if first == second: return True 
    cut_first = cut_insignificant_digits_recursively(first, places) 
    cut_second = cut_insignificant_digits_recursively(second, places) 
    return cut_first == cut_second 

और यहाँ cut_insignificant_digits_recursively(obj, places) की एक काम कार्यान्वयन है:

def cut_insignificant_digits(number, places): 
    '''cut the least significant decimal digits of a number, 
    leave only [places] decimal digits''' 
    if type(number) != float: return number 
    number_as_str = str(number) 
    end_of_number = number_as_str.find('.')+places+1 
    if end_of_number > len(number_as_str): return number 
    return float(number_as_str[:end_of_number]) 

def cut_insignificant_digits_lazy(iterable, places): 
    for obj in iterable: 
     yield cut_insignificant_digits_recursively(obj, places) 

def cut_insignificant_digits_recursively(obj, places): 
    '''return a copy of obj except that every float loses its least significant 
    decimal digits remaining only [places] decimal digits''' 
    t = type(obj) 
    if t == float: return cut_insignificant_digits(obj, places) 
    if t in (list, tuple, set): 
     return t(cut_insignificant_digits_lazy(obj, places)) 
    if t == dict: 
     return {cut_insignificant_digits_recursively(key, places): 
       cut_insignificant_digits_recursively(val, places) 
       for key,val in obj.items()} 
    return obj 

कोड और उसके इकाई परीक्षण यहां उपलब्ध हैं: https://github.com/snakile/approximate_comparator। मैं किसी भी सुधार और बग फिक्स का स्वागत करता हूं।

+0

फ्लोट की तुलना करने के बजाय, आप स्ट्रिंग की तुलना कर रहे हैं? ठीक है ... लेकिन फिर, क्या एक सामान्य प्रारूप सेट करना आसान नहीं होगा? 'Fmt =" {{0: {0} f}} "प्रारूप (दशमलव)' की तरह, और अपने फ़्लोट्स को "स्ट्रिंग" करने के लिए इस 'fmt' प्रारूप का उपयोग करें? –

+0

(मुझे पता है कि यह स्वाद का विषय है, लेकिन मुझे एक ही पंक्ति पर एक ही कथन मिला जैसे 'अगर' मुश्किल से पठनीय)। –

+0

तो, जब आप कोई ऑर्डर नहीं करते हैं, तो आप सूचियों, tuples और सेट के लिए एक ही MO का उपयोग करते हैं ?! –

3

ऐसी कोई विधि नहीं है, आपको इसे स्वयं करना होगा।

सूचियों और tuples के लिए परिभाषा स्पष्ट है, लेकिन ध्यान दें कि आपके द्वारा उल्लेख किए गए अन्य मामले स्पष्ट नहीं हैं, इसलिए इसमें कोई आश्चर्य नहीं कि ऐसा कोई कार्य प्रदान नहीं किया गया है। उदाहरण के लिए, {1.00001: 1.00002} लगभग {1.00002: 1.00001} के बराबर है?ऐसे मामलों को संभालने के लिए इस बारे में कोई विकल्प बनाना आवश्यक है कि निकटता कुंजी या मूल्य या दोनों पर निर्भर करती है या नहीं। सेट के लिए आपको सार्थक परिभाषा खोजने की संभावना नहीं है, क्योंकि सेट अनियंत्रित हैं, इसलिए "संबंधित" तत्वों की कोई धारणा नहीं है।

+0

'{1.00001: 1.00002]' टाइपो के बराबर होना चाहिए? –

+0

इसे फिक्स्ड, धन्यवाद। – BrenBarn

+0

ब्रेनबर्न: मैंने प्रश्न में स्पष्टीकरण जोड़े हैं। आपके प्रश्न का उत्तर यह है कि '{1.00001: 1.00002}' लगभग '{1.00002: 1.00001}' के बराबर है, यदि केवल और केवल 1.00001 लगभग 1.00002 के बराबर है। डिफ़ॉल्ट रूप से वे लगभग बराबर नहीं होते हैं (क्योंकि डिफ़ॉल्ट परिशुद्धता 7 दशमलव स्थान होती है) लेकिन 'स्थानों' के लिए एक छोटे से पर्याप्त मूल्य के लिए वे लगभग बराबर होती हैं। – snakile

0

आपको इसे स्वयं लागू करना पड़ सकता है, जबकि यह सच है कि सूची और सेट को उसी तरह से फिर से चालू किया जा सकता है, शब्दकोश एक अलग कहानी है, आप अपनी चाबियाँ मानते हैं, और तीसरा उदाहरण मेरे लिए थोड़ा अस्पष्ट नहीं लगता है, क्या आप सेट के भीतर प्रत्येक मान, या प्रत्येक सेट से प्रत्येक मान की तुलना करना चाहते हैं।

एक साधारण कोड स्निपेट है।

def almost_equal(value_1, value_2, accuracy = 10**-8): 
    return abs(value_1 - value_2) < accuracy 

x = [1,2,3,4] 
y = [1,2,4,5] 
assert all(almost_equal(*values) for values in zip(x, y)) 
+0

धन्यवाद, समाधान सूचियों और tuples के लिए सही है, लेकिन अन्य प्रकार के संग्रह (या घोंसला संग्रह) के लिए नहीं है। प्रश्न में मैंने जो स्पष्टीकरण जोड़े हैं, उन्हें देखें। मुझे उम्मीद है कि मेरा इरादा अब स्पष्ट है। दो सेट लगभग बराबर होते हैं यदि उन्हें ऐसी दुनिया में बराबर माना जाता था जहां संख्याओं को बहुत सटीक रूप से मापा नहीं जाता है। – snakile

38

अगर आप NumPy का उपयोग कर कोई आपत्ति नहीं है (जो के साथ अपने अजगर (एक्स आता है, y)), आप np.testing मॉड्यूल है, जो परिभाषित करता है दूसरों के बीच में, एक assert_almost_equal समारोह को देखने के लिए चाहते हो सकता है।

पहले, वस्तुओं आप इसकी तुलना कर (first और second) की जरूरत है नकल, लेकिन डॉन ':

हस्ताक्षर np.testing.assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True)

>>> x = 1.000001 
>>> y = 1.000002 
>>> np.testing.assert_almost_equal(x, y) 
AssertionError: 
Arrays are not almost equal to 7 decimals 
ACTUAL: 1.000001 
DESIRED: 1.000002 
>>> np.testing.assert_almost_equal(x, y, 5) 
>>> np.testing.assert_almost_equal([x, x, x], [y, y, y], 5) 
>>> np.testing.assert_almost_equal((x, x, x), (y, y, y), 5) 
+0

यह करीब है, लेकिन 'numpy.testing' लगभग-बराबर विधियां केवल संख्याओं, सरणी, tuples और सूचियों पर काम करते हैं। वे संग्रह, सेट और संग्रह के संग्रह पर काम नहीं करते हैं। – snakile

+0

दरअसल, लेकिन यह एक शुरुआत है। इसके अलावा, आपके पास स्रोत कोड तक पहुंच है जिसे आप संशोधित कर सकते हैं ताकि शब्दकोश, संग्रह और आगे की तुलना की अनुमति दी जा सके। 'np.testing.assert_equal' शब्दकोशों को तर्क के रूप में पहचानता है, उदाहरण के लिए (भले ही तुलना' ==' द्वारा की जाती है जो आपके लिए काम नहीं करेगी)। –

+0

बेशक, सेट्स की तुलना करते समय भी आप परेशानियों में भाग लेंगे, जैसा कि @ ब्रेनबर्न ने उल्लेख किया है। –

4

यदि आपको numpy पैकेज का उपयोग करने में कोई बात नहीं है तो numpy.testing में assert_array_almost_equal विधि है।

यह array_like वस्तुओं के लिए काम करता है, इसलिए यह अरण, सूचियों और फ्लोट्स के टुपल्स के लिए ठीक है, लेकिन यह सेट और शब्दकोशों के लिए काम नहीं करता है।

प्रलेखन here है।

2

अजगर 3.5 के रूप में आप pep-0485 में वर्णित है

math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0) 

का उपयोग कर की तुलना कर सकते हैं। कार्यान्वयन

abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) 
संबंधित मुद्दे