我有以下代码,我想根据给定条件从其他列表中提取某些值。但是我的数据集很大~每个列表中有 100 万个值。因此这种嵌套循环的方法耗时太长。是否有使用 Numpy 的矢量化或更快的方法,我可以使用它来加速我的代码并使用更少的内存?
import random
import numpy as np
x=[random.randrange(0,10) for _ in range(0,100)]
y=[random.randrange(0,10) for _ in range(0,100)]
z=[random.randrange(0,10) for _ in range(0,100)]
x_unique=np.unique(x)
xx_list=[]
y_list=[]
z_list=[]
for i in range(len(x_unique)):
xx_list.append([])
y_list.append([])
z_list.append([])
for ii, i in enumerate(x_unique):
for j,k in enumerate(x):
if i == k:
xx_list[ii].append(x[j])
y_list[ii].append(y[j])
z_list[ii].append(z[j])
[编辑:添加了一个预期的例子]
在列表:y_list 和 z_list 中,我想存储与 xx_list 中存储的相同索引号对应的值。
例如考虑以下示例列表:
x = [0.1,0.1,1,0.1,2,1,0.1]
y = [1.1,2.1,3,4,5,6,7]
z = [10,11,12,13.1,14,15,16]
因此,x_unique 如下:
x_unique = [0.1,1,2]
xx_list、y_list 和 z_list 应包含以下内容:
xx_list = [[0.1,0.1,0.1,0.1],[1,1],[2]]
y_list = [[1.1,2.1,4,7],[3,6],[5]]
z_list = [[10,11,13.1,16],[12,15],[14]]
最佳答案
我找到了一个解决方案,该解决方案处理 Python 列表的 100 万个项目大约需要 400 毫秒。以及处理 numpy 数组时需要 100 毫秒的解决方案。
python
我使用它为每个输入列表(x
、y
、z
)构建一个字典的策略。这些中的每一个都将充当一组标记的垃圾箱。对于每个输入列表,bin i
将包含其在列表 x
中的相应索引等于 i
的项目。对应意味着他们在各自的列表中处于相同的位置。
def compute_bins(x, y, z):
# You can see this as an ordered-set:
x_bin_indexes = {a:i for i, a in enumerate(sorted(set(x)))}
# Each input list has its own set of labeled bins:
x_bins = defaultdict(list)
y_bins = defaultdict(list)
z_bins = defaultdict(list)
for item_x, item_y, item_z in zip(x, y, z):
index = x_bin_indexes[item_x]
# Drop the item in the corresponding bin:
x_bins[index].append(item_x)
y_bins[index].append(item_y)
z_bins[index].append(item_z)
# Now we transform the result back to lists of list:
x_bins = list(x_bins.values())
y_bins = list(y_bins.values())
z_bins = list(z_bins.values())
return x_bins, y_bins, z_bins
这里的关键因素是我们在循环中进行的每个操作都是在常数时间内进行的。该函数可以这样调用:
>>> xx_list, y_list, z_list = compute_bins(x, y, z)
>>> xx_list
[[0, 0, 0, 0], [1, 1], [2]]
>>> y_list
[[1, 2, 4, 7], [3, 6], [5]]
>>> z_list
[[10, 11, 13, 16], [12, 15], [14]]
NumPy
使用 numpy,我想到了一个不同的策略:根据 x
中的项目对所有数组进行排序,然后根据 x
中连续相同值的数量拆分它们。这是代码(考虑到 x
、y
和 z
是 numpy 数组):
import numpy as np
def compute_bins(x, *others):
x_bin_indexes, bin_sizes = np.unique(x, return_counts=True)
sort_order = np.argsort(x)
split_rule = np.cumsum(bin_sizes)[:-1]
return tuple(np.split(o[sort_order], split_rule) for o in (x, *others))
请注意,np.cumsum(bin_sizes)[:-1]
之所以存在,是因为 split
采用要切割的索引列表,而不是切割列表尺寸。如果我们想将[0, 0, 0, 1, 1, 2]
拆分为[[0, 0, 0], [1, 1], [2]]
我们不将 [3, 2, 1]
传递给 np.split
,而是传递给 [3, 5]
。
表演
关于性能,这是我机器上的表现:
from random import randint
test_size = int(1e6)
x = [randint(0, 100) for _ in range(test_size)]
y = [i+1 for i in range(test_size)]
z = [i+test_size+1 for i in range(test_size)]
%timeit xx_list, y_list, z_list = compute_bins(x, y, z)
纯 python 版本的输出:
396 ms ± 5.98 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
numpy 版本(x
、y
和 z
的输出是 np.array
):
105 ms ± 1.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
为了比较,您首先提出的解决方案给出了:
19.7 s ± 282 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
关于python - 如何通过应用 numpy 向量化使用条件检查从 python 列表或 numpy 数组中提取值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55430966/