Совокупная сумма Pandas, если между определенными временами/значениями

Я хочу вставить новый столбец с именем total в final_df, который представляет собой кумулятивную сумму value в df, если это происходит между временами в final_df. Он суммирует значения, если они встречаются между start и end в final_df. Так, например, в диапазоне времени с 01:30 до 02:00 в final_df - оба индекса 0 и 1 в df встречаются между этим временным диапазоном, поэтому общее количество равно 15 (10+5).

У меня есть два фрейма данных pandas:

дф

import pandas as pd

d = {'start_time': ['01:00','00:00','00:30','02:00'], 
     'end_time': ['02:00','03:00','01:30','02:30'], 
     'value': ['10','5','20','5']}

df = pd.DataFrame(data=d)

final_df

final_df = {'start_time': ['00:00, 00:30, 01:00, 01:30, 02:00, 02:30'],
            'end_time': ['00:30, 01:00, 01:30, 02:00, 02:30, 03:00']}

final_df = pd.DataFrame(data=final_d)

выходные данные Я хочу final_df

start_time  end_time total
00:00       00:30    5
00:30       01:00    25
01:00       01:30    35
01:30       02:00    15
02:30       03:00    10

Моя попытка

final_df['total'] = final_df.apply(lambda x: df.loc[(df['start_time'] >= x.start_time) & 
                                            (df['end_time'] <= x.end_time), 'value'].sum(), axis=1)

Проблема 1

Я получаю сообщение об ошибке: TypeError: («›=» не поддерживается между экземплярами «str» и «datetime.time», «произошло по индексу 0»)

Я преобразовал соответствующие столбцы в datetime следующим образом:

df[['start_time','end_time']] = df[['start_time','end_time']].apply(pd.to_datetime, format='%H:%M')
final_df[['start_time','end_time']] = final_df[['start_time','end_time']].apply(pd.to_datetime, format='%H:%M:%S')

Но я не хочу конвертировать в datetime. Есть ли способ обойти это?

Проблема 2

Сумма не работает должным образом. Он ищет только точное совпадение для временного диапазона. Итак, вывод:

 start_time  end_time total
    00:00       00:30    0
    00:30       01:00    0
    01:00       01:30    0
    01:30       02:00    0
    02:30       03:00    5

person thor    schedule 12.06.2020    source источник


Ответы (1)


Один из способов не использовать apply может быть таким.

df_ = (df.rename(columns={'start_time':1, 'end_time':-1}) #to use in the calculation later
         .rename_axis(columns='mult') # mostly for esthetic
         .set_index('value').stack() #reshape the data
         .reset_index(name='time') # put the index back to columns
      )
df_ = (df_.set_index(pd.to_datetime(df_['time'], format='%H:%M')) #to use resampling technic
          .assign(total=lambda x: x['value'].astype(float)*x['mult']) #get plus or minus the value depending start/end
          .resample('30T')[['total']].sum() # get the sum at the 30min bounds
          .cumsum() #cumulative sum from the beginning
      )
# create the column for merge with final resul
df_['start_time'] = df_.index.strftime('%H:%M')

# merge
final_df = final_df.merge(df_)

и вы получаете

print (final_df)
  start_time end_time  total
0      00:00    00:30    5.0
1      00:30    01:00   25.0
2      01:00    01:30   35.0
3      01:30    02:00   15.0
4      02:00    02:30   10.0
5      02:30    03:00    5.0

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

df['start_time'] = pd.to_datetime(df['start_time'], format='%H:%M')
df['end_time'] = pd.to_datetime(df['end_time'], format='%H:%M')
df['value'] = df['value'].astype(float)
final_df['start_time'] = pd.to_datetime(final_df['start_time'], format='%H:%M')
final_df['end_time'] = pd.to_datetime(final_df['end_time'], format='%H:%M')

final_df.apply(
    lambda x: df.loc[(df['start_time'] <= x.start_time) & #see other inequality
                     (df['end_time'] >= x.end_time), 'value'].sum(), axis=1)
0     5.0
1    25.0
2    35.0
3    15.0
4    10.0
5     5.0
dtype: float64
person Ben.T    schedule 12.06.2020
comment
Хороший ответ @Ben.T. - person Shubham Sharma; 12.06.2020
comment
Хороший ответ, оба решения сработали. Что касается решения для применения, по какой-то причине последний временной диапазон в моем final_df (т.е. с 23:30 до 00:00. Обратите внимание, что мой образец выше не включен) суммировал все в столбце, чего не должно быть. - person thor; 12.06.2020
comment
@thor хм вот так, я не знаю, почему, я попытаюсь проверить несколько вещей, но да, эффект границ возможен, особенно при игре с интервалами :) - person Ben.T; 12.06.2020
comment
@thor, поэтому, немного подумав над вашим комментарием, имеет смысл получить сумму всего, и поэтому я не уверен, что это легко исправить. если вы запишете неравенство с этим интервалом, то вы получите (df['start_time'] ‹= 23:30) & (df['end_time'] ›= 00:00), что действительно дает все значения в df. - person Ben.T; 12.06.2020