【问题标题】:Efficient way for calculating selected differences in array计算数组中选定差异的有效方法
【发布时间】:2016-10-05 11:39:48
【问题描述】:

我有两个数组作为模拟脚本的输出,其中一个包含 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

但是,这非常低效,并且两个数组可能非常大。有没有人知道如何更有效地做到这一点?

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    可以使用array.argsort(),忽略ids变化对应的值:

    >>> 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])
    

    要查看需要丢弃哪些值,可以使用 Counter 来计算每个 id 的次数 (from collections import Counter)

    或者只是对 id 进行排序,看看它的差异在哪里非零:这些是 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() 默认算法(快速排序)可能不会保留相等时间之间的顺序,因此必须使用 argsort(kind='mergesort') 选项。

    【讨论】:

    • 当您已经具备对数组进行排序的缺陷时,是否有理由使用sorted(ids),即ids[id_ind]
    • @obachtos 不,这只是懒惰。修复它
    • 再说一句:argsort() 使用其标准算法快速排序可能会弄乱时间顺序。最好使用稳定的归并排序,即argsort(kind='mergesort')
    • @obachtos 好话。以后把它作为评论让我编辑我的答案:如果你尝试自己编辑,审稿人会拒绝它,因为“这个编辑偏离了帖子的初衷。即使是必须做出重大改变的编辑也应该努力做到保留帖子所有者的目标。”
    【解决方案2】:

    通过ids说你np.argsort

    inds = np.argsort(ids, kind='mergesort')
    >>> array([1, 3, 2, 4, 5, 0, 6])
    

    现在按 np.difftimes 进行排序,并在前面加上 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])
    

    【讨论】:

      【解决方案3】:

      我正在添加另一个答案,因为即使这些事情在 numpy 中是可能的,我认为更高级别的 pandas 对他们来说更自然。

      pandas 中,您可以在创建 DataFrame 后一步完成:

      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
      

      【讨论】:

      • 这是一个很好的答案。让我补充一下,有时您的数据框有多个列。在需要时包含列是个好主意。 df['diffs'] = df.groupby(['ids'])['times'].transform(pd.Series.diff)
      【解决方案4】:

      numpy_indexed 包(免责声明:我是它的作者)包含用于此类分组操作的高效灵活的功能:

      import numpy_indexed as npi
      unique_ids, diffed_time_groups = npi.group_by(keys=ids, values=times, reduction=np.diff)
      

      与 pandas 不同,它不需要专门的数据结构来执行这种相当基本的操作。

      【讨论】:

      • 一般来说,当有人宣传他/她自己的图书馆时,习惯上会添加他/她是作者的免责声明。
      • 啊,是的;我有这样做的习惯,但我忘记了;谢谢。
      • 祝你的包裹好运。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-25
      • 2017-10-24
      • 1970-01-01
      • 2020-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多