Python 熊猫数据帧-如何找到满足某些条件的连续行?

Python 熊猫数据帧-如何找到满足某些条件的连续行?,python,pandas,Python,Pandas,我正在尝试制作一个程序来查找满足某些条件的连续行。例如,如果有如下数据帧: df = pd.DataFrame([1,1,2,-13,-4,-5,6,17,8,9,-10,-11,-12,-13,14,15], index=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], columns=['value']) >>> df value 0 1 1 1 2

我正在尝试制作一个程序来查找满足某些条件的连续行。例如,如果有如下数据帧:

df = pd.DataFrame([1,1,2,-13,-4,-5,6,17,8,9,-10,-11,-12,-13,14,15], 
            index=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 
            columns=['value'])

>>> df
    value
0       1
1       1
2       2
3     -13
4      -4
5      -5
6       6
7      17
8       8
9       9
10    -10
11    -11
12    -12
13    -13
14    -14
15     15
我希望它返回一个数据帧,显示满足以下条件的行:

1) 顺序必须是
(正行)
(负行)
,而不是相反

2) 每个正行或负行组必须至少有3行

3) 正组和负组必须彼此相邻

          posIdx,   negIdx,  posLength,  negLength
0              2          3           3          3    # (1,1,2) (-13,-4,-5)
1              9         10           4          5    # (6,17,8,9) (-10,-11,-12,-13,-14)

有什么简单的方法可以使用python或pandas命令执行此操作吗?

我创建了帮助器列以方便验证解决方案:

#column for negative and positive
df['sign'] = np.where(df['value'] < 0, 'neg','pos')
#consecutive groups
df['g'] = df['sign'].ne(df['sign'].shift()).cumsum()

#removed groups with length more like 2
df = df[df['g'].map(df['g'].value_counts()).gt(2)]

#tested if order `pos-neg` of groups, if not removed groups
m1 = df['sign'].eq('pos') & df['sign'].shift(-1).eq('neg')
m2 = df['sign'].eq('neg') & df['sign'].shift().eq('pos')
groups = df.loc[m1 | m2, 'g']
df = df[df['g'].isin(groups)].copy()

df['pairs'] = (df['sign'].ne(df['sign'].shift()) & df['sign'].eq('pos')).cumsum()
print (df)
    value sign  g  pairs
0       1  pos  1      1
1       1  pos  1      1
2       2  pos  1      1
3     -13  neg  2      1
4      -4  neg  2      1
5      -5  neg  2      1
6       6  pos  3      2
7      17  pos  3      2
8       8  pos  3      2
9       9  pos  3      2
10    -10  neg  4      2
11    -11  neg  4      2
12    -12  neg  4      2
13    -13  neg  4      2

这只是另一种选择,我没有对这种速度进行基准测试:

首先,创建一个“符号”列,指示数字是正数还是负数。

其次,还要创建一个“检查”列,以指示在哪一行发生了从正到负或从负到正的更改。如果是-1,则表示从+ve到-ve的变化;反之则表示+1。

下一步,获取索引,其中check为-1(neg_id)和+1(pos_id)
我使用中的函数来点缀neg_id和pos_id。其目的是得到那些完全是正的或负的行块。

下一个阶段是运行for循环,该循环对结果变量中创建的每个元组使用iloc函数,并找出“value”列中的所有值是正值还是负值。根据符号,我们将结果分配给“K”字典中的键。请注意,posIdx将是该块中的最后一行(对于完全正值),而对于negIdx,它将是负块中的第一行。iloc执行start:end-1,因此posIdx将是end-1,而对于negIdx,start不需要任何加法或减法。

最后一个阶段是将数据读入数据帧

df = pd.DataFrame([1,1,2,-13,-4,-5,6,17,8,9,-10,-11,-12,-13,-14,15], 
        index=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 
        columns=['value'])

df['sign'] = np.where(df.value.lt(0),0,1)
df['check'] = df.sign.sub(df.sign.shift().fillna(0))

neg_ids = df.loc[df.check==-1].index.tolist()
pos_ids = df.loc[df.check==1].index.tolist()

from more_itertools import interleave_longest, windowed
outcome = list(interleave_longest(pos_ids,neg_ids))
outcome = list(windowed(outcome,2))

print(outcome)

[(0, 3), (3, 6), (6, 10), (10, 15)]

from collections import defaultdict

K = defaultdict(list)

for start, end in outcome:
    checker = df.iloc[start:end,0]
    if checker.ge(0).all() and checker.shape[0]>2:
        K['posIdx'].append(end-1)
        K['posLength'].append(checker.shape[0])
    elif checker.lt(0).all() and checker.shape[0]>2:
       K['negIdx'].append(start)
       K['negLength'].append(checker.shape[0])

pd.DataFrame(K)

   posIdx   posLength   negIdx  negLength
0     2        3          3         3
1     9        4          10        5

如果第一个值为负值会发生什么?应该忽略它。换句话说,它必须从至少3行正值开始。非常感谢您的帮助。这比我想象的要复杂!我从你的代码中学到了很多!谢谢你的回答。您能告诉我代码中的结果变量是什么吗?谢谢您的帮助。我完全无法理解我是如何跳过这三行代码的。对代码进行了更改,并包含“结果”变量的打印。
df = pd.DataFrame([1,1,2,-13,-4,-5,6,17,8,9,-10,-11,-12,-13,-14,15], 
        index=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 
        columns=['value'])

df['sign'] = np.where(df.value.lt(0),0,1)
df['check'] = df.sign.sub(df.sign.shift().fillna(0))

neg_ids = df.loc[df.check==-1].index.tolist()
pos_ids = df.loc[df.check==1].index.tolist()

from more_itertools import interleave_longest, windowed
outcome = list(interleave_longest(pos_ids,neg_ids))
outcome = list(windowed(outcome,2))

print(outcome)

[(0, 3), (3, 6), (6, 10), (10, 15)]

from collections import defaultdict

K = defaultdict(list)

for start, end in outcome:
    checker = df.iloc[start:end,0]
    if checker.ge(0).all() and checker.shape[0]>2:
        K['posIdx'].append(end-1)
        K['posLength'].append(checker.shape[0])
    elif checker.lt(0).all() and checker.shape[0]>2:
       K['negIdx'].append(start)
       K['negLength'].append(checker.shape[0])

pd.DataFrame(K)

   posIdx   posLength   negIdx  negLength
0     2        3          3         3
1     9        4          10        5