2 votes

Vectoriser une simple boucle for en numpy

Je suis assez nouveau dans numpy et j'essaie de vectoriser une simple boucle for pour des raisons de performance, mais je n'arrive pas à trouver de solution. J'ai un tableau numpy avec des mots uniques et pour chacun de ces mots j'ai besoin du nombre de fois qu'ils apparaissent dans un autre tableau numpy, appelé array_to_compare. Le nombre est passé à un troisième tableau numpy, qui a la même forme que le tableau de mots uniques. Voici le code qui contient la boucle for :

import numpy as np

unique_words = np.array(['a', 'b', 'c', 'd'])
array_to_compare = np.array(['a', 'b', 'a', 'd'])
vector_array = np.zeros(len(unique_words))

for word in np.nditer(unique_words):
    counter = np.count_nonzero(array_to_compare == word)
    vector_array[np.where(unique_words == word)] = counter

vector_array = [2. 1. 0. 1.]    #the desired output

J'ai essayé avec np.where et np.isin, mais je n'ai pas obtenu le résultat souhaité. Je suis reconnaissant pour toute aide !

2voto

Daniel Lenz Points 1187

J'utiliserais probablement un Counter et une compréhension de liste pour résoudre ce problème :

In [1]: import numpy as np
   ...:
   ...: unique_words = np.array(['a', 'b', 'c', 'd'])
   ...: array_to_compare = np.array(['a', 'b', 'a', 'd'])

In [2]: from collections import Counter

In [3]: counter = Counter(array_to_compare)

In [4]: counter
Out[4]: Counter({'a': 2, 'b': 1, 'd': 1})

In [5]: vector_array = np.array([counter[key] for key in unique_words])

In [6]: vector_array
Out[6]: array([2, 1, 0, 1])

Assemblage de la Counter se fait en temps linéaire et en itérant à travers vos unique_words est également linéaire.

1voto

Kraigolas Points 884

Similaire à la réponse de @DanielLenz, mais en utilisant np.unique pour créer un dict :

import numpy as np
unique_words = np.array(['a', 'b', 'c', 'd'])
array_to_compare = np.array(['a', 'b', 'a', 'd'])
counts = dict(zip(*np.unique(array_to_compare, return_counts=True)))
result = np.array([counts[word] if word in counts else 0 for word in unique_words])
[2 1 0 1]

1voto

hpaulj Points 6132

A numpy comparaison des valeurs d'un tableau à l'aide de broadcasting :

In [76]: unique_words[:,None]==array_to_compare
Out[76]: 
array([[ True, False,  True, False],
       [False,  True, False, False],
       [False, False, False, False],
       [False, False, False,  True]])
In [77]: (unique_words[:,None]==array_to_compare).sum(1)
Out[77]: array([2, 1, 0, 1])

In [78]: timeit (unique_words[:,None]==array_to_compare).sum(1)
9.5 µs ± 2.79 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Mais Counter est également un bon choix :

In [72]: %%timeit
    ...: c=Counter(array_to_compare)
    ...: [c[key] for key in unique_words]
12.7 µs ± 30.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Votre utilisation de count_nonzero peut être améliorée avec

In [73]: %%timeit
    ...: words=unique_words.tolist()
    ...: vector_array = np.zeros(len(words))
    ...: for i,word in enumerate(words):
    ...:     counter = np.count_nonzero(array_to_compare == word)
    ...:     vector_array[i] = counter
    ...: 
23.4 µs ± 505 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

L'itération sur les listes est plus rapide que sur les tableaux ( nditer n'apporte pas grand-chose). Et enumerate nous permet de sauter l'étape where test.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X