当时间戳不唯一时,在Pandas DataFrame中按日期计算观察数的最佳方法是什么?
df = pd.DataFrame({'User' : ['A', 'B', 'C'] * 40,
'Value' : np.random.randn(120),
'Time' : [np.random.choice(pd.date_range(datetime.datetime(2013,1,1,0,0,0),datetime.datetime(2013,1,3,0,0,0),freq='H')) for i in range(120)]})
理想情况下,输出将提供每天的观测数量(或其他一些更高阶的时间单位)。然后可以使用它来绘制活动随时间的变化。
2013-01-01 60
2013-01-02 60
答案 0 :(得分:3)
“un-Panda-ic”这样做的方法是在转换为日期的一系列日期时间上使用Counter对象,将此计数器转换回系列,并将此系列的索引强制转换为日期时间。
In[1]: from collections import Counter
In[2]: counted_dates = Counter(df['Time'].apply(lambda x: x.date()))
In[3]: counted_series = pd.Series(counted_dates)
In[4]: counted_series.index = pd.to_datetime(counted_series.index)
In[5]: counted_series
Out[5]:
2013-01-01 60
2013-01-02 60
更多“Panda-ic”方式是对系列使用groupby操作,然后按长度聚合输出。
In[1]: grouped_dates = df.groupby(df['Time'].apply(lambda x : x.date()))
In[2]: grouped_dates['Time'].aggregate(len)
Out[2]:
2013-01-01 60
2013-01-02 60
编辑:从here借来的另一个非常简洁的可能性是使用nunique
类:
In[1]: df.groupby(df['Time'].apply(lambda x : x.date())).agg({'Time':pd.Series.nunique})
Out[1]:
2013-01-01 60
2013-01-02 60
除了风格差异之外,还有一个比其他人具有显着的性能优势吗?是否有其他内置的方法我忽略了?
答案 1 :(得分:3)
编辑: 更快的另一个解决方案是使用value_counts
(以及normalize):
In [41]: %timeit df1 = df.set_index('Time'); pd.value_counts(df1.index.normalize(), sort=False)
1000 loops, best of 3: 586 µs per loop
如果您使用DatetimeIndex,我原以为这更简洁地写成resample
:
然而它似乎明显变慢了,并且(令人惊讶的是)Counter解决方案是最快的!
In [11]: df1 = df.set_index('Time')
In [12]: df1.User.resample('D', how=len)
Out[12]:
Time
2013-01-01 59
2013-01-02 58
2013-01-03 3
Freq: D, Name: User, dtype: int64
始终值得查看一些时间段:
In [21]: %timeit df1.User.resample('D', how=len)
1000 loops, best of 3: 720 µs per loop
不幸的是set_index
使这更加昂贵:
In [22]: %timeit df1 = df.set_index('Time'); df1.User.resample('D', how=len)
1000 loops, best of 3: 1.1 ms per loop
比较
In [23]: %%timeit
....: grouped_dates = df.groupby(df['Time'].apply(lambda x : x.date()))
....: grouped_dates['Time'].aggregate(len)
....:
1000 loops, best of 3: 788 µs per loop
In [24]: %%timeit
....: counted_dates = Counter(df['Time'].apply(lambda x: x.date()))
....: counted_series = pd.Series(counted_dates)
....: counted_series.index = pd.to_datetime(counted_series.index)
....:
1000 loops, best of 3: 568 µs per loop
我曾怀疑过更多日期会有所不同......
In [31]: df = pd.DataFrame({'User' : ['A', 'B', 'C'] * 400,
'Value' : np.random.randn(1200),
'Time' : [np.random.choice(pd.date_range(datetime.datetime(1992,1,1,0,0,0),datetime.datetime(2014,1,1,0,0,0),freq='H')) for i in range(1200)]})
In [32]: %timeit df1 = df.set_index('Time'); df1.User.resample('D', how=len)
10 loops, best of 3: 28.7 ms per loop
In [33]: %%timeit
....: grouped_dates = df.groupby(df['Time'].apply(lambda x : x.date()))
....: grouped_dates['Time'].aggregate(len)
....:
100 loops, best of 3: 6.82 ms per loop
In [34]: %%timeit
....: counted_dates = Counter(df['Time'].apply(lambda x: x.date()))
....: counted_series = pd.Series(counted_dates)
....: counted_series.index = pd.to_datetime(counted_series.index)
....:
100 loops, best of 3: 3.04 ms per loop
但是Counter仍然赢了......!
编辑:但是被value_counts打破了:
In [42]: %timeit df1 = df.set_index('Time'); pd.value_counts(df1.index.normalize(), sort=False)
1000 loops, best of 3: 989 µs per loop
答案 2 :(得分:0)
len(Series.unique())可能更快。
在我的电脑上:
%timeit df1 = df.set_index('Time'); pd.value_counts(df1.index.normalize(), sort=False)
1000 loops, best of 3: 2.06 ms per loop
,而
%timeit df1 = df.set_index('Time'); len(df1.index.normalize().unique())
1000 loops, best of 3: 1.04 ms per loop
有趣的是, len(Series.unique())通常比 Series.nunique()快得多。 对于具有多达x000个项目的小型阵列,其速度提高了10-15倍,对于具有数百万个项目的大型阵列,其速度提高了3-4倍。