42

假设我有

>>> v
array([1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 3, 4, 3, 4, 3, 4, 5, 5, 5])

是否有一种有效的 numpy 方法来查找值发生变化的每个索引?例如,我想要一些结果,例如,

>>> index_of_changed_values(v)
[0, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16]

如果某些 numpy 例程无法做到这一点,那么在 python 中快速的方法是什么?因为我是一个 numpy 初学者,所以参考一些好的 numpy 教程对我也很有用。

4

4 回答 4

75

您可以通过将每个元素与其相邻元素进行比较,在 numpy 中获得此功能;

v[:-1] != v[1:]


array([False, False, False, False,  True, False, False,  True,  True,
    True,  True,  True,  True,  True,  True,  True, False, False], dtype=bool)

要获取您使用“where”功能的索引

np.where(v[:-1] != v[1:])[0]

array([ 4,  7,  8,  9, 10, 11, 12, 13, 14, 15])

从这里您可以添加第一个元素并添加一个以获取与您的问题相同的索引方案。

于 2013-10-01T21:03:09.633 回答
11

类似于@kith 答案,但需要较少的结果按摩:

np.where(np.roll(v,1)!=v)[0]

无需添加 0 或添加 1。示例:

>>> v=np.array([1, 1, 1, 2, 2, 3, 3, 4, 4, 4])
>>> np.where(np.roll(v,1)!=v)[0]
array([0, 3, 5, 7])

编辑:正如@Praveen 提到的,当最后一个元素和第一个元素相等时,这会失败。

于 2016-10-12T02:02:31.273 回答
5

差不多十年后,但我今天遇到了这个。

@kith 答案很好,但可能不像我们想要的那样整洁(还考虑到答案中未明确的步骤)。

完整形式的答案是,

v = np.array([1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 3, 4, 3, 4, 3, 4, 5, 5, 5])
np.concatenate((np.array([0]),np.where(v[:-1] != v[1:])[0]+1),axis=0)

我更喜欢的另一种选择是,

np.where(np.diff(v,prepend=np.nan))[0]

这也返回

array([ 0,  5,  8,  9, 10, 11, 12, 13, 14, 15, 16], dtype=int64)

正如我所说,这个想法与@kith 的想法相同,但是,

  • 我替换v[:-1] != v[1:]for np.diff(),然后np.where将数组转换为布尔值,这并没有太大变化,但看起来更整洁。
  • 我删除了添加 1 和前置 0 的额外步骤。这是通过np.nan在执行之前添加np.diff(). diff 输出的第一个元素将是np.nan,并且在 python np.nan 中总是计算True
于 2021-01-10T19:09:53.757 回答
2

很好的问题和答案!

我正在使用一个向量,它有大约 100 万个从 1 到 100,000 的单调非递减整数(例如 [1, 1, 1, 2, 3, 3, 4, ..., 100000])。对于这个数据集,似乎上面讨论的 2 个习语与是否使用 prepend kwarg 之间存在明显的性能差异:

%timeit np.where(np.diff(v, prepend=np.nan))                                                                                                                             
15.3 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit np.where(np.diff(v))[0] + 1                                                                                                                                     
7.41 ms ± 72 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit np.where(v[:-1] != v[1:])[0] + 1                                                                                                                                       
2.85 ms ± 41.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

因此,与使用带有前置 kwarg 的 diff() 相比,花式索引调用快 5 倍,并且比使用不带前置的 diff 快两倍(无论如何在我的旧 MacBook Air 上)。对于大多数用例来说,这种性能差异并不重要,但我正在处理数千个这样的数据集(总共数十亿行),所以我需要牢记性能。

于 2021-08-28T13:00:41.237 回答