【问题标题】:Numpy/pandas optimization: bins countingNumpy/pandas 优化:垃圾箱计数
【发布时间】:2016-11-06 00:45:23
【问题描述】:

我想通过计算 beginend 之间的值的数量 N 来“合并”numpy.arraypandas.Series x。结果存储在pandas.DataFrame

import numpy as np
import pandas as pd
bins = pd.DataFrame({'from': np.arange(0, 1, 0.01), 'to': np.arange(0, 1, 0.01) + 0.1})
x = np.random.rand(1000000)
bins['N'] = bins.apply(lambda r:  ((x >= r['from']) & (x < r['to'])).sum(), axis=1)

当我分析代码时,整个脚本中最慢的部分(包含更多内容)如果最后一行,尤其是 lambda:15 % 的时间花在那个 lambda 上!

我感觉我必须以矢量化方式实现,而不是使用lambda,但我不知道如何。

我正在使用 Python 3.5、numpy 1.11 和 pandas 0.18.1


编辑:附加信息 + 使用间隔树进行测试

实际上,这种分箱是迭代使用的:分箱是用一些数据启动的,并且可能会用其他数据集进行更新。

正如建议的那样,我尝试过使用 intervaltree,但它的性能甚至变得最差。从第二次迭代开始,我收到了用户警告,因为 boold dtype 上的 numexpr 不支持“+”,所以它切换到纯 python 模式。

【问题讨论】:

  • 这是一个真实的例子吗?我问的原因是通常你可以只比较整个 df 但你的长度不匹配所以在这种情况下你不能这样做
  • 谢谢@EdChum。现实世界是这样的。事实上,我在 x 中有数百万个数据,我想将它们压缩到 1000 个 bin 中。因此,长度不匹配的事实确实是意料之中的 - 这就是压缩(近似)的结果
  • 那么你可以使用某种直方图之类的方法或pd.cut吗?
  • 你能检查root对这个问题的回答吗? stackoverflow.com/a/37287621/2285236
  • 谢谢@ayhan。我还没有看到那个帖子......但是间隔树变得最糟糕;我已经相应地更新了问题。

标签: python numpy pandas optimization lambda


【解决方案1】:

间隔是固定大小的事实可能会被滥用来大大加快代码速度。因此,设置好参数后,您可以使用NumPy's bincount procedure,就像这样-

# First off, filter out elements that are outside the min,max limits.
# Then subtract min_val from the filtered elements so that they all start from 0
# Then, scale them w.r.t width and floor them, thus converting them into IDs
IDs = ((x[(x >= min_val) & (x<=max_val)]-min_val)/width).astype(int)

# Finally count those IDs, which is the desired output as new column
bins['N'] = np.bincount(IDs)

因此,对于发布的示例,我们将参数设置为:

min_val = 0
max_val = 1
width = 0.1

示例运行 -

In [156]: # Params
     ...: min_val = 4
     ...: max_val = 8
     ...: width = 0.4
     ...: 
     ...: # Create inputs
     ...: bins = pd.DataFrame({'from': np.arange(4, 8, 0.4), 'to': 
     ...:                                   np.arange(4, 8, 0.4) + 0.4})
     ...: x = 10*np.random.rand(1000)
     ...: 

In [157]: bins['N'] = bins.apply(lambda r:  ((x >= r['from']) & \
     ...:                                      (x < r['to'])).sum(), axis=1)

In [158]: bins
Out[158]: 
   from   to   N
0   4.0  4.4  42
1   4.4  4.8  40
2   4.8  5.2  36
3   5.2  5.6  43
4   5.6  6.0  45
5   6.0  6.4  29
6   6.4  6.8  40
7   6.8  7.2  46
8   7.2  7.6  41
9   7.6  8.0  45

In [159]: IDs = ((x[(x >= min_val) & (x<=max_val)]-min_val)/width).astype(int)

In [160]: np.bincount(IDs)
Out[160]: array([42, 40, 36, 43, 45, 29, 40, 46, 41, 45])

【讨论】:

    【解决方案2】:

    如果 "...边界有固定宽度,如 [[min + 0 width, min + 1 width], [min + 1 width, min + 2 width], ..., [max - 1宽度,最大]]...",使用numpy.histogram:

    bins["N"] = numpy.histogram(x, numpy.concatenate([bins["from"], bins["to"].tail(1)]))[0]
    

    这会比这更简单,但如果你在 bins["to"] 中有最后一条边,则需要将它包含在 bins 边列表中。

    详情:http://docs.scipy.org/doc/numpy/reference/generated/numpy.histogram.html

    【讨论】:

    • 非常感谢@honza_p。这正是我所需要的。抱歉,我完全忘记了 np.histogram...
    • 还不完全正确...正在处理最后一块
    • 它似乎对我有用:binsList = np.concatenate([bins['from'].values, [bins['to'].values[-1], ]]); bins['N'] = np.histogram(data, binsList)[0]
    • 我上次的编辑现在应该是正确的。无论如何,我想表明方向;-)
    • 查看我的评论,它不是使用tail(1),而是[-1]。我认为它会更“直接”,但我不知道它是否会影响表演......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 2016-05-22
    • 1970-01-01
    • 2019-03-15
    相关资源
    最近更新 更多