2017-08-27 52 views
5

Rozważmy następujący dataframeJak warunkowo usunąć duplikaty z dataframe pandy

import pandas as pd 
df = pd.DataFrame({'A' : [1, 2, 3, 3, 4, 4, 5, 6, 7], 
        'B' : ['a','b','c','c','d','d','e','f','g'], 
        'Col_1' :[np.NaN, 'A','A', np.NaN, 'B', np.NaN, 'B', np.NaN, np.NaN], 
        'Col_2' :[2,2,3,3,3,3,4,4,5]}) 
df 
Out[92]: 
    A B Col_1 Col_2 
0 1 a NaN  2 
1 2 b  A  2 
2 3 c  A  3 
3 3 c NaN  3 
4 4 d  B  3 
5 4 d NaN  3 
6 5 e  B  4 
7 6 f NaN  4 
8 7 g NaN  5 

Chcę usunąć wszystkie wiersze, które są duplikatami w odniesieniu do kolumny 'A' 'B'. Chcę usunąć wpis, który ma wpis NaN (wiem, że dla wszystkich dulicates będzie wpis NaN i not-NaN). Końcowe wyniki powinny wyglądać następująco

A B Col_1 Col_2 
0 1 a NaN  2 
1 2 b  A  2 
2 3 c  A  3 
4 4 d  B  3 
6 5 e  B  4 
7 6 f NaN  4 
8 7 g NaN  5 

Wszystkie wydajne, jedna wkładki są jak najbardziej mile widziane

+0

W obronie przeciwnej wyborcy mogłeś wybrać przykład, w którym po prostu 'df.drop_duplicates' dał ci odpowiedź, której nie chcesz. –

+0

tak, tak. Akceptuję downvote :) – mortysporty

Odpowiedz

3

Oto alternatywa:

df[~((df[['A', 'B']].duplicated(keep=False)) & (df.isnull().any(axis=1)))] 
# A B Col_1 Col_2 
# 0 1 a NaN  2 
# 1 2 b  A  2 
# 2 3 c  A  3 
# 4 4 d  B  3 
# 6 5 e  B  4 
# 7 6 f NaN  4 
# 8 7 g NaN  5 

używa bitowego „nie” operator ~ negować wiersze, które spełniają wspólną warunek bycia duplikat wiersz (argument keep=False powoduje, że metoda ma wartość True dla wszystkich nieunikalnych wierszy) i zawiera co najmniej jedną wartość pustą. Więc przy czym wyrażenie df[['A', 'B']].duplicated(keep=False) zwrotu tej serii:

# 0 False 
# 1 False 
# 2  True 
# 3  True 
# 4  True 
# 5  True 
# 6 False 
# 7 False 
# 8 False 

... i wyrażenia df.isnull().any(axis=1) zwrotu tej serii:

# 0  True 
# 1 False 
# 2 False 
# 3  True 
# 4 False 
# 5  True 
# 6 False 
# 7  True 
# 8  True 

... możemy owinąć zarówno w nawiasach (wymagane przez składni pandy, gdy za pomocą wielu wyrażenia w operacjach indeksowania), a następnie zawiń je w nawiasach ponownie ponownie, abyśmy mogli zanegować całe wyrażenie (np. ~(...)), tak jak:

~((df[['A','B']].duplicated(keep=False)) & (df.isnull().any(axis=1))) & (df['Col_2'] != 5) 

# 0  True 
# 1  True 
# 2  True 
# 3 False 
# 4  True 
# 5 False 
# 6  True 
# 7  True 
# 8 False 

Można budować bardziej złożone warunki przy dalszym użyciu operatorów logicznych & i | (operator "lub"). Podobnie jak w SQL, pogrupuj swoje warunki zgodnie z dodatkowymi nawiasami; na przykład filtr oparty na warunku logicznym "zarówno stan X, jak i stan Y jest zgodny z prawdą, lub stan Z jest prawdziwy" z df[ ((X) & (Y)) | (Z) ].

+0

Myślę, że musisz przekazać 'keep = False' do' duplicated' aby to działało. – ayhan

+0

@ayhan Właśnie myślałem to samo :) – cmaher

+0

Cześć. Czy to działałoby, gdyby istniała jakaś inna wartość niż "NaN", której chcieliśmy się pozbyć? Czy możemy po prostu naprawić argument po '&'? – mortysporty

5

Jeśli celem jest tylko upuść NaN duplikatów, potrzebne jest nieco bardziej zaangażowane rozwiązanie.

Najpierw sortuj na A, B i Col_1, tak aby NaN s zostały przesunięte na dół dla każdej grupy. Następnie zadzwonić df.drop_duplicates z keep=first:

out = df.sort_values(['A', 'B', 'Col_1']).drop_duplicates(['A', 'B'], keep='first') 
print(out) 

    A B Col_1 Col_2 
0 1 a NaN  2 
1 2 b  A  2 
2 3 c  A  3 
4 4 d  B  3 
6 5 e  B  4 
7 6 f NaN  4 
8 7 g NaN  5 
+0

Może :) ale czy to gwarantuje, że wartości "NanaN" z 'Col_1' są wiersze, które są przechowywane? – mortysporty

+1

@mortysporty Edytowane. –

+0

Dostałem zarówno głosowanie, jak i przegłosowanie tego postu.Sądzę, że zasłużyłem na stwierdzenie, jak łatwa jest odpowiedź. Ale rozwiązałeś go o wiele szybciej, niż bym to zrobił :) dzięki! – mortysporty

1

Lub możesz po prostu użyć first(), używając pierwszego, zwróci pierwszą wartość notnull, więc kolejność oryginalnych danych wejściowych tak naprawdę nie ma znaczenia.

df.groupby(['A','B']).first() 

Out[180]: 
    Col_1 Col_2 
A B    
1 a NaN  2 
2 b  A  2 
3 c  A  3 
4 d  B  3 
5 e  B  4 
6 f NaN  4 
7 g NaN  5