Удаление строк df, если заданному идентификатору не предшествует один набор значений или за ним следует другой набор значений

#python #pandas

#python #pandas

Вопрос:

У меня есть df, который содержит различные элементы. Использование ниже ID должно содержать элементы из последующих списков в любом порядке. Таким образом, либо A или B должно предшествовать X или Y , а затем следовать либо C или D .

Чего я надеюсь достичь, так это когда X или Y не предшествует A или B или за которым следует C или D , затем уведомить с помощью оператора и полностью удалить оскорбительные строки.

 import pandas as pd

df = pd.DataFrame({   
    'Time' : [1,1,1,2,2,2,3,3,4,4,4,5,5],             
    'ID' : ['A','Y','C','B','X','D','B','X','A','Y','D','Y','D'],                 
    })

items_before = ['A','B']

items_after = ['C','D']

specific_items = ['X','Y']

# Notify when no preceding item 
for line in df[(df['ID'].isin(specific_items) amp; df['ID'].shift(1).isin(specific_items))].iterrows():
print(f'No item found for: Time {line[1].Time}')

# Notify when no subsequent item 
for line in df[(df['ID'].isin(specific_items) amp; df['ID'].shift(-1).isin(specific_items))].iterrows():
print(f'No item found for: Time {line[1].Time}')

    
 

Текущий вывод:

     Time ID
0      1  A
1      1  Y
2      1  C
3      2  B
4      2  X
5      2  D
6      3  B
7      3  X
8      4  A # C or D is missing here
9      4  Y
10     4  D
11     5  Y # A or B is missing here
12     5  D
 

Предполагаемый результат:

 No subsequent item found for: Time 4
No previous item found for: Time 5

    Time ID
0      1  A
1      1  Y
2      1  C
3      2  B
4      2  X
5      2  D
6      4  A 
7      4  Y
8      4  D
 

Ответ №1:

Вам нужно протестировать все три маски вместе на наличие последовательных значений:

 #if necessary first filter only matched rows by all joined values
df = df[df['ID'].isin(items_before   items_after   specific_items)]

m1 = df['ID'].isin(items_before) amp; df['ID'].shift(-1).isin(specific_items) amp; df['ID'].shift(-2).isin(items_after)
m2 = df['ID'].shift().isin(items_before) amp; df['ID'].isin(specific_items) amp;  df['ID'].shift(-1).isin(items_after)
m3 = df['ID'].shift(2).isin(items_before) amp; df['ID'].shift().isin(specific_items) amp; df['ID'].isin(items_after)

mask = (m1| m2 | m3)
df1 = df[mask]
print (df1)
    Time ID
0      1  A
1      1  Y
2      1  C
3      2  B
4      2  X
5      2  D
8      4  A
9      4  Y
10     4  D
 

Затем анализируются не совпадающие строки для предыдущих и последующих Time :

 df2 = df[~mask]
print (df2)
    Time ID
6      3  B
7      3  X
11     5  Y
12     5  D

m11 = df2['ID'].isin(items_before) amp; df2['ID'].shift(-1).isin(specific_items)
m12 = df2['ID'].isin(specific_items) amp; df2['ID'].shift().isin(items_before)

m21 = df2['ID'].isin(specific_items) amp; df2['ID'].shift(-1).isin(items_after)
m22 = df2['ID'].isin(items_after) amp; df2['ID'].shift().isin(specific_items)

# Notify when no preceding item 
for t in df2.loc[m11| m12, 'Time'].unique():
    print(f'No subsequent item found for: Time {t}')

for t in df2.loc[m21| m22, 'Time'].unique():
    print(f'No previous item found for: Time {t}')

No subsequent item found for: Time 3
No previous item found for: Time 5
 

Если требуется решение для тестирования по группам с помощью Time :

 df = df[df['ID'].isin(items_before   items_after   specific_items)]

m1 = df['ID'].isin(items_before) amp; df.groupby('Time')['ID'].shift(-1).isin(specific_items) amp; df.groupby('Time')['ID'].shift(-2).isin(items_after)
m2 = df.groupby('Time')['ID'].shift().isin(items_before) amp; df['ID'].isin(specific_items) amp;  df.groupby('Time')['ID'].shift(-1).isin(items_after)
m3 = df.groupby('Time').shift(2)['ID'].isin(items_before) amp; df.groupby('Time')['ID'].shift().isin(specific_items) amp; df['ID'].isin(items_after)

mask = (m1| m2 | m3)
df1 = df[mask]
print (df1)

df2 = df[~mask]
print (df2)

m11 = df2['ID'].isin(items_before) amp; df2.groupby('Time')['ID'].shift(-1).isin(specific_items)
m12 = df2['ID'].isin(specific_items) amp; df2.groupby('Time')['ID'].shift().isin(items_before)

m21 = df2['ID'].isin(specific_items) amp; df2.groupby('Time')['ID'].shift(-1).isin(items_after)
m22 = df2['ID'].isin(items_after) amp; df2.groupby('Time')['ID'].shift().isin(specific_items)

# Notify when no preceding item 
for t in df2.loc[m11| m12, 'Time'].unique():
    print(f'No subsequent item found for: Time {t}')

for t in df2.loc[m21| m22, 'Time'].unique():
    print(f'No previous item found for: Time {t}')
 

Комментарии:

1. Ты такой хвастун со всеми своими классными трюками :). Просто шучу. никогда не думал об использовании или для получения истинного утверждения. Классная штука. Спасибо, Джо

2. Не забудьте также фактически удалить оскорбительные строки 😉