web-dev-qa-db-fra.com

Application de fonction sur la ligne / colonne de matrice de numpy

J'utilise Numpy pour stocker des données dans des matrices. En venant de l'arrière-plan R, il y avait un moyen extrêmement simple d'appliquer une fonction sur une ligne/des colonnes ou les deux d'une matrice.

Existe-t-il quelque chose de similaire pour la combinaison python/numpy? Ce n'est pas un problème d'écrire ma propre petite implémentation, mais il me semble que la plupart des versions que je propose seront beaucoup moins efficaces/plus gourmandes en mémoire que n'importe quelle implémentation existante.

Je voudrais éviter de copier de la matrice numpy vers une variable locale, etc., est-ce possible?

Les fonctions que j'essaie d'implémenter sont principalement des comparaisons simples (par exemple, combien d'éléments d'une certaine colonne sont plus petits que le nombre x ou combien d'entre eux ont une valeur absolue supérieure à y).

36
petr

Presque toutes les fonctions numpy fonctionnent sur des tableaux entiers et/ou peuvent être commandées sur un axe particulier (ligne ou colonne).

Tant que vous pouvez définir votre fonction en termes de fonctions numpy agissant sur des tableaux numpy ou des tranches de tableau, votre fonction fonctionnera automatiquement sur des tableaux entiers, des lignes ou des colonnes.

Il peut être plus utile de demander comment mettre en œuvre une fonction particulière pour obtenir des conseils plus concrets.


Numpy fournit np.vectorize et np.frompyfunc pour transformer Python fonctions qui opèrent sur des nombres en fonctions qui opèrent sur des tableaux numpy.

Par exemple,

def myfunc(a,b):
    if (a>b): return a
    else: return b
vecfunc = np.vectorize(myfunc)
result=vecfunc([[1,2,3],[5,6,9]],[7,4,5])
print(result)
# [[7 4 5]
#  [7 6 9]]

(Les éléments du premier tableau sont remplacés par l'élément correspondant du deuxième tableau lorsque le second est plus grand.)

Mais ne vous excitez pas trop; np.vectorize et np.frompyfunc sont juste du sucre syntaxique . Ils ne rendent pas votre code plus rapide. Si votre fonction sous-jacente Python fonctionne sur une valeur à la fois, alors np.vectorize le nourrira un élément à la fois, et toute l'opération va être assez lente (par rapport à l'utilisation d'une fonction numpy qui appelle une implémentation C ou Fortran sous-jacente).


Pour compter le nombre d'éléments de la colonne x qui sont plus petits qu'un nombre y, vous pouvez utiliser une expression telle que:

(array['x']<y).sum()

Par exemple:

import numpy as np
array=np.arange(6).view([('x',np.int),('y',np.int)])
print(array)
# [(0, 1) (2, 3) (4, 5)]

print(array['x'])
# [0 2 4]

print(array['x']<3)
# [ True  True False]

print((array['x']<3).sum())
# 2
43
unutbu

La sélection d'éléments dans un tableau NumPy en fonction d'une ou de plusieurs conditions est simple en utilisant la syntaxe magnifiquement dense de NumPy:

>>> import numpy as NP
>>> # generate a matrix to demo the code
>>> A = NP.random.randint(0, 10, 40).reshape(8, 5)
>>> A
  array([[6, 7, 6, 4, 8],
         [7, 3, 7, 9, 9],
         [4, 2, 5, 9, 8],
         [3, 8, 2, 6, 3],
         [2, 1, 8, 0, 0],
         [8, 3, 9, 4, 8],
         [3, 3, 9, 8, 4],
         [5, 4, 8, 3, 0]])


( combien d'éléments de la colonne 2 sont supérieurs à 6?

>>> ndx = A[:,1] > 6
>>> ndx
      array([False,  True, False, False,  True,  True,  True,  True], dtype=bool)
>>> NP.sum(ndx)
      5


( combien d'éléments dans la dernière colonne de A ont une valeur absolue supérieure à 3?

>>> A = NP.random.randint(-4, 4, 40).reshape(8, 5)
>>> A
  array([[-4, -1,  2,  0,  3],
         [-4, -1, -1, -1,  1],
         [-1, -2,  2, -2,  3],
         [ 1, -4, -1,  0,  0],
         [-4,  3, -3,  3, -1],
         [ 3,  0, -4, -1, -3],
         [ 3, -4,  0, -3, -2],
         [ 3, -4, -4, -4,  1]])

>>> ndx = NP.abs(A[:,-1]) > 3
>>> NP.sum(ndx)
      0


combien d'éléments dans les deux premières lignes de A sont supérieurs ou égaux à 2?

>>> ndx = A[:2,:] >= 2
>>> NP.sum(ndx.ravel())    # 'ravel' just flattens ndx, which is originally 2D (2x5)
      2

La syntaxe d'indexation de NumPy est assez proche des R; étant donné votre maîtrise de R, voici les principales différences entre R et NumPy dans ce contexte:

NumPy ( les indices sont à base zéro , dans R, l'indexation commence par 1

NumPy (comme Python) vous permet de indexer de droite à gauche en utilisant des indices négatifs - par exemple,

# to get the last column in A
A[:, -1], 

# to get the penultimate column in A
A[:, -2] 

# this is a big deal, because in R, the equivalent expresson is:
A[, dim(A)[0]-2]

NumPy utilise deux points ":" notation pour indiquer "non coupé" , par exemple, dans R, pour obtenir les trois premières lignes dans A, vous utiliseriez, A [1: 3,]. Dans NumPy, vous utiliseriez A [0: 2,:] (dans NumPy, le "0" n'est pas nécessaire, en fait il est préférable d'utiliser A [: 2,:]

14
doug

Je viens également d'un milieu plus R, et je suis tombé sur le manque d'une application plus polyvalente qui pourrait prendre de courtes fonctions personnalisées. J'ai vu les forums suggérant d'utiliser des fonctions numpy de base car beaucoup d'entre eux gèrent des tableaux. Cependant, je suis devenu confus sur la façon dont les fonctions numpy "natives" gèrent les tableaux (parfois 0 est rangé et 1 colonne, parfois le contraire).

Ma solution personnelle à des fonctions plus flexibles avec apply_along_axis a été de les combiner avec les fonctions lambda implicites disponibles en python. Les fonctions lambda devraient être très faciles à comprendre pour les personnes à l'esprit R qui utilisent un style de programmation plus fonctionnel, comme dans les fonctions R s'appliquent, sapply, lapply, etc.

Ainsi, par exemple, je voulais appliquer la normalisation des variables dans une matrice. Typiquement, dans R, il y a une fonction pour cela (échelle) mais vous pouvez également le construire facilement avec apply:

(Code R)

apply(Mat,2,function(x) (x-mean(x))/sd(x) ) 

Vous voyez comment le corps de la fonction à l'intérieur de apply (x-mean (x))/sd (x) est le bit que nous ne pouvons pas taper directement pour le python apply_along_axis. Avec lambda c'est facile à mettre en œuvre POUR UN SEUL ENSEMBLE DE VALEURS, donc:

(Python)

import numpy as np
vec=np.random.randint(1,10,10)  # some random data vector of integers

(lambda x: (x-np.mean(x))/np.std(x)  )(vec)

Ensuite, tout ce dont nous avons besoin est de le brancher dans le python apply et de passer le tableau d'intérêt via apply_along_axis

Mat=np.random.randint(1,10,3*4).reshape((3,4))  # some random data vector
np.apply_along_axis(lambda x: (x-np.mean(x))/np.std(x),0,Mat )

De toute évidence, la fonction lambda pourrait être implémentée en tant que fonction distincte, mais je suppose que le but est d'utiliser des fonctions plutôt petites contenues dans la ligne où s'appliquent les origines.

J'espère que tu trouves cela utile !

7
markcelo

Pandas est très utile pour cela. Par exemple, DataFrame.apply () et groupby's apply () devrait vous aider.

3
Peter Battaglia