在datafram中反转给定日期的时间

2024-05-16 02:15:15 发布

您现在位置:Python中文网/ 问答频道 /正文

对于包含开始和结束时间的数据帧,我想“反转”给定日期的时间。当然有一种“蛮力”方法可以通过循环遍历数据帧并具有许多if条件来实现,但是我想知道是否有一种更优雅的方法,比如回填/前向填充。你知道吗

假设它有一个数据帧,其中工作时间由带有开始时间和结束时间的行表示,我最终想要的是这个日期的空闲时间。 数据帧以单调递增的开始时间排序,并在日期更改时截止,因此从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

非常感谢您的帮助。你知道吗


Tags: 数据方法importdatetimedateif排序时间
2条回答

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

^{}就可以简单地做到这一点。问题出现在最后一行,我正在努力找出如何最好地重建。你知道吗

编辑:我在最后一排给了它我最好的机会,但最后它变成了一个笨拙的烂摊子。我很乐意得到关于最后一排的任何反馈。原则上,使用shift会使这变得非常简单。很明显,您可以在添加最后一行之前删除startend,我只是演示了如何做到不丢失数据。你知道吗

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)

相关问题 更多 >