2015-01-05 5 views
34

napisałem poniższy skrypt:numpy podział ze RuntimeWarning: nieprawidłowa wartość napotkanych w double_scalars

import numpy 

d = numpy.array([[1089, 1093]]) 
e = numpy.array([[1000, 4443]]) 
answer = numpy.exp(-3 * d) 
answer1 = numpy.exp(-3 * e) 
res = answer.sum()/answer1.sum() 
print res 

Ale mam ten wynik iz wystąpił błąd:

nan 
C:\Users\Desktop\test.py:16: RuntimeWarning: invalid value encountered in double_scalars 
    res = answer.sum()/answer1.sum() 

Wydaje się, że element wejściowy były zbyt małe, że python zamienił je na zera, ale rzeczywiście podział ma swój wynik.

Jak rozwiązać ten problem?

Odpowiedz

39

Nie możesz go rozwiązać. Po prostu answer1.sum()==0, a nie można wykonać podziału przez zero.

Dzieje się tak, ponieważ answer1 jest wykładniczą z 2 bardzo dużych liczb ujemnych, tak że wynik jest zaokrąglany do zera.

nan jest zwracana w tym przypadku z powodu podziału przez zero.

teraz, aby rozwiązać swój problem możesz:

  • iść do biblioteki dla precyzyjnych matematycznych, jak mpmath. Ale to mniej zabawne.
  • jako alternatywa dla większej broni, wykonaj kilka czynności matematycznych, jak opisano poniżej.
  • przejdź do dostosowanej funkcji scipy/numpy, która robi dokładnie to, co chcesz! Sprawdź odpowiedź @Warren Weckesser.

Tutaj wyjaśniam, jak wykonać kilka operacji matematycznych, które pomagają w tym problemie. Mamy to na liczniku:

exp(-x)+exp(-y) = exp(log(exp(-x)+exp(-y))) 
       = exp(log(exp(-x)*[1+exp(-y+x)])) 
       = exp(log(exp(-x) + log(1+exp(-y+x))) 
       = exp(-x + log(1+exp(-y+x))) 

gdzie powyżej x=3* 1089 i y=3* 1093. Teraz, argument ten wykładniczej

-x + log(1+exp(-y+x)) = -x + 6.1441934777474324e-06

Dla mianownika można postępować podobnie, ale uzyskanie że log(1+exp(-z+k)) już jest zaokrąglana do 0, tak że argument funkcji wykładniczej w mianowniku jest po prostu zaokrągleniu do -z=-3000 . Trzeba wówczas, że wynik jest

exp(-x + log(1+exp(-y+x)))/exp(-z) = exp(-x+z+log(1+exp(-y+x)) 
            = exp(-266.99999385580668) 

który jest już bardzo blisko do wyniku, który chcesz uzyskać, jeśli były, aby zachować tylko 2 wiodące warunki (czyli pierwszego numeru 1089 w liczniku oraz pierwszy numer 1000 w mianowniku):

exp(3*(1089-1000))=exp(-267) 

w trosce o to, zobaczmy, jak blisko jesteśmy z roztworu Wolfram alpha (link):

Log[(exp[-3*1089]+exp[-3*1093])/([exp[-3*1000]+exp[-3*4443])] -> -266.999993855806522267194565420933791813296828742310997510523 

Różnica między tą liczbą a wykładnikiem powyżej to +1.7053025658242404e-13, więc przybliżenie, którego dokonaliśmy przy mianowniku, było w porządku.

Wynik końcowy jest

'exp(-266.99999385580668) = 1.1050349147204485e-116 

Od Wolfram Alpha jest (link)

1.105034914720621496.. × 10^-116 # Wolfram alpha. 

i znowu, że jest bezpieczny w użyciu numpy tu.

+0

Ale w tym przypadku trzeba uzyskać wartości z podziałem o 2 bardzo małe wartości. – Heinz

+1

Co masz na myśli? – gg349

+0

@Heinz Myślę, że chodziło o przypadek, w którym mała liczba jest podzielona przez małą liczbę. W takim przypadku zmień swój algorytm, aby skalować obie liczby w górę, jest o wiele lepszy niż znalezienie mechanicznych zwrotów akcji. Weźmy na przykład logarytm równań analitycznych, które twój kod próbuje symulować. Istnieje wiele problemów ze stabilnością obliczeń przy małych liczbach. Miło jest unikać posiadania któregokolwiek z nich, jeśli to możliwe. – Mai

7

Można użyć np.logaddexp (który realizuje ideę w użytkownika @ gg349 odpowiedź):

In [33]: d = np.array([[1089, 1093]]) 

In [34]: e = np.array([[1000, 4443]]) 

In [35]: log_res = np.logaddexp(-3*d[0,0], -3*d[0,1]) - np.logaddexp(-3*e[0,0], -3*e[0,1]) 

In [36]: log_res 
Out[36]: -266.99999385580668 

In [37]: res = exp(log_res) 

In [38]: res 
Out[38]: 1.1050349147204485e-116 

Albo można użyć scipy.misc.logsumexp:

In [52]: from scipy.misc import logsumexp 

In [53]: res = np.exp(logsumexp(-3*d) - logsumexp(-3*e)) 

In [54]: res 
Out[54]: 1.1050349147204485e-116