在熊猫中按日期分组后,计算观察结果,而日期是非唯一的



当时间戳不是唯一时,按日期计数日期计算观察的最佳方法是什么?

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

编辑:更快的另一个解决方案是使用 value_counts(并归一化):

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但是它似乎较慢,并且(令人惊讶的是)计数器解决方案最快

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

但计数器仍然赢了...

编辑:但是被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

这样做的" un-panda-ic"方法将在转换为日期的一系列数据列表上使用计数器对象,将此计数器转换回系列,然后胁迫索引在该系列到数据时。

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

一种更"熊猫-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

编辑:从这里借来的另一种高度简洁的可能性是使用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

除了风格上的差异外,一个人是否具有明显的性能优势?我是否忽略了其他内置方法?

len(series.unique())可能更快。

在我的PC上:

%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())通常比 seriper.nunique()要快得多。对于最多X000项目的小阵列,它的速度快10-15倍,对于具有数百万个项目的较大阵列,它的速度更快。

最新更新