У меня есть фрейм данных панды. Я хочу создать подкадры данных на основе некоторых условий. Если typeId == 15, возьмите все предыдущие строки только с typeId == 1 и результатом == 1 и сохраните их в подкадре данных.
У меня должно быть два подкадра данных, первый из которых
И второй
Что-то подобное работает для вас?
subdataframes = []
start_idx = 0
for idx in df[df['typeId'] == 15].index:
subdf = df.loc[start_idx:idx][(df['typeId'] == 1) & (df['result'] == 1)]
subdf = pd.concat([subdf, df.loc[[idx]]])
subdataframes.append(subdf)
start_idx = idx
Результат:
Subdataframe 1:
typeId result
2 1 1
3 1 1
4 15 1
Subdataframe 2:
typeId result
7 1 1
8 1 1
9 15 1
Индексы отклонены на 1, потому что они отсчитываются от нуля.
Код
Чтобы получить строки, соответствующие всем условиям, существующим выше 15, на основе 15, используйте следующий код (строки, соответствующие условию, даже если они не смежны с 15, будут импортированы)
cond1 = df['typeId'].eq(15)
grp = cond1.cumsum() - cond1
cond2 = df['typeId'].eq(1) & df['result'].eq(1)
cond3 = cond1.groupby(grp).transform(any)
out = [d for _, d in df[cond1 | (cond2 & cond3)].groupby(grp)]
вне:
[ typeId result
3 1 1
4 1 1
5 15 1,
typeId result
8 1 1
9 1 1
10 15 1]
Если вы хотите извлечь только строки, соответствующие условию, смежному с 15, на основе 15, используйте следующий код (получить последовательные строки 1–1, смежные с 15).
cond1 = df['typeId'].eq(15)
grp = cond1.cumsum() - cond1
cond2 = df['typeId'].eq(1) & df['result'].eq(1)
cond3 = df['typeId'].mask(cond2).bfill().eq(15)
out = [d for _, d in df[cond3].groupby(grp)]
Если вы хотите сохранить только смежные строки:
res = []
for i, g in df.groupby((df["typeId"].eq(15))[::-1].cumsum()):
g = g[(g["typeId"].eq(15) | (g["typeId"].eq(1) & g["result"].eq(1)))]
if not g.empty:
m = g.index.to_series().diff().fillna(1).ne(1).cumsum()
m = m[m == m.max()].index
g = g.loc[m]
res.append(g)
for df in res:
print(df)
typeId result
8 1 1
9 1 1
10 15 1
typeId result
3 1 1
4 1 1
5 15 1
Вы можете добиться этого с помощью простой группы . Определите участки typeId==1/result==1, сгруппируйте их со следующей строкой (с обратной совокупной суммой), сохраните группу только в том случае, если последний typeId==15:
# identify stretches of typeId==1/result==1
m = df['typeId'].eq(1) & df['result'].eq(1)
# group with following row
# only keep it last typeId==15
out = [g for _,g in df.groupby((~m)[::-1].cumsum(), sort=False)
if g['typeId'].iloc[-1] == 15]
Выход:
[ typeId result
3 1 1
4 1 1
5 15 1,
typeId result
8 1 1
9 1 1
10 15 1]
Примечание. если вы хотите, чтобы выходные кадры данных содержали строки с единицами, измените условие фильтрации на if len(g)>1 and g['typeId'].iloc[-1] == 15
.
Промежуточные продукты:
typeId result m group keep
1 2 3 False 8
2 4 1 False 7
3 1 1 True 6 |
4 1 1 True 6 |
5 15 1 False 6 X
6 3 4 False 5
7 2 1 False 4
8 1 1 True 3 |
9 1 1 True 3 |
10 15 1 False 3 X
11 4 4 False 2
12 3 3 False 1