2016-03-29 2 views
3

У меня есть очень большой панд набор данных, где данные выглядят какКак ускорить замену отсутствующих значений для каждой группы groupby в Pandas?

df = pd.DataFrame({'group1' : ['A', 'A', 'A', 'A', 
         'B', 'B', 'B', 'B'], 
        'group2' : ['C', 'C', 'C', 'D', 
         'E', 'E', 'F', 'F'], 
        'B' : ['one', np.NaN, np.NaN, np.NaN, 
         np.NaN, 'two', np.NaN, np.NaN], 
        'C' : [np.NaN, 1, np.NaN, np.NaN, 
         np.NaN, np.NaN, np.NaN, 4]})  




df 
Out[64]: 
    B C group1 group2 
0 one NaN  A  C 
1 NaN 1  A  C 
2 NaN NaN  A  C 
3 NaN NaN  A  D 
4 NaN NaN  B  E 
5 two NaN  B  E 
6 NaN NaN  B  F 
7 NaN 4  B  F 

Здесь вы можете увидеть, что для каждой уникальной комбинации group1 и group2, столбцы B и C содержат более одного не-неопределенную переменную ,

В каждой группе groupby(['group1','group2']), я заменяю все недостающие значения, используя это уникальное не пропущенное значение (в этой группе), если это значение существует.

Для этого я использую функцию first доступной после groupby, которая распространяется первым-нонами недостающего значения для B или C в каждой группе к остальной части недостающих значений в этой группе:

df[['B','C']]=df.groupby(['group1','group2']).transform('first')  



df 
Out[62]: 
    B C group1 group2 
0 one 1  A  C 
1 one 1  A  C 
2 one 1  A  C 
3 NaN NaN  A  D 
4 two NaN  B  E 
5 two NaN  B  E 
6 NaN 4  B  F 
7 NaN 4  B  F 

К сожалению, это болезненно медленное на моем очень большом наборе данных. Вы видите какой-либо способ улучшить скорость здесь? Я думал о fillna, но, похоже, мне нужно будет применять его дважды (ffill и bfill) ... Любые идеи?

ОБНОВЛЕНИЕ Возможно ли эффективное решение, предложенное ajcr, работать с группами, определенными несколькими столбцами? map в этом случае не работает. Может быть merge?

ответ

3

На моей машине, он может почти в 100 раз быстрее использовать groupby, а затем map:

g = df.groupby('group', sort=False).first() 

df['B'] = df['group'].map(g['B']) 
df['C'] = df['group'].map(g['C']) 

Вот тест DataFrame 1000 групп и 10000 строк:

df = pd.DataFrame({'group': np.repeat(np.arange(1000), 10), 
        'B': np.nan, 
        'C': np.nan}) 

df.ix[4::10, 'B':'C'] = 5 # every 4th row of a group is non-null 

И тайминги:

%%timeit 

df2 = df.copy() 

g = df2.groupby('group', sort=False).first() 

df2['B'] = df2['group'].map(g['B']) 
df2['C'] = df2['group'].map(g['C']) 

Это возвращает 100 loops, best of 3: 2.29 ms per loop ,

Метод transform находится почти в 100 раз медленнее:

%%timeit 

df3 = df.copy() 

df3[['B','C']] = df3.groupby('group').transform('first') 

Это возвращает 1 loops, best of 3: 205 ms per loop.


Что касается Вас обновленными вопрос относительно использования нескольких групп, @ предложение Джеффа в комментариях ниже использования

df['B'] = df.groupby(['group1','group2']).B.transform('first') 
df['C'] = df.groupby(['group1','group2']).C.transform('first') 

примерно в 50 раз быстрее, чем преобразование обе колонки на одном дыхании. Это связано с тем, что в настоящее время transform гораздо более эффективен для серии, хотя есть newly-created issue, чтобы увеличить скорость работы с DataFrames.

+0

гений. lemme попробуйте это решение –

+0

вы можете просто объяснить, что здесь делает «карта»? –

+1

Я играл с чем-то похожим на 'a [['B', 'C']] = a.apply (lambda x: g.loc [x.group, ['B', 'C']], axis = 1) ', но он был намного медленнее. Итак, +1 за ваше решение – MaxU

1

Давайте скорость его немного:

In [130]: a = df.copy() 

In [131]: %timeit a['B'],a['C'] = a.B.fillna(method='ffill'), a.C.fillna(method='bfill') 
1000 loops, best of 3: 538 µs per loop 

In [132]: a = df.copy() 

In [133]: %timeit a[['B','C']]=a.groupby('A').transform('first') 
100 loops, best of 3: 3 ms per loop 
+0

спасибо maxU, но мы точно не знаем, где это не пропущенное значение в каждой группе. Другими словами, выполнение «ffill» на B может не работать. Вот почему я упоминал двойные 'ffill' и' bfill' в каждом столбце ... –

+1

@Noobie, не могли бы вы, пожалуйста, обновить свой образец DF соответственно? – MaxU

+0

уверен, что одна секунда –

1

Что делать, если вы пошли об этом по-другому и не пытаться заполнить, но вместо этого перестроен?

unique_df = df.drop_duplicates() 

bVal = unique_df.drop(['B'],axis = 1).dropna().set_index(['A']) 
cVal = unique_df.drop(['C'],axis = 1).dropna().set_index(['A']) 

colVals = pd.merge(bVal,cVal, how = 'outer',left_index = True, right_index = True) 
output = pd.merge(df[['A']],colVals, how = 'left',left_on = 'A',right_index = True) 

термоусадочного ФР до уникальных деталей, найти уникальные строки на меньшем dataframe, а затем объединить обратно восстановить большую рамку - быстрее?

Смежные вопросы