Python 从groupby返回列表的最有效方法
我有一个130M行数据帧,下面是一个示例:Python 从groupby返回列表的最有效方法,python,list,pandas,dataframe,grouping,Python,List,Pandas,Dataframe,Grouping,我有一个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.
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
第一个问题:
是否有更有效的方法来计算每个id
的唯一id2
的数量,但仍使用此groupby?我的意思是,我不想拆分两个groupby,因为这可能需要更长的时间(使用两个聚合执行一个groupby大约需要1.5倍的时间)
第二个问题:
是否有更有效的方法检索唯一日期列表?我知道它已经在中解决了,但我不能简单地使用
。应用(列表)
要获得唯一的日期,请使用。要计算每组中唯一的id2
的数量,请使用
事先不删除重复项可能会更快—pandas只需在所有数据上迭代一次,而不是两次
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
编辑:
在评论中,@ptrj建议将日期转换为datetime64
,则SeriesGroupBy.unique()和SeriesGroupBy.nunique()可能会更快。唉,情况似乎并非如此,至少对于这一小部分数据来说是如此
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建议连接100000份样本数据,这确实导致了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
您的示例df中似乎没有值
。@JonClements好吧,实际上我对它简化得太多了。一开始就修复了它-set
将使用一个iterable,因此您可以执行set(x)
而不是set(x.tolist())
,这将节省一些开销。。。这对你的时间安排有什么影响?你把日期当作字符串<如果将日期转换为datetime64
类型(且数据较大),则code>unique
可能比set
更快。Timeit使用1M行DF:data=pd.concat([data]*10**5,忽略索引=True)
,但不要删除重复项,因为它太小了。。。设置(…)
解决方案应该慢得多。。。
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