【问题标题】:Most efficient way to count sub-dataframe rows for each row?为每行计算子数据帧行的最有效方法?
【发布时间】:2019-12-10 23:23:24
【问题描述】:

想象一下,我有一些 pandas 数据框,如下所示:

                    creationDate
188080 2019-08-01 21:28:39+03:00
188081 2019-08-01 21:33:13+03:00
188082 2019-08-01 21:39:53+03:00
188083 2019-08-01 21:43:24+03:00
188084 2019-08-01 21:48:17+03:00
188085 2019-08-01 21:52:56+03:00
188086 2019-08-01 21:58:27+03:00
188087 2019-08-01 22:10:50+03:00
188088 2019-08-01 22:14:58+03:00
188089 2019-08-01 22:17:43+03:00

我想创建一个名为density 的新列。对于每一行,它的意思是“在当前单元格creationDate 之后和当前单元格creationDate + 1 hour 之前有多少行”

我有一个矢量化函数,但在我的数据集(大约 1.500.000 行)上,它的运行速度不是很快(在我的笔记本电脑上花了大约 5 分钟)。

def get_density(date, distance_in_minute):
    start_datetime = date
    end_datetime = date + np.timedelta64(distance_in_minute, 'm')

    return df.loc[
        (df['creationDate'] >= str(start_datetime)) & (df['creationDate'] < str(end_datetime))
    ].shape[0]

np_get_density = np.vectorize(lambda x: get_density(x, 60))

df['density'] = np_get_density(df['creationDate'])

如何优化此查询?提前致谢!

【问题讨论】:

    标签: python pandas dataframe optimization


    【解决方案1】:

    这似乎是使用multiprocessing.Pool 的map 方法的好选择。 get_density 函数本质上是工作函数。

    然而,一个工作函数只有一个参数,最好也提供一个对数据框的引用。所以使用functools.partial 将distance_in_minute 和数据帧提供给get_density,然后再将结果用作工作函数。

    在最好的情况下,如果您的 CPU 有 N 个内核,这将使其大​​约快 N 倍。所以如果你有一个 4 核 CPU,时间应该从 5 分钟到大约 1.25 分钟。

    【讨论】:

      【解决方案2】:

      如何使用 Dask 。它是一个python并行计算库,可以比纯python快很多。

      一些笔记(很高兴拥有)-

      1) 它不支持多索引。

      2) 应为列分配适当的数据类型。

      from dask import dataframe as dd
      from multiprocessing import cpu_count
      import pandas as pd
      
      df=df.reset_index()
      df.creationDate=pd.DataFrame(df.creationDate)
      
      def get_density(date):
          distance_in_minute=60
          start_datetime = date
          end_datetime = pd.to_datetime(date) + np.timedelta64(distance_in_minute, 'm')
      
          return (df.loc[
              (df['creationDate'] >= str(start_datetime)) & (df['creationDate'] < str(end_datetime))
          ].shape[0])
      
      nCores = cpu_count()
      
      dd=dd.from_pandas(df,npartitions=nCores)
      dd['density']=dd.creationDate.apply(get_density,meta=('density', int))
      df=dd.compute()
      

      比np.vectorize() 更好的解决方案可以是df.apply。 您可以尝试以下方法:

      df['density']=(df.apply(lambda x: get_density(x.creationDate),axis=1))
      

      【讨论】:

        【解决方案3】:

        您可以在使用datetimeIndex 创建系列后将rolling 与时间一起使用。因为您想及时向前看,所以一旦您使用[::-1] 反转了列 creationDate 的顺序,您需要首先通过将每个日期和最大值之间的时间增量添加到随机日期来反转索引的顺序。这是一种方法:

        df['density'] = (pd.Series(1, #create a Series with 1 as value but you can use anything
                                          # index need a start date, anyone is fine
                                   index= pd.to_datetime("today") + 
                                          # time delta between each rows once reverse and the max
                                       (df.creationDate.max() - df.creationDate[::-1]))
                           .rolling('20T') # with the given data, I use 20 minutes as interval,
                                           # change it to 1H for 1 hour, or 60T
                           .count() #count the number of rows within the rolling window
                           .values[::-1]) #reverse the values to come back to the original order
        

        你得到

        print (df)
                            creationDate  density
        188080 2019-08-01 21:28:39+03:00      5.0
        188081 2019-08-01 21:33:13+03:00      5.0
        188082 2019-08-01 21:39:53+03:00      5.0
        188083 2019-08-01 21:43:24+03:00      4.0
        188084 2019-08-01 21:48:17+03:00      3.0
        188085 2019-08-01 21:52:56+03:00      3.0 #here you have 3 rows within 20 minutes
        188086 2019-08-01 21:58:27+03:00      4.0
        188087 2019-08-01 22:10:50+03:00      3.0
        188088 2019-08-01 22:14:58+03:00      2.0
        188089 2019-08-01 22:17:43+03:00      1.0
        

        【讨论】:

          猜你喜欢
          • 2011-03-22
          • 1970-01-01
          • 2021-08-21
          • 1970-01-01
          • 1970-01-01
          • 2013-04-17
          • 2020-10-11
          • 2020-05-21
          • 2011-07-13
          相关资源
          最近更新 更多