Инвертировать время на данную дату в кадре данных - PullRequest
2 голосов
/ 05 апреля 2019

Для фрейма данных, содержащего время начала и окончания, я хотел бы «инвертировать» это время для данной даты. Конечно, есть метод "грубой силы", который делает это, циклически перебирая фрейм данных и имея много условий if, но мне интересно, есть ли более элегантный способ, например, с backfill / forwardfill.

Считайте, что в нем есть фрейм данных, в котором рабочее время представлено строками с временем начала и окончания, а в конечном итоге мне нужно свободное время этой даты. Кадр данных упорядочен с монотонным увеличением времени начала и имеет отсечки при смене даты, поэтому время начинается, например, с. 2019-04-04 22:00 и заканчивающийся в 2019-04-05 04:00 представлен двумя строками 2019-04-04 с 22:00 до 2019-04-05 00:00 и 2019-04-05 00:00 до 2019-04-05 04:00. Это должно облегчить проблему.

Код для примера:

import pandas as pd
import datetime

df = pd.DataFrame({'date': [datetime.date(2019, 4, 4), datetime.date(2019, 4, 5), datetime.date(2019, 4, 5)],
                   'start': [pd.Timestamp(2019, 4, 4, 10), pd.Timestamp(2019, 4, 5, 0), pd.Timestamp(2019, 4, 5, 14)],
                   'end': [pd.Timestamp(2019, 4, 4, 16), pd.Timestamp(2019, 4, 5, 4), pd.Timestamp(2019, 4, 5, 18)]})

Итак, начиная с этого:

2019-04-04   2019-04-04 10:00:00   2019-04-04 16:00:00
2019-04-05   2019-04-05 00:00:00   2019-04-05 04:00:00
2019-04-05   2019-04-05 14:00:00   2019-04-05 18:00:00

Я ожидал бы, что результатом будет такой фрейм данных:

2019-04-04   2019-04-04 00:00:00   2019-04-04 10:00:00
2019-04-04   2019-04-04 16:00:00   2019-04-05 00:00:00
2019-04-05   2019-04-05 04:00:00   2019-04-05 14:00:00
2019-04-05   2019-04-05 18:00:00   2019-04-06 00:00:00

Любая помощь очень ценится.

Ответы [ 2 ]

1 голос
/ 05 апреля 2019

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

РЕДАКТИРОВАТЬ: Я сделал лучший снимок в последнем ряду, но в итоге получился неуклюжий беспорядок. Буду рад любым отзывам об этом последнем ряду. В принципе, использование shift сделает это очень просто. Очевидно, вы могли бы просто удалить start и end перед добавлением последней строки, я просто показал, как это сделать без потери данных.

import pandas as pd
import numpy as np
import datetime as dt

df = pd.DataFrame({'date': [dt.date(2019, 4, 4), dt.date(2019, 4, 5), dt.date(2019, 4, 5)],
                   'start': [pd.Timestamp(2019, 4, 4, 10), pd.Timestamp(2019, 4, 5, 0), pd.Timestamp(2019, 4, 5, 14)],
                   'end': [pd.Timestamp(2019, 4, 4, 16), pd.Timestamp(2019, 4, 5, 4), pd.Timestamp(2019, 4, 5, 18)]})

df = df[['date', 'start', 'end']]

saved_shift_ending = df['end'].iloc[-1]  # we want end of last shift
saved_end_date = df['date'].iloc[-1]     # we also want the date value

start_date = df['date'].min()
end_date = (df['date'].max() + dt.timedelta(days=1))

df['other_start'] = df['end'].shift(1)
df['other_end'] = df['start']

df.loc[0, 'other_start'] = start_date # The first value is NaT after shift

last_row = pd.DataFrame([[saved_end_date.strftime('%Y-%m-%d'), 
                         np.nan, 
                         np.nan, 
                         saved_shift_ending, 
                         end_date]],
                        columns=['date', 'start', 'end', 'other_start',
                                'other_end'])

df = df.append(last_row)

df.drop(['start', 'end'], axis=1, inplace=True)
print(df)
0 голосов
/ 06 апреля 2019

roganjosh отвечает на общий случай, однако мне пришлось посмотреть на ежедневное «свободное время», и для этого мне пришлось добавить некоторые искусственные границы даты в виде строк с нулевым временем между началом и концом.В итоге .shift() было то, что я был после.Я упаковал это в функцию для увеличения возможности повторного использования, и если у кого-то есть более элегантное решение, пожалуйста, не стесняйтесь поделиться.

Вот мой код:

def invertDailyTimes(df, dateCol, starttimeCol, endtimeCol):
    """
    requires a input df with a date column (dateCol) and two timestamp columns (starttimeCol, endttimeCol)
    which is monotonic ordered in (starttimeCol, endttimeCol)
    """
    dates = list(df[dateCol].unique())
    for d in dates:
        df_tmp = df[df[dateCol] == d].iloc[0:1]
        df_tmp[starttimeCol] = pd.Timestamp(d)
        df_tmp[endtimeCol] = pd.Timestamp(d)
        df_tmp = df_tmp.append(df_tmp)
        df_tmp[starttimeCol].iloc[-1] = pd.Timestamp(d + datetime.timedelta(days=1))
        df_tmp[endtimeCol].iloc[-1] = pd.Timestamp(d + datetime.timedelta(days=1))
        df_tmp[dateCol].iloc[-1] = d + datetime.timedelta(days=1)
        df = df.append(df_tmp)

    df.drop_duplicates(inplace=True)
    df.sort_values(by=[starttimeCol, endtimeCol], inplace=True)

    df['invert_start'] = df[endtimeCol].shift(1)
    df['invert_end'] = df[starttimeCol]

    df = df[(abs(df['invert_start'] - df['invert_end']) < pd.Timedelta(days=1)) &
            (abs(df['invert_start'] - df['invert_end']) > pd.Timedelta(seconds=0))]

    df[starttimeCol] = df['invert_start']
    df[endtimeCol] = df['invert_end']
    df.drop(columns=['invert_start', 'invert_end'], inplace=True)

    return df
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...