IT

파이썬에서 플로트를 거의 평등하게 비교하는 가장 좋은 방법은 무엇입니까?

lottoking 2020. 3. 22. 10:55
반응형

파이썬에서 플로트를 거의 평등하게 비교하는 가장 좋은 방법은 무엇입니까?


플로팅을 평등에 대해 비교하는 것은 반올림 및 정밀도 문제로 인해 약간 미묘한 것으로 잘 알려져 있습니다.

예를 들면 다음과 같습니다. https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

파이썬에서 이것을 처리하기 위해 권장되는 방법은 무엇입니까?

분명히 어딘가에 표준 라이브러리 기능이 있습니까?


Python 3.5는 PEP 485에 설명 된대로 math.isclosecmath.isclose기능추가합니다 .

이전 버전의 Python을 사용하는 경우 해당 기능이 documentation에 제공 됩니다.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

rel_tol상대 허용 오차이며, 두 인수의 크기가 더 큰 값을 곱합니다. 값이 커질수록 여전히 동일한 것으로 간주하면서 허용되는 차이가 커집니다.

abs_tol모든 경우에 그대로 적용되는 절대 공차입니다. 차이가 해당 공차 중 하나보다 작 으면 값이 동일한 것으로 간주됩니다.


다음과 같은 간단한 것이 충분하지 않습니까?

return abs(f1 - f2) <= allowed_error

Gareth의 답변이 가벼운 기능 / 솔루션으로 가장 적합하다는 데 동의합니다.

그러나 NumPy를 사용 중이거나 고려 중이라면 패키지 기능이 있음을 알면 도움이 될 것이라고 생각했습니다.

numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

약간의 면책 조항 : NumPy를 설치하는 것은 플랫폼에 따라 사소한 경험이 될 수 있습니다.


클래스 decimal를 제공하는 Python 모듈을 사용하십시오 Decimal.

의견에서 :

수학이 많은 작업을하고 있고 소수점 이하의 정밀도가 절대적으로 필요하지 않으면 실제로 문제가 발생할 수 있습니다. 플로트는 처리하기에 더 빠르지 만 정확하지 않습니다. 십진법은 매우 정확하지만 느립니다.


부동 소수점 숫자가 평등을 위해 비교 될 수 없다는 일반적인 지혜는 부정확합니다. 부동 소수점 숫자는 정수와 다르지 않습니다. "a == b"로 평가하면 숫자가 동일하면 true를, 그렇지 않으면 false를 얻습니다 (두 NaN은 물론 동일하지 않음).

실제 문제는 이것입니다 : 계산을했는데 비교 해야하는 두 숫자가 정확히 맞는지 확실하지 않으면 어떻게됩니까? 이 문제는 정수에서와 같이 부동 소수점에서 동일합니다. 정수 식 "7 / 3 * 3"을 평가하면 "7 * 3 / 3"과 같지 않습니다.

"정수와 정수를 어떻게 비교합니까?" 그런 상황에서. 단일 답변이 없습니다. 수행해야 할 작업은 특정 상황, 특히 어떤 종류의 오류 및 달성하려는 항목에 따라 다릅니다.

가능한 선택 사항은 다음과 같습니다.

수학적으로 정확한 숫자가 같으면 "진정한"결과를 얻으려면 수행 한 계산의 속성을 사용하여 두 숫자에서 동일한 오류가 발생 함을 증명할 수 있습니다. 이것이 가능하고 정확하게 계산 된 경우 동일한 숫자를 제공하는 표현식의 결과 인 두 숫자를 비교하면 비교에서 "참"이됩니다. 또 다른 방법은 계산의 속성을 분석하고 오류가 절대량 또는 입력 중 하나 또는 출력 중 하나에 상대적인 양을 절대 초과하지 않음을 증명할 수 있다는 것입니다. 이 경우 계산 된 두 숫자가 최대량만큼 다른지 물어보고 해당 간격 내에 있으면 "true"를 반환 할 수 있습니다. 에러 바운드를 증명할 수 없다면 당신은 추측하고 최선을 다할 수 있습니다. 추측하는 한 가지 방법은 많은 무작위 표본을 평가하고 결과에 어떤 종류의 분포가 있는지 확인하는 것입니다.

물론 수학적으로 정확한 결과가 같으면 "참"이라는 요구 사항 만 설정하기 때문에 결과가 같지 않더라도 "참"이 될 가능성을 열어 두었습니다. (실제로 항상 "true"를 반환하여 요구 사항을 충족 할 수 있습니다. 이렇게하면 계산이 간단 해지지 만 일반적으로 바람직하지 않으므로 아래 상황 개선에 대해 논의하겠습니다.)

