numpy.array ha un comportamento bizzarro con l'operatore / =?

Sep 11 2020

Sto cercando di normalizzare un array di numeri nell'intervallo (0, 1] in modo da poterli utilizzare come pesi per una random.choice () ponderata, quindi ho inserito questa riga:

# weights is a nonzero numpy.array
weights /= weights.max()

Tuttavia, Pycharm ha detto che c'è un parametro non riempito per max()function ( Parameter 'initial' unfilled). Ho provato questo in REPL con l'operatore / = e con la divisione "regolare" ( a = a / b) e ho ottenuto risultati diversi per entrambi e un errore diverso da quello che Pycharm pensava:

>>> a = numpy.array([1,2,3])
>>> a.max()
3
>>> a /= a.max()
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    a /= a.max()
TypeError: No loop matching the specified signature and casting was found for ufunc true_divide
>>> a = a/a.max()
>>> a
array([0.33333333, 0.66666667, 1.        ])

Mi sono anche reso conto che per un casuale ponderato, i pesi dovevano sommarsi a uno piuttosto che essere normalizzati ad esso. Ma dividendolo per la somma ha prodotto lo stesso identico TypeErrorutilizzo dell'operazione / = (ma Pycharm pensava che andasse bene):

>>> a = numpy.array([1,2,3])
>>> sum(a)
6
>>> a
array([1, 2, 3])
>>> a /= sum(a)
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    a /= sum(a)
TypeError: No loop matching the specified signature and casting was found for ufunc true_divide
>>> a = a / sum(a)
>>> a
array([0.16666667, 0.33333333, 0.5       ])

Cosa ho trovato qui? È un bug bizzarro in Numpy o l'operatore / = ha un uso diverso o qualcosa del genere? So che usano __truediv__e __itruediv__ma non riesco a capire perché uno ha un problema e l'altro no. Ho confermato questo comportamento con l'ultima versione di Numpy da pip (1.19.2 su Windows x64).

Risposte

1 Fahim Sep 11 2020 at 23:24

È perché a numpy interessa il tipo. Quando applichi la divisione, stai passando inta float. Ma numpy non te lo lascerà fare! Ecco perché i tuoi valori dovrebbero essere già presenti float. Prova questo:

>>> a = np.array([1.0,2.0,3.0])
>>> a /= sum(a)
>>> a
array([0.16666667, 0.33333333, 0.5       ])

Ma perché l'altro ha funzionato? È perché non è un'operazione "sul posto". Quindi viene creata una nuova posizione di memoria. Nuova variabile, nuovo tipo, quindi a numpy non interessa qui.