У меня есть фрейм данных панды. Я хочу создать подкадры данных на основе некоторых условий. Если 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, потому что они отсчитываются от нуля.
Привет, Милош, спасибо за быстрый ответ. Это работает, но не тот результат, который я хочу. Код выбирает все строки с typeId == 1 и результатом == 1, даже если есть строка, которая не равна typeId 1 и результату 1.
Можете ли вы поделиться примером, где это не работает? Должен ли подфрейм данных включать строку с typeId == 15, если результат в этой строке не равен 1?
Можете ли вы проверить, установлен ли начальный индекс в переменную start_idx. Когда я впервые опубликовал сообщение, я его не разместил, но отредактировал ответ за несколько секунд, поэтому не думаю, что это проблема, но лучше перестраховаться, чем потом сожалеть.
Я проверю это. Спасибо.
Код
Чтобы получить строки, соответствующие всем условиям, существующим выше 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
Можете ли вы красиво отформатировать примеры?