是否有一种pythonic方法可以在可迭代中获取相同值的簇的开始和结束索引?

时间:2013-03-05 17:17:25

标签: python numpy pandas indexing iterable

以下问题可以通过循环轻松解决,但我怀疑可能有更多的pythonic方式来实现这一点。

从本质上讲,我有一个可迭代的布尔值,它们往往被聚集成组。这是一个说明性的例子:

[True, True, True, True, False, False, False, True, True, True, True, True]

我想为每个True群集挑选起始索引和结束索引。使用循环,这很容易 - 每次我的迭代器都是True时,我只需要检查我是否已经在True集群中。如果没有,我将in_true_cluster变量设置为true并存储索引。找到False后,我将索引-1存储为终点。

有更多的pythonic方式吗?请注意,我也使用PANDAS和NumPy,因此使用逻辑索引的解决方案是可以接受的。

2 个答案:

答案 0 :(得分:3)

实际上,这是一种numpy方式,它应该比使用itertools或手动循环更快:

>>> a = np.array([True, True, True, True, False, False, False, True, True, True, True, True])
>>> np.diff(a)
array([False, False, False,  True, False, False,  True, False, False,
       False, False], dtype=bool)
>>> _.nonzero()
(array([3, 6]),)

正如你在评论中提到的那样,大熊猫的groupby也可以。


时间来说服@poke这是值得的:

>>> %%timeit a = np.random.randint(2, size=1000000)
... np.diff(a).nonzero()
...
100 loops, best of 3: 12.2 ms per loop
>>> def cluster_changes(array):
...     changes = []
...     last = None
...     for i, elt in enumerate(array):
...         if elt != last:
...             last = elt
...             changes.append(i)
...     return changes
...
>>> %%timeit a = np.random.randint(2, size=1000000)
cluster_changes(a)
...
1 loops, best of 3: 348 ms per loop

与7线手动功能相比,使用单线程的阵列数量为30。 (当然,这里的数据比OP的数据有更多的集群变化,但这并不能弥补这么大的差异。)

答案 1 :(得分:3)

怎么样:

In [25]: l = [True, True, True, True, False, False, False, True, True, True, True, True]

In [26]: d = np.diff(np.array([False] + l + [False], dtype=np.int))

In [28]: zip(np.where(d == 1)[0], np.where(d == -1)[0] - 1)
Out[28]: [(0, 3), (7, 11)]

此处,两次分别位于索引[0; 3][7; 11]