Панды тают, но для дат внутри диапазона

#python #pandas

#python #панды

Вопрос:

У меня есть фрейм данных с датами «От» и «До» для каждой строки, как в примере ниже:

  ------------ ------------ --------- ----------- ----- ----- 
|    From    |   Until    | Product | BaseValue | Tax | Int |
 ------------ ------------ --------- ----------- ----- ----- 
| 01/01/2020 | 01/02/2020 | A       |       200 |  50 |  10 |
| 01/01/2020 | 01/02/2020 | B       |       500 |  15 |   5 |
| 01/01/2020 | 01/02/2020 | C       |       150 |  10 |   2 |
 ------------ ------------ --------- ----------- ----- ----- 
 

Тем не менее, мне нужно «расплавить» даты из каждой строки, создавая одну новую строку для каждой даты, которая помещается между «От» и «До».
Например, ожидаемый результат:

  ------------ --------- ----------- ----- ----- 
|    Date    | Product | BaseValue | Tax | Int |
 ------------ --------- ----------- ----- ----- 
| 01/01/2020 | A       | 200       | 50  | 10  |
| 02/01/2020 | A       | 200       | 50  | 10  |
| 03/01/2020 | A       | 200       | 50  | 10  |
| ...        | ...     | ...       | ... | ... |
| 01/02/2020 | A       | 200       | 50  | 10  |
| 01/01/2020 | B       | 500       | 15  | 5   |
| 02/01/2020 | B       | 500       | 15  | 5   |
| 03/01/2020 | B       | 500       | 15  | 5   |
| ...        | ...     | ...       | ... | ... |
| 01/02/2020 | B       | 500       | 15  | 5   |
 ------------ --------- ----------- ----- ----- 
 

Какой самый простой способ сделать это?

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

1. Каков ваш путь в сравнении? Возможно, у вас уже есть «самый острый способ» (что бы это ни значило).

2. Мой путь ужасен… Это будет итерация между «01/01/2020» и любой конечной датой, и для каждой повторяемой даты я бы также выполнял итерацию по продукту, и я искал в исходном df строку, которая подходила бы, а затем добавлял к другому фрейму данных. Однако такая итерация в большом фрейме данных, как у меня, просто расплавляет процессор… Кроме того, это совсем не pythonic.

3. Ewww. Звучит по-моему. Можете ли вы добавить это к вопросу, чтобы никому не пришла в голову идея опубликовать это в качестве ответа?

4. Я мог бы также создать функцию для запуска «apply (lambda x; …, axis = 1)», что означает, что она будет выполнять итерацию по каждой строке df, и эта функция будет выбирать даты «От» и «До» и копировать каждую строку для каждой из этих дат. Это было бы более плавно, но я не знаю, смогу ли я добавить к df изнутри лямбда-функции. В любом случае, я думаю, что может быть лучшее решение.

Ответ №1:

Я предполагаю, что столбцы From и Until имеют тип datetime .

Определите следующую функцию для преобразования каждой строки в фрейм данных с этой строкой, «увеличенной» для каждой даты:

 def proc(row):
    dct = row.loc['Product':'Int'].to_dict()
    return pd.DataFrame({'Date': pd.date_range(row.From, row.Until)}).assign(**dct)
 

Затем примените его к каждой строке и объедините полученный результат:

 result = pd.concat(df.apply(proc, axis=1).tolist(), ignore_index=True)
 

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

1. Прекрасный ответ! Прошло много времени с тех пор, как я так много узнал из одного ответа. Мне нравится, как вы применили, а затем tolist() для создания нескольких фреймов данных. Я даже не знал, что это возможно. Спасибо. Этот ответ заслуживает гораздо большей наглядности