我有两个数组作为模拟脚本的输出,其中一个包含ID和一次,例如:
ids = np.array([2, 0, 1, 0, 1, 1, 2])
times = np.array([.1, .3, .3, .5, .6, 1.2, 1.3])
这些数组总是大小相同。现在我需要计算times
的差异,但仅适用于具有相同ids
的时间。当然,我可以简单地遍历不同的ids
和
for id in np.unique(ids):
diffs = np.diff(times[ids==id])
print diffs
# do stuff with diffs
然而,这是非常低效的,并且两个阵列可能非常大。有谁知道如何更有效地做到这一点?
答案 0 :(得分:3)
您可以使用array.argsort()
并忽略与ID中的更改相对应的值:
>>> id_ind = ids.argsort(kind='mergesort')
>>> times_diffs = np.diff(times[id_ind])
array([ 0.2, -0.2, 0.3, 0.6, -1.1, 1.2])
要查看需要丢弃的值,可以使用计数器计算每个ID的次数(from collections import Counter
)
或者只是排序ID,并查看其diff非零的位置:这些是id更改的索引,以及时间差异无关紧要的位置:
times_diffs[np.diff(ids[id_ind]) == 0] # ids[id_ind] being the sorted indices sequence
最后你可以用np.split和np.where:
分割这个数组np.split(times_diffs, np.where(np.diff(ids[id_ind]) != 0)[0])
正如您在评论中提到的,argsort()
默认算法(quicksort)可能无法在等于时间之间保留顺序,因此必须使用argsort(kind='mergesort')
选项。
答案 1 :(得分:2)
按np.argsort
:
ids
inds = np.argsort(ids, kind='mergesort')
>>> array([1, 3, 2, 4, 5, 0, 6])
现在按{H} times
排序np.diff
,然后添加nan
:
diffs = np.concatenate(([np.nan], np.diff(times[inds])))
>>> diffs
array([ nan, 0.2, -0.2, 0.3, 0.6, -1.1, 1.2])
除边界外,这些差异是正确的。让我们计算那些
boundaries = np.concatenate(([False], ids[inds][1: ] == ids[inds][: -1]))
>>> boundaries
array([False, True, False, True, True, False, True], dtype=bool)
现在我们可以做到
diffs[~boundaries] = np.nan
让我们看看我们得到了什么:
>>> ids[inds]
array([0, 0, 1, 1, 1, 2, 2])
>>> times[inds]
array([ 0.3, 0.5, 0.3, 0.6, 1.2, 0.1, 1.3])
>>> diffs
array([ nan, 0.2, nan, 0.3, 0.6, nan, 1.2])
答案 2 :(得分:1)
我添加了另一个答案,因为即使numpy
中有可能出现这些问题,我认为更高级pandas
对他们来说更为自然。
在pandas
中,您可以在创建数据框后一步完成此操作:
df = pd.DataFrame({'ids': ids, 'times': times})
df['diffs'] = df.groupby(df.ids).transform(pd.Series.diff)
这给出了:
>>> df
ids times diffs
0 2 0.1 NaN
1 0 0.3 NaN
2 1 0.3 NaN
3 0 0.5 0.2
4 1 0.6 0.3
5 1 1.2 0.6
6 2 1.3 1.2
答案 3 :(得分:1)
numpy_indexed包(免责声明:我是其作者)包含用于这类分组操作的高效且灵活的功能:
import numpy_indexed as npi
unique_ids, diffed_time_groups = npi.group_by(keys=ids, values=times, reduction=np.diff)
与pandas不同,它不需要专门的数据结构来执行这种相当基本的操作。