2016-10-31 2 views
3

У меня есть DataFrame с интервалами даты и времени, как этот:Панда: интервалы перерыва DateTime дня

 
    id   start_date    end_date 
1 1 2016-10-01 00:00:00 2016-10-01 03:00:00 
2 1 2016-10-03 05:30:00 2016-10-03 06:30:00 
3 2 2016-10-03 23:30:00 2016-10-04 01:00:00 # This line should be splitted 
4 1 2016-10-04 05:00:00 2016-10-04 06:00:00 
5 2 2016-10-04 05:50:00 2016-10-04 06:00:00 
6 1 2016-10-05 18:50:00 2016-10-06 02:00:00 # This one too 
.... 

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

 
    id   start_date    end_date 
1  1 2016-10-01 00:00:00 2016-10-01 03:00:00 
2  1 2016-10-03 05:30:00 2016-10-03 06:30:00 
3  2 2016-10-03 23:30:00 2016-10-03 23:59:59 # Splitted 
4  2 2016-10-04 00:00:00 2016-10-04 01:00:00 # Splitted 
5  1 2016-10-04 05:00:00 2016-10-04 06:00:00 
6  2 2016-10-04 05:50:00 2016-10-04 06:00:00 
7  1 2016-10-05 18:50:00 2016-10-05 23:59:59 # Splitted 
8  1 2016-10-06 00:00:00 2016-10-06 02:00:00 # Splitted 
.... 

ответ

2

Вы можете использовать .dt accessor создать логический индекс, где для выполнения обновления, а затем внести соответствующие коррективы:

# Get the rows to split. 
split_rows = (df['start_date'].dt.date != df['end_date'].dt.date) 

# Get the new rows to append, adjusting the start_date to the next day. 
new_rows = df[split_rows].copy() 
new_rows['start_date'] = new_rows['start_date'].dt.date + pd.DateOffset(days=1) 

# Adjust the end_date of the existing rows. 
df.loc[split_rows, 'end_date'] = df.loc[split_rows, 'start_date'].dt.date + pd.DateOffset(days=1, seconds=-1) 

# Append the new rows to the existing dataframe. 
df = df.append(new_rows).sort_index().reset_index(drop=True) 

Этот процесс предполагает, что между разницей между датами между start_date и end_date будет только один день. Если это возможно, что есть многодневные пролеты, вы можете обернуть вышеупомянутый процесс в while цикле:

# Get the rows to split. 
split_rows = (df['start_date'].dt.date != df['end_date'].dt.date) 

while split_rows.any(): 
    # Get the new rows, adjusting the start_date to the next day. 
    new_rows = df[split_rows].copy() 
    new_rows['start_date'] = new_rows['start_date'].dt.date + pd.DateOffset(days=1) 

    # Adjust the end_date of the existing rows. 
    df.loc[split_rows, 'end_date'] = df.loc[split_rows, 'start_date'].dt.date + pd.DateOffset(days=1, seconds=-1) 

    # Append the new rows to the existing dataframe. 
    df = df.append(new_rows).sort_index().reset_index(drop=True) 

    # Get new rows to split (if the start_date to end_date span is more than 1 day). 
    split_rows = (df['start_date'].dt.date != df['end_date'].dt.date) 

В результате выход из ваших данных выборки:

id   start_date   end_date 
0 1 2016-10-01 00:00:00 2016-10-01 03:00:00 
1 1 2016-10-03 05:30:00 2016-10-03 06:30:00 
2 2 2016-10-03 23:30:00 2016-10-03 23:59:59 
3 2 2016-10-04 00:00:00 2016-10-04 01:00:00 
4 1 2016-10-04 05:00:00 2016-10-04 06:00:00 
5 2 2016-10-04 05:50:00 2016-10-04 06:00:00 
6 1 2016-10-05 18:50:00 2016-10-05 23:59:59 
7 1 2016-10-06 00:00:00 2016-10-06 02:00:00 
1

Это работает:

def date_split(row): 
    starts = pd.Series(pd.date_range(row['start_date'].date(), 
            periods=row['diff']+1, freq='D')) 
    starts[0] = row['start_date'] 
    ends = starts[1:] - pd.to_timedelta(1, unit='s') 
    ends.loc[len(ends)+1] = row['end_date'] 
    ends.reset_index(drop=True, inplace=True) 

    ret = pd.concat([starts, ends], axis=1, keys=['start_date', 'end_date']) 
    ret['id'] = row['id'] 
    return ret 

df['diff'] = df['end_date'].dt.day - df['start_date'].dt.day 

req = pd.concat([df[df['diff'] == 0]] +\ 
       [date_split(row) for _, row in df[df['diff'] > 0].iterrows()]) 
req = req.drop('diff', axis=1).reset_index(drop=True) 
req 

Обратите внимание, что это общий метод и будет обрабатывать любое количество дней между ними. Только ваши позиции индекса будут разными.

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