Я случайно наткнулся на ту же проблему, когда мне пришлось вычислять что-то подобное, но на 15-минутном таймфрейме вместо 1-дневного. Я пытался применить свое решение к вашей проблеме.
1) Как описано Сержем Баллестой, важно разбить строки на фиксированные единицы времени, в ваших случаях дней. Мой подход состоял в том, чтобы преобразовать время начала и окончания в время начала и продолжительность.
Я пробовал этот код с фрагментом ваших данных, который добавляется в конце.
df = pd.DataFrame(data, columns=cols) # data and cols as defined at end of my post
# Convert relevant entries to datetime format
df['Start_date'] = pd.to_datetime(df['Start_date'])
df['End_date'] = pd.to_datetime(df['End_date'])
# Set start_date as index
df.set_index('Start_date', inplace=True)
# Add row with duration, based on start and end-time
df['duration'] = pd.to_timedelta(df['End_date'] - df.index)
Я создаю новый фрейм данных с ежедневными записями (вы можете установить время соответствующим образом)
# Online is where status is 0, offline is where status is {1,2,3}
# Step-by-step-explanation:
# df[df['Status'] == '0'] -> filters only for entries wit hstatus 'online'
# df[df['Status'] == '0']['duration'] -> of the filtered entries, select the duration row
# df[df['Status'] == '0']['duration'].resample('1d') -> resample to daily bins
# df[df['Status'] == '0']['duration'].resample('1d').sum() -> sum 'duration' per bin
#(df[df['Status'] == '0']['duration'].resample('1d').sum().astype(np.int64)/1e9) -> convert summed duration to seconds (convertion from nanoseconds!)
online = (df[df['Status'] == '0']['duration'].resample('1d').sum().astype(np.int64)/1e9)
offline = (df[(df['Status'] == '1') | (df['Status'] == '2') | (df['Status'] == '3')]['duration'].resample('1d').sum().astype(np.int64)/1e9)
index = online.index
# The new dataframe is put together
new_sort = pd.DataFrame(index=index)
new_sort['online'] = online
new_sort['offline']= offline
new_sort.fillna(0, inplace=True)
Поскольку продолжительность может перекрываться более чем на один день, у вас могут быть записи продолжительности с временем, превышающим один день. Я исправил это с помощью функции. Эта функция ограничивает время и, если есть «избыточное» время, оно переносит его на следующий день.
def split_duration(df, col, dt):
diff = 0
for e,row in df.iterrows():
if diff > 0:
row[col] += diff
diff = 0
if row[col]>dt:
diff = row[col] - dt
row[col] = dt
return df
dt = 60*60*24 # time unit is in seconds, dt is seconds per day
df = split_duration(new_sort, 'online', dt)
df = split_duration(new_sort, 'offline', dt)
Полученный в результате фрейм данных new_sort предоставит вам всю необходимую информацию.
new_sort
online offline
Start_date
2018-10-22 6034.0 86400.0
2018-10-23 1199.0 86400.0
2018-10-24 0.0 76175.0
2018-10-25 86400.0 1718.0
2018-10-26 86400.0 0.0
2018-10-27 86400.0 0.0
2018-10-28 86400.0 0.0
2018-10-29 86400.0 0.0
2018-10-30 86400.0 0.0
Этот код все еще может быть неоптимальным, улучшения приветствуются.
Вот только датафрейм, который я использовал с этим кодом.
data = np.array([[40162, 'AUH888', 1, '2018-10-22 08:33:22', '2018-10-22 08:34:26'],
[40163, 'AUH888', 0, '2018-10-22 08:34:26', '2018-10-22 10:15:00'],
[40167, 'AUH888', 3, '2018-10-22 10:15:00', '2018-10-23 12:40:01'],
[40224, 'AUH888', 0, '2018-10-23 12:40:01', ' 2018-10-23 13:00:00'],
[40227, 'AUH888', 3, '2018-10-23 13:00:00', ' 2018-10-25 07:43:30'],
[40296, 'AUH888', 0, '2018-10-25 07:43:30', ' 2018-10-25 08:00:00'],
[40298, 'AUH888', 3, '2018-10-25 08:00:00', ' 2018-10-25 08:28:38'],
[40301, 'AUH888', 0, '2018-10-25 08:28:38', ' 2018-11-05 12:15:00'],
[40965, 'AUH888', 3, '2018-11-05 12:15:00', ' 2018-11-07 08:06:58'],
[41085, 'AUH888', 0, '2018-11-07 08:06:58', ' 2018-11-12 07:15:00'],
[41256, 'AUH888', 3, '2018-11-12 07:15:00', ' 2018-11-12 07:19:29'],
[41257, 'AUH888', 0, '2018-11-12 07:19:29', ' 2018-11-15 10:45:00'],
[41412, 'AUH888', 3, '2018-11-15 10:45:00', ' 2018-11-17 09:38:42'],
[41469, 'AUH888', 0, '2018-11-17 09:38:42', ' 2018-11-19 10:15:00']])
cols = ['History_id', 'Device_id','Status', 'Start_date', 'End_date']
df = pd.DataFrame(data, columns=cols)