用字典中的频率/值替换 NumPy 数组条目

Replacing NumPy array entries with their frequencies / values from dictionary

问题:我想从两个输入数组中输出一个数组,其真值频率(来自input_2)对应于input_1的每个值。

import numpy as np   # import everything from numpy
from scipy.stats import itemfreq
input_1 = np.array([3,6,6,3,6,4])
input_2 = np.array([False, True, True, False, False, True])

对于此示例,我想要的输出是:

output_1 = np.array([0,2,2,0,2,1])

我目前的方法涉及编辑 input_1,因此只保留对应于 True 的值:

locs=np.where(input_2==True,input_1,0)

然后计算每个答案的频率,创建字典并将 input_1 的适当键替换为值(真实频率)。

loc_freq = itemfreq(locs)
dic = {}
for key,val in loc_freq:
    dic[key]=val
print dic
for k, v in dic.iteritems():
    input_1[input_1==k]=v

输出 [3,2,2,3,2,1].

这里的问题是双重的: 1) 这仍然不对不在字典中的键做任何事情(因此应该更改为 0)。例如,如何将 3 转换为 0? 2)这看起来很不优雅/无效。有没有更好的方法来解决这个问题?

np.bincount 就是您要找的。

output_1 = np.bincount(input_1[input_2])[input_1]

@memecs 解决方案正确,+1。但是,如果 input_1 中的值非常大,即它们不是数组的索引,而是说它们是秒或其他一些可以取非常大值的整数数据,它将非常慢并且占用大量内存.

在这种情况下,np.bincount(input_1[input_2]).size 等于 input_1 中的最大整数,input_2 中的值为 True

使用unique and bincount要快得多。我们使用第一个来提取 input_1 的唯一元素的索引,然后使用 bincount 来计算这些索引在同一数组中出现的频率,并对它们进行权衡 10基于数组的值input_2TrueFalse):

# extract unique elements and the indices to reconstruct the array
unq, idx = np.unique(input_1, return_inverse=True)
# calculate the weighted frequencies of these indices
freqs_idx = np.bincount(idx, weights=input_2)
# reconstruct the array of frequencies of the elements
frequencies = freqs_idx[idx]
print(frequencies)

这个解决方案非常快并且对内存的影响最小。感谢@Jaime,请参阅下面的评论。下面我以不同的方式使用 unique 报告我的原始答案。

其他可能性

使用 unique:

寻求其他解决方案可能会更快
import numpy as np
input_1 = np.array([3, 6, 6, 3, 6, 4])
input_2 = np.array([False, True, True, False, False, True])

non_zero_hits, counts = np.unique(input_1[input_2], return_counts=True)
all_hits, idx = np.unique(input_1, return_inverse=True)
frequencies = np.zeros_like(all_hits)

#2nd step, with broadcasting
idx_non_zero_hits_in_all_hits = np.where(non_zero_hits[:, np.newaxis] - all_hits == 0)[1]
frequencies[idx_non_zero_hits_in_all_hits] = counts
print(frequencies[idx])

如果input_2中具有True值的input_1中的唯一元素数量很多,这将需要大量内存,因为2D数组已创建并传递给 where。为了减少内存占用,您可以在算法的第 2 步中使用 for 循环:

#2nd step, but with a for loop.
for j, val in enumerate(non_zero_hits):
    index = np.where(val == all_hits)[0]
    frequencies[index] = counts[j]
print(frequencies[idx])

第二个解决方案的内存占用非常小,但需要 for 循环。这取决于您的典型数据输入大小和最佳解决方案的值。

目前接受的 bincount 解决方案非常优雅,但是 numpy_indexed 包为此类问题提供了更通用的解决方案:

import numpy_indexed as npi
idx = npi.as_index(input_1)
unique_labels, true_count_per_label = npi.group_by(idx).sum(input_2)
print(true_count_per_label[idx.inverse])