수학적으로 정확한 숫자가 다른 경우 "거짓"결과를 얻으려면 수학적으로 정확한 숫자가 다른 경우 숫자 평가에서 다른 숫자를 산출한다는 것을 증명해야합니다. 많은 일반적인 상황에서 실제적인 목적으로는 불가능할 수 있습니다. 대안을 생각해 봅시다.

수학적으로 정확한 숫자가 일정량 이상 차이가 나는 경우 "거짓"결과를 얻는 것이 유용한 요구 사항 일 수 있습니다. 예를 들어, 컴퓨터 게임에서 던진 공이 어디로 갔는지 계산할 것입니다. 이 경우, 공이 방망이에 부딪히면 반드시 "참"을 원하고, 공이 방망이에서 멀어지면 "거짓"을하려고합니다. 수학적으로 정확한 시뮬레이션은 박쥐를 놓쳤지만 박쥐를 때리는 밀리미터 이내에 있습니다. 이 경우, 볼의 위치와 타석의 위치 계산시 최대 1 밀리미터 (모든 관심 위치에 대한)의 결합 오차가 있음을 증명 (또는 추측 / 추정)해야합니다. 이렇게하면 항상 "

따라서 부동 소수점 숫자를 비교할 때 반환 할 항목을 결정하는 방법은 특정 상황에 따라 크게 다릅니다.

계산에 대한 오류 범위를 증명하는 방법에 대해서는 복잡한 주제가 될 수 있습니다. 가장 가까운 반올림 모드에서 IEEE 754 표준을 사용하는 부동 소수점 구현은 기본 연산 (특히 곱셈, 나눗셈, 덧셈, 뺄셈, 제곱근)에 대한 정확한 결과에 가장 가까운 부동 소수점 숫자를 반환합니다. (동점의 경우 라운드가 낮으므로 낮은 비트가 고르게됩니다.) (제곱근과 나눗셈에 특히주의하십시오. 언어 구현에는 IEEE 754를 준수하지 않는 메소드가 사용될 수 있습니다.)이 요구 사항 때문에 단일 결과의 오류는 최하위 비트 값의 최대 1/2입니다. (더 많으면 반올림은 값의 1/2 내에있는 다른 숫자로 넘어 갔을 것입니다.)

거기에서 나아가는 것은 훨씬 더 복잡해진다. 다음 단계는 입력 중 하나에 이미 오류가있는 작업을 수행하는 것입니다. 간단한 표현식의 경우, 이러한 오류는 계산을 통해 최종 오류에 대한 경계에 도달 할 수 있습니다. 실제로 이것은 고품질 수학 라이브러리 작업과 같은 몇 가지 상황에서만 수행됩니다. 물론 어떤 작업이 수행되는지 정확하게 제어해야합니다. 고급 언어는 종종 컴파일러에 많은 여유를 주므로 어떤 순서로 작업이 수행되는지 알 수 없습니다.

이 주제에 관해 쓰여질 수있는 것이 더 많지만 여기서 그만 두어야합니다. 요약하면 답은 다음과 같습니다. 라이브러리 루틴에 넣을 가치가있는 대부분의 요구에 맞는 단일 솔루션이 없기 때문에이 비교에는 라이브러리 루틴이 없습니다. (상대 또는 절대 오류 간격과 비교하면 충분하므로 라이브러리 루틴없이 간단하게 수행 할 수 있습니다.)


