【问题标题】:"Reduce" function for Series系列的“减少”功能
【发布时间】:2016-05-02 11:47:07
【问题描述】:

熊猫系列是否有reduce 的类似物?

例如,map 的模拟是 pd.Series.apply,但我找不到 reduce 的任何模拟。


我的应用是,我有一个 pandas 系列的列表:

>>> 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 将一系列列表合并在一起,如下所示:

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

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

【问题讨论】:

    标签: python performance pandas vectorization reduce


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      您可以使用business["categories"].str.join('') 试试运气,但我猜 Pandas 使用 Python 的字符串函数。我怀疑你能比 Python 已经为你提供的更好。

      【讨论】:

        【解决方案3】:

        在值上使用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
        

        对于这个数据集,chaining 大约快 68 倍。

        矢量化?

        当您拥有原生 NumPy 数据类型时,向量化就可以工作(毕竟 pandas 使用 NumPy 作为其数据)。由于我们已经在 Series 中有列表并且想要一个列表作为结果,因此矢量化不太可能加快速度。标准 Python 对象和 pandas/NumPy 数据类型之间的转换可能会消耗掉你可能从矢量化中获得的所有性能。我尝试在另一个答案中对算法进行矢量化。

        【讨论】:

        • 有趣。我会对链的这些优化如何在后台实现感兴趣。
        • reduce 构建了很多中间列表,这些中间列表都需要分配内存。分配内存很慢。使用chain 可以显着减少内存分配次数。
        • 它有效。但我希望有一种更加矢量化的方法。现在,我不会选择这个作为答案,即使它非常好。
        • 我在另一个答案中添加了矢量化解决方案。但它要慢得多。请参阅上面的解释。
        • 我刚刚运行了性能指标,在我的机器上,第二个算法始终快了约 30 µs。也许您可以再次运行它们并更新答案?可能是某些 Python 性能发生了变化。
        【解决方案4】:

        矢量化但速度慢

        你可以使用 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 给出的,这会更快,对吗?
        猜你喜欢
        • 2020-11-01
        • 2018-10-29
        • 1970-01-01
        • 2020-03-25
        • 2014-09-26
        • 1970-01-01
        • 2019-11-02
        • 2014-03-08
        • 2012-03-18
        相关资源
        最近更新 更多