Создайте месяц для каждой даты между периодами и сделайте их столбцами

#python #pandas #pivot-table

#python #pandas #сводная таблица

Вопрос:

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

 subscription|values| start   | end
x           |1     |5/5/2018 |6/5/2018
y           |2     |5/5/2018 |8/5/2018
z           |1     |5/5/2018 |9/5/2018
a           |3     |5/5/2018 |10/5/2018
b           |4     |5/5/2018 |11/5/2018
c           |2     |5/5/2018 |12/5/2018
  

Желаемый результат:

 subscription|jan| feb | mar | abr | jun | jul | aug | sep | out | nov | dez
x           |   |     |     |     | 1   | 1   |     |     |     |     |
y           |   |     |     |     | 2   | 2   | 2   |     |     |     |
z           |   |     |     |     | 1   | 1   | 1   | 1   |     |     |
a           |   |     |     |     | 3   | 3   | 3   | 3   | 3   |     |
b           |   |     |     |     | 4   | 4   | 4   | 4   | 4   | 4   |
c           |   |     |     |     | 2   | 2   | 2   | 2   | 2   | 2   | 2
  

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

1. Что вы уже пробовали?

2. сделал разницу между концом и началом df[‘dif’]=df[‘end’]-df[‘start’] также попытался сгенерировать сводную таблицу даже без месяцев df3 = pd.сводная таблица(df, значения=’values’, индекс=’subscription’, столбцы =’dif’)

3. Не очень полезно 😉 Наивным подходом было бы создавать новый столбец для каждого месяца, а затем устанавливать значение этого столбца в зависимости от того, находится ли этот месяц между «концом» и «началом». Вот несколько указателей: DataFrame.assign для создания столбцов, Series.where для установки значений на основе условия, Series.between для проверки того, что значение находится между двумя другими значениями, pandas.Timestamp для создания дат.

Ответ №1:

Используя простой pd.Series.cumsum

 import calendar
df2 = pd.DataFrame(np.zeros(shape=[len(df),13]), 
                   columns=map(lambda s: calendar.month_abbr[s], 
                                        np.arange(13)))
  

Первый набор начинается как значения и заканчивается как -values .

 r = np.arange(len(df))
df2.values[r, df.start.dt.month] =  df['values']
df2.values[r, df.end.dt.month]   = -df['values']
  

Затем cumsum через axis=1
df2 = df2.cumsum(1)

Установите конечное значение в values

 df2.values[r, df.end.dt.month]= df['values']
  

Конечный результат:

         Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
0   0   0   0   0   0   1   1   0   0   0   0   0   0
1   0   0   0   0   0   2   2   2   2   0   0   0   0
2   0   0   0   0   0   1   1   1   1   1   0   0   0
3   0   0   0   0   0   3   3   3   3   3   3   0   0
4   0   0   0   0   0   4   4   4   4   4   4   4   0
5   0   0   0   0   0   2   2   2   2   2   2   2   2
  

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

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

2. @RicardoFernandes Каков результат в этом случае?

Ответ №2:

Метод из sklearn MultiLabelBinarizer

 from sklearn.preprocessing import MultiLabelBinarizer
df['L'] = [pd.date_range(x, y, freq='M') for x, y in zip(df.start, df.end)]
mlb = MultiLabelBinarizer()
yourdf=pd.DataFrame(mlb.fit_transform(df['L']),columns=mlb.classes_, index=df.index).mul(df['values'],0)
yourdf.columns=yourdf.columns.strftime('%Y%B')
yourdf['subscription']=df['subscription']
yourdf
Out[75]: 
   2018May  2018June      ...       2018November  subscription
0        1         0      ...                  0             x
1        2         2      ...                  0             y
2        1         1      ...                  0             z
3        3         3      ...                  0             a
4        4         4      ...                  0             b
5        2         2      ...                  2             c
[6 rows x 8 columns]
  

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

1. что такое df[‘L]?