Группировка и сдвиг без использования лямбда-функции

У меня есть такой фрейм данных:

data = {
    'ID': [1, 1, 2, 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1],
    'timestamp': pd.date_range(start='1/1/2023', periods=14, freq='D'),
    'value': [11, 22, 33, 44, 55, 66, 77, 88, 99, 11, 22, 33, 44, 55]
}

Мой фактический фрейм данных содержит миллионы строк. Я сортирую столбцы временных меток, поэтому столбец идентификатора перемежается, когда вы смотрите на необработанный фрейм данных.

Я хочу сгруппировать по идентификатору и найти разницу между каждой строкой и третьей предыдущей строкой. Сейчас у меня это работает так:

# Sort by ID and timestamp
df = df.sort_values(by=['ID', 'timestamp'])

# Group by 'ID' and calculate the difference with the 5th previous row
df['value_diff'] = df.groupby('ID', group_keys=False)['value'].apply(lambda x: x - x.shift(3))

Однако, поскольку мой фактический фрейм данных огромен, это занимает довольно много времени. Я также читал, что использование лямбды происходит медленно. В конце концов я хочу фильтровать по тому, увеличивается или уменьшается столбец value_diff. Обычно я бы использовал

inc_check=df['value'].diff(3).ge(0)
df=df[inc_check]

но он не группируется для расчета разницы.

Есть ли более элегантный способ добиться этого?

сколько строк и групп у вас в наборе данных?

mozway 07.06.2024 16:54

@mozway миллионы строк и, возможно, сотни групп

thentangler 07.06.2024 16:56
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Не используйте apply, просто используйте groupby.shift напрямую, а затем вычтите исходный столбец:

df['value_diff'] = df['value'] - df.groupby('ID')['value'].shift(3)

Или groupby.diff :

df['value_diff'] = df.groupby('ID')['value'].diff(3)

Выход:

    ID  timestamp  value  value_diff
0    1 2023-01-01     11         NaN
1    1 2023-01-02     22         NaN
3    1 2023-01-04     44         NaN
5    1 2023-01-06     66        55.0
6    1 2023-01-07     77        55.0
10   1 2023-01-11     22       -22.0
13   1 2023-01-14     55       -11.0
2    2 2023-01-03     33         NaN
4    2 2023-01-05     55         NaN
7    2 2023-01-08     88         NaN
8    2 2023-01-09     99        66.0
9    2 2023-01-10     11       -44.0
11   2 2023-01-12     33       -55.0
12   2 2023-01-13     44       -55.0

Спасибо за это предложение. Я как раз добавлял свой вопрос, когда вы отправили ответ :) Есть ли способ сделать groupby.shift и отфильтровать фрейм данных за один шаг без необходимости создания вычисляемого столбца?

thentangler 07.06.2024 16:56

Конечно: out = df[df.groupby('ID')['value'].diff(3).ge(0)] должно помочь

mozway 07.06.2024 16:57
import pandas as pd
import numpy as np

# Sample DataFrame
data = {
    'ID': [1, 1, 2, 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1],
    'timestamp': pd.date_range(start='1/1/2023', periods=14, freq='D'),
    'value': [11, 22, 33, 44, 55, 66, 77, 88, 99, 11, 22, 33, 44, 55]
}
df = pd.DataFrame(data)


df = df.sort_values(by=['ID', 'timestamp'])

# Calculate the difference with the 3rd previous row
df['value_diff'] = df.groupby('ID')['value'].diff(3)

df['diff_trend'] = np.where(df['value_diff'] >= 0, 'increasing', 'decreasing')

# Drop rows where value_diff is NaN
df_filtered = df.dropna(subset=['value_diff'])

print(df_filtered)
"""
   ID  timestamp  value  value_diff  diff_trend
5    1 2023-01-06     66        55.0  increasing
6    1 2023-01-07     77        55.0  increasing
10   1 2023-01-11     22       -22.0  decreasing
13   1 2023-01-14     55       -11.0  decreasing
8    2 2023-01-09     99        66.0  increasing
9    2 2023-01-10     11       -44.0  decreasing
11   2 2023-01-12     33       -55.0  decreasing
12   2 2023-01-13     44       -55.0  decreasing
"""

Другие вопросы по теме