我有一个130M行的数据帧,这是一个示例:
id id2 date value
0 33208381500016 1927637 2014-07-31 120.0
1 77874276700016 3418498 2014-11-22 10.5
2 77874276700016 1174018 2014-11-22 8.4
3 77874276700016 1174018 2014-11-20 1.4
4 77874276700016 1643839 2014-06-27 4.2
5 77874276700016 1972929 2014-06-27 6.7
6 77874276700016 1972929 2014-06-27 12.7
7 77874276700016 1588191 2014-02-20 123.4
8 77874276700016 1966627 2014-02-20 973.1
9 77874276700016 1830252 2014-02-20 0.5
我需要在此数据框上执行groupby
(称为data
)。对于像groupby
这样的简单sum
没问题:
data[['id','value']].groupby('id',as_index=False).sum()
time: 11.19s
但现在我需要检索另一列(或它的长度)中的值列表。以下代码有效,但需要很长时间,有更有效的方法吗?
temp = data[['id','date','id2']].drop_duplicates()
temp.groupby('id',as_index = False).agg({'date': lambda x: set(x.tolist()),'id2':lambda x: len(set(x.tolist()))})
time: 159s
第一个问题:
是否有更有效的方法可以计算每id2
个唯一id
的数量,但仍使用此群组?我的意思是我不想拆分两个小组,因为它可能需要更长的时间(执行一个组合,2个聚合大约需要1.5倍的单一组合)。
第二个问题:
是否有更有效的方法来检索唯一日期列表?我知道它已在this question中解决,但我不能简单地使用.apply(list)
。
答案 0 :(得分:3)
要获取唯一日期,请使用SeriesGroupBy.unique()
。要计算每个组中唯一id2
的数量,请使用SeriesGroupBy.nunique()
。
temp = data[['id', 'date', 'id2']].drop_duplicates()
temp.groupby('id', as_index=False).agg({'date': 'unique', 'id2': 'nunique'})
预先不丢弃重复项可能会更快 - 熊猫只需要在所有数据上迭代一次而不是两次。
data.groupby('id', as_index=False).agg({'date': 'unique', 'id2': 'nunique'})
编辑:
以下是一些基准测试。有趣的是,SeriesGroupBy.unique()
和SeriesGroupBy.nunique()
似乎不比使用集更快。但是之前不要删除重复项。
import io
import pandas as pd
raw = io.StringIO("""\
id id2 date value
0 33208381500016 1927637 2014-07-31 120.0
1 77874276700016 3418498 2014-11-22 10.5
2 77874276700016 1174018 2014-11-22 8.4
3 77874276700016 1174018 2014-11-20 1.4
4 77874276700016 1643839 2014-06-27 4.2
5 77874276700016 1972929 2014-06-27 6.7
6 77874276700016 1972929 2014-06-27 12.7
7 77874276700016 1588191 2014-02-20 123.4
8 77874276700016 1966627 2014-02-20 973.1
9 77874276700016 1830252 2014-02-20 0.5
""")
data = pd.read_csv(raw, delim_whitespace=True)
def using_sets_drop_then_group():
temp = data[['id', 'date', 'id2']].drop_duplicates()
temp.groupby('id', as_index=False).agg({'date': lambda x: set(x),
'id2': lambda x: len(set(x))})
def using_sets_drop_just_group():
data.groupby('id', as_index=False).agg({'date': lambda x: set(x),
'id2': lambda x: len(set(x))})
def using_unique_drop_then_group():
temp = data[['id', 'date', 'id2']].drop_duplicates()
temp.groupby('id', as_index=False).agg({'date': 'unique', 'id2': 'nunique'})
def using_unique_just_group():
data.groupby('id', as_index=False).agg({'date': 'unique', 'id2': 'nunique'})
%timeit using_sets_drop_then_group() # => 100 loops, best of 3: 4.82 ms per loop
%timeit using_sets_drop_just_group() # => 100 loops, best of 3: 2.91 ms per loop
%timeit using_unique_drop_then_group() # => 100 loops, best of 3: 5.14 ms per loop
%timeit using_unique_just_group() # => 100 loops, best of 3: 3.26 ms per loop
编辑:
在评论中,如果日期转换为SeriesGroupBy.unique()
,@ ptrj建议SeriesGroupBy.nunique()
和datetime64
可能会更快。唉,似乎并非如此,至少对于这个小数据样本而言。
data['parsed_date'] = pd.to_datetime(data['date'])
def using_sets_and_datetime64():
data.groupby('id', as_index=False).agg({'parsed_date': lambda x: set(x),
'id2': lambda x: len(set(x))})
def using_unique_and_datetime64():
data.groupby('id', as_index=False).agg({'parsed_date': 'unique',
'id2': 'nunique'})
%timeit using_sets_and_datetime64() # => 100 loops, best of 3: 3.2 ms per loop
%timeit using_unique_and_datetime64() # => 100 loops, best of 3: 3.53 ms per loop
编辑:
@ MaxU建议连接100,000份样本数据确实导致SeriesGroupBy.unique()
和SeriesGroupBy.nunique()
超越set
。
large_data = pd.concat([data] * 10**5, ignore_index=True)
def using_sets():
large_data.groupby('id', as_index=False).agg({'date': lambda x: set(x),
'id2': lambda x: len(set(x))})
def using_unique():
large_data.groupby('id', as_index=False).agg({'date': 'unique',
'id2': 'nunique'})
def using_sets_and_datetime64():
large_data.groupby('id', as_index=False).agg({'parsed_date': lambda x: set(x),
'id2': lambda x: len(set(x))})
def using_unique_and_datetime64():
large_data.groupby('id', as_index=False).agg({'parsed_date': 'unique',
'id2': 'nunique'})
%timeit using_sets() # => 1 loops, best of 3: 295 ms per loop
%timeit using_unique() # => 1 loops, best of 3: 327 ms per loop
%timeit using_sets_and_datetime64() # => 1 loops, best of 3: 5.02 s per loop
%timeit using_unique_and_datetime64() # => 1 loops, best of 3: 248 ms per loop