Hai câu trả lời hàng đầu ở đây gợi ý:
df.groupby(cols).agg(lambda x:x.value_counts().index[0])
hoặc, tốt hơn là
df.groupby(cols).agg(pd.Series.mode)
Tuy nhiên, cả hai đều không thành công trong các trường hợp cạnh đơn giản, như được minh họa ở đây:
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]
})
Đầu tiên:
df.groupby(['client_id', 'date']).agg(lambda x:x.value_counts().index[0])
sản lượng IndexError
(vì Dòng trống được trả về theo nhóm C
). Thư hai:
df.groupby(['client_id', 'date']).agg(pd.Series.mode)
trả về ValueError: Function does not reduce
, vì nhóm đầu tiên trả về một danh sách gồm hai (vì có hai chế độ). (Theo tài liệu ở đây , nếu nhóm đầu tiên trả về một chế độ duy nhất, điều này sẽ hoạt động!)
Hai giải pháp khả thi cho trường hợp này là:
import scipy
x.groupby(['client_id', 'date']).agg(lambda x: scipy.stats.mode(x)[0])
Và giải pháp được cs95 đưa ra cho tôi trong phần bình luận ở đây :
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)
Tuy nhiên, tất cả những điều này đều chậm và không phù hợp với các bộ dữ liệu lớn. Một giải pháp mà tôi đã kết thúc bằng cách sử dụng a) có thể giải quyết những trường hợp này và b) nhanh hơn rất nhiều, là một phiên bản được sửa đổi nhẹ của câu trả lời abw33 (phải cao hơn):
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
Về cơ bản, phương thức này hoạt động trên một col tại một thời điểm và xuất ra một df, vì vậy, thay vì concat
chuyên sâu, bạn xử lý đầu tiên như một df, và sau đó thêm lặp đi lặp lại mảng đầu ra ( values.flatten()
) dưới dạng một cột trong df.