Заполнение недостающих временных меток и применение разных операций к разным столбцам

#python #pandas

#python #pandas

Вопрос:

У меня есть данные в формате ниже

 user   timestamp             flowers     total_flowers

xyz   01-01-2020 00:05:00    15          15
xyz   01-01-2020 00:10:00    5           20
xyz   01-01-2020 00:15:00    21          41
xyz   01-01-2020 00:35:00    1           42

...

xyz   01-01-2020 11:45:00   57           1029
xyz   01-01-2020 11:55:00   18           1047
 

Ожидаемый результат:

 user   timestamp             flowers     total_flowers

xyz   01-01-2020 00:05:00    15          15
xyz   01-01-2020 00:10:00    5           20
xyz   01-01-2020 00:15:00    21          41
xyz   01-01-2020 00:20:00    0           41
xyz   01-01-2020 00:25:00    0           41
xyz   01-01-2020 00:30:00    0           41
xyz   01-01-2020 00:35:00    1           42

...

xyz   01-01-2020 11:45:00   57           1029
xyz   01-01-2020 11:50:00   0            1029
xyz   01-01-2020 11:55:00   18           1047
 

Итак, я хочу заполнить временные метки с интервалом в 5 минут и заполнить столбец цветов на 0, а столбец total_flowers — предыдущим значением (ffill)

Мои усилия:

 start_day = "01-01-2020"
end_day = "01-01-2020"

start_time = pd.to_datetime(f"{start_day} 00:05:00 05:30")
end_time = pd.to_datetime(f"{end_day} 23:55:00 05:30")
dates = pd.date_range(start=start_time, end=end_time, freq='5Min')

df =  df.set_index('timestamp').reindex(dates).reset_index(drop=False).reindex(columns=df.columns)
 

Как мне заполнить flowers столбец нулями, а total_flower столбец ffill — и я также получаю значения в timestamp столбце как Nan

Фактический результат:

 user   timestamp flowers     total_flowers

xyz   Nan    15          15
xyz   Nan    5           20
xyz   Nan    21          41
xyz   Nan    Nan         Nan
xyz   Nan    Nan         Nan
xyz   Nan    Nan         Nan
xyz   Nan    1           42

...

xyz   Nan   57           1029
xyz   Nan   Nan          Nan
xyz   Nan   18           1047
 

Ответ №1:

Переиндексация и пополнение счета

Если вы сконструируете dates его таким образом, чтобы вы могли reindex использовать свои временные метки, вы можете просто выполнить некоторые fillna ffill операции и . Мне пришлось удалить информацию о часовом поясе, но вы должны иметь возможность добавить ее обратно, если ваши данные соответствуют часовому поясу. Вот полный пример использования некоторых ваших данных:

 d = {'user': {0: 'xyz', 1: 'xyz', 2: 'xyz', 3: 'xyz'},
 'timestamp': {0: Timestamp('2020-01-01 00:05:00'),
  1: Timestamp('2020-01-01 00:10:00'),
  2: Timestamp('2020-01-01 00:15:00'),
  3: Timestamp('2020-01-01 00:35:00')},
 'flowers': {0: 15, 1: 5, 2: 21, 3: 1},
 'total_flowers': {0: 15, 1: 20, 2: 41, 3: 42}}

df = pd.DataFrame(d)

#  user           timestamp  flowers  total_flowers
#0  xyz 2020-01-01 00:05:00       15             15
#1  xyz 2020-01-01 00:10:00        5             20
#2  xyz 2020-01-01 00:15:00       21             41
#3  xyz 2020-01-01 00:35:00        1             42

#as you did, but with no TZ
start_day = "01-01-2020"
end_day = "01-01-2020"

start_time = pd.to_datetime(f"{start_day} 00:05:00")
end_time = pd.to_datetime(f"{end_day} 00:55:00")
dates = pd.date_range(start=start_time, end=end_time, freq='5Min', name="timestamp")

