Unterschied zwischen Map-, Applymap- und Apply-Methoden in Pandas

465

Können Sie mir anhand grundlegender Beispiele sagen, wann ich diese Vektorisierungsmethoden verwenden soll?

Ich sehe, dass dies mapeine SeriesMethode ist, während der Rest DataFrameMethoden sind. Ich war verwirrt über applyund applymapMethoden. Warum gibt es zwei Methoden zum Anwenden einer Funktion auf einen DataFrame? Auch hier wären einfache Beispiele, die die Verwendung veranschaulichen, großartig!

Marillion
quelle
5
Korrigieren Sie mich, wenn ich falsch liege, aber ich glaube, dass diese Funktionen keine Vektorisierungsmethoden sind, da sie alle eine Schleife über die Elemente beinhalten, auf die sie angewendet werden.
Tanguy
1
Ich kann hier keinen Unterschied feststellen
Martin Thoma

Antworten:

533

Direkt aus Wes McKinneys Python for Data Analysis- Buch, S. 132 (Ich habe dieses Buch sehr empfohlen):

Eine weitere häufige Operation ist das Anwenden einer Funktion auf 1D-Arrays auf jede Spalte oder Zeile. Die Apply-Methode von DataFrame macht genau das:

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

Viele der am häufigsten verwendeten Array-Statistiken (wie Summe und Mittelwert) sind DataFrame-Methoden, daher ist die Verwendung von apply nicht erforderlich.

Elementweise Python-Funktionen können ebenfalls verwendet werden. Angenommen, Sie möchten aus jedem Gleitkommawert im Frame eine formatierte Zeichenfolge berechnen. Sie können dies mit applymap tun:

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

Der Grund für den Namen applymap ist, dass Series über eine Kartenmethode zum Anwenden einer elementweisen Funktion verfügt:

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

Zusammenfassend lässt sich applysagen, dass ein DataFrame zeilen- / spaltenbasiert, ein DataFrame applymapelementweise und mapeine Serie elementweise arbeitet.

jeremiahbuddha
quelle
31
streng genommen applymap intern über gelten mit etwas Nachbereitungsübergeben Funktionsparameter (rougly gesprochen ersetzt implementiert funczu lambda x: [func(y) for y in x], und die Anwendung spaltenweise)
Alko
5
Danke für die Erklärung. Da mapund applymapbeide Elemente funktionieren, würde ich eine einzelne Methode (entweder mapoder applymap) erwarten, die sowohl für eine Serie als auch für einen DataFrame funktioniert. Wahrscheinlich gibt es andere Designüberlegungen, und Wes McKinney hat beschlossen, zwei verschiedene Methoden zu entwickeln.
Marillion
2
Es ist aus irgendeinem Grund auf Seite 129 in meiner Kopie. Es gibt kein Label für die zweite Ausgabe oder so.
Jody
1
Gibt es eine Möglichkeit, applymapmit der groupbyFunktion von Pandas zusammenzuarbeiten?
everestial007
Wie wende ich eine Funktion auf gruppierte spaltenweise Daten an?
hhh
83

Vergleicht man map, applymapund : Context Mattersapply

Erster großer Unterschied: DEFINITION

  • map ist NUR für Serien definiert
  • applymap wird NUR in DataFrames definiert
  • apply ist auf BEIDEN definiert

Zweiter großer Unterschied: INPUT ARGUMENT

  • mapakzeptiert dicts Series, oder aufrufbar
  • applymapund applynur Callables akzeptieren

Dritter Hauptunterschied: VERHALTEN

  • map ist elementweise für Serie
  • applymap ist elementweise für DataFrames
  • applyfunktioniert auch elementweise, eignet sich jedoch für komplexere Operationen und Aggregationen. Das Verhalten und der Rückgabewert hängen von der Funktion ab.

