Modyfikowanie podzbioru wierszy w ramce danych pandy

143

Załóżmy, że mam pandę DataFrame z dwiema kolumnami, A i B. Chciałbym zmodyfikować tę ramkę DataFrame (lub utworzyć kopię), aby B zawsze miało wartość NaN, gdy A wynosi 0. Jak mogę to osiągnąć?

Wypróbowałem następujące

df['A'==0]['B'] = np.nan

i

df['A'==0]['B'].values.fill(np.nan)

bezskutecznie.

Arthur B.
źródło
Jeśli szukasz bardzo szybkiego rozwiązania, użyj NumPy, wherejak widać na poniższym rozwiązaniu
Ted Petrou

Odpowiedzi:

243

Użyj .locdo indeksowania na podstawie etykiet:

df.loc[df.A==0, 'B'] = np.nan

df.A==0Ekspresji tworzy logiczną serii, które indeksów wierszy, 'B'wybiera kolumny. Możesz również użyć tego do przekształcenia podzbioru kolumny, np .:

df.loc[df.A==0, 'B'] = df.loc[df.A==0, 'B'] / 2

Nie wiem wystarczająco dużo o elementach wewnętrznych pand, aby dokładnie wiedzieć, dlaczego to działa, ale podstawowym problemem jest to, że czasami indeksowanie do DataFrame zwraca kopię wyniku, a czasami zwraca widok oryginalnego obiektu. Zgodnie z dokumentacją tutaj zachowanie to zależy od podstawowego zachowania numpy. Zauważyłem, że dostęp do wszystkiego w jednej operacji (zamiast [jednej] [dwóch]) jest bardziej prawdopodobny dla ustawienia.

BrenBarn
źródło
Druga część to miła odpowiedź na pytanie, które nawet nie zostało zadane ;-) Zastanawiam się, czy to nadal jest kanoniczna odpowiedź pandy, w szczególności b / c to oczywiste naruszenie DRY, chociaż zakładam, że jest w fakt konieczny do naruszenia DRY, biorąc pod uwagę ograniczenia wewnętrzne pand? (Mogę zadać dokładnie tego rodzaju pytanie, bardziej szczegółowo, ale chciałem sprawdzić, czy masz szybką odpowiedź, zanim to zrobię)
JohnE
Jak podzielić Dataframe, która nie ma nazw kolumn, jak podzielić df tylko według indeksu? df.loc [df [0] == 0] nie działa ... Jaka jest alternatywa? Dziękuję
amipro
89

Oto dokumentacja pandas dotycząca zaawansowanego indeksowania:

Sekcja wyjaśni dokładnie, czego potrzebujesz! Okazuje się, że df.loc(ponieważ .ix został przestarzały - jak wielu wskazało poniżej) może być użyty do fajnego krojenia / kostkowania ramki danych. I. Może być również używany do ustawiania rzeczy.

df.loc[selection criteria, columns I want] = value

Więc odpowiedź Brena brzmi: „znajdź mi wszystkie miejsca df.A == 0, wybierz kolumnę Bi ustaw ją na np.nan

badgley
źródło
2
Uczyniłeś mój dzień. Jasne wyjaśnienie.
TwinPenguins
1
Tak, jakoś loc[selection criteria, columns I want]doskonale trzyma się twojego umysłu ...
EmE,
29

Począwszy od pand w wersji 0.20 ix jest przestarzały . Właściwym sposobem jest użycie df.loc

oto działający przykład

>>> import pandas as pd 
>>> import numpy as np 
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN
>>> 

Wyjaśnienie:

Jak wyjaśniono w dokumencie tutaj , .loc jest oparty głównie etykieta, ale może być również używany z logicznej tablicy .

Tak więc to, co robimy powyżej, to stosowanie df.loc[row_index, column_index]przez:

  • Wykorzystując fakt, że locmoże przyjąć tablicę logiczną jako maskę, która mówi pandom, w którym podzbiorze wierszy chcemy zmienićrow_index
  • Wykorzystanie tego faktu locjest również oparte na etykiecie, aby wybrać kolumnę za pomocą etykiety 'B'wcolumn_index

Możemy użyć operacji logicznej, warunkowej lub dowolnej operacji, która zwraca serię wartości logicznych, aby skonstruować tablicę wartości logicznych. W powyższym przykładzie chcemy, rowsaby zawierała a 0, w tym celu możemy użyć df.A == 0, jak widać w przykładzie poniżej, zwraca to serię wartości logicznych.

>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df 
   A  B
0  0  2
1  1  0
2  0  5
>>> df.A == 0 
0     True
1    False
2     True
Name: A, dtype: bool
>>> 

Następnie używamy powyższej tablicy wartości logicznych, aby wybrać i zmodyfikować niezbędne wiersze:

>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN

Aby uzyskać więcej informacji, zapoznaj się z zaawansowaną dokumentacją dotyczącą indeksowania tutaj .

Mohamed Ali JAMAOUI
źródło
11

Aby uzyskać ogromny wzrost prędkości, użyj funkcji Where NumPy.

Ustawiać

Utwórz dwukolumnową ramkę DataFrame ze 100 000 wierszy z kilkoma zerami.

df = pd.DataFrame(np.random.randint(0,3, (100000,2)), columns=list('ab'))

Szybkie rozwiązanie dzięki numpy.where

df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)

Czasy

%timeit df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
685 µs ± 6.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.loc[df['a'] == 0, 'b'] = np.nan
3.11 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy wherejest około 4x szybszy

Ted Petrou
źródło
Byłem tego ciekawy, więc przetestowałem to sam i różnica była jeszcze większa przy innych parametrach. Numpy był prawie 10 razy szybszy w zamianie zera na liczbę całkowitą zamiast np.nan. Zastanawiam się, co zajmuje dodatkowy czas.
Alexander,
Czy konieczne jest użycie .valuesw np.where(df.a.values == 0, np.nan, df.b.values)? Wygląda na to, że np.where(df.a == 0, np.nan, df.b)też działa?
hsl
4

Aby zamienić kolumny wielokrotności, przekonwertuj je na tablicę numpy używając .values:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
Adrien Renaud
źródło