2016-01-13 2 views
1

У меня есть список устройств и их продолжительность (время начала и окончания). Устройство может иметь один или несколько журналов активности. Я пытаюсь создать дистрибутив для каждого устройства, когда устройство было активным.Заполнение часовых поясов во время действия - Python

Моего текущий dataframe выглядит примерно так:

device_id start_time end_time 
1 03:53 10:54 
1 06:00 14:00 
2 20:29 06:17 

Чтобы создать распределение времени активности для каждого устройства, я думал, что я хотел бы создать почасовое ведро (соответствующие часы с 00 до 23) и заполнить ведра, где устройство было активным. Таким образом, для устройства 1, например, первая строка будет

[0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0] 

и второй ряд

[0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0] 

добавляя их, чтобы создать распределение активности для устройства 1 даст:

[0,0,0,1,1,1,2,2,2,2,2,1,1,1,1,0,0,0,0,0,0,0,0,0] 

У меня была следующая попытка создания необходимых списков, однако она работает только в том случае, если конечное время больше, чем время начала (например, первые две строки в примере данных выше) и не будет работать для времени запуска больше, чем времени окончания (например, строка 3 в примере данных выше).

for start, end in zip(df[df['start_time'].notnull() & df['end_time'].notnull()]['start_time'],df[df['start_time'].notnull() & df['end_time'].notnull()]['end_time']) : 
    start_time = pd.to_datetime(start, format ='%H:%M') 
    end_time = pd.to_datetime(end, format ='%H:%M') 
    activity = [0]*24 
    i = (start_time + dt.timedelta(minutes=((start_time.minute // 60 + (1 if start_time.minute>30 else 0)) * 60) - start_time.minute)).hour 
    rounded_end_time = (end_time + dt.timedelta(minutes=((end_time.minute // 60 + (1 if end_time.minute>30 else 0)) * 60) - end_time.minute)).hour 
    while i < rounded_end_time: 
     activity[i] = 1 
     i = i + 1 
    print activity 

Любые предложения по исправлению? (Или более разумный способ для выполнения этой задачи в первую очередь?)

+0

Как вы интерпретируете строку, где время начала больше, чем время окончания? То есть что это на самом деле представляет с точки зрения реальной информации? – 8one6

+0

@ 8one6 означает, что устройство работает на ночь. Третья строка, например, должна заполнить первые 6 индексов и последние 4 в часовых ведрах. – Fate

ответ

0

решаемые его! Я отправляю ответ с комментариями только в случае, если кто нуждается в этом:

# for each pair of start and end time that are not null 
for start, end in zip(df[df['start_time'].notnull() & df['end_time'].notnull()]['start_time'],df[df['start_time'].notnull() & df['end_time'].notnull()]['end_time']) : 

    start_time = pd.to_datetime(start, format ='%H:%M') 
    end_time = pd.to_datetime(end, format ='%H:%M') 
    #create a list of 24 indexes and initialize them to 0 
    activity = [0]*24 
    #round start and end time to the nearest hour 
    i = (start_time + dt.timedelta(minutes=((start_time.minute // 60 + (1 if start_time.minute>30 else 0)) * 60) - start_time.minute)).hour 
    rounded_end_time = (end_time + dt.timedelta(minutes=((end_time.minute // 60 + (1 if end_time.minute>30 else 0)) * 60) - end_time.minute)).hour 
    #calculate the number of hours of activity (which is also the number of buckets to be filled)  
    duration = (pd.to_datetime(rounded_end_time , format ='%H') - pd.to_datetime(i, format ='%H')).seconds//3600 
    #initialize a count to count the number of buckets we fill 
    count = 0 
    while duration > count: 
     activity[i] = 1 
     count = count +1 
     #set the index of the bucket to be filled to the next indes, unless it goes beyond the last bucket, in which case continue from the first bucket 
     i = (i+1 if i+1 < 24 else 0) 
    print activity 
1

Вы можете сделать это, используя только панд, как показано ниже:

x=pd.DataFrame([[1, '03:53', '10:54'],[1, '06:00', '14:00'],[2, '20:29', '06:17']]) 
x.columns=['device_id', 'start_time', 'end_time'] 
x['start_time']=pd.to_datetime(x['start_time'],format ='%H:%M') 
x['end_time']=pd.to_datetime(x['end_time'],format ='%H:%M') 
x['type'] = x['end_time']-x['start_time']>0 
x['type'] = x['type'].apply(lambda x: 0 if x else 1) 
x['min'] = x[['start_time','end_time']].min(axis=1) 
x['max'] = x[['start_time','end_time']].max(axis=1) 
for i in range(24): 
    h = '0'+str(i) 
    h = h[-2:] 
    l = x['min']<=pd.to_datetime(h + ':59',format ='%H:%M') 
    e = x['max']>=pd.to_datetime(h+':00',format ='%H:%M') 
    l=l.apply(lambda x: 1 if x else -1) 
    e=e.apply(lambda x: 1 if x else -1) 
    x[i]=l+e+x['type'] 
    x[i]=x[i].apply(lambda x: 1 if x > 0 and x < 3 else 0) 
x = x.drop(['start_time','end_time'],axis=1).groupby('device_id').agg(np.max) 
x.reset_index().drop('device_id',axis=1).sum() 
+0

Моя основная проблема - это время, когда время начала больше, чем время окончания. У меня нет полной даты, к сожалению, всего лишь в 24-м формате. Это предлагаемое решение не решает проблему. – Fate

+0

@Fate: Я отредактировал свое решение, чтобы решить проблему, которую вы подняли. Для больших наборов данных это должно быть намного быстрее, чем повторение списков. – zuku

0

Принимая строки начала/конца и биннинга их время ведер (общая продолжительность минут в данном случае)

Примечание: не все крайние случаи были покрыты, но вы можете расширить код, если вы найдете его полезным

#your imports 
import numpy as np 
import pandas as pd 
from pandas.tseries.offsets import Hour, Minute 
#optional 
from IPython.core.debugger import set_trace 

# construct a sample raw data dataframe 
start_times = ['2000-01-01 09:00', '2000-01-01 10:00'] 
end_times = ['2000-01-01 17:00', '2000-01-01 18:00'] 
index = ['Timeframe ' + str(i) for i in range(len(start_times))] 
df = pd.DataFrame({'Start Time': pd.to_datetime(start_times), 
       'End Time' : pd.to_datetime(end_times)}, index=index) 

dataframe ДФ будет выглядеть ниже

   End Time    Start Time 

Временной интервал 0   2000-01-01 17:00:00   2000-01-01 09:00:00
Временной интервал 1   2000-01-01 18:00:00   2000 -01-01 10:00:00

#Construct your dataframe for time buckets 
rng = pd.date_range('2000-01-01 09:00', periods=9, freq='H') 
ts = pd.DataFrame(0, index=rng, columns=['minutes'], dtype='float') 

dataframe ц будет выглядеть ниже

     minutes 

2000-01-01 09:00:00   0.0
2000-01-01 10:00:00   0,0
2000-01-01 11:00:00   0,0
2000-01-01 12:00:00   0,0
2000-01-01 13:00:00   0,0
2000-01-01 14:00:00   0,0
2000-01-01 15:00:00   0,0
2000-01-01 16:00:00   0,0
2000-01-01 17:00:00   0,0

for index, row in ts.iterrows(): 
    #set_trace() 
    start_boundary = index 
    end_boundary = index + Hour() 
    time_count = pd.Timedelta('0 m') 
     for _, raw_data in df.iterrows(): 
      #set_trace() 
      start_time = raw_data['Start Time'] 
      end_time = raw_data['End Time'] 
      if end_time > start_boundary: 
       if start_time < end_boundary: 
        if start_time <= start_boundary: 
         if end_time >= end_boundary: 
          time_count = time_count + (end_boundary - start_boundary) 
         else: 
          time_count = time + (end_time - start_boundary) 
        else: 
         if end_time >= end_boundary: 
          time_count = time_count + (end_boundary - start_time) 
         else: 
          time_count = time_count + (end_time - start_time) 
    #set_trace() 
    ts.at[index, 'minutes'] = time_count.seconds/60 

Выполнить код выше и ваши ц dataframe (смотри ниже), должны иметь общую продолжительность в минутах Binned на основе необработанных данных в DF dataframe

     minutes 

2000-01-01 09:00:00   60,0
2000-01-01 10:00:00   120,0
2000-01-01 11:00:00   120,0
2000-01-01 12:00:00   120,0
2000-01-01 13:00:00   120,0
2000-01-01 14:00:00   120,0
2000-01-01 15:00:00   120,0
2000-01-01 16:00:00   120,0
2000-01-01 17:00:00   60,0

Смежные вопросы