Różnica między mapą, mapą aplikacji i metodami zastosowania w Pandach

465

Czy możesz mi powiedzieć, kiedy stosować te metody wektoryzacji w podstawowych przykładach?

Widzę, że mapjest to Seriesmetoda, podczas gdy reszta to DataFramemetody. Miałem jednak wątpliwości applyi applymapmetody. Dlaczego mamy dwie metody zastosowania funkcji do DataFrame? Ponownie proste przykłady ilustrujące użycie byłyby świetne!

marillion
źródło
5
Popraw mnie, jeśli się mylę, ale uważam, że te funkcje nie są metodami wektoryzacji, ponieważ wszystkie zawierają pętlę nad elementami, na których są stosowane.
Tanguy,
1
Nie widzę tutaj różnicy: gist.github.com/MartinThoma/e320cbb937afb4ff766f75988f1c65e6
Martin Thoma

Odpowiedzi:

533

Prosto z książki Pythona do analizy danych Wesa McKinneya , str. 132 (Bardzo polecam tę książkę):

Inną częstą operacją jest zastosowanie funkcji na tablicach 1D do każdej kolumny lub wiersza. Metoda zastosowania DataFrame robi dokładnie to:

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

Wiele z najbardziej powszechnych statystyk tablicowych (takich jak suma i średnia) to metody DataFrame, więc użycie polecenia Apply nie jest konieczne.

Można również użyć elementarnych funkcji Pythona. Załóżmy, że chcesz obliczyć sformatowany ciąg z każdej wartości zmiennoprzecinkowej w ramce. Możesz to zrobić za pomocą 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

Powodem nazwy mapy aplikacji jest to, że Series ma metodę mapowania do zastosowania funkcji elementarnej:

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

Podsumowując, applydziała w oparciu o wiersz / kolumnę DataFrame, applymapdziała elementowo na DataFrame i mapdziała elementowo na Series.

jeremiahbuddha
źródło
31
ściśle mówiąc, applymap wewnętrznie realizowany jest poprzez zastosowanie przy odrobinie oblewania się nad przekazany parametr funkcji (rougly mówiąc wymianie funcdo lambda x: [func(y) for y in x], i stosując kolumnę SCSI)
Alko
5
Dziękuję za wyjaśnienie. Ponieważ mapi applymapoba działają pod względem elementów, oczekiwałbym jednej metody (albo mapalbo applymap), która działałaby zarówno dla Series, jak i DataFrame. Prawdopodobnie istnieją inne względy projektowe i Wes McKinney postanowił wymyślić dwie różne metody.
marillion
2
Z jakiegoś powodu jest na stronie 129 w mojej kopii. Nie ma etykiety na drugie wydanie ani nic takiego.
Jody
1
Czy jest jakiś sposób, aby zrobić to applymaprazem z groupbyfunkcją w pandach?
everestial007
Jak zastosować funkcję do zgrupowanych danych kolumnowych?
hhh
81

Porównując map, applymapi : Context Mattersapply

Pierwsza ważna różnica: DEFINICJA

  • map jest zdefiniowany TYLKO w Serii
  • applymap jest zdefiniowany TYLKO w DataFrames
  • apply jest zdefiniowany na OBU

Druga główna różnica: ARGUMENT WEJŚCIOWY

  • mapakceptuje dicts Serieslub wywoływalne
  • applymapi applyakceptuj tylko kallazy

Trzecia główna różnica: ZACHOWANIE

  • map jest elementarne dla serii
  • applymap jest elementowe dla DataFrames
  • applydziała również elementarnie, ale nadaje się do bardziej złożonych operacji i agregacji. Zachowanie i wartość zwracana zależy od funkcji.

Czwarta główna różnica (najważniejsza): WYKORZYSTAĆ PRZYPADEK

  • mapjest przeznaczony do mapowania wartości z jednej domeny do drugiej, więc jest zoptymalizowany pod kątem wydajności (np. df['A'].map({1:'a', 2:'b', 3:'c'}))
  • applymapnadaje się do transformacji elementarnych w wielu wierszach / kolumnach (np. df[['A', 'B', 'C']].applymap(str.strip))
  • applysłuży do zastosowania dowolnej funkcji, której nie można wektoryzować (np. df['sentences'].apply(nltk.sent_tokenize))

Zreasumowanie

wprowadź opis zdjęcia tutaj

Przypisy

  1. mappo przejściu słownik / seria odwzoruje elementy na podstawie kluczy w tym słowniku / serii. Brakujące wartości zostaną zapisane na wyjściu jako NaN.
  2. applymapw nowszych wersjach został zoptymalizowany dla niektórych operacji. Znajdziesz applymapnieco szybciej niż applyw niektórych przypadkach. Moją sugestią jest przetestowanie ich obu i wykorzystanie tego, co działa lepiej.

  3. mapjest zoptymalizowany do mapowań elementarnych i transformacji. Operacje obejmujące słowniki lub serie umożliwią pandom korzystanie z szybszych ścieżek kodu w celu uzyskania lepszej wydajności.

  4. Series.applyzwraca skalar dla operacji agregujących, w przeciwnym razie Series. Podobnie dla DataFrame.apply. Należy pamiętać, że applyma również fastpaths gdy wywołana z pewnych funkcji, takich jak numpy mean, sumitp
cs95
źródło
70

