серия pandas добавляет предыдущую строку, если разница отрицательная

#python #pandas #dataframe #data-wrangling

#python #pandas #фрейм данных #данные-споры

Вопрос:

У меня есть df, который содержит некоторые значения дохода, и я хочу интерполировать значения по датам, которые не включены в индекс. Для этого я нахожу разницу между строками и интерполирую:

 rev_diff = df.revenue.diff().fillna(0)
df = df.resample("M").mean()
df["revenue"] = df.revenue.interpolate().diff()
  

У меня есть это в функции, и она выполняется в цикле по тысячам таких вычислений (каждое из которых создает такой df). Это работает в большинстве случаев, но есть несколько случаев, когда «проверка до» сбрасывается, и, следовательно, разница отрицательна:

             revenue
2015-10-19  203.0
2016-04-03  271.0
2016-06-13  301.0
2016-06-13  0.0
2016-09-27  30.0
2017-03-14  77.0
2017-09-19  128.0
2018-09-19  0.0
2018-03-19  10.0
2019-03-22  287.0
2020-03-20  398.0
  

Приведенный выше код будет выдавать отрицательные значения интерполяции, поэтому мне интересно, есть ли быстрый способ учесть это, когда это произойдет, не слишком сильно влияя на время выполнения, потому что оно вызывается тысячи раз. Конечный результат для дохода df (до выполнения интерполяции) должен быть:

             revenue
2015-10-19  203.0
2016-04-03  271.0
2016-06-13  301.0
2016-09-27  331.0
2017-03-14  378.0
2017-09-19  429.0
2018-03-19  439.0
2019-03-22  716.0   
2020-03-20  827.0
  

Таким образом, в основном, если есть «сброс», разница должна быть добавлена к значению в строке выше. И это произойдет для всех строк ниже.

Я надеюсь, что это имеет смысл. Я изо всех сил пытаюсь найти способ сделать это, который не требует больших вычислительных затрат.

Заранее спасибо.

Комментарии:

1. Почему доход 2017-03-14 не равен 378? Я не понимаю, почему происходит сброс, когда 77.0> 30.0 (предыдущий доход).

2. И я думаю, что такая идентификация сброса с использованием текущей разницы была бы слишком ненадежной для производственного использования при наличии больших пробелов в данных или больших вариаций доходов. Например: 2016-04-01 = 50.0 за которым следует 2017-03-25 = 70.0 . Есть ли сброс между ними? Из бизнес-логики это вероятно, но из данных вы не можете сказать.

3. Привет @BillHuang, вы правы во 2-м комментарии, я пропустил добавление пары строк, указывающих на сброс дохода. Скажем, что они происходят в тот же день, когда регистрируется предыдущий доход. Я отредактировал 1-й фрейм данных. Спасибо, что указали на это.

4. Вы все еще не объяснили 1-й комментарий. 2017-03-14 = 408 не соответствует правилу, указанному вами в названии.

5. упс, вы правы. Я быстро создал df и не заметил, что не добавляю diff. 2017-03-14 Доход составит 378, затем 429 и т.д. Спасибо @BillHuang!

Ответ №1:

Никакой магии. Шаги:

  1. Определите точки останова, вычисляя разницу в доходах.
  2. Заполните revenue значения, которые будут добавлены для последующих данных.
  3. Подведите итог.
  4. Удалите повторяющиеся записи.

Код

 import pandas as pd
import numpy as np

df.reset_index(inplace=True)

# 1. compute difference
df["rev_diff"] = 0.0
df.loc[1:, "rev_diff"] = df["revenue"].values[1:] - df["revenue"].values[:-1]

# get breakpoint locations
breakpoints = df[df["rev_diff"] < 0].index.values

# 2. accumulate the values to be added
df["rev_add"] = 0.0
for idx in breakpoints:
    add_value = df.at[idx-1, "revenue"]
    df.loc[idx:, "rev_add"]  = add_value  # accumulate

# 3. sum up
df["rev_new"] = df["revenue"]   df["rev_add"]

# 4. remove duplicate rows
df_new = df[["index", "rev_new"]].drop_duplicates().set_index("index")
df_new.index.name = None
  

Результат

 df_new
Out[85]:
            rev_new
2015-10-19    203.0
2016-04-03    271.0
2016-06-13    301.0
2016-09-27    331.0
2017-03-14    378.0
2017-09-19    429.0
2018-03-19    439.0
2019-03-22    716.0
2020-03-20    827.0