2015-10-09 14 views
10

Często potrzebuję nowej kolumny, która jest najlepsza z innych kolumn i mam określoną listę priorytetów preferencji. Jestem gotów przyjąć pierwszą wartość nie mającą wartości null.Czy jest lepszy czytelniejszy sposób na zwijanie kolumn w pandach?

def coalesce(values): 
    not_none = (el for el in values if el is not None) 
    return next(not_none, None) 

df = pd.DataFrame([{'third':'B','first':'A','second':'C'}, 
        {'third':'B','first':None,'second':'C'}, 
        {'third':'B','first':None,'second':None},     
        {'third':None,'first':None,'second':None}, 
        {'third':'B','first':'A','second':None}]) 

df['combo1'] = df.apply(coalesce, axis=1) 
df['combo2'] = df[['second','third','first']].apply(coalesce, axis=1) 
print df 

Wyniki

first second third combo1 combo2 
0  A  C  B  A  C 
1 None  C  B  C  C 
2 None None  B  B  B 
3 None None None None None 
4  A None  B  A  B 

ten kod działa (a wynik jest to, co chcę), ale to nie jest bardzo szybki.
Wybieram priorytety, jeśli chcę [["drugi", "trzeci", "pierwszy"]]

Łączą się trochę jak funkcja o tej samej nazwie z tsql.
Podejrzewam, że mogę przeoczyć łatwy sposób go osiągnąć dobrą wydajność na dużych DataFrames (+400,000 wierszach)

Wiem, że istnieje wiele sposobów na uzupełnienie brakujących danych, które często używam na osi = 0 to właśnie sprawia, że ​​mogłem przegapić łatwą opcję dla osi = 1

Czy możesz zaproponować coś ładniejszego/szybciej ... lub potwierdzić, że jest to tak dobre, jak to tylko możliwe.

Odpowiedz

0

Można użyć pd.isnull znaleźć null - w tym przypadku None - wartości:

In [169]: pd.isnull(df) 
Out[169]: 
    first second third 
0 False False False 
1 True False False 
2 True True False 
3 True True True 
4 False True False 

a następnie użyć np.argmin znaleźć indeks pierwszego wartości niż null. Jeżeli wszystkie wartości są null, np.argmin zwraca 0:

In [186]: np.argmin(pd.isnull(df).values, axis=1) 
Out[186]: array([0, 1, 2, 0, 0]) 

Następnie można wybrać żądane wartości z df użyciu NumPy całkowitą-indeksowania:

In [193]: df.values[np.arange(len(df)), np.argmin(pd.isnull(df).values, axis=1)] 
Out[193]: array(['A', 'C', 'B', None, 'A'], dtype=object) 

Na przykład,

import pandas as pd 
df = pd.DataFrame([{'third':'B','first':'A','second':'C'}, 
        {'third':'B','first':None,'second':'C'}, 
        {'third':'B','first':None,'second':None},     
        {'third':None,'first':None,'second':None}, 
        {'third':'B','first':'A','second':None}]) 

mask = pd.isnull(df).values 
df['combo1'] = df.values[np.arange(len(df)), np.argmin(mask, axis=1)] 
order = np.array([1,2,0]) 
mask = mask[:, order] 
df['combo2'] = df.values[np.arange(len(df)), order[np.argmin(mask, axis=1)]] 

plony

first second third combo1 combo2 
0  A  C  B  A  C 
1 None  C  B  C  C 
2 None None  B  B  B 
3 None None None None None 
4  A None  B  A  B 

Stosując argmin zamiast df3.apply(coalesce, ...) znacznie szybciej, jeśli DataFrame ma wiele rzędów:

df2 = pd.concat([df]*1000) 

In [230]: %timeit mask = pd.isnull(df2).values; df2.values[np.arange(len(df2)), np.argmin(mask, axis=1)] 
1000 loops, best of 3: 617 µs per loop 

In [231]: %timeit df2.apply(coalesce, axis=1) 
10 loops, best of 3: 84.1 ms per loop 
24

pandy równoważne COALESCE jest metoda fillna():

result = column_a.fillna(column_b) 

wynikiem jest kolumna, w której każda wartość jest pobierana z column_a, jeśli kolumna zawiera wartość inną niż null, w przeciwnym razie wartość jest pobierana z column_b.Więc combo1 mogą być produkowane z:

df['first'].fillna(df['second']).fillna(df['third']) 

dawanie:

0  A 
1  C 
2  B 
3 None 
4  A 

i twoi combo2 mogą być produkowane z:

(df['second']).fillna(df['third']).fillna(df['first']) 

który zwraca nową kolumnę:

0  C 
1  C 
2  B 
3 None 
4  B 

Jeśli chciał sprawnego funkcjonowania nazwie coalesce, to może po prostu połączyć z fillna() kolumny od lewej do prawej, a następnie zwraca wynik:

def coalesce(df, column_names): 
    i = iter(column_names) 
    column_name = next(i) 
    answer = df[column_name] 
    for column_name in i: 
     answer = answer.fillna(df[column_name]) 
    return answer 

print coalesce(df, ['first', 'second', 'third']) 
print coalesce(df, ['second', 'third', 'first']) 

co daje:

0  A 
1  C 
2  B 
3 None 
4  A 

0  C 
1  C 
2  B 
3 None 
4  B 
+1

wielką odpowiedź. . – thecheech

Powiązane problemy