Pandy: Jak mogę użyć funkcji Apply () dla pojedynczej kolumny?

254

Mam ramkę danych pand z dwiema kolumnami. Muszę zmienić wartości pierwszej kolumny bez wpływu na drugą i odzyskać całą ramkę danych ze zmienionymi tylko wartościami pierwszej kolumny. Jak mogę to zrobić za pomocą opcji Zastosuj w pandach?

Amani
źródło
4
Proszę zamieścić przykładowe dane wejściowe i pożądane dane wyjściowe.
Fabio Lamanna
Prawie nigdy nie powinieneś używać applyw takiej sytuacji. Zamiast tego operuj bezpośrednio na kolumnie.
Ted Petrou,
Jak powiedział Ted Petrou, unikaj używania applyjak najwięcej. Jeśli nie jesteś pewien, czy musisz go użyć, prawdopodobnie nie. Polecam rzucić okiem Kiedy powinienem kiedykolwiek chcieć używać pand Apply () w moim kodzie? .
cs95
Pytanie nie jest całkowicie jasne: czy stosuje funkcję do każdego elementu kolumny, czy stosuje funkcję do kolumny jako całości (na przykład: odwraca kolumnę)?
Pierre ALBARÈDE

Odpowiedzi:

336

Podano przykładową ramkę danych dfjako:

a,b
1,2
2,3
3,4
4,5

czego chcesz to:

df['a'] = df['a'].apply(lambda x: x + 1)

który zwraca:

   a  b
0  2  2
1  3  3
2  4  4
3  5  5
Fabio Lamanna
źródło
9
applynigdy nie należy go używać w takiej sytuacji
Ted Petrou,
5
@TedPetrou masz całkowitą rację, był to tylko przykład zastosowania ogólnej funkcji do pojedynczej kolumny, jak poprosił OP.
Fabio Lamanna
14
Kiedy próbuję to zrobić, pojawia się następujące ostrzeżenie: „Próbuję ustawić wartość na kopii wycinka z DataFrame. Spróbuj zamiast tego użyć .loc [wiersz_indexer, col_indexer] = wartość”
dagrun
24
Z ciekawości: dlaczego nie należy stosować w takiej sytuacji? Jaka jest dokładnie sytuacja?
Wujek Ben Ben
19
@UncleBenBen ogólnie applywykorzystuje wewnętrzną pętlę nad wierszami, która jest znacznie wolniejsza niż funkcje wektoryzowane, takie jak np. df.a = df.a / 2(Patrz odpowiedź Mike'a Mullera).
Fabio Lamanna
66

Aby użyć pojedynczej kolumny, lepiej użyć map():

df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9



df['a'] = df['a'].map(lambda a: a / 2.)

      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9
George Petrov
źródło
78
Dlaczego jest map()lepszy niż apply()dla pojedynczej kolumny?
ChaimG
2
To było bardzo przydatne. Użyłem go do wyodrębnienia nazw plików ze ścieżek przechowywanych w kolumniedf['file_name'] = df['Path'].map(lambda a: os.path.basename(a))
mmann1123
46
map () dotyczy Serii (tj. pojedynczych kolumn) i działa na jednej komórce na raz, podczas gdy Apply () dotyczy DataFrame i działa na całym wierszu na raz.
jpcgt
3
@jpcgt Czy to oznacza, że ​​mapa jest szybsza niż stosowana w tym przypadku?
Viragos,
@ChaimG, widzę, że ten system operacyjny dobrze wyjaśnia: stackoverflow.com/a/19798528/571828
象 嘉 道
40

W ogóle nie potrzebujesz funkcji. Możesz pracować bezpośrednio nad całą kolumną.

Przykładowe dane:

>>> df = pd.DataFrame({'a': [100, 1000], 'b': [200, 2000], 'c': [300, 3000]})
>>> df

      a     b     c
0   100   200   300
1  1000  2000  3000

Połowa wszystkich wartości w kolumnie a:

>>> df.a = df.a / 2
>>> df

     a     b     c
0   50   200   300
1  500  2000  3000
Mike Müller
źródło
Co jeśli chcę podzielić każdy element w kolumnie przez „/” i wziąć pierwszą część?
K47
12

Chociaż podane odpowiedzi są poprawne, modyfikują one początkową ramkę danych, co nie zawsze jest pożądane (a biorąc pod uwagę, że OP poprosił o przykłady „za pomocą apply”, może być tak, że chcieli wersji, która zwraca nową ramkę danych, jak to applyma miejsce).

Jest to możliwe przy użyciu assign: dotyczy assignistniejących kolumn, jak stwierdzono w dokumentacji (nacisk jest mój):

Przypisz nowe kolumny do DataFrame.

Zwraca nowy obiekt ze wszystkimi oryginalnymi kolumnami oprócz nowych. Istniejące kolumny, które zostaną ponownie przypisane, zostaną zastąpione .

W skrócie:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

In [3]: df.assign(a=lambda df: df.a / 2)
Out[3]: 
      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9

In [4]: df
Out[4]: 
    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9

Zauważ, że funkcja przejdzie całą ramkę danych, nie tylko kolumnę, którą chcesz zmodyfikować, więc musisz upewnić się, że wybierasz odpowiednią kolumnę w lambda.

Thibaut Dubernet
źródło
9

Jeśli naprawdę martwisz się szybkością wykonywania funkcji wprowadzania i masz ogromny zestaw danych do pracy, możesz użyć przełącznika szybszego, aby przyspieszyć wykonanie, oto przykład przełącznika szybszego na ramce danych pandy:

import pandas as pd
import swifter

def fnc(m):
    return m*3+4

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})

# apply a self created function to a single column in pandas
df["y"] = df.m.swifter.apply(fnc)

Umożliwi to wszystkim rdzeniom procesora obliczenie wyniku, dlatego będzie znacznie szybsze niż normalne funkcje stosowania. Daj mi znać, jeśli okaże się przydatny.

durjoy
źródło
1

Pozwól mi wypróbować złożone obliczenia przy użyciu datetime i z uwzględnieniem zer lub pustych spacji. Skracam 30 lat w kolumnie daty i godziny i korzystam z applymetody lambdaoraz konwertuję format daty i godziny. Linia odpowiednio if x != '' else xzajmie się wszystkimi pustymi spacjami lub zerami.

df['Date'] = df['Date'].fillna('')
df['Date'] = df['Date'].apply(lambda x : ((datetime.datetime.strptime(str(x), '%m/%d/%Y') - datetime.timedelta(days=30*365)).strftime('%Y%m%d')) if x != '' else x)
Harry_pb
źródło