Vierter Hauptunterschied (der wichtigste): USE CASE

  • mapgemeint ist , für die Zuordnung von Werten von einer Domäne zu einer anderen, so dass für die Leistung optimiert ist (beispielsweise df['A'].map({1:'a', 2:'b', 3:'c'}))
  • applymapist gut für elementweise Transformationen über mehrere Zeilen / Spalten (z. B. df[['A', 'B', 'C']].applymap(str.strip))
  • applyist für jede Funktion der Anwendung , die nicht vektorisiert werden kann ( zum Beispiel df['sentences'].apply(nltk.sent_tokenize))

Zusammenfassen

Geben Sie hier die Bildbeschreibung ein

Fußnoten

  1. mapWenn ein Wörterbuch / eine Reihe übergeben wird, werden Elemente basierend auf den Schlüsseln in diesem Wörterbuch / dieser Reihe zugeordnet. Fehlende Werte werden in der Ausgabe als NaN aufgezeichnet.
  2. applymapin neueren Versionen wurde für einige Operationen optimiert. Sie finden applymapetwas schneller als applyin einigen Fällen. Mein Vorschlag ist, beide zu testen und alles zu verwenden, was besser funktioniert.

  3. mapist für elementweise Zuordnungen und Transformationen optimiert. Vorgänge mit Wörterbüchern oder Serien ermöglichen es Pandas, schnellere Codepfade für eine bessere Leistung zu verwenden.

  4. Series.applyGibt einen Skalar für Aggregationsoperationen zurück, andernfalls Series. Ähnliches gilt für DataFrame.apply. Beachten Sie, dass applyauch fastpaths hat , wenn sie mit bestimmten NumPy Funktionen wie genannt mean, sumusw.
cs95
quelle
70

Diese Antworten enthalten großartige Informationen, aber ich füge meine eigenen hinzu, um klar zusammenzufassen, welche Methoden in Bezug auf Arrays und Elemente funktionieren. jeremiahbuddha tat dies meistens, erwähnte aber nicht Series.apply. Ich habe nicht den Repräsentanten, um einen Kommentar abzugeben.

  • DataFrame.apply arbeitet mit ganzen Zeilen oder Spalten gleichzeitig.

  • DataFrame.applymap, Series.applyUnd Series.mapauf einem Element zu einem Zeitpunkt betrieben werden .

Es gibt viele Überschneidungen zwischen den Funktionen von Series.applyund Series.map, was bedeutet, dass beide in den meisten Fällen funktionieren. Sie haben jedoch einige geringfügige Unterschiede, von denen einige in der Antwort von osa erörtert wurden.

MarredCheese
quelle
38

Neben den anderen Antworten Seriesgibt es in a auch Map und Apply .

Apply kann aus einer Reihe einen DataFrame machen . Map fügt jedoch nur eine Reihe in jede Zelle einer anderen Reihe ein, was wahrscheinlich nicht das ist, was Sie wollen.

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

Auch wenn ich eine Funktion mit Nebenwirkungen hätte, wie "Verbindung zu einem Webserver herstellen", würde ich sie wahrscheinlich applynur aus Gründen der Klarheit verwenden.

series.apply(download_file_for_every_element) 

Mapkann nicht nur eine Funktion, sondern auch ein Wörterbuch oder eine andere Reihe verwenden. Angenommen, Sie möchten Permutationen manipulieren .

Nehmen

1 2 3 4 5
2 1 4 5 3

Das Quadrat dieser Permutation ist

1 2 3 4 5
1 2 5 3 4

Sie können es mit berechnen map. Ich bin nicht sicher, ob die Selbstanwendung dokumentiert ist, aber sie funktioniert in 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
quelle
3
Mit .apply () können Sie außerdem kwargs an die Funktion übergeben, während .map () dies nicht tut.
neilxdims
19

@jeremiahbuddha erwähnte, dass apply für Zeilen / Spalten funktioniert, während applymap elementweise funktioniert. Aber es scheint, dass Sie Apply für elementweise Berechnungen verwenden können ....

    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
