Как разбить панду dataframe одной строки на две строки?

1

Я пытаюсь разделить одну строку Dataframe на две строки. В 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
Теги:
pandas
dataframe

2 ответа

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 операцию 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)
  • 0
    @ Herrlvan - Итерация DataFrame, что делает инструмент очень трудоемким. У вас есть идея сделать это без итерации DataFrame?
  • 0
    @ kit- Вариант 2 теперь не повторяется по строке
1

Этот ответ позволяет избежать итерации и не копировать ненужные строки, поэтому вы сэкономите время и пространство.

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)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню