重复和选择函数的轻微变化的快速 Numpy 计算
Fast Numpy calculation of a slight variation of the repeat and choose functions
我想用 numpy 来解决一个与 numpy.repeat 函数解决的问题非常相似但不完全相同的问题。我看不出如何使用我熟悉的任何 numpy 函数来解决这个问题,所以我正在寻求帮助,看看这是否可以用 numpy 来完成。我的数组很大(>1e6 个元素),高性能至关重要,所以我无法承受 python for 循环的性能损失。
最小示例
我有一个 length-num_pts 排序的整数数组 objID
存储(可能重复)对象标识符。
objID = np.array([0, 0, 5, 5, 5, 7, 8, 8])
我使用 numpy.unique 确定 objID
的唯一条目,以及它们在 objID
中出现的索引。
unique_objIDs, idx_unique_objIDs = np.unique(objID, return_index=True)
num_unique_objIDs = len(unique_objIDs)
我还有一个 length-num_unique_objIDs 数组 occupations
,它指定我想要 select 来自 objID
的 unique_objIDs
的每个条目多少次。
occupations = np.array([0, 2, 1, 2])
我想根据 occupations
确定可用于检索 objID
元素的索引数组。下面我举一个具体的例子。
desired_array_of_indices = np.array([2, 3, 5, 6, 7])
数组desired_array_of_indices
就是我要用numpy计算的。 desired_array_of_indices
的条目计算如下。
desired_array_of_indices
的明确解释
occupations
数组的第 i 个元素指定 unique_objID[i]
将被 select 编辑多少次。 desired_array_of_indices
数组存储这些 select 离子的 objID
的索引。对于 select 多次 select 的值,选择 objID
的 连续 个索引,因此没有索引存储在 desired_array_of_indices
重复。
为了具体起见,请考虑 occupations
的第一个元素。该值为零,告诉我们我们不想 select 存储 unique_objIDs[0]=0
的 objID
的任何索引,因此所有此类索引都被排除在 desired_array_of_indices
之外。
occupations
的下一个元素是 2,告诉我们要 select unique_objIDs[1]=5
在 objID
中的前 2 次出现的索引。这就是为什么 desired_array_of_indices
的前两个条目是 2 和 3。
occupations
的下一个元素是1,告诉我们要selectunique_objIDs[2]=7
在objID
中第一次出现的索引。所以 desired_array_of_indices
的下一个条目是 5。
occupations
的最后一个元素是2,告诉我们要selectunique_objIDs[3]=8
在objID
中的前2次出现的索引。这就是为什么 desired_array_of_indices
的最后两个条目是 6 和 7。
与 np.repeat
的区别
请注意此计算与 numpy.repeat
之间的细微差别。对于 numpy.repeat
,返回的索引属于唯一条目数组 unique_objIDs
。这里我需要 objID
的索引,对于重复条目的情况,我还需要 select 连续的索引。可以假定 occupations
的每个条目小于或等于相应条目在 objID
中出现的总次数,因此不存在索引错误的危险。
有没有人看到如何根据可用的矢量化 Numpy 函数(可能是一些集合)来表述这个问题?
这是一种方法。
首先,您的示例代码:
In [102]: objID = np.array([0, 0, 5, 5, 5, 7, 8, 8])
In [103]: unique_objIDs, idx_unique_objIDs = np.unique(objID, return_index=True)
[[注:unique()
对其参数进行排序。您知道您的输入已经排序,因此获得 idx_unique_objIDs
的更有效方法是:
idx_unique_objIDs = np.concatenate(([0], np.nonzero(np.diff(objID))[0] + 1))
此操作是 O(n) 而不是 unique
所需的 O(n*log(n))。然后你可以使用
unique_objIDs = objID[idx_unique_objIDs]
如果您需要唯一对象 ID 的数组。]]
In [104]: occupations = np.array([0, 2, 1, 2])
现在找到所需的索引。结果在 Out[107]
:
行
In [105]: csum = occupations.cumsum()
In [106]: n = csum[-1]
In [107]: np.arange(n) + np.repeat(idx_unique_objIDs - csum + occupations, occupations)
Out[107]: array([2, 3, 5, 6, 7])
细看:
csum
是occupations
的累加和,n
是occupations
的总和:
In [114]: csum
Out[114]: array([0, 2, 3, 5])
In [115]: n
Out[115]: 5
csum
可以解释为与每个职业相关联的索引范围(pythonic "end",即)的 end 的索引。然后 csum - occupations
保存范围开始的索引:
In [116]: csum - occupations
Out[116]: array([0, 0, 2, 3])
根据occupations
中的值重复那些起始索引:
In [117]: np.repeat(csum - occupations, occupations)
Out[117]: array([0, 0, 2, 3, 3])
如果从 np.arange(n)
中减去它,对于每个职业 k
,我们有一个从 0 到 occupation[k]-1
的范围串联在一个数组中:
In [118]: np.arange(n) - np.repeat(csum - occupations, occupations)
Out[118]: array([0, 1, 0, 0, 1])
这不是您想要的结果。我们必须添加(重复)idx_unique_objIDs
,以便这些值是数组 objID
:
中的索引
In [119]: np.arange(n) - np.repeat(csum - occupations, occupations) + np.repeat(idx_unique_objIDs, occupations)
Out[119]: array([2, 3, 5, 6, 7])
现在组合这两个 repeat()
调用以获得最终表达式:
In [120]: np.arange(n) + np.repeat(idx_unique_objIDs - csum + occupations, occupations)
Out[120]: array([2, 3, 5, 6, 7])
另一个建议,用 return_counts
代替 return_index
:
unique_objIDs, objID_counts = np.unique(objID, return_counts=True)
num_unique_objIDs = len(unique_objIDs)
yesno = np.tile([True, False], num_unique_objIDs)
amounts = np.c_[occupations, objID_counts-occupations].ravel()
desired_array_of_indices = np.flatnonzero(np.repeat(yesno, amounts))
我想用 numpy 来解决一个与 numpy.repeat 函数解决的问题非常相似但不完全相同的问题。我看不出如何使用我熟悉的任何 numpy 函数来解决这个问题,所以我正在寻求帮助,看看这是否可以用 numpy 来完成。我的数组很大(>1e6 个元素),高性能至关重要,所以我无法承受 python for 循环的性能损失。
最小示例
我有一个 length-num_pts 排序的整数数组 objID
存储(可能重复)对象标识符。
objID = np.array([0, 0, 5, 5, 5, 7, 8, 8])
我使用 numpy.unique 确定 objID
的唯一条目,以及它们在 objID
中出现的索引。
unique_objIDs, idx_unique_objIDs = np.unique(objID, return_index=True)
num_unique_objIDs = len(unique_objIDs)
我还有一个 length-num_unique_objIDs 数组 occupations
,它指定我想要 select 来自 objID
的 unique_objIDs
的每个条目多少次。
occupations = np.array([0, 2, 1, 2])
我想根据 occupations
确定可用于检索 objID
元素的索引数组。下面我举一个具体的例子。
desired_array_of_indices = np.array([2, 3, 5, 6, 7])
数组desired_array_of_indices
就是我要用numpy计算的。 desired_array_of_indices
的条目计算如下。
desired_array_of_indices
的明确解释
occupations
数组的第 i 个元素指定 unique_objID[i]
将被 select 编辑多少次。 desired_array_of_indices
数组存储这些 select 离子的 objID
的索引。对于 select 多次 select 的值,选择 objID
的 连续 个索引,因此没有索引存储在 desired_array_of_indices
重复。
为了具体起见,请考虑 occupations
的第一个元素。该值为零,告诉我们我们不想 select 存储 unique_objIDs[0]=0
的 objID
的任何索引,因此所有此类索引都被排除在 desired_array_of_indices
之外。
occupations
的下一个元素是 2,告诉我们要 select unique_objIDs[1]=5
在 objID
中的前 2 次出现的索引。这就是为什么 desired_array_of_indices
的前两个条目是 2 和 3。
occupations
的下一个元素是1,告诉我们要selectunique_objIDs[2]=7
在objID
中第一次出现的索引。所以 desired_array_of_indices
的下一个条目是 5。
occupations
的最后一个元素是2,告诉我们要selectunique_objIDs[3]=8
在objID
中的前2次出现的索引。这就是为什么 desired_array_of_indices
的最后两个条目是 6 和 7。
与 np.repeat
的区别请注意此计算与 numpy.repeat
之间的细微差别。对于 numpy.repeat
,返回的索引属于唯一条目数组 unique_objIDs
。这里我需要 objID
的索引,对于重复条目的情况,我还需要 select 连续的索引。可以假定 occupations
的每个条目小于或等于相应条目在 objID
中出现的总次数,因此不存在索引错误的危险。
有没有人看到如何根据可用的矢量化 Numpy 函数(可能是一些集合)来表述这个问题?
这是一种方法。
首先,您的示例代码:
In [102]: objID = np.array([0, 0, 5, 5, 5, 7, 8, 8])
In [103]: unique_objIDs, idx_unique_objIDs = np.unique(objID, return_index=True)
[[注:unique()
对其参数进行排序。您知道您的输入已经排序,因此获得 idx_unique_objIDs
的更有效方法是:
idx_unique_objIDs = np.concatenate(([0], np.nonzero(np.diff(objID))[0] + 1))
此操作是 O(n) 而不是 unique
所需的 O(n*log(n))。然后你可以使用
unique_objIDs = objID[idx_unique_objIDs]
如果您需要唯一对象 ID 的数组。]]
In [104]: occupations = np.array([0, 2, 1, 2])
现在找到所需的索引。结果在 Out[107]
:
In [105]: csum = occupations.cumsum()
In [106]: n = csum[-1]
In [107]: np.arange(n) + np.repeat(idx_unique_objIDs - csum + occupations, occupations)
Out[107]: array([2, 3, 5, 6, 7])
细看:
csum
是occupations
的累加和,n
是occupations
的总和:
In [114]: csum
Out[114]: array([0, 2, 3, 5])
In [115]: n
Out[115]: 5
csum
可以解释为与每个职业相关联的索引范围(pythonic "end",即)的 end 的索引。然后 csum - occupations
保存范围开始的索引:
In [116]: csum - occupations
Out[116]: array([0, 0, 2, 3])
根据occupations
中的值重复那些起始索引:
In [117]: np.repeat(csum - occupations, occupations)
Out[117]: array([0, 0, 2, 3, 3])
如果从 np.arange(n)
中减去它,对于每个职业 k
,我们有一个从 0 到 occupation[k]-1
的范围串联在一个数组中:
In [118]: np.arange(n) - np.repeat(csum - occupations, occupations)
Out[118]: array([0, 1, 0, 0, 1])
这不是您想要的结果。我们必须添加(重复)idx_unique_objIDs
,以便这些值是数组 objID
:
In [119]: np.arange(n) - np.repeat(csum - occupations, occupations) + np.repeat(idx_unique_objIDs, occupations)
Out[119]: array([2, 3, 5, 6, 7])
现在组合这两个 repeat()
调用以获得最终表达式:
In [120]: np.arange(n) + np.repeat(idx_unique_objIDs - csum + occupations, occupations)
Out[120]: array([2, 3, 5, 6, 7])
另一个建议,用 return_counts
代替 return_index
:
unique_objIDs, objID_counts = np.unique(objID, return_counts=True)
num_unique_objIDs = len(unique_objIDs)
yesno = np.tile([True, False], num_unique_objIDs)
amounts = np.c_[occupations, objID_counts-occupations].ravel()
desired_array_of_indices = np.flatnonzero(np.repeat(yesno, amounts))