quelle
29
Guter Fang damit. Der Grund, warum dies in Ihrem Beispiel funktioniert, ist, dass np.sqrt ein ufunc ist. Wenn Sie ihm also ein Array geben, wird die sqrt-Funktion auf jedes Element des Arrays übertragen. Wenn also apply np.sqrt für jede Spalte drückt, arbeitet np.sqrt selbst für jedes der Elemente der Spalten, sodass Sie im Wesentlichen das gleiche Ergebnis wie applymap erhalten.
Jeremiahbuddha
11

Ich wollte nur darauf hinweisen, als ich ein bisschen damit zu kämpfen hatte

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

df.applymap(f)
df.describe()

Dies ändert den Datenrahmen selbst nicht, muss neu zugewiesen werden

df = df.applymap(f)
df.describe()
Myon
quelle
1
Ich habe manchmal Probleme herauszufinden, ob Sie neu zuweisen müssen oder nicht, nachdem Sie etwas mit dem df gemacht haben. Für mich ist es meistens Versuch und Irrtum, aber ich wette, es gibt eine Logik, wie es funktioniert (die ich verpasse).
Marillion
2
Im Allgemeinen wird ein Pandas-Datenrahmen nur durch Neuzuweisung df = modified_dfoder durch Setzen eines Flags geändert inplace=True. Auch der Datenrahmen ändert sich, wenn Sie einen Datenrahmen als Referenz an eine Funktion übergeben und die Funktion den Datenrahmen ändert
Myon
1
Dies ist nicht ganz richtig, denken Sie an .ixoder .whereusw. Sie sind sich nicht sicher, wofür die vollständige Erklärung ist, wenn Sie sie neu zuweisen müssen und wenn nicht.
Thanos
10

Wahrscheinlich einfachste Erklärung für den Unterschied zwischen apply und applymap:

apply verwendet die gesamte Spalte als Parameter und weist das Ergebnis dieser Spalte zu

applymap verwendet den separaten Zellenwert als Parameter und weist das Ergebnis dieser Zelle zurück.

NB Wenn apply den einzelnen Wert zurückgibt, haben Sie diesen Wert nach der Zuweisung anstelle der Spalte und schließlich nur eine Zeile anstelle der Matrix.

Kath
quelle
3

Mein Verständnis:

Aus funktionaler Sicht:

Wenn die Funktion Variablen enthält, die innerhalb einer Spalte / Zeile verglichen werden müssen, verwenden Sie apply.

zB : lambda x: x.max()-x.mean().

Wenn die Funktion auf jedes Element angewendet werden soll:

1> Wenn sich eine Spalte / Zeile befindet, verwenden Sie apply

2> Wenn auf den gesamten Datenrahmen angewendet, verwenden Sie 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
quelle
Bitte geben Sie zur besseren Übersicht auch df2 an, damit wir Ihren Code testen können.
Ashish Anand
1

Basierend auf der Antwort von cs95

  • map ist NUR für Serien definiert
  • applymap wird NUR in DataFrames definiert
  • apply ist auf BEIDEN definiert

gib ein Paar Beispiele

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
quelle
0

FOMO:

Das folgende Beispiel zeigt applyund applymapangewendet auf a DataFrame.

mapFunktion ist etwas, das Sie nur auf Serien anwenden. Sie können sich nicht map auf DataFrame bewerben .

Das ist daran zu erinnern ist , dass applytun alles applymap kann, aber applyhat eXtra Optionen.

Die X-Faktor-Optionen sind: axisund result_typewo result_typefunktioniert nur wann axis=1(für Spalten).

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

Als Nebenbemerkung mapsollte die Serienfunktion nicht mit der Python- mapFunktion verwechselt werden.

Der erste wird auf Serien angewendet, um die Werte abzubilden, und der zweite auf jedes Element einer Iterierbarkeit.


Verwechseln Sie die Datenrahmenmethode nicht applymit der groupby- applyMethode.

Prosti
quelle