#python #pandas #numpy #percentage
#python #pandas #numpy #процент
Вопрос:
У меня есть набор данных, df, где я хочу найти процентную разницу и разницу. Я хочу посмотреть на самую раннюю дату и сравнить это значение со следующей датой:
id date value
1 11/01/2020 10
2 11/01/2020 5
1 10/01/2020 20
2 10/01/2020 30
1 09/01/2020 15
2 09/01/2020 10
3 11/01/2020 5
Желаемый результат
id date diff percent
1 10/01/2020 5 33
1 11/01/2020 -10 -50
2 10/01/2020 20 200
2 11/01/2020 -25 -83.33
3 11/01/2020 0 0
Я хочу посмотреть на одну группу за раз и сравнить предыдущее значение со следующим значением и найти процентное увеличение и разницу.
Например,
Идентификатор 1, с 09/01/2020 по 10/01/2020: изменяется от 15 до 20, что дает разницу в 5
процентов разница составляет 33%
с 10/01/2020 по 11/01/2020: от 20 до 10,
разница в -10 и разница в 50% процентов.
Это то, что я делаю:
a['date'] = pd.to_datetime(a['date'])
grouped = a.sort_values('date').groupby(['id'])
output = pd.DataFrame({
'date': grouped['date'].agg(lambda x: x.iloc[-1]).values,
'diff': grouped['value'].agg(lambda x: x.diff().fillna(0).iloc[-1]).values,
'percentdiff': grouped['value'].agg(lambda x: x.pct_change().fillna(0).iloc[-1] * 100).values,
'type': grouped['id'].agg(lambda x: x.iloc[0]).values
})
Однако я замечаю, что некоторые значения отсутствуют, так как это мой вывод:
Возможно ли достичь желаемого результата?Возможно, потребуется реализовать цикл для возврата к предыдущей строке даты и сравнения со следующей?
Любое предложение приветствуется
Ответ №1:
Вот один из способов обойти это, предполагая, что я правильно понял вашу логику :
Идея состоит в том, чтобы использовать shift
для каждой группы для вычисления разницы и процента,
result = (df.sort_values(["id", "date", "value"])
# use this later to drop the first row per group
# if number is greater than 1, else leave as-is
.assign(counter=lambda x: x.groupby("id").date.transform("size"),
date_shift=lambda x: x.groupby(["id"]).date.shift(1),
value_shift=lambda x: x.groupby("id").value.shift(1),
diff=lambda x: x.value - x.value_shift,
percent=lambda x: x["diff"].div(x.value_shift).mul(100).round(2))
# here is where the counter column becomes useful
# drop rows where date_shift is null and counter is > 1
# this way if number of rows in the group is just one it is kept,
# if greater than one, the first row is dropped,
# as the first row would have nulls due to the `shift` method.
.query("not (date_shift.isna() and counter>1)")
.loc[:, ["id", "date", "diff", "percent"]]
.fillna(0))
result
id date diff percent
2 1 10/01/2020 5.0 33.33
0 1 11/01/2020 -10.0 -50.00
3 2 10/01/2020 20.0 200.00
1 2 11/01/2020 -25.0 -83.33
6 3 11/01/2020 0.0 0.00
Комментарии:
1. Ах, хорошо, я не был точно уверен, как это реализовать! Спасибо, позвольте мне попробовать это сейчас! — я полагаю, не имеет значения, есть ли отключенный набор групп при использовании shift?
2. Действительно полезно, я буду продолжать исследовать это. Спасибо
3. Привет @Sammywemmy, по какой-то причине, когда я добавляю имена столбцов моего большого набора данных: ‘POD’, ‘Date’, ‘Size’, я продолжаю получать эту ошибку: AttributeError: объект ‘DataFrameGroupBy’ не имеет атрибута ‘date’, пытаясь это выяснить. Есть предложения?
4. Мой полный набор данных имеет дату в качестве имени столбца. Возможно, я что-то неправильно ввожу. Образец набора данных работает нормально
5. есть ли способ включить начальную и конечную дату для вывода? Я могу создать новый пост и сейчас изучаю это