#python #pandas
#python #панды
Вопрос:
У меня есть такая серия:
s = pd.DataFrame({'ts': [1, 2, 3, 6, 7, 11, 12, 13]})
s
ts
0 1
1 2
2 3
3 6
4 7
5 11
6 12
7 13
Я хотел бы свернуть строки, разница между которыми меньше, чем MAX_DIFF (2) . Это означает, что желаемый результат должен быть:
[{'ts_from': 1, 'ts_to': 3},
{'ts_from': 6, 'ts_to': 7},
{'ts_from': 11, 'ts_to': 13}]
Я сделал некоторое кодирование:
s['close'] = s.diff().shift(-1)
s['close'] = s[s['close'] > MAX_DIFF].astype('bool')
s['close'].iloc[-1] = True
parts = []
ts_from = None
for _, row in s.iterrows():
if row['close'] is True:
part = {'ts_from': ts_from, 'ts_to': row['ts']}
parts.append(part)
ts_from = None
continue
if not ts_from:
ts_from = row['ts']
Это работает, но не кажется оптимальным из-за iterrows() . Я думал о рангах, но не мог понять, как их реализовать, чтобы группировать по рангу дальше.
Есть ли способ оптимизировать алгоритм?
Комментарии:
1. Можете ли вы уточнить один момент. Если в начале были дополнительные строки, так что это было 1,2,3,4,5,6,7,11,12,13, группируются ли 1-7 вместе или они разделяются на несколько групп, например 1-3, 4-6, 7-7(?), ?
2. 1-7 будут сгруппированы вместе
Ответ №1:
Вы можете создавать группы, проверяя, где разница превышает ваш порог, и получать итоговую сумму. Тогда действуйте так, как вам хотелось бы, возможно first
, и last
в этом случае.
gp = s['ts'].diff().abs().ge(2).cumsum().rename(None)
res = s.groupby(gp).agg(ts_from=('ts', 'first'),
ts_to=('ts', 'last'))
# ts_from ts_to
#0 1 3
#1 6 7
#2 11 13
И если вам нужен список dicts, тогда:
res.to_dict('records')
#[{'ts_from': 1, 'ts_to': 3},
# {'ts_from': 6, 'ts_to': 7},
# {'ts_from': 11, 'ts_to': 13}]
Для полноты картины вот как группировщик выравнивается с фреймом данных:
s['gp'] = gp
print(s)
ts gp
0 1 0 # `1` becomes ts_from for group 0
1 2 0
2 3 0 # `3` becomes ts_to for group 0
3 6 1 # `6` becomes ts_from for group 1
4 7 1 # `7` becomes ts_to for group 1
5 11 2 # `11` becomes ts_from for group 2
6 12 2
7 13 2 # `13` becomes ts_to for group 2
Комментарии:
1. Можете ли вы объяснить вторую строку более подробно?
2. @ggaurav итак, первая часть является базовой
groupby
, я создаю серию, которая идентифицирует группы, и pandas затем применяет вычисления внутри этой группы. Все, что послеagg
, является более поздним синтаксическим добавлениемnamedAggregations
. Это полезный синтаксис, который позволяет выполнять несколько агрегаций в одном столбце и переименовывать выходные данные (без создания мультииндекса для столбцов). Итак, в этом случае я создаю столбцы вывода с именами ts_from и ts_to, которые, соответственно, являются первым и последним значениями'ts'
столбца в каждой группе3. Спасибо! Узнал немало вещей. Например, я не знал, что для использования group by столбец не обязательно должен быть частью таблицы, а также называться агрегациями.
4. Подход правильный. Но это не сработает, если в серии есть последовательные числа, например, от 1 до 8 в серии
5. @ggaurav оОо. Я полагаю, что тогда я неправильно понял вопрос. Я понимаю вашу точку зрения, в этом случае это гораздо более сложная проблема, и я не думаю, что есть какой-либо возможный способ сделать это без итерации, поскольку группировка более поздних строк зависит от того, что было раньше.