ここでの2つの上位の回答は次のことを示唆しています。
df.groupby(cols).agg(lambda x:x.value_counts().index[0])
または、できれば
df.groupby(cols).agg(pd.Series.mode)
ただし、ここに示すように、これらは両方とも単純なエッジケースでは失敗します。
df = pd.DataFrame({
'client_id':['A', 'A', 'A', 'A', 'B', 'B', 'B', 'C'],
'date':['2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01'],
'location':['NY', 'NY', 'LA', 'LA', 'DC', 'DC', 'LA', np.NaN]
})
最初:
df.groupby(['client_id', 'date']).agg(lambda x:x.value_counts().index[0])
収量IndexError
(グループによって返された空のシリーズのためC
)。二番目:
df.groupby(['client_id', 'date']).agg(pd.Series.mode)
戻りValueError: Function does not reduce
、最初のグループは、2つのリストを返すので、(2つのモードがあるので)。(ここに記載されているように、最初のグループがシングルモードを返した場合、これは機能します!)
この場合の2つの可能な解決策は次のとおりです。
import scipy
x.groupby(['client_id', 'date']).agg(lambda x: scipy.stats.mode(x)[0])
そして、ここのコメントでcs95によって私に与えられた解決策:
def foo(x):
m = pd.Series.mode(x);
return m.values[0] if not m.empty else np.nan
df.groupby(['client_id', 'date']).agg(foo)
ただし、これらはすべて低速であり、大規模なデータセットには適していません。私が最終的に使用した解決策は、a)これらのケースに対処でき、b)はるかに高速で、abw33の回答を少し変更したものです(これはもっと高いはずです)。
def get_mode_per_column(dataframe, group_cols, col):
return (dataframe.fillna(-1)
.groupby(group_cols + [col])
.size()
.to_frame('count')
.reset_index()
.sort_values('count', ascending=False)
.drop_duplicates(subset=group_cols)
.drop(columns=['count'])
.sort_values(group_cols)
.replace(-1, np.NaN))
group_cols = ['client_id', 'date']
non_grp_cols = list(set(df).difference(group_cols))
output_df = get_mode_per_column(df, group_cols, non_grp_cols[0]).set_index(group_cols)
for col in non_grp_cols[1:]:
output_df[col] = get_mode_per_column(df, group_cols, col)[col].values
基本的に、このメソッドは一度に1つの列で機能し、dfを出力します。したがってconcat
、集中的なの代わりに、最初values.flatten()
の列をdfとして扱い、次に出力配列()をdfの列として繰り返し追加します。