Python 应急 Table
Python Contingency Table
作为我正在编写的项目的一部分,我正在生成很多很多偶然事件 table。
工作流程是:
- 采用具有连续(浮点)行的大型数据数组,并通过装箱将其转换为离散整数值(例如,结果行的值为 0-9)
- 将两行切成向量 X 和 Y,并从中生成 contingency table,这样我就有了二维频率分布
- 例如,我有一个 10 x 10 的数组,计算出现的 (xi, yi) 的数量
- 使用偶然性table做一些信息论数学
最初,我这样写:
def make_table(x, y, num_bins):
ctable = np.zeros((num_bins, num_bins), dtype=np.dtype(int))
for xn, yn in zip(x, y):
ctable[xn, yn] += 1
return ctable
这工作正常,但是太慢了,以至于它占用了整个项目 90% 的运行时间。
我能想到的最快的 python 优化是这样的:
def make_table(x, y, num_bins):
ctable = np.zeros(num_bins ** 2, dtype=np.dtype(int))
reindex = np.dot(np.stack((x, y)).transpose(),
np.array([num_bins, 1]))
idx, count = np.unique(reindex, return_counts=True)
for i, c in zip(idx, count):
ctable[i] = c
return ctable.reshape((num_bins, num_bins))
这(不知何故)快了很多,但对于看起来不应该成为瓶颈的东西来说,它仍然相当昂贵。是否有任何我没有看到的有效方法来执行此操作,或者我应该放弃并在 cython 中执行此操作?
另外,这里有一个基准测试函数。
def timetable(func):
size = 5000
bins = 10
repeat = 1000
start = time.time()
for i in range(repeat):
x = np.random.randint(0, bins, size=size)
y = np.random.randint(0, bins, size=size)
func(x, y, bins)
end = time.time()
print("Func {na}: {ti} Ms".format(na=func.__name__, ti=(end - start)))
将 np.stack((x, y))
的元素表示为整数的巧妙技巧可以更快:
In [92]: %timeit np.dot(np.stack((x, y)).transpose(), np.array([bins, 1]))
109 µs ± 6.55 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [94]: %timeit bins*x + y
12.1 µs ± 260 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
此外,您的第二个解决方案的最后部分可以稍微简化,只需考虑
np.unique(bins * x + y, return_counts=True)[1].reshape((bins, bins))
更重要的是,由于我们处理的是等距非负整数,np.bincount
将优于 np.unique
;这样,以上归结为
np.bincount(bins * x + y).reshape((bins, bins))
总而言之,这比您当前正在做的事情提供了相当多的性能:
In [78]: %timeit make_table(x, y, bins) # Your first solution
3.86 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [79]: %timeit make_table2(x, y, bins) # Your second solution
443 µs ± 23.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [101]: %timeit np.unique(bins * x + y, return_counts=True)[1].reshape((bins, bins))
307 µs ± 25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [118]: %timeit np.bincount(bins * x + y).reshape((10, 10))
30.3 µs ± 3.44 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
您可能还想了解 np.histogramdd
,它同时处理舍入和合并,尽管它可能比舍入和使用 np.bincount
慢。
作为我正在编写的项目的一部分,我正在生成很多很多偶然事件 table。
工作流程是:
- 采用具有连续(浮点)行的大型数据数组,并通过装箱将其转换为离散整数值(例如,结果行的值为 0-9)
- 将两行切成向量 X 和 Y,并从中生成 contingency table,这样我就有了二维频率分布
- 例如,我有一个 10 x 10 的数组,计算出现的 (xi, yi) 的数量
- 使用偶然性table做一些信息论数学
最初,我这样写:
def make_table(x, y, num_bins):
ctable = np.zeros((num_bins, num_bins), dtype=np.dtype(int))
for xn, yn in zip(x, y):
ctable[xn, yn] += 1
return ctable
这工作正常,但是太慢了,以至于它占用了整个项目 90% 的运行时间。
我能想到的最快的 python 优化是这样的:
def make_table(x, y, num_bins):
ctable = np.zeros(num_bins ** 2, dtype=np.dtype(int))
reindex = np.dot(np.stack((x, y)).transpose(),
np.array([num_bins, 1]))
idx, count = np.unique(reindex, return_counts=True)
for i, c in zip(idx, count):
ctable[i] = c
return ctable.reshape((num_bins, num_bins))
这(不知何故)快了很多,但对于看起来不应该成为瓶颈的东西来说,它仍然相当昂贵。是否有任何我没有看到的有效方法来执行此操作,或者我应该放弃并在 cython 中执行此操作?
另外,这里有一个基准测试函数。
def timetable(func):
size = 5000
bins = 10
repeat = 1000
start = time.time()
for i in range(repeat):
x = np.random.randint(0, bins, size=size)
y = np.random.randint(0, bins, size=size)
func(x, y, bins)
end = time.time()
print("Func {na}: {ti} Ms".format(na=func.__name__, ti=(end - start)))
将 np.stack((x, y))
的元素表示为整数的巧妙技巧可以更快:
In [92]: %timeit np.dot(np.stack((x, y)).transpose(), np.array([bins, 1]))
109 µs ± 6.55 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [94]: %timeit bins*x + y
12.1 µs ± 260 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
此外,您的第二个解决方案的最后部分可以稍微简化,只需考虑
np.unique(bins * x + y, return_counts=True)[1].reshape((bins, bins))
更重要的是,由于我们处理的是等距非负整数,np.bincount
将优于 np.unique
;这样,以上归结为
np.bincount(bins * x + y).reshape((bins, bins))
总而言之,这比您当前正在做的事情提供了相当多的性能:
In [78]: %timeit make_table(x, y, bins) # Your first solution
3.86 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [79]: %timeit make_table2(x, y, bins) # Your second solution
443 µs ± 23.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [101]: %timeit np.unique(bins * x + y, return_counts=True)[1].reshape((bins, bins))
307 µs ± 25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [118]: %timeit np.bincount(bins * x + y).reshape((10, 10))
30.3 µs ± 3.44 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
您可能还想了解 np.histogramdd
,它同时处理舍入和合并,尽管它可能比舍入和使用 np.bincount
慢。