Division numpy avec RuntimeWarning: valeur non valide rencontrée dans les scalaires doubles

J'ai écrit le script suivant:

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

Mais j'ai obtenu ce résultat et l'erreur s'est produite:

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

Il semble que l'élément d'entrée était trop petit pour que python les transforme en zéros, mais en effet la division a son résultat.

Comment résoudre ce genre de problème?

47
demandé sur Heinz 2015-01-05 20:15:04

2 réponses

Vous ne pouvez pas le résoudre. Simplement answer1.sum()==0, et vous ne pouvez pas effectuer une division par zéro.

Cela se produit parce que answer1 est l'exponentielle de 2 très grands nombres négatifs, de sorte que le résultat est arrondi à zéro.

nan est retourné dans ce cas en raison de la division par zéro.

Maintenant, pour résoudre votre problème, vous pouvez:

  • optez pour une bibliothèque pour les mathématiques de haute précision, comme mpmath . Mais c'est moins amusant.
  • comme alternative à un plus grande arme, faites une manipulation mathématique, comme détaillé ci-dessous.
  • optez pour une fonction scipy/numpy sur mesure qui fait exactement ce que vous voulez! Consultez la réponse @ Warren Weckesser.

Ici, j'explique comment faire une manipulation mathématique qui aide à résoudre ce problème. Nous avons cela pour le numérateur:

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)))

Où au-dessus de x=3* 1089 et y=3* 1093. Maintenant, l'argument de cette exponentielle est

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

Pour le dénominateur, vous pouvez procéder de la même manière mais obtenir que log(1+exp(-z+k)) est déjà arrondi à 0, de sorte que l'argument de la fonction exponentielle au dénominateur est simplement arrondi à -z=-3000. Vous avez alors que votre résultat est

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

Qui est déjà extrêmement proche du résultat que vous obtiendriez si vous ne conserviez que les 2 termes principaux (c'est-à-dire le premier nombre 1089 dans le numérateur et le premier nombre 1000 au dénominateur):

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

Pour le bien de cela, voyons à quel point nous sommes proches de la solution de Wolfram alpha (lien):

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

La différence entre ce nombre et l'exposant ci-dessus est +1.7053025658242404e-13, donc l'approximation que nous avons faite au dénominateur était bonne.

Le résultat final est

'exp(-266.99999385580668) = 1.1050349147204485e-116

De wolfram alpha est (lien)

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

Et encore une fois, il est sûr d'utiliser numpy ici aussi.

49
répondu gg349 2015-01-06 07:50:28

Vous pouvez utiliser np.logaddexp (qui implémente l'idée dans la réponse de @gg349):

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

, Ou vous pouvez utiliser 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
11
répondu Warren Weckesser 2015-01-05 21:12:38