我正在尝试实现一个函数,用于识别Pandas Series
中的第一次连续次出现,它已经被我想要的条件掩盖了:(例如)
[True, True, True, False, True, False, True, True, True, True]
我希望上面的输入给出3
的结果,即从系列的开头连续出现3 True
次。
我知道有一个很大的for
循环可以完成这项工作,但是有没有以矢量化/ Pandas为中心的方法来解决它?
非常感谢。
答案 0 :(得分:10)
查找第一个连续True
s 的计数
考虑a
a = np.array([True, True, True, False, True, False, True, True, True, True])
回答1
numpy
:对np.logical_and.accumulate
的否定使用a
并对其进行否定,制作一个掩码,如果它们存在,则删除第一个False
系列。然后在末尾附加一个False
,以确保我们有一个非True
分钟。最后,使用np.argmin
找到第一个最小值。如果找到位置3
,则会在其前面显示3
True
个值。
np.argmin(np.append(a[~np.logical_and.accumulate(~a)], False))
3
回答2
numba.njit
我想使用numba
所以我可以循环并确保在需要时能够短路。这是一个肯定会在阵列的早期回答的问题。无需在整个阵列上进行评估。
from numba import njit
@njit
def first_true(a):
true_started = False
c = 0
for i, j in enumerate(a):
if true_started and not j:
return c
else:
c += j
true_started = true_started or j
return c
first_true(a)
3
回答3
numpy
更智能地使用argmin
和argmax
。我使用a
围绕False
,然后使用argmax
查找第一个True
,然后从该点开始,使用argmin
查找第一个False
之后。
注意: @Divakar对此答案进行了改进,取消了np.concatenate
的使用并改为使用if/then/else
。这使得已经非常快速的解决方案减少了3
!
def first_true2(a):
a = np.concatenate([[False], a, [False]])
return np.argmin(a[np.argmax(a):])
first_true2(a)
3
这些答案的速度有多快?
See @Divakar's Answer for source code of other functions being timed
%timeit first_true(a)
%timeit np.argmin(np.append(a[~np.logical_and.accumulate(~a)], False))
%timeit np.diff(np.flatnonzero(np.diff(np.r_[0,a,0])))[0]
%timeit first_True_island_len(a)
%timeit first_true2(a)
%timeit first_True_island_len_IFELSE(a)
a = np.array([True, True, True, False, True, False, True, True, True, True])
1000000 loops, best of 3: 353 ns per loop
100000 loops, best of 3: 8.32 µs per loop
10000 loops, best of 3: 27.4 µs per loop
100000 loops, best of 3: 5.48 µs per loop
100000 loops, best of 3: 5.38 µs per loop
1000000 loops, best of 3: 1.35 µs per loop
a = np.array([False] * 100000 + [True] * 10000)
10000 loops, best of 3: 112 µs per loop
10000 loops, best of 3: 127 µs per loop
1000 loops, best of 3: 513 µs per loop
10000 loops, best of 3: 110 µs per loop
100000 loops, best of 3: 13.9 µs per loop
100000 loops, best of 3: 4.55 µs per loop
a = np.array([False] * 100000 + [True])
10000 loops, best of 3: 102 µs per loop
10000 loops, best of 3: 115 µs per loop
1000 loops, best of 3: 472 µs per loop
10000 loops, best of 3: 108 µs per loop
100000 loops, best of 3: 14 µs per loop
100000 loops, best of 3: 4.45 µs per loop
答案 1 :(得分:6)
使用NumPy功能,一个解决方案是 -
np.diff(np.flatnonzero(np.diff(np.r_[0,s,0])))[0]
示例运行 -
In [16]: s
Out[16]:
0 True
1 True
2 True
3 False
4 True
5 False
6 True
7 True
8 True
9 True
dtype: bool
In [17]: np.diff(np.flatnonzero(np.diff(np.r_[0,s,0])))[0]
Out[17]: 3
为了提高性能,我们需要使用np.concatenate
到位np.r_
,然后切片以替换最后的差异 -
def first_True_island_len(a): # a is NumPy array
v = np.concatenate(([False],a,[False]))
idx = np.flatnonzero(v[1:] != v[:-1])
if len(idx)>0:
return idx[1] - idx[0]
else:
return 0
受@piRSquared's argmax
and argmin
trickery的启发,这里还有一堆IF-ELSE
的 -
def first_True_island_len_IFELSE(a): # a is NumPy array
maxidx = a.argmax()
pos = a[maxidx:].argmin()
if a[maxidx]:
if pos==0:
return a.size - maxidx
else:
return pos
else:
return 0
答案 2 :(得分:4)
尝试这种方法会找到True
或False
的第一次连续出现,并且仅针对True
:
import pandas as pd
df = pd.DataFrame([True, True, True, False, True, False, True, True, True, True],columns=["Boolean"])
df['consecutive'] = df.Boolean.groupby((df.Boolean != df.Boolean.shift()).cumsum()).transform('size')
count_true_false = df['consecutive'][df['consecutive']>1].iloc[0] # count first consecutive occurrences for True or False
count_true = df['consecutive'][(df.Boolean == True) & (df.consecutive>1)].iloc[0] # count first consecutive occurrences for True
print count_true_false
print count_true
输出:
3
3
答案 3 :(得分:3)
s = pd.Series([True, True, True, False, True, False, True, True, True, True])
cs = s.groupby((~s).cumsum()).agg('sum')
cs[cs > 1].iloc[0]
输出:
3
答案 4 :(得分:1)
我最初没有想到这个问题是关于熊猫系列的。下面的答案使用列表方法,但是如果将熊猫系列转换为列表(方法a.to_list()
)仍然有效。
不进行任何导入,就可以使用列表的方法False
找到index
的第一个匹配项,这就是您要查找的数字:
a = [True, True, True, False, True, False, True, True, True, True]
n_head_true = a.index(False) if False in a else len(a)
print(n_head_true)
> 3
a = [True, True, True]
n_head_true = a.index(False) if False in a else len(a)
print(n_head_true)
> 3
a = [False, True, True]
n_head_true = a.index(False) if False in a else len(a)
print(n_head_true)
> 0
if
是必需的,因为如果所有值均为True,index
会给出错误。