【问题标题】:Counting observations after grouping by dates in pandas, when dates are non-unique当日期不唯一时,在熊猫中按日期分组后计数观察值
【发布时间】:2014-02-16 01:55:53
【问题描述】:

当时间戳不唯一时,在 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

【问题讨论】:

    标签: python pandas datetime pandas-groupby


    【解决方案1】:

    编辑:另一个更快的解决方案是使用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
    

    但计数器仍然获胜......!

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

      执行此操作的“非 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
      

      一种更“熊猫式”的方式是对系列使用 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
      

      除了风格上的差异之外,其中一种是否比另一种具有显着的性能优势?还有其他我忽略的内置方法吗?

      【讨论】:

        【解决方案3】:

        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 倍。

        【讨论】:

          猜你喜欢
          • 2020-10-22
          • 1970-01-01
          • 1970-01-01
          • 2013-10-26
          • 2017-05-26
          • 2016-12-16
          • 2020-07-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多