Como retornar 0 com divisão por zero

100

Estou tentando realizar uma divisão inteligente de elemento em python, mas se um zero for encontrado, preciso que o quociente seja apenas zero.

Por exemplo:

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

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

Eu sempre poderia usar um loop for em meus dados, mas para realmente utilizar as otimizações de numpy, preciso que a função divide retorne 0 sobre erros de divisão por zero em vez de ignorar o erro.

A menos que esteja faltando alguma coisa, não parece que numpy.seterr () pode retornar valores em caso de erros. Alguém tem alguma outra sugestão sobre como eu poderia tirar o melhor proveito do numpy enquanto definia minha própria divisão por zero de tratamento de erros?

hlin117
fonte
Na minha versão python (Python 2.7.11 | Continuum Analytics, Inc.), essa é exatamente a saída que você obtém. Com um aviso.
Ramon Martinez
A resposta correta mais sucinta é stackoverflow.com/a/37977222/2116338
mrplants

Respostas:

181

No numpy v1.7 +, você pode tirar proveito da opção "onde" para ufuncs . Você pode fazer as coisas em uma linha e não precisa lidar com o gerenciador de contexto errstate.

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

Nesse caso, ele faz o cálculo de divisão em qualquer lugar 'onde' b não é igual a zero. Quando b for igual a zero, ele permanecerá inalterado de qualquer valor que você forneceu originalmente no argumento 'out'.

DStauffman
fonte
3
E se a e / ou bpuderem ser matrizes inteiras, então é o mesmo conceito, você só precisa definir explicitamente o tipo de saída correto:c = np.divide(a, b, out=np.zeros(a.shape, dtype=float), where=b!=0)
DStauffman
out=np.zeros_like(a)é crítica, conforme indicado na linha comentada.
Jonatan Öström
1
Se eu usar np.divide(a, b, out=np.zeros_like(a), where=b!=0), recebo o erro Assigning to function call which doesn't return. O estranho é que eu uso duas vezes e o erro só aparece uma vez.
Jelmer Mulder
45

Com base na resposta de @Franck Dernoncourt, corrigindo -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
fonte
Obrigado, eu nem peguei aquele bug com o código de @Frank Dernoncourt.
hlin117
Olá, estou tentando fazer matemática de matriz e quero que 0/0 resulte em 0, mas também desejo ignorar np.NaN em meus cálculos. Isso funcionará para isso? Além disso, estou tentando entender. O que c [~ np.isfinite (c)] = 0 faz? Eu nunca usei ~ em python. Para que serve? Obrigado
user20408
@ user20408, ~inverte Truee Falseem matrizes numpy: print ~ np.array([ True, False, False ]). c[ ~ np.isfinite( c )] = 0significa: encontre as posições onde cé finito, inverta-os para NÃO finito com ~e defina os valores não finitos para 0. Veja também stackoverflow.com/search?q=[numpy]+"boolean+indexing "
denis
42

Com base nas outras respostas e melhorando:

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

Resultado:

c: [ 0.          0.          0.          1.          0.66666667]
Franck Dernoncourt
fonte
2
Bom trabalho para verificação 0/0e também para 1/0erros.
hlin117 de
Eu tentei seu método com os arrays de exemplo fornecidos na resposta de DStauffman e parece resultar em números muito altos em vez de np.inf, que permanece no resultado final
Gal Avineri
Eu desencorajaria essa abordagem. Se aou bcontiver NaN, sua solução repentinamente dará 0um resultado. Isso pode facilmente ocultar erros em seu código e é absolutamente inesperado.
DerWeh
De acordo com o manual numpy recente, nan_to_num () usa valores para substituir inf positivo e inf negativo também. numpy.nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None)é a assinatura.
Craig Hicks
18

One-liner (lança um aviso)

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

Tente fazer isso em duas etapas. Divida primeiro, depois substitua.

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

A numpy.errstatelinha é opcional e apenas evita que numpy informe sobre o "erro" de dividir por zero, uma vez que você já pretende fazer isso e cuidar desse caso.

Pi Marillion
fonte
5
Você provavelmente deve realizar a divisão no contextonp.errstate(divide='ignore'):
Warren Weckesser
@WarrenWeckesser Ponto justo. Eu editei a resposta para incluir o contexto. divide='warn'também pode ser útil se ele / ela ainda quiser ser notificado.
Pi Marillion,
2

Você também pode substituir com base em inf, apenas se os tipos da matriz forem flutuantes, de acordo com esta resposta :

>>> 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
fonte
0

Uma resposta que encontrei ao pesquisar uma questão relacionada foi manipular a saída com base no denominador zero ou não.

Suponha arrayAe arrayBforam inicializados, mas arrayBtem alguns zeros. Poderíamos fazer o seguinte se quisermos computar arrayC = arrayA / arrayBcom segurança.

Neste caso, sempre que tenho uma divisão por zero em uma das células, eu defino a célula para ser igual a myOwnValue, que neste caso seria zero

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 de rodapé: Em retrospecto, esta linha é desnecessária de qualquer maneira, pois arrayC[i]é instanciada em zero. Mas se fosse esse o caso myOwnValue != 0, essa operação faria alguma coisa.

hlin117
fonte
0

Outra solução que vale a 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
fonte