파이썬 표준 라이브러리 (또는 다른 곳)에서 도슨의 AlmostEqual2sComplement기능 을 구현하는 것을 알지 못합니다 . 이것이 원하는 행동이라면 직접 구현해야합니다. (이 경우 Dawson의 영리한 비트 해킹을 사용하는 것보다 일반적인 형식의 if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2유사 테스트를 사용하는 것이 좋습니다 . Dawson과 유사한 동작을 얻으려면 if abs(a-b) <= eps*max(EPS,abs(a),abs(b))작은 고정형 과 같은 것을 말할 수 있습니다 EPS. 정확하지는 않습니다. 도슨과 동일하지만 정신이 비슷합니다.


math.isclose ()가이 를 위해 Python 3.5에 추가 되었습니다 ( 소스 코드 ). 여기에 파이썬 2의 포트가 있습니다. Mark Ransom의 한 줄짜리와 다른 점은 "inf"와 "-inf"를 올바르게 처리 할 수 ​​있다는 것입니다.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 implementation of Python 3.5 math.isclose()
    https://hg.python.org/cpython/file/tip/Modules/mathmodule.c#l1993
    '''
    # sanity check on the inputs
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError("tolerances must be non-negative")

    # short circuit exact equality -- needed to catch two infinities of
    # the same sign. And perhaps speeds things up a bit sometimes.
    if a == b:
        return True

    # This catches the case of two infinities of opposite sign, or
    # one infinity and one finite number. Two infinities of opposite
    # sign would otherwise have an infinite relative tolerance.
    # Two infinities of the same sign are caught by the equality check
    # above.
    if math.isinf(a) or math.isinf(b):
        return False

    # now do the regular computation
    # this is essentially the "weak" test from the Boost library
    diff = math.fabs(b - a)
    result = (((diff <= math.fabs(rel_tol * b)) or
               (diff <= math.fabs(rel_tol * a))) or
              (diff <= abs_tol))
    return result

테스트 / TDD 컨텍스트에서 사용하려면 표준 방법이라고 말하고 싶습니다.

from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) #default is 7

다음 비교가 도움이되었다는 것을 알았습니다.

str(f1) == str(f2)

소스 번호 표시에 영향을 줄 수있는 일부 경우 정수 정수 및 분모를 사용하여 플로트 대신 분수로 표시 할 수 있습니다. 그렇게하면 정확한 비교를 할 수 있습니다.

자세한 내용은 분수 에서 분수 모듈을 참조하십시오.


@Sesquipedal의 제안을 좋아했지만 수정했습니다 (두 값이 모두 0 인 특수 사용 사례는 False를 반환합니다). 필자의 경우 Python 2.7을 사용하고 간단한 함수를 사용했습니다.

if f1 ==0 and f2 == 0:
    return True
else:
    return abs(f1-f2) < tol*max(abs(f1),abs(f2))

2 개의 숫자가 동일한 '정확도까지'인지 확인하고 공차를 지정할 필요가없는 경우에 유용합니다.

  • 두 숫자의 최소 정밀도를 구합니다

  • 둘 다 최소 정밀도로 반올림하고 비교

def isclose(a,b):                                       
    astr=str(a)                                         
    aprec=len(astr.split('.')[1]) if '.' in astr else 0 
    bstr=str(b)                                         
    bprec=len(bstr.split('.')[1]) if '.' in bstr else 0 
    prec=min(aprec,bprec)                                      
    return round(a,prec)==round(b,prec)                               

작성된 바와 같이 문자열 표현에서 'e'가없는 숫자에 대해서만 작동합니다 (0.9999999999995e-4 <number <= 0.9999999999995e11 의미).

예:

>>> isclose(10.0,10.049)
True
>>> isclose(10.0,10.05)
False

이것은 약간 추한 해킹 일 수도 있지만 기본 부동 소수점 정밀도 (소수 11 자릿수) 이상이 필요하지 않은 경우 꽤 잘 작동합니다. 파이썬 2.7에서 잘 작동합니다.

round_to의 기능은 사용 형식 방법 에서를 내장 한 후 필요한 소수의 수에 플로트를 나타내며, 문자열로 부동 소수점을 반올림하는 STR 클래스에 적용되는 평가 내장 된 기능을 다시 얻을 수있는 둥근 플로트 문자열 부동 숫자 유형으로.

is_close 기능은 둥근 최대 플로트에 대한 간단한 조건을 적용합니다.

def round_to(float_num, decimal_precision):
    return eval("'{:." + str(int(decimal_precision)) + "f}'.format(" + str(float_num) + ")")

def is_close(float_a, float_b, decimal_precision):
    if round_to(float_a, decimal_precision) == round_to(float_b, decimal_precision):
        return True
    return False

a = 10.0 / 3
# Result: 3.3333333333333335
b = 10.0001 / 3
# Result: 3.3333666666666666

print is_close(a, b, decimal_precision=4)
# Result: False

print is_close(a, b, decimal_precision=3)
# Result: True

없이 주어진 소수까지 비교하려면 atol/rtol:

def almost_equal(a, b, decimal=6):
    return '{0:.{1}f}'.format(a, decimal) == '{0:.{1}f}'.format(b, decimal)

print(almost_equal(0.0, 0.0001, decimal=5)) # False
print(almost_equal(0.0, 0.0001, decimal=4)) # True 

참고 URL : https://stackoverflow.com/questions/5595425/what-is-the-best-way-to-compare-floats-for-almost-equality-in-python

반응형