Данный
import pandas as pd
df = pd.DataFrame({
"a": [1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 2, 2, 3, 3, ],
})
print(df)
a 0 1 1 1 2 1 3 2 4 2 5 2 6 2 7 3 8 3 9 3 10 3 11 1 12 1 13 2 14 2 15 3 16 3
Мне нужно рассчитать следующий результат:
res_df = pd.DataFrame({
"starts": [0, 3, 7, 11, 13, 15],
"ends": [3, 7, 11, 13, 15, 17]
})
print(res_df)
starts ends 0 0 3 1 3 7 2 7 11 3 11 13 4 13 15 5 15 17
Если бы значения не дублировались, я мог бы сделать что-то вроде обнуления всех дубликатов, сохраняя длину группы в groupby, а затем cumsum.
Однако есть дубликаты, и порядок должен быть сохранен.
Есть ли способ сделать это в пандах?
В качестве продолжения я хотел бы рассчитать starts
и ends
только для df["a"] == 3, если бы это было менее затратным в вычислительном отношении.
Давайте попробуем это:
blocks = df['a'].diff().ne(0).cumsum()
# depending on your mask
out = (df[some_mask]
.index.to_frame()
.groupby(blocks)[0]
.agg(['min','max'])
)
out['max'] += 1
Выход:
min max
a
1 0 3
2 3 7
3 7 11
4 11 13
5 13 15
6 15 17
что означают значения "a"
в выводе?
@Gulzar, это просто имя blocks
. При группировке по ряду имя ряда устанавливается в качестве имени индекса новых данных. Передайте as_index=False
groupby
, если не хотите.
вы можете нарезать индекс и столбец с маской, где shift
не равно текущему значению, а затем создать фрейм данных. Результат также может включать исходное значение столбца a.
m = df['a'].ne(df['a'].shift())
res = pd.DataFrame({'a':df.loc[m,'a'],
'starts':df.index[m]})
res['ends'] = res['starts'].shift(-1, fill_value=len(df))
print(res)
a starts ends
0 1 0 3
3 2 3 7
7 3 7 11
11 1 11 13
13 2 13 15
15 3 15 17
Спасибо! Пожалуйста, смотрите редактирование, если бы я заранее знал, что меня волнуют только начало и конец определенной группы, например
df["a"] == 3
, можно ли это сделать быстрее? Кроме того, можете ли вы добавить столбец, чтобы указать, из какой группы получен результат? чтобы иметь возможность выбрать на этом.