【问题标题】:How to split pandas dataframe single row into two rows?如何将熊猫数据框单行拆分为两行?
【发布时间】:2018-06-21 07:25:51
【问题描述】:

我正在尝试将 Dataframe 的单行拆分为两行。在 Dataframe 开始和结束列可用。我想根据条件拆分行。

我有一个如下数据框:

symbol,start,end,size
ABC,2015-08-27 18:00:00,2015-08-28 05:00:00,12
ABC,2015-11-20 02:00:00,2015-11-20 06:00:00,5
ABC,2016-01-22 03:00:00,2016-01-22 06:00:00,4
PQR,2016-02-12 02:00:00,2016-02-12 06:00:00,5
PQR,2016-02-12 22:00:00,2016-02-13 03:00:00,6
PQR,2016-02-12 02:00:00,2016-02-12 07:00:00,6

条件:

  1. 如果开始和结束是同一天,则无需执行任何操作。
  2. 如果开始和结束日期不同,则需要将其分成两行。

示例:让我们考虑如下行:

PQR,2016-02-12 22:00:00,2016-02-13 03:00:00,6

在上面的行中,开始包含第 12 天,结束包含第 13 天,所以需要将其分成两行,如下所示:

PQR,2016-02-12 22:00:00,2016-02-12 23:00:00,2
PQR,2016-02-12 00:00:00,2016-02-13 03:00:00,4

如果该行包含三天,例如从 12 日开始到 14 日结束,则需要将其分成三行。

预期输出为:

symbol,start,end,size
ABC,2015-08-27 18:00:00,2015-08-27 23:00:00,6
ABC,2015-08-28 00:00:00,2015-08-28 05:00:00,6
ABC,2015-11-20 02:00:00,2015-11-20 06:00:00,5
ABC,2016-01-22 03:00:00,2016-01-22 06:00:00,4
PQR,2016-02-12 02:00:00,2016-02-12 06:00:00,5
PQR,2016-02-12 22:00:00,2016-02-12 23:00:00,2
PQR,2016-02-12 00:00:00,2016-02-13 03:00:00,4
PQR,2016-02-12 02:00:00,2016-02-12 07:00:00,6

【问题讨论】:

  • 第三个代码块第一行的“23:00:00”是从哪里来的?我在原始数据中没有看到它。同一代码块第二行中的“00:00:00”也是如此。您是否忘记命名另一个条件?
  • @sudonym- 这是几个小时。在一天之内,小时以 23 结尾并以 00 开头,需要按天拆分此数据。
  • 我明白了,但是,您的输入数据中不存在小时“23:00:00”。 “00:00:00”也是如此。这意味着您必须有一个规则来确定这个小时。您的代码中不存在此规则。因此(再次)问题:如何知道其他行的时间?
  • @sudonym 我的猜测是,您必须想象开始时间(00:00)和结束时间,就好像他们将分钟和秒设置为 59:59
  • 面对模棱两可,拒绝猜测的诱惑:)

标签: python pandas dataframe


【解决方案1】:

选项 1

遍历行并构建一个新的DataFrame,逐行追加。

import pandas as pd
import datetime

df2 = pd.DataFrame(columns=df.columns)

for (_,r) in df.iterrows():

    while r['start'].date()<r['end'].date():
        # create new row
        newR = r.copy()
        newR['end']=newR['start']
        newR['end']=newR['end'].replace(hour=23)

        newSize = 24-newR['start'].hour
        newR['size']=newSize

        # update row to process 
        r['start']=r['start']+datetime.timedelta(days=1)
        r['start']=r['start'].replace(hour=0)

        r['size'] = r['size'] - newSize

        df2 = df2.append(newR)

    df2 = df2.append(r)

df2.reset_index(drop=True, inplace=True)

选项 2

使用掩码执行操作Dataframe-wise,并使用递归调用,以防原始Dataframe 中的行在两天内被拆分。

import pandas as pd
import numpy as np
import datetime


def splitMultiDayRows(df):
    mask = df['end'].dt.day>df['start'].dt.day

    if np.any(mask):
        df_new = df.loc[mask]

        newSizes = 24-df.loc[mask,'start'].dt.hour

        df.loc[mask,'end'] = df.loc[mask,'start']
        df.loc[mask,'end'] = df.loc[mask,
                                    'end'].apply(lambda x:
                                                 x.replace(hour=23))
        df.loc[mask,'size'] = newSizes

        df_new.loc[:,'start'] = df_new['start']+datetime.timedelta(days=1)
        df_new.loc[:,'start'] = df_new['start'].apply(lambda x:
                                                      x.replace(hour=0))

        df_new.loc[:,'size'] = df_new['size'] - newSizes

        return pd.concat([df,splitMultiDayRows(df_new)])
    else:
        return df

与调用一起使用:

splitMultiDayRows(df.copy()).\
sort_values(['symbol','start']).\
reset_index(drop=True)

【讨论】:

  • @Herrlvan- 迭代 DataFrame 这使得工具非常耗时。您有想法在不迭代 DataFrame 的情况下完成此任务吗?
  • @kit- 选项 2 现在不逐行迭代
【解决方案2】:

此答案避免了迭代并且不会复制不必要的行,因此您将节省时间和空间。

df['start'] = pd.to_datetime(df['start'])
df['end'] = pd.to_datetime(df['end'])

df2 = pd.DataFrame(columns=df.columns)

mask_to_change = df.apply(lambda x: x['end'].day > x['start'].day, axis=1)

for (_,r) in df[mask_to_change].iterrows():

    while r['start'].date()<r['end'].date():
        # create new row
        newR = r.copy()
        newR['end']=newR['start']
        newR['end']=newR['end'].replace(hour=23)

        newSize = 24-newR['start'].hour
        newR['size']=newSize

        # update row to process 
        r['start']=r['start']+datetime.timedelta(days=1)
        r['start']=r['start'].replace(hour=0)

        r['size'] = r['size'] - newSize

        df2 = df2.append(newR)

    df2 = df2.append(r)

df = pd.concat([df[~mask_to_change], df2])
df.sort_values(['symbol', 'start'], inplace=True)

【讨论】:

    猜你喜欢
    • 2021-12-04
    • 2018-11-16
    • 1970-01-01
    • 1970-01-01
    • 2014-10-07
    • 2016-12-03
    • 1970-01-01
    • 2020-02-19
    • 2023-03-21
    相关资源
    最近更新 更多