我有一个很长的布尔数组:
bool_array = [ True, True, True, True, True, False, False, False, False, False, True, True, True, False, False, True, True, True, True, False, False, False, False, False, False, False ]
我需要确定值翻转的位置,即True
和False
的序列开始的地址。在这种特殊情况下,我想得到
index = [0, 5, 10, 13, 15, 19, 26]
有没有一种简单的方法可以不用手动循环来检查第(i + 1)行中的每个第i个元素?
答案 0 :(得分:17)
作为大数据集的更有效方法,在python 3.X中,您可以使用itertools
模块中的accumulate
和groupby
函数。
>>> from itertools import accumulate, groupby
>>> [0] + list(accumulate(sum(1 for _ in g) for _,g in groupby(bool_array)))
[0, 5, 10, 13, 15, 19, 26]
代码背后的逻辑:
此代码使用groupby()
函数对连续的重复项进行分类,然后循环遍历由groupby()
返回的迭代器,该迭代器包含成对的键(我们使用下线而不是扔掉它来逃避它变量)和这些分类的迭代器。
>>> [list(g) for _, g in groupby(bool_array)]
[[True, True, True, True, True], [False, False, False, False, False], [True, True, True], [False, False], [True, True, True, True], [False, False, False, False, False, False, False]]
所以我们需要的是计算这些迭代器的长度并将每个长度与其先前的长度相加,以获得第一项的索引,该索引恰好是项目的更改位置,这正是{{1}功能是为了。
在Numpy中,您可以使用以下方法:
accumulate()
答案 1 :(得分:11)
这会告诉你哪里:
>>> import numpy as np
>>> np.argwhere(np.diff(bool_array)).squeeze()
array([ 4, 9, 12, 14, 18])
np.diff
计算每个元素与下一个元素之间的差异。对于布尔值,它实质上将值解释为整数(0:False,非零:True),因此差异显示为+1或-1值,然后映射回布尔值(当发生更改时为True)。 / p>
np.argwhere
函数会告诉您值为True的位置 - 现在是更改。
答案 2 :(得分:5)
>>> [i for i,(m,n) in enumerate(zip(bool_array[:-1],bool_array[1:])) if m!=n]
[4, 9, 12, 14, 18]
现在你有[4, 9, 12, 14, 18]
,你可以
>>> [0]+[i+1 for i in [4, 9, 12, 14, 18]]+[len(bool_array)]
[0, 5, 10, 13, 15, 19, 26]
实现您的输出。
代码背后的逻辑:
zip
接受两个迭代器并返回两个元素的序列。我们为从第一个元素开始的迭代器和从第二个元素开始的迭代器传递相同的列表。因此我们得到一个相邻数字列表enumerate
为您提供一系列索引和迭代器的值。另一个单步程序是
>>> [i for i,(m,n) in enumerate(zip([2]+bool_array,bool_array+[2])) if m!=n]
[0, 5, 10, 13, 15, 19, 26]
这里我们故意将[2]
引入列表中,这是因为第一个和最后一个值将始终不同(因为[2]
永远不会出现在列表中)。因此我们将直接获得这些索引。
答案 3 :(得分:0)
从Python 3.8
开始并引入assignment expressions (PEP 572)(:=
运算符),我们可以在列表推导中使用和增加变量。加上groupby
:
from itertools import groupby
# bool_array = [True, True, True, True, True, False, False, False, False, False, True, True, True, False, False, True, True, True, True, False, False, False, False, False, False, False]
total = 0
[total := total + len(list(gp)) for _, gp in groupby(bool_array)]
# [5, 10, 13, 15, 19, 26]
此:
total
初始化为0
,这象征着累加和groupby
将连续的项目分组(连续的True
将被分组在一起,连续的False
也是如此)total
)的当前长度递增total := total + len(list(gp))
total
当然要从0
开始,您始终可以将[0]
插入列表的前面。
答案 4 :(得分:0)
使用 pandas shift 查找 value != next 的位置。
import pandas as pd
bool_array = [ True, True, True, True, True, False, False, False, False, False, True, True, True, False, False, True, True, True, True, False, False, False, False, False, False, False ]
bools = pd.Series(bool_array)
flips = bools != bools.shift(1)
flips[flips].index
Int64Index([0, 5, 10, 13, 15, 19], dtype='int64')
或作为列表
list(flips[flips].index)
[0, 5, 10, 13, 15, 19]