Найти процентную разницу и разницу с последовательным, но нечетным числом дат

#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. есть ли способ включить начальную и конечную дату для вывода? Я могу создать новый пост и сейчас изучаю это