如何在海量数据帧上提高 lambda 函数的性能

ale*_*xtc 6 python numpy pandas

我有一个df超过数亿行的数据。

     latitude                    longitude                   time                    VAL     
0   -39.20000076293945312500    140.80000305175781250000    1972-01-19 13:00:00     1.20000004768371582031  
1   -39.20000076293945312500    140.80000305175781250000    1972-01-20 13:00:00     0.89999997615814208984 
2   -39.20000076293945312500    140.80000305175781250000    1972-01-21 13:00:00     1.50000000000000000000 
3   -39.20000076293945312500    140.80000305175781250000    1972-01-22 13:00:00     1.60000002384185791016 
4   -39.20000076293945312500    140.80000305175781250000    1972-01-23 13:00:00     1.20000004768371582031
... ...
Run Code Online (Sandbox Code Playgroud)

它包含一个time类型为datetime64UTC的列。下面的代码是创建一个新列isInDST来指示time本地时区是否处于夏令时。

df['isInDST'] = pd.DatetimeIndex(df['time']).tz_localize('UTC').tz_convert('Australia/Victoria').map(lambda x : x.dst().total_seconds()!=0)
Run Code Online (Sandbox Code Playgroud)

处理 15,223,160 行大约需要 400 秒。

有没有更好的方法以更好的性能实现这一目标?是vectorize一个更好的办法?

Per*_*Pon 2

所有结果均根据 1M 数据点计算。

\n

赛通+np.vectorize

\n

比原始代码快 7.2 倍

\n
%%cython\nfrom cpython.datetime cimport datetime\ncpdef bint c_is_in_dst(datetime dt):\n    return dt.dst().total_seconds() != 0 \n\n%%timeit\ndf[\'isInDST\'] = np.vectorize(c_is_in_dst)(df[\'time\'].dt.tz_localize(\'UTC\').dt.tz_convert(\'Australia/Victoria\').dt.to_pydatetime())\n
Run Code Online (Sandbox Code Playgroud)\n

1.08 s \xc2\xb1 每个循环 10.2 ms

\n

np.vectorize

\n

比原始代码快 6.5 倍

\n
def is_in_dst(dt):\n    return dt.dst().total_seconds() != 0 \n\n%%timeit\ndf[\'isInDST\'] = np.vectorize(is_in_dst)(df[\'time\'].dt.tz_localize(\'UTC\').dt.tz_convert(\'Australia/Victoria\').dt.to_pydatetime())\n
Run Code Online (Sandbox Code Playgroud)\n

1.2 秒 \xc2\xb1 每个循环 29.3 毫秒

\n

根据文档The implementation is essentially a for loop ),我预计结果与列表理解相同,但它始终比列表理解好一点。

\n

列表理解

\n

比原代码快5.9倍

\n
%%timeit\ndf[\'isInDST\'] = [x.dst().total_seconds()!=0 for x in pd.DatetimeIndex(df[\'time\']).tz_localize(\'UTC\').tz_convert(\'Australia/Victoria\')]\n
Run Code Online (Sandbox Code Playgroud)\n

1.33 秒 \xc2\xb1 每个循环 48.4 毫秒

\n

这个结果表明,pandas map/apply非常慢,它增加了额外的开销,可以通过使用 python for 循环来消除。

\n

原始方法(map在 pandas DatetimeIndex 上)

\n
%%timeit\ndf[\'isInDST\'] = pd.DatetimeIndex(df[\'time\']).tz_localize(\'UTC\').tz_convert(\'Australia/Victoria\').map(lambda x : x.dst().total_seconds()!=0)\n
Run Code Online (Sandbox Code Playgroud)\n

7.82 秒 \xc2\xb1 每个循环 84.3 毫秒

\n

在 100 万行虚拟数据上进行测试

\n
N = 1_000_000\ndf = pd.DataFrame({"time": [datetime.datetime.now().replace(hour=random.randint(0,23),minute=random.randint(0,59)) for _ in range(N)]})\n
Run Code Online (Sandbox Code Playgroud)\n

另外,在 100K 和 10M 行上运行代码 - 结果与行数线性相关

\n