W tych odpowiedziach jest wiele informacji, ale dodaję własną, aby jasno podsumować, które metody działają w układzie tablicowym w porównaniu do elementów. jeremiahbuddha najczęściej to robił, ale nie wspomniał o Series.apply. Nie mam przedstawiciela do komentowania.

  • DataFrame.apply działa jednocześnie na całych wierszach lub kolumnach.

  • DataFrame.applymap, Series.applyi Series.mapdziałają jednocześnie na jednym elemencie.

Możliwości Series.applyi są w dużym stopniu pokrywające się Series.map, co oznacza, że ​​jedno z nich będzie działać w większości przypadków. Mają jednak niewielkie różnice, niektóre z nich zostały omówione w odpowiedzi osy.

MarredCheese
źródło
38

Dodając do innych odpowiedzi, Seriestam jest również mapa i zastosowanie .

Apply może zrobić DataFrame z serii ; jednak mapa po prostu umieści serię w każdej komórce innej serii, co prawdopodobnie nie jest tym, czego chcesz.

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

Również gdybym miał funkcję z efektami ubocznymi, takimi jak „połączenie z serwerem internetowym”, prawdopodobnie applyużyłbym tego ze względu na przejrzystość.

series.apply(download_file_for_every_element) 

Mapmoże korzystać nie tylko z funkcji, ale także ze słownika lub innej serii. Powiedzmy, że chcesz manipulować permutacjami .

Brać

1 2 3 4 5
2 1 4 5 3

Kwadrat tej permutacji to

1 2 3 4 5
1 2 5 3 4

Możesz to obliczyć za pomocą map. Nie jestem pewien, czy aplikacja jest udokumentowana, ale działa 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
źródło
3
Ponadto .apply () pozwala przekazywać kwargs do funkcji, podczas gdy .map () nie.
neilxdims,
19

@jeremiahbuddha wspomniał, że zastosowanie działa na wierszach / kolumnach, podczas gdy applymap działa na elementach. Wygląda jednak na to, że nadal można zastosować zastosowanie do obliczeń elementarnych ....

    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
użytkownik2921752
źródło
29
Z tym dobrze. Powodem tego jest to, że np.sqrt jest ufunc, tzn. Jeśli dasz mu tablicę, wyemituje funkcję sqrt na każdy element tablicy. Więc kiedy zastosuj wypycha np.sqrt na każdej kolumnie, np.sqrt działa sam na każdym z elementów kolumny, więc zasadniczo otrzymujesz taki sam wynik jak applymap.
jeremiahbuddha
11

Chciałem tylko wskazać, ponieważ przez chwilę z tym walczyłem

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

df.applymap(f)
df.describe()

nie modyfikuje to samej ramki danych, należy ją ponownie przypisać

df = df.applymap(f)
df.describe()
mion
źródło
1
Czasami mam problem z ustaleniem, czy musisz zmienić przypisanie po zrobieniu czegoś z df. Jest to dla mnie głównie próba i błąd, ale założę się, że istnieje logika tego, jak to działa (że brakuje mi).
marillion
2
ogólnie ramka danych pandy jest modyfikowana tylko przez ponowne przypisanie df = modified_dflub ustawienie inplace=Trueflagi. Również dataframe zmieni jeśli zdać dataframe do funkcji przez odniesienie i modyfikuje funkcja dataframe
mion
1
To nie jest do końca prawda, myśleć .ixlub .whereitd. Nie wiem, co pełne wyjaśnienie jest, gdy trzeba ponownie przypisać, a kiedy nie.
Thanos,
10

Prawdopodobnie najprostsze wyjaśnienie różnicy między aplikacją a mapą aplikacji:

Zastosuj przyjmuje całą kolumnę jako parametr, a następnie przypisuje wynik do tej kolumny

applymap przyjmuje osobną wartość komórki jako parametr i przypisuje wynik z powrotem do tej komórki.

Uwaga: jeśli zastosuje zwraca pojedynczą wartość, po przypisaniu będziesz miał tę wartość zamiast kolumny, a ostatecznie będzie miał tylko wiersz zamiast macierzy.

Kath
źródło
3

Moje zrozumienie:

Z punktu widzenia funkcji:

Jeśli funkcja ma zmienne, które należy porównać w kolumnie / wierszu, użyj apply.

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

Jeśli funkcja ma być zastosowana do każdego elementu:

1> Jeśli znajduje się kolumna / wiersz, użyj apply

2> Jeśli dotyczy całej ramki danych, użyj 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
źródło
Podaj także df2, aby uzyskać większą przejrzystość, abyśmy mogli przetestować Twój kod.
Ashish Anand
1

Na podstawie odpowiedzi cs95

  • map jest zdefiniowany TYLKO w Serii
  • applymap jest zdefiniowany TYLKO w DataFrames
  • apply jest zdefiniowany na OBU

podać kilka przykładów

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
Alfa
źródło
0

FOMO:

Poniższy przykład ilustruje applyi applymapnałożono na DataFrame.

mapfunkcja jest stosowana tylko w przypadku serii. Nie możesz aplikować map na DataFrame.

Należy pamiętać, że applymożna zrobić wszystko applymap , co można, ale applyma opcje eXtra .

Opcje współczynnika X to: axisi result_typegdzie result_typedziała tylko wtedy, gdy axis=1(dla kolumn).

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

Jako sidenote, mapfunkcja Series nie powinna być mylona z mapfunkcją Python .

Pierwszy jest stosowany w Serii, aby zmapować wartości, a drugi do każdego elementu iterowalnego.


Na koniec nie należy mylić applymetody ramki danych z applymetodą grupowania .

prosti
źródło