【发布时间】:2025-12-24 16:30:12
【问题描述】:
尊敬的 * 用户,
我的 python 脚本面临性能问题,因为我必须处理包含超过 10 亿个元素的二维表,以获取数百个输入文件的潜在列表。我一直在用 numpy 数组操作调用替换我的嵌套 for 循环,在这个过程中,我发现 numpy.take (根据一组索引查找元素)和 numpy.outer (评估两个一维数组元素之间的所有可能乘积)非常有用。这些函数让我可以在可以使用它们的地方将代码的性能提高数百倍。
但是在我的代码中仍有一个地方存在问题,这个地方是我将包含十亿个元素的二维数组聚集成一个元素少得多(比如几千个)的 4D 数组。具体来说,我有两个索引列表,其大小等于矩阵的行数(这是一个方阵)。
第一个索引列表是 th_t,第二个列表是 dm_t,矩阵是 p_contact。聚集元素的 4D 数组被命名为 rc_p。聚类过程如下嵌套for循环:
import numpy as np
th_t = [1, 3, 2, 1, 1, 3, 3, 0, 1, 0, 2, 1]
dm_t = [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0]
n_th = len(set(th_t))
n_dm = len(set(dm_t))
p_contact = [[0.0129, 0.0134, 0.0062, 0.0021, 0.0107, 0.0106, 0.0076, 0.0134, 0.0087, 0.0031, 0.0026, 0.0114]
[0.0123, 0.0021, 0.0033, 0.0120, 0.0099, 0.0125, 0.0001, 0.0018, 0.0030, 0.0059, 0.0038, 0.0125]
[0.0082, 0.0125, 0.0004, 0.0120, 0.0040, 0.0108, 0.0101, 0.0063, 0.0072, 0.0098, 0.0017, 0.0121]
[0.0096, 0.0008, 0.0073, 0.0100, 0.0123, 0.0104, 0.0077, 0.0025, 0.0106, 0.0126, 0.0031, 0.0033]
[0.0112, 0.0091, 0.0134, 0.0002, 0.0129, 0.0081, 0.0087, 0.0036, 0.0102, 0.0002, 0.0019, 0.0131]
[0.0099, 0.0081, 0.0037, 0.0004, 0.0135, 0.0005, 0.0025, 0.0086, 0.0091, 0.0016, 0.0130, 0.0011]
[0.0078, 0.0005, 0.0044, 0.0089, 0.0127, 0.0106, 0.0113, 0.0048, 0.0057, 0.0133, 0.0077, 0.0033]
[0.0017, 0.0010, 0.0048, 0.0052, 0.0113, 0.0066, 0.0133, 0.0092, 0.0020, 0.0125, 0.0011, 0.0023]
[0.0027, 0.0124, 0.0096, 0.0047, 0.0134, 0.0020, 0.0129, 0.0114, 0.0087, 0.0114, 0.0090, 0.0001]
[0.0032, 0.0014, 0.0038, 0.0114, 0.0058, 0.0017, 0.0089, 0.0057, 0.0022, 0.0056, 0.0046, 0.0094]
[0.0033, 0.0020, 0.0042, 0.0040, 0.0110, 0.0016, 0.0100, 0.0014, 0.0087, 0.0123, 0.0004, 0.0031]
[0.0010, 0.0029, 0.0054, 0.0015, 0.0064, 0.0060, 0.0131, 0.0064, 0.0073, 0.0097, 0.0132, 0.0092]]
n_sg = len(p_contact)
rc_p = np.zeros((n_th, n_dm, n_th, n_dm))
for i in range(n_sg): #n_sg can be about 40000
for j in range(n_sg):
rc_p[th_t[i]][dm_t[i]][th_t[j]][dm_t[j]] += p_contact[i][j]
我尝试使用各种 numpy 函数来避免嵌套十亿个元素的 for 循环,最后我得到了以下过程:
import numpy as np
th_t = [1, 3, 2, 1, 1, 3, 3, 0, 1, 0, 2, 1]
dm_t = [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0]
n_th = len(set(th_t))
n_dm = len(set(dm_t))
p_contact = [[0.0129, 0.0134, 0.0062, 0.0021, 0.0107, 0.0106, 0.0076, 0.0134, 0.0087, 0.0031, 0.0026, 0.0114]
[0.0123, 0.0021, 0.0033, 0.0120, 0.0099, 0.0125, 0.0001, 0.0018, 0.0030, 0.0059, 0.0038, 0.0125]
[0.0082, 0.0125, 0.0004, 0.0120, 0.0040, 0.0108, 0.0101, 0.0063, 0.0072, 0.0098, 0.0017, 0.0121]
[0.0096, 0.0008, 0.0073, 0.0100, 0.0123, 0.0104, 0.0077, 0.0025, 0.0106, 0.0126, 0.0031, 0.0033]
[0.0112, 0.0091, 0.0134, 0.0002, 0.0129, 0.0081, 0.0087, 0.0036, 0.0102, 0.0002, 0.0019, 0.0131]
[0.0099, 0.0081, 0.0037, 0.0004, 0.0135, 0.0005, 0.0025, 0.0086, 0.0091, 0.0016, 0.0130, 0.0011]
[0.0078, 0.0005, 0.0044, 0.0089, 0.0127, 0.0106, 0.0113, 0.0048, 0.0057, 0.0133, 0.0077, 0.0033]
[0.0017, 0.0010, 0.0048, 0.0052, 0.0113, 0.0066, 0.0133, 0.0092, 0.0020, 0.0125, 0.0011, 0.0023]
[0.0027, 0.0124, 0.0096, 0.0047, 0.0134, 0.0020, 0.0129, 0.0114, 0.0087, 0.0114, 0.0090, 0.0001]
[0.0032, 0.0014, 0.0038, 0.0114, 0.0058, 0.0017, 0.0089, 0.0057, 0.0022, 0.0056, 0.0046, 0.0094]
[0.0033, 0.0020, 0.0042, 0.0040, 0.0110, 0.0016, 0.0100, 0.0014, 0.0087, 0.0123, 0.0004, 0.0031]
[0.0010, 0.0029, 0.0054, 0.0015, 0.0064, 0.0060, 0.0131, 0.0064, 0.0073, 0.0097, 0.0132, 0.0092]]
#prepare the flattened list of index pairs
th_t = np.asarray(th_t)
dm_t = np.asarray(dm_t)
thdm_stack = np.stack((th_t, dm_t))
thdm_stack = np.transpose(thdm_stack)
thdm_table = np.asarray(list(product(thdm_stack, thdm_stack)))
p_contact_f = p_contact.flatten()
#calculate clustered probabilities for each contact type
rc_p = np.zeros((n_th, n_dm, n_th, n_dm))
for th1 in range(n_th):
for dm1 in range(n_dm):
for th2 in range(n_th):
for dm2 in range(n_dm):
to_find = np.zeros((2, 2))
to_find[0][0] = th1
to_find[0][1] = dm1
to_find[1][0] = th2
to_find[1][1] = dm2
condition = np.isin(thdm_table, to_find)
condition = np.all(condition, axis=(1, 2))
to_add = np.extract(condition, p_contact_f)
rc_p[th1][dm1][th2][dm2] = np.sum(to_add)
这最终比原始过程慢,而不是更快,可能是因为我必须生成一个 10 亿大小的布尔矩阵,并在 4D for 循环的数千个步骤中处理它(它的元素比最初的 for 循环,只是为了提醒)。
那么,你们中是否有人知道如何替换这个昂贵的嵌套 for 循环并最大限度地利用 numpy 的底层 C 代码将这个大的 2D 矩阵聚集到更小的 4D 数组中?
请注意,这些数组中的各个元素是概率。 2D 数组和 4D 集群数组中所有元素的总和为 1,并且“集群”是指将概率分组为类型(显示相同索引集的 2D 矩阵的所有单元格将它们的概率加到一个4D 聚集数组元素)。
一切顺利!
【问题讨论】:
-
什么是
i_th和i_dm? -
你能加一个minimal reproducible example吗?基本上我们需要一些测试数组和一个预期的输出,即使它比你的实际用例小得多,这样我们就知道输出应该是什么。
-
最后,您需要
numpy唯一的解决方案,还是我们也可以使用scipy? -
好的,感谢您的评论。我可以接受 scipy 解决方案。我唯一想做的就是加速这段代码,这要归功于将 python 代码投影到任何库提供的底层 C 代码上的可能性。我将立即使示例最小化且可验证。
-
你绑定到
python 2.7了吗?你看过numba吗?
标签: arrays python-2.7 numpy