Différence entre les méthodes map, applymap et apply dans Pandas

468

Pouvez-vous me dire quand utiliser ces méthodes de vectorisation avec des exemples basiques?

Je vois que mapc'est une Seriesméthode alors que les autres sont des DataFrameméthodes. Cependant, je me suis trompé applyet les applymapméthodes. Pourquoi avons-nous deux méthodes pour appliquer une fonction à un DataFrame? Encore une fois, des exemples simples qui illustrent l'utilisation seraient formidables!

marillion
la source
5
Corrigez-moi si je me trompe, mais je pense que ces fonctions ne sont pas des méthodes vectorielles car elles impliquent toutes une boucle sur les éléments sur lesquels elles sont appliquées.
Tanguy
1
Je ne vois pas de différence ici: gist.github.com/MartinThoma/e320cbb937afb4ff766f75988f1c65e6
Martin Thoma

Réponses:

534

Directement tiré du livre Python for Data Analysis de Wes McKinney , p. 132 (j'ai fortement recommandé ce livre):

Une autre opération fréquente consiste à appliquer une fonction sur des tableaux 1D à chaque colonne ou ligne. La méthode apply de DataFrame fait exactement cela:

In [116]: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [117]: frame
Out[117]: 
               b         d         e
Utah   -0.029638  1.081563  1.280300
Ohio    0.647747  0.831136 -1.549481
Texas   0.513416 -0.884417  0.195343
Oregon -0.485454 -0.477388 -0.309548

In [118]: f = lambda x: x.max() - x.min()

In [119]: frame.apply(f)
Out[119]: 
b    1.133201
d    1.965980
e    2.829781
dtype: float64

La plupart des statistiques de tableau les plus courantes (comme la somme et la moyenne) sont des méthodes DataFrame, il n'est donc pas nécessaire d'utiliser apply.

Les fonctions Python par élément peuvent également être utilisées. Supposons que vous vouliez calculer une chaîne formatée à partir de chaque valeur à virgule flottante dans le cadre. Vous pouvez le faire avec applymap:

In [120]: format = lambda x: '%.2f' % x

In [121]: frame.applymap(format)
Out[121]: 
            b      d      e
Utah    -0.03   1.08   1.28
Ohio     0.65   0.83  -1.55
Texas    0.51  -0.88   0.20
Oregon  -0.49  -0.48  -0.31

La raison du nom applymap est que Series a une méthode de carte pour appliquer une fonction élément par élément:

In [122]: frame['e'].map(format)
Out[122]: 
Utah       1.28
Ohio      -1.55
Texas      0.20
Oregon    -0.31
Name: e, dtype: object

En résumé, applyfonctionne sur une base ligne / colonne d'un DataFrame, applymapfonctionne élément par élément sur un DataFrame et mapfonctionne élément par élément sur une série.

jeremiahbuddha
la source
31
à proprement parler, applymap est mis en oeuvre en interne par l' intermédiaire d' appliquer avec un peu de récapitulation paramètre de fonction sur passé ( à peu prés exprimant le remplacement funcde lambda x: [func(y) for y in x], et l' application par colonne)
alko
5
Merci pour l'explication. Depuis mapet les applymapdeux fonctionnent par élément, je m'attendrais à une seule méthode (soit mapou applymap) qui fonctionnerait à la fois pour une série et un DataFrame. Il y a probablement d'autres considérations de conception, et Wes McKinney a décidé de proposer deux méthodes différentes.
marillion
2
C'est à la page 129 de mon exemplaire pour une raison quelconque. Il n'y a pas d'étiquette pour la deuxième édition ou quoi que ce soit.
Jody
1
Y a-t-il un moyen de faire applymapavec la groupbyfonction dans les pandas?
everestial007
Comment appliquer une fonction sur des données groupées en colonnes?
hhh
84

En comparant map, applymapet : Questions de contexteapply

Première différence majeure: DÉFINITION

  • map est défini sur la série UNIQUEMENT
  • applymap est défini sur DataFrames UNIQUEMENT
  • apply est défini sur LES DEUX

Deuxième différence majeure: ARGUMENT D'ENTRÉE

  • mapaccepte dicts Series, ou appelable
  • applymapet applyaccepter les callables uniquement

Troisième différence majeure: COMPORTEMENT

  • map est élément par élément pour la série
  • applymap est élément par élément pour les DataFrames
  • applyfonctionne également par éléments mais convient aux opérations et agrégations plus complexes. Le comportement et la valeur de retour dépendent de la fonction.

Quatrième différence majeure (la plus importante): CAS D'UTILISATION

  • map est destiné à mapper des valeurs d'un domaine à un autre, il est donc optimisé pour les performances (par exemple, df['A'].map({1:'a', 2:'b', 3:'c'}) )
  • applymap est bon pour les transformations élément par élément sur plusieurs lignes / colonnes (par exemple, df[['A', 'B', 'C']].applymap(str.strip) )
  • applyest pour appliquer une fonction qui ne peut pas être vectorisée (par exemple, df['sentences'].apply(nltk.sent_tokenize))

Résumer

entrez la description de l'image ici

Notes de bas de page

  1. mapune fois passé, un dictionnaire / série mappera les éléments en fonction des clés de ce dictionnaire / série. Les valeurs manquantes seront enregistrées sous forme de NaN dans la sortie.
  2. applymapdans les versions plus récentes a été optimisé pour certaines opérations. Vous trouverez un applymappeu plus rapide que applydans certains cas. Ma suggestion est de les tester tous les deux et d'utiliser ce qui fonctionne mieux.

  3. mapest optimisé pour les mappages élémentaires et la transformation. Les opérations qui impliquent des dictionnaires ou des séries permettront aux pandas d'utiliser des chemins de code plus rapides pour de meilleures performances.

  4. Series.applyrenvoie un scalaire pour l'agrégation des opérations, sinon Series. De même pour DataFrame.apply. Notez que applya également fastpaths lorsqu'elle est appelée avec certaines fonctions numpy telles que mean, sum, etc.
cs95
la source
70

Il y a d'excellentes informations dans ces réponses, mais j'ajoute les miennes pour résumer clairement les méthodes qui fonctionnent par tableau plutôt que par élément. jeremiahbuddha a principalement fait cela mais n'a pas mentionné Series.apply. Je n'ai pas le représentant pour commenter.

  • DataFrame.apply fonctionne sur des lignes ou des colonnes entières à la fois.

  • DataFrame.applymap, Series.applyEt Series.mapopérer sur un élément à la fois.

Il y a beaucoup de chevauchement entre les capacités de Series.applyet Series.map, ce qui signifie que l'un ou l'autre fonctionnera dans la plupart des cas. Ils ont cependant quelques légères différences, dont certaines ont été discutées dans la réponse d'osa.

MarredCheese
la source
38

En plus des autres réponses, Seriesil y a aussi une carte et une application .

Appliquer peut faire un DataFrame d'une série ; cependant, map ne mettra qu'une série dans chaque cellule d'une autre série, ce qui n'est probablement pas ce que vous voulez.

In [40]: p=pd.Series([1,2,3])
In [41]: p
Out[31]:
0    1
1    2
2    3
dtype: int64

In [42]: p.apply(lambda x: pd.Series([x, x]))
Out[42]: 
   0  1
0  1  1
1  2  2
2  3  3

In [43]: p.map(lambda x: pd.Series([x, x]))
Out[43]: 
0    0    1
1    1
dtype: int64
1    0    2
1    2
dtype: int64
2    0    3
1    3
dtype: int64
dtype: object

De plus, si j'avais une fonction avec des effets secondaires, comme «se connecter à un serveur Web», je l'emploierais probablement applyjuste pour des raisons de clarté.

series.apply(download_file_for_every_element) 

Mappeut utiliser non seulement une fonction, mais aussi un dictionnaire ou une autre série. Disons que vous voulez manipuler les permutations .

Prendre

1 2 3 4 5
2 1 4 5 3

Le carré de cette permutation est

1 2 3 4 5
1 2 5 3 4

Vous pouvez le calculer en utilisant map. Je ne sais pas si l'auto-application est documentée, mais cela fonctionne 0.15.1.

In [39]: p=pd.Series([1,0,3,4,2])

In [40]: p.map(p)
Out[40]: 
0    0
1    1
2    4
3    2
4    3
dtype: int64
osa
la source
3
De plus, .apply () vous permet de passer des kwargs dans la fonction tandis que .map () ne le fait pas.
neilxdims
19

@jeremiahbuddha a mentionné que l'application fonctionne sur les lignes / colonnes, tandis que applymap fonctionne par élément. Mais il semble que vous pouvez toujours utiliser Apply pour le calcul par élément ....

    frame.apply(np.sqrt)
    Out[102]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN

    frame.applymap(np.sqrt)
    Out[103]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN
user2921752
la source
29
Bonne prise avec ça. La raison pour laquelle cela fonctionne dans votre exemple est que np.sqrt est un ufunc, c'est-à-dire que si vous lui donnez un tableau, il diffusera la fonction sqrt sur chaque élément du tableau. Ainsi, lorsque apply pousse np.sqrt sur chaque colonne, np.sqrt fonctionne lui-même sur chacun des éléments des colonnes, vous obtenez donc essentiellement le même résultat que applymap.
jeremiahbuddha
11

Je voulais juste souligner, car j'ai lutté un peu avec ça

def f(x):
    if x < 0:
        x = 0
    elif x > 100000:
        x = 100000
    return x

df.applymap(f)
df.describe()

cela ne modifie pas la trame de données elle-même, doit être réaffecté

df = df.applymap(f)
df.describe()
muon
la source
1
J'ai parfois du mal à déterminer si vous devez réaffecter ou non après avoir fait quelque chose avec le df. C'est principalement des essais et des erreurs pour moi, mais je parie qu'il y a une logique à la façon dont cela fonctionne (que je manque).
marillion
2
en général, une trame de données pandas n'est modifiée que par la réaffectation df = modified_dfou si vous définissez l' inplace=Trueindicateur. La trame de données changera également si vous passez une trame de données à une fonction par référence et que la fonction modifie la trame de données
muon
1
Ce n'est pas tout à fait vrai, pensez à .ixou .whereetc. Je ne sais pas quelle est l'explication complète pour quand vous devez réaffecter et quand non.
Thanos
10

Explication probablement la plus simple de la différence entre apply et applymap:

appliquer prend la colonne entière comme paramètre, puis attribue le résultat à cette colonne

applymap prend la valeur de cellule séparée comme paramètre et attribue le résultat à cette cellule.

NB Si appliquer renvoie la valeur unique, vous aurez cette valeur au lieu de la colonne après l'attribution et, finalement, vous n'aurez qu'une ligne au lieu de la matrice.

Kath
la source
3

Ma compréhension:

Du point de vue de la fonction:

Si la fonction a des variables qui doivent être comparées dans une colonne / ligne, utilisez apply.

par exemple: lambda x: x.max()-x.mean().

Si la fonction doit être appliquée à chaque élément:

1> Si une colonne / ligne est localisée, utilisez apply

2> Si appliquer à l'ensemble de la trame de données, utilisez applymap

majority = lambda x : x > 17
df2['legal_drinker'] = df2['age'].apply(majority)

def times10(x):
  if type(x) is int:
    x *= 10 
  return x
df2.applymap(times10)
Vicky Miao
la source
Veuillez également fournir df2 pour plus de clarté afin que nous puissions tester votre code.
Ashish Anand
1

Basé sur la réponse de cs95

  • map est défini sur la série UNIQUEMENT
  • applymap est défini sur DataFrames UNIQUEMENT
  • apply est défini sur LES DEUX

donner quelques exemples

In [3]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [4]: frame
Out[4]:
            b         d         e
Utah    0.129885 -0.475957 -0.207679
Ohio   -2.978331 -1.015918  0.784675
Texas  -0.256689 -0.226366  2.262588
Oregon  2.605526  1.139105 -0.927518

In [5]: myformat=lambda x: f'{x:.2f}'

In [6]: frame.d.map(myformat)
Out[6]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [7]: frame.d.apply(myformat)
Out[7]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [8]: frame.applymap(myformat)
Out[8]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93

In [9]: frame.apply(lambda x: x.apply(myformat))
Out[9]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93


In [10]: myfunc=lambda x: x**2

In [11]: frame.applymap(myfunc)
Out[11]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289

In [12]: frame.apply(myfunc)
Out[12]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289
Alpha
la source
0

FOMO:

L'exemple suivant montre applyet applymaps'applique à a DataFrame.

mapla fonction est quelque chose que vous appliquez uniquement sur la série. Vous ne pouvez pas postuler map sur DataFrame.

La chose à retenir est que applypeut tout faire applymappeut, mais applya des options eXtra .

Les options du facteur X sont: axiset result_typeresult_typene fonctionne que lorsque axis=1(pour les colonnes).

df = DataFrame(1, columns=list('abc'),
                  index=list('1234'))
print(df)

f = lambda x: np.log(x)
print(df.applymap(f)) # apply to the whole dataframe
print(np.log(df)) # applied to the whole dataframe
print(df.applymap(np.sum)) # reducing can be applied for rows only

# apply can take different options (vs. applymap cannot)
print(df.apply(f)) # same as applymap
print(df.apply(sum, axis=1))  # reducing example
print(df.apply(np.log, axis=1)) # cannot reduce
print(df.apply(lambda x: [1, 2, 3], axis=1, result_type='expand')) # expand result

En tant que sidenote, la mapfonction Series ne doit pas être confondue avec la mapfonction Python .

Le premier est appliqué sur Series, pour mapper les valeurs, et le second à chaque élément d'un itérable.


Enfin, ne confondez pas la applyméthode dataframe avec la applyméthode groupby .

prosti
la source