2015-04-13 30 views
5

Chcę indeksować ramkę danych Pandy przy użyciu maski boolowskiej, a następnie ustawić wartość w podzbiorze przefiltrowanej ramki danych na podstawie indeksu całkowitoliczbowego i odzwierciedlić tę wartość w ramce danych. To znaczy, byłbym szczęśliwy, gdyby działało to na widok ramki danych.Indeksowanie pand przez oba boolowskie `loc` i kolejne` iloc`

przykład:

In [293]: 

df = pd.DataFrame({'a': [0, 1, 2, 3, 4, 5, 6, 7], 
        'b': [5, 5, 2, 2, 5, 5, 2, 2], 
        'c': [0, 0, 0, 0, 0, 0, 0, 0]}) 

mask = (df['a'] < 7) & (df['b'] == 2) 
df.loc[mask, 'c'] 

Out[293]: 
2 0 
3 0 
6 0 
Name: c, dtype: int64 

teraz ja je ustawić wartości dwóch pierwszych elementów zwróconych w filtrowanej dataframe. Łączenia się iloc na wezwanie loc powyżej działa index:

In [294]: 

df.loc[mask, 'c'].iloc[0: 2] 

Out[294]: 

2 0 
3 0 
Name: c, dtype: int64 

Ale nie przypisywać:

In [295]: 

df.loc[mask, 'c'].iloc[0: 2] = 1 

print(df) 

    a b c 
0 0 5 0 
1 1 5 0 
2 2 2 0 
3 3 2 0 
4 4 5 0 
5 5 5 0 
6 6 2 0 
7 7 2 0 

Making wartość przypisać taką samą długość jak wycinka (tj = [1, 1]) również nie działa . Czy istnieje sposób na przypisanie tych wartości?

+0

try 'df.loc [ mask [0: 2], 'c'] = 1' –

+0

@PaulH, który wywołuje 'IndexingError: Niepowtarzalny klucz Boolean Series provided' – EdChum

+0

oh, zapomniałem, że' maska' była serią ... może 'df.loc [ mask.iloc [0: 2], "c"] '? –

Odpowiedz

4

To działa, ale jest trochę brzydki, w zasadzie używamy indeksu wygenerowanego z maską i zrobić dodatkowe wezwanie do loc:

In [57]: 

df.loc[df.loc[mask,'c'].iloc[0:2].index, 'c'] = 1 
df 
Out[57]: 
    a b c 
0 0 5 0 
1 1 5 0 
2 2 2 1 
3 3 2 1 
4 4 5 0 
5 5 5 0 
6 6 2 0 
7 7 2 0 

Więc łamiąc powyższy dół:

In [60]: 
# take the index from the mask and iloc 
df.loc[mask, 'c'].iloc[0: 2] 
Out[60]: 
2 0 
3 0 
Name: c, dtype: int64 
In [61]: 
# call loc using this index, we can now use this to select column 'c' and set the value 
df.loc[df.loc[mask,'c'].iloc[0:2].index] 
Out[61]: 
    a b c 
2 2 2 0 
3 3 2 0 
+0

To działa, dziękuję @EdChum. Być może ktoś wymyśli coś bardziej eleganckiego. – tsawallis

+0

@tsawallis Mam nadzieję, że jest to jeden z tych przypadków, w których musisz to zrobić, ponieważ różne metody indeksowania nie pozwalają na tak skomplikowane kryteria wyboru i jeśli zaczniesz wywoływać łańcuchy wywołań indeksowania, to po cichu przestaną działać, w ten sposób nie ma dwuznaczności i zawsze będzie działać na widoku – EdChum

2

Nie wiem, czy jest to bardziej eleganckie, ale jest nieco inne:

mask = mask & (mask.cumsum() < 3) 

df.loc[mask, 'c'] = 1 

    a b c 
0 0 5 0 
1 1 5 0 
2 2 2 1 
3 3 2 1 
4 4 5 0 
5 5 5 0 
6 6 2 0 
7 7 2 0 
+0

Neat answer @JohnE. – tsawallis

4

Co powiesz na.

ix = df.index[mask][:2] 
df.loc[ix, 'c'] = 1 

Ten sam pomysł co EdChum, ale bardziej elegancki, jak sugerowano w komentarzu.

EDYCJA: Trzeba być trochę ostrożnym z tym, ponieważ może dać niepożądane wyniki z nieunikalnym indeksem, ponieważ może istnieć wiele wierszy indeksowanych przez jedną z etykiet w powyższej ix. Jeśli indeks nie jest unikatowy i chcesz tylko pierwsze 2 (lub n) wiersze, które spełniają klawisz logiczną byłoby bezpieczniejsze w użyciu .iloc z całkowitą indeksowania coś jak

ix = np.where(mask)[0][:2] 
df.iloc[ix, 'c'] = 1 
Powiązane problemy