Series的“Reduce”函数

31

在pandas Series中有没有类似于reduce的模拟函数?

例如,map的仿函数是pd.Series.apply,但我找不到任何reduce的仿函数。


我的应用场景是,我有一个包含列表的pandas Series:

>>> business["categories"].head()

0                      ['Doctors', 'Health & Medical']
1                                        ['Nightlife']
2                 ['Active Life', 'Mini Golf', 'Golf']
3    ['Shopping', 'Home Services', 'Internet Servic...
4    ['Bars', 'American (New)', 'Nightlife', 'Loung...
Name: categories, dtype: object

我想使用reduce将不同列表的Series合并在一起,就像这样:

categories = reduce(lambda l1, l2: l1 + l2, categories)

但是这需要很长时间,因为在Python中合并两个列表需要 O(n) 的时间。我希望pd.Series有一种矢量化的方法可以更快地执行此操作。

5个回答

33

使用itertools.chain()在值上操作

这可能会更快:

from itertools import chain
categories = list(chain.from_iterable(categories.values))

性能

from functools import reduce
from itertools import chain

categories = pd.Series([['a', 'b'], ['c', 'd', 'e']] * 1000)

%timeit list(chain.from_iterable(categories.values))
1000 loops, best of 3: 231 µs per loop

%timeit list(chain(*categories.values.flat))
1000 loops, best of 3: 237 µs per loop

%timeit reduce(lambda l1, l2: l1 + l2, categories)
100 loops, best of 3: 15.8 ms per loop

对于这个数据集,chain 连锁法比其他方法快约 68 倍。

向量化?

向量化适用于具有本地 NumPy 数据类型的数据(毕竟 pandas 使用 NumPy 进行其数据处理)。由于我们在 Series 中已经有了列表并且希望得到列表作为结果,因此向量化很可能不会加速处理速度。标准 Python 对象与 pandas/NumPy 数据类型之间的转换可能会消耗掉向量化可能带来的所有性能优势。我在另一个答案中尝试过将算法向量化。


有趣。我会对链的这些优化在底层是如何实现感兴趣。 - hlin117
2
reduce 函数会构建许多需要内存分配的中间列表,而内存分配是很慢的。使用 chain 可以显著减少内存分配的数量。 - Mike Müller
它有效。但我希望有一种更向量化的方法。暂时,即使它非常好,我也会放弃选择它作为答案。 - hlin117
1
我在另一个答案中添加了一个向量化的解决方案。但它要慢得多。请参见上面的解释。 - Mike Müller
我刚刚运行了性能指标,在我的机器上,第二个算法始终比第一个快约30微秒。也许你可以再次运行它们并更新答案?可能是Python的性能发生了变化。 - Niels Bom

7

向量化但速度较慢

您可以使用NumPy的concatenate函数:

import numpy as np

list(np.concatenate(categories.values))

性能

但是我们已经有了列表,即Python对象。因此,向量化必须在Python对象和NumPy数据类型之间切换。这会使事情变得很慢:

categories = pd.Series([['a', 'b'], ['c', 'd', 'e']] * 1000)

%timeit list(np.concatenate(categories.values))
100 loops, best of 3: 7.66 ms per loop

%timeit np.concatenate(categories.values)
100 loops, best of 3: 5.33 ms per loop

%timeit list(chain.from_iterable(categories.values))
1000 loops, best of 3: 231 µs per loop

如果输入是以numpy的形式给出的,那么这个会更快,对吗? - Gulzar

1
你可以尝试使用 business["categories"].str.join(''),但我猜想 Pandas 使用了 Python 的字符串函数。我怀疑你无法比 Python 提供的更好。

0
如果你有空值,你可以先过滤它们。
from itertools import chain

my_series = my_df.apply(lambda x: [j for j in x if j != None])
list(chain.from_iterable(my_series.values))

0

我使用了 "".join(business["categories"])

这比business["categories"].str.join('') 快得多,但仍然比 itertools.chain 方法慢4倍。 我更喜欢它,因为它更易读且不需要导入。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接