#filling the nas and reformatting
df = df.set_index('timestamp')
df = df.reindex(dates)
df['user'].ffill(inplace=True)
df['flowers'].fillna(0, inplace=True)
df['total_flowers'].ffill(inplace=True)
df.reset_index(inplace=True)
 

Вывод:

              timestamp user  flowers  total_flowers
0  2020-01-01 00:05:00  xyz     15.0           15.0
1  2020-01-01 00:10:00  xyz      5.0           20.0
2  2020-01-01 00:15:00  xyz     21.0           41.0
3  2020-01-01 00:20:00  xyz      0.0           41.0
4  2020-01-01 00:25:00  xyz      0.0           41.0
5  2020-01-01 00:30:00  xyz      0.0           41.0
6  2020-01-01 00:35:00  xyz      1.0           42.0
7  2020-01-01 00:40:00  xyz      0.0           42.0
8  2020-01-01 00:45:00  xyz      0.0           42.0
9  2020-01-01 00:50:00  xyz      0.0           42.0
10 2020-01-01 00:55:00  xyz      0.0           42.0
 

Повторная выборка и пополнение

Вы также можете использовать resample здесь, используя asfreq() , затем выполнить заполнение, как и раньше. Это удобно для поиска дат (и должно обойти часовой пояс).:

 # resample and then fill the gaps
# same df as constructed above
df = df.set_index('timestamp')
df.resample('5T').asfreq()

df['user'].ffill(inplace=True)
df['flowers'].fillna(0, inplace=True)
df['total_flowers'].ffill(inplace=True)
df.index.name='timestamp'
df.reset_index(inplace=True)
 

Тот же результат:

             timestamp  flowers  total_flowers user
0 2020-01-01 00:05:00       15           15.0  xyz
1 2020-01-01 00:10:00        5           20.0  xyz
2 2020-01-01 00:15:00       21           41.0  xyz
3 2020-01-01 00:20:00        0           41.0  xyz
4 2020-01-01 00:25:00        0           41.0  xyz
5 2020-01-01 00:30:00        0           41.0  xyz
6 2020-01-01 00:35:00        1           42.0  xyz
 

Я не смог найти способ выполнить заполнение во время повторной выборки. Например, используя

 df = df.resample('5T').agg({'flowers':'sum',
                            'total_flowers':'ffill',
                            'user':'ffill'})
 

не работает (это приведет вас к тому же месту, asfreq что и, но здесь больше места для случайного пропуска столбцов). Что странно, потому что при применении ffill ко всему фрейму данных недостающие данные могут быть заполнены вперед (но мы хотим этого только для некоторых столбцов, и пользовательский столбец также удаляется). Но простое использование asfreq и заполнение после факта мне кажется вполне приемлемым с несколькими столбцами.

Ответ №2:

пересечение с @Tom

Вы почти на месте:

 df = pd.DataFrame({'user': ['xyz', 'xyz', 'xyz', 'xyz'],
    'timestamp': ['01-01-2020 00:05:00', '01-01-2020 00:10:00', '01-01-2020 00:15:00', '01-01-2020 00:35:00'],
                   'flowers':[15, 5, 21, 1],
                   'total_flowers':[15, 20, 41, 42]
})
df['timestamp'] = pd.to_datetime(df['timestamp'])
r = pd.date_range(start=df['timestamp'].min(), end=df['timestamp'].max(), freq='5Min')
df = df.set_index('timestamp').reindex(r).rename_axis('timestamp').reset_index()
df['user'].ffill(inplace=True)
df['total_flowers'].ffill(inplace=True)
df['flowers'].fillna(0, inplace=True)
 

приводит к следующему выводу:

     timestamp           user    flowers total_flowers
0   2020-01-01 00:05:00 xyz     15.0    15.0
1   2020-01-01 00:10:00 xyz      5.0    20.0
2   2020-01-01 00:15:00 xyz     21.0    41.0
3   2020-01-01 00:20:00 xyz      0.0    41.0
4   2020-01-01 00:25:00 xyz      0.0    41.0
5   2020-01-01 00:30:00 xyz      0.0    41.0
6   2020-01-01 00:35:00 xyz      1.0    42.0