计算每行子数据帧行的最有效方法?

时间:2019-08-02 10:28:56

标签: python pandas dataframe optimization

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

                    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'])

如何优化此查询?预先感谢!

3 个答案:

答案 0 :(得分:1)

这似乎是使用map的{​​{1}}方法的不错选择。 multiprocessing.Pool函数本质上将是辅助函数。

但是,一个辅助函数只能得到一个参数,最好还提供一个对数据帧的引用。因此,在将结果用作辅助函数之前,请使用get_densityfunctools.partial提供distance_in_minute和数据帧。

在最佳情况下,如果您的CPU具有 N 个内核,这将使其速度快大约 N 倍。因此,如果您有4核CPU,则时间应该从5分钟缩短到大约1.25分钟。

答案 1 :(得分:1)

如何使用 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))

答案 2 :(得分:1)

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

[::-1]

你会得到

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