【问题标题】:efficient, fast numpy histograms高效、快速的 numpy 直方图
【发布时间】:2014-08-22 09:00:27
【问题描述】:
我有一个 2D numpy 数组,由 ca. 15'000'000 个数据点。每个数据点都有一个时间戳和一个整数值(40 到 200 之间)。我必须创建数据点分布的直方图(16 个箱:40-49、50-59 等),按年份、当年的月份、当年的周和当月的天排序。
现在,我想知道实现此目的最有效的方法是什么。考虑到阵列的大小,性能是一个突出的考虑因素。我正在考虑嵌套“for”循环,按年、按月等分解数组。但我读到 numpy 数组具有很高的内存效率,并且有各种技巧可以快速处理。所以我想知道是否有更快的方法来做到这一点。您可能已经意识到,我是一名业余程序员(“现实生活”中的分子生物学家),我的问题可能相当幼稚。
【问题讨论】:
标签:
python
arrays
performance
numpy
histogram
【解决方案1】:
首先,填写您的 16 个垃圾箱,完全不考虑日期。
然后,按日期对每个 bin 中的元素进行排序。
现在,您可以使用二分搜索在每个 bin 中有效地定位给定的年/月/周。
【解决方案2】:
为了做到这一点,numpy、numpy.bincount 中有一个函数。它的速度非常快。它是如此之快,以至于您可以为每个整数(161 个 bin)和天(可能 30000 个不同的天?)创建一个 bin,从而产生几百万个 bin。
程序:
- 计算每个 bin 的整数索引(例如 17 x 从文件第一天算起的天数 + (integer - 40)//10)
- 运行
np.bincount
- 重塑为正确的形状(天数,17)
现在您有了分箱数据,然后可以将这些数据集中到时间维度中所需的任何分箱中。
在不知道输入数据形式的情况下,整数 bin 计算代码可能是这样的:
# let us assume we have the data as:
# timestamps: 64-bit integer (seconds since something)
# values: 8-bit unsigned integer with integers between 40 and 200
# find the first day in the sample
first_day = np.min(timestamps) / 87600
# we intend to do this but fast:
indices = (timestamps / 87600 - first_day) * 17 + ((values - 40) / 10)
# get the bincount vector
b = np.bincount(indices)
# calculate the number of days in the sample
no_days = (len(b) + 16) / 17
# reshape b
b.resize((no_days, 17))
需要注意的是b中的第一天和最后一天取决于数据。在测试中,大部分时间都花在计算指数上(使用 i7 处理器大约需要 400 毫秒)。如果需要减少,可以使用numexpr 模块在大约 100 毫秒内完成。然而,实际的实现很大程度上取决于时间戳的形式。有些计算速度更快,有些计算速度较慢。
但是,如果每天需要数据,我怀疑是否有任何其他分箱方法会更快。
如果您想对(逐年、逐周等)或其他一些分箱方法有不同的看法,我不太了解您的问题。无论如何,归结为将相关行加在一起。
【解决方案3】:
这是一个解决方案,它使用了以下链接中的 group_by 功能:
http://pastebin.com/c5WLWPbp
import numpy as np
dates = np.arange('2004-02', '2005-05', dtype='datetime64[D]')
np.random.shuffle(dates)
values = np.random.randint(40,200, len(dates))
years = np.array(dates, dtype='datetime64[Y]')
months = np.array(dates, dtype='datetime64[M]')
weeks = np.array(dates, dtype='datetime64[W]')
from grouping import group_by
bins = np.linspace(40,200,17)
for m, g in zip(group_by(months)(values)):
print m
print np.histogram(g, bins=bins)[0]
或者,你可以看看 pandas 包,它可能也有一个优雅的解决这个问题的方法。