J'ai une question concernant numpy.

Supposons que j'ai une fonction

def calcSomething(A,t):

    return t*A

Je veux passer A un tableau numpy constant, par exemple A=np.array([1.0,2.0]) et t comme points équidistants, par ex. t = np.linspace(0.0,1.0,10). Maintenant, quand j'exécute

x = calcSomething(A,t)

Je souhaite obtenir x[0] = [0,0], x[1] = [.1,.2], ..., x[9] = [1.0,2.0]. Existe-t-il un moyen simple d'y parvenir ou dois-je l'implémenter en utilisant des boucles for et en ajoutant?

Edit: 'calcSomething (): est juste un espace réservé pour une fonction plus complexe.

Par exemple. y=np.sin(t) donne y[i]=np.sin(t[i]). C'est ce que je veux réaliser pour une fonction générique.

Merci d'avance!

0
J.Doe 11 avril 2018 à 18:06

3 réponses

Meilleure réponse

Dans la mesure du possible, en utilisant des fonctions et des opérateurs numpy qui opèrent sur l'ensemble des tableaux, et faites le broadcasting nécessaire pour vous:

In [24]: A = np.array([1.0,2.0]); t = np.linspace(0.0, 1.0, 10)
In [25]: x = t[:,None] * A[None,:]
In [26]: x.shape
Out[26]: (10, 2)
In [27]: x[:3,:]
Out[27]: 
array([[0.        , 0.        ],
       [0.11111111, 0.22222222],
       [0.22222222, 0.44444444]])
In [28]: np.sin(t)
Out[28]: 
   array([0.        , 0.11088263, 0.22039774, 0.3271947 , 0.42995636,
          0.52741539, 0.6183698 , 0.70169788, 0.77637192, 0.84147098])

Si vous avez une fonction qui ne fonctionne qu'avec des entrées scalaires, vous pouvez utiliser np.vectorize pour lui fournir ces valeurs - à partir de tableaux diffusés:

In [30]: def foo(A,t):
    ...:     return t*A
    ...: 
In [31]: f = np.vectorize(foo, otypes=[float])
In [32]: f(A[None,:], t[:,None])
Out[32]: 
array([[0.        , 0.        ],
       [0.11111111, 0.22222222],
       [0.22222222, 0.44444444],
       ....
       [1.        , 2.        ]])

vectorize est une fonction pratique; il ne promet pas de vitesse. Comparez son heure avec In[25]:

In [33]: timeit f(A[None,:], t[:,None])
41.3 µs ± 48.5 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [34]: timeit x = t[:,None] * A[None,:]
5.41 µs ± 8.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

8x plus lent.

Ou avec la fonction scalaire math.sin:

In [35]: import math
In [36]: g = np.vectorize(math.sin)
...
In [39]: timeit g(t)
39 µs ± 72.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [40]: timeit np.sin(t)
1.4 µs ± 2.72 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Appelé de manière itérative, math.sin est plus rapide que np.sin, mais np.sin est beaucoup plus rapide quand on lui donne un tableau entier.

La différence de base est que vectorize, comme les boucles explicites, itère en Python interprété et appelle votre fonction une fois pour chaque élément de sortie. np.sin et le tableau * sont en train d'itérer, mais en code compilé.

Il existe différentes façons d'itérer en Python et numpy, mais elles vous donnent rarement plus d'une accélération 2x.

Il existe des outils pour déplacer des calculs vers du code compilé, tels que cython et numba. Recherchez ces balises pour obtenir des idées sur leur utilisation.

1
hpaulj 11 avril 2018 à 23:55

Utilisez simplement None pour spécifier un nouvel axe:

A[None, :] * t[:, None] 
0
ben thorne 11 avril 2018 à 15:15

Vous pouvez utiliser la multiplication matricielle:

>>> A = np.array([1.0,2.0])
>>> t = np.linspace(0.0,1.0,10)
>>> np.matmul(t.reshape(10, 1), A.reshape(1, 2))
array([[0.        , 0.        ],
       [0.11111111, 0.22222222],
       [0.22222222, 0.44444444],
       [0.33333333, 0.66666667],
       [0.44444444, 0.88888889],
       [0.55555556, 1.11111111],
       [0.66666667, 1.33333333],
       [0.77777778, 1.55555556],
       [0.88888889, 1.77777778],
       [1.        , 2.        ]])
0
ducminh 11 avril 2018 à 15:13