Cómo devolver 0 con dividir por cero

100

Estoy tratando de realizar una división inteligente de elementos en Python, pero si se encuentra un cero, necesito que el cociente sea cero.

Por ejemplo:

array1 = np.array([0, 1, 2])
array2 = np.array([0, 1, 1])

array1 / array2 # should be np.array([0, 1, 2])

Siempre podría usar un bucle for a través de mis datos, pero para utilizar realmente las optimizaciones de numpy, necesito que la función de división devuelva 0 al dividir por cero errores en lugar de ignorar el error.

A menos que me esté perdiendo algo, no parece que numpy.seterr () pueda devolver valores en caso de errores. ¿Alguien tiene alguna otra sugerencia sobre cómo podría obtener lo mejor de numpy mientras establezco mi propia división por manejo de cero errores?

hlin117
fuente
En mi versión de Python (Python 2.7.11 | Continuum Analytics, Inc.) ese es exactamente el resultado que obtienes. Con una advertencia.
Ramon Martinez
La respuesta correcta más
concisa

Respuestas:

182

En numpy v1.7 +, puede aprovechar la opción "where" para ufuncs . Puede hacer cosas en una línea y no tiene que lidiar con el administrador de contexto de estado de error.

>>> a = np.array([-1, 0, 1, 2, 3], dtype=float)
>>> b = np.array([ 0, 0, 0, 2, 2], dtype=float)

# If you don't pass `out` the indices where (b == 0) will be uninitialized!
>>> c = np.divide(a, b, out=np.zeros_like(a), where=b!=0)
>>> print(c)
[ 0.   0.   0.   1.   1.5]

En este caso, realiza el cálculo de la división en cualquier lugar 'donde' b no sea igual a cero. Cuando b es igual a cero, entonces permanece sin cambios con respecto al valor que le dio originalmente en el argumento 'out'.

DStauffman
fuente
3
Si ay / o bpueden ser matrices de números enteros, entonces es el mismo concepto, solo necesita establecer explícitamente el tipo de salida correcto:c = np.divide(a, b, out=np.zeros(a.shape, dtype=float), where=b!=0)
DStauffman
out=np.zeros_like(a)es fundamental, como se indica en la línea comentada.
Jonatan Öström
1
Si lo uso np.divide(a, b, out=np.zeros_like(a), where=b!=0), obtengo el error Assigning to function call which doesn't return. Lo extraño es que lo uso dos veces y el error solo aparece una vez.
Jelmer Mulder
46

Sobre la base de la respuesta de @Franck Dernoncourt, arreglando -1 / 0:

def div0( a, b ):
    """ ignore / 0, div0( [-1, 0, 1], 0 ) -> [0, 0, 0] """
    with np.errstate(divide='ignore', invalid='ignore'):
        c = np.true_divide( a, b )
        c[ ~ np.isfinite( c )] = 0  # -inf inf NaN
    return c

div0( [-1, 0, 1], 0 )
array([0, 0, 0])
denis
fuente
Gracias, ni siquiera atrapé ese error con el código de @Frank Dernoncourt.
hlin117
Hola, estoy tratando de hacer una matriz matemática y quiero que 0/0 resulte en 0, pero también quiero ignorar np.NaN en mis cálculos. ¿Funcionará esto para eso? Además, estoy tratando de entender. ¿Qué hace c [~ np.isfinite (c)] = 0? Nunca he usado ~ en Python. ¿Para qué sirve? Gracias
user20408
@ user20408, ~se invierte Truey Falseen arreglos numpy: print ~ np.array([ True, False, False ]). c[ ~ np.isfinite( c )] = 0significa: encuentra las posiciones donde ces finito, inviértelas para que NO sean finitas ~y establezca los valores no finitos en 0. Consulte también stackoverflow.com/search?q=[numpy]+"boolean+indexing "
denis
43

Aprovechando las otras respuestas y mejorando:

Código:

import numpy as np

a = np.array([0,0,1,1,2], dtype='float')
b = np.array([0,1,0,1,3], dtype='float')

with np.errstate(divide='ignore', invalid='ignore'):
    c = np.true_divide(a,b)
    c[c == np.inf] = 0
    c = np.nan_to_num(c)

print('c: {0}'.format(c))

Salida:

c: [ 0.          0.          0.          1.          0.66666667]
Franck Dernoncourt
fuente
2
Buen trabajo tanto de comprobación 0/0como de 1/0errores.
hlin117
Probé su método con las matrices de ejemplo dadas en la respuesta de DStauffman y parece resultar en números muy altos en lugar de np.inf, que permanece en el resultado final
Gal Avineri
Desalentaría este enfoque. Si contiene ao bcontiene NaN, su solución de repente cede 0como resultado. Esto puede ocultar fácilmente errores en su código y es absolutamente inesperado.
DerWeh
De acuerdo con el reciente manual de numpy, nan_to_num () toma valores para sustituir inf positivo e inf negativo también. numpy.nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None)es la firma.
Craig Hicks
18

One-liner (lanza advertencia)

np.nan_to_num(array1 / array2)
Ulf Aslak
fuente
13

Intente hacerlo en dos pasos. División primero, luego reemplace.

with numpy.errstate(divide='ignore'):
    result = numerator / denominator
    result[denominator == 0] = 0

La numpy.errstatelínea es opcional y solo evita que numpy le diga sobre el "error" de dividir por cero, ya que usted ya tiene la intención de hacerlo y está manejando ese caso.

Pi Marillion
fuente
5
Probablemente debería realizar la división en el contextonp.errstate(divide='ignore'):
Warren Weckesser
@WarrenWeckesser Punto justo. Edité la respuesta para incluir el contexto. divide='warn'también podría ser útil si desea que se le notifique.
Pi Marillion
2

También puede reemplazar basado en inf, solo si los tipos de matriz son flotantes, según esta respuesta :

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> c = a / b
>>> c
array([ inf,   2.,   1.])
>>> c[c == np.inf] = 0
>>> c
array([ 0.,  2.,  1.])
atomh33ls
fuente
0

Una respuesta que encontré al buscar una pregunta relacionada fue manipular la salida en función de si el denominador era cero o no.

Supongamos que arrayAy arrayBse han inicializado, pero arrayBtiene algunos ceros. Podríamos hacer lo siguiente si queremos calcular de arrayC = arrayA / arrayBforma segura.

En este caso, siempre que tengo una división por cero en una de las celdas, configuro la celda para que sea igual a myOwnValue, que en este caso sería cero

myOwnValue = 0
arrayC = np.zeros(arrayA.shape())
indNonZeros = np.where(arrayB != 0)
indZeros = np.where(arrayB = 0)

# division in two steps: first with nonzero cells, and then zero cells
arrayC[indNonZeros] = arrayA[indNonZeros] / arrayB[indNonZeros]
arrayC[indZeros] = myOwnValue # Look at footnote

Nota al pie: en retrospectiva, esta línea es innecesaria de todos modos, ya que arrayC[i]se instancia a cero. Pero si fuera el caso myOwnValue != 0, esta operación haría algo.

hlin117
fuente
0

Otra solución que vale la pena mencionar:

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> b_inv = np.array([1/i if i!=0 else 0 for i in b])
>>> a*b_inv
array([0., 2., 1.])
T. Gwen
fuente