L oop для сдвига временной шкалы для столбца в DataFrame Datetime - PullRequest
0 голосов
/ 05 февраля 2020

У меня есть фрейм данных, в котором мне нужно сместить график времени для каждой категории A, B и C.

Например, категория a первоначально начинается в 2020-01-01 и заканчивается 2020-01-04, однако дата начала 2020-01-01 неверна. Это на самом деле 2020-03-01, поэтому новая временная шкала должна быть с 2020-03-01 по 2020-03-04.

Я разместил свое решение для l oop ниже, однако оно только добавляет одно дата для каждой категории.

import pandas as pd
import numpy as np
from datetime import timedelta

df = pd.DataFrame({
    'date': ['2019-01-01','2019-01-02','2019-01-03', '2019-01-04',
             '2019-05-20','2019-05-21','2019-05-22', '2019-05-23',
             '2019-03-01','2019-03-02','2019-03-03', '2019-03-04'],
    'new_start_date': ['2020-03-01','2020-03-01','2020-03-01', '2020-03-01',
             '2020-02-25','2020-02-25','2020-02-25', '2020-02-25',
             '2020-01-19','2020-01-19','2020-01-19', '2020-01-19'],
})

df['date'] = pd.to_datetime(df['date'], format="%Y/%m/%d").dt.date
df['new_start_date'] = pd.to_datetime(df['new_start_date'], format="%Y/%m/%d").dt.date

print(df)
              date    category new_start_date
0   2019-01-01  category A     2020-03-01
1   2019-01-02  category A     2020-03-01
2   2019-01-03  category A     2020-03-01
.....
10  2019-03-03  category C     2020-01-19
11  2019-03-04  category C     2020-01-19

Мое решение:

cat_list = df.category.unique()

newdf = pd.DataFrame()

for i in cat_list:
    new_start_date = df['new_start_date'].max()
    values = []
    for x in range(len(df)):
        values.append(new_start_date)
        new_start_date + timedelta(days=1)
    df['new'] = values 

Желаемый фрейм данных:

          date    category new_start_date
0   2020-03-01  category A     2020-03-01
1   2020-03-02  category A     2020-03-01
2   2020-03-03  category A     2020-03-01
3   2020-03-04  category A     2020-03-01
4   2020-02-25  category B     2020-02-25
5   2020-02-26  category B     2020-02-25
6   2020-02-27  category B     2020-02-25
7   2020-02-28  category B     2020-02-25
8   2020-01-19  category C     2020-01-19
9   2020-01-20  category C     2020-01-19
10  2020-01-21  category C     2020-01-19
11  2020-01-22  category C     2020-01-19

Ответы [ 2 ]

1 голос
/ 06 февраля 2020

Более логичное решение c Pandas, вместо того, чтобы вручную циклически проходить по каждой строке, состоит в том, чтобы получить диапазон дат, который вы хотите для каждой строки, а затем транспонировать (используя explode), чтобы получить по одной строке на дату:

import pandas as pd
import numpy as np
from datetime import timedelta

# Set up input data (taken from original post)
df = pd.DataFrame({
    'date': ['2019-01-01','2019-01-02','2019-01-03', '2019-01-04',
             '2019-05-20','2019-05-21','2019-05-22', '2019-05-23',
             '2019-03-01','2019-03-02','2019-03-03', '2019-03-04'],
    'new_start_date': ['2020-03-01','2020-03-01','2020-03-01', '2020-03-01',
             '2020-02-25','2020-02-25','2020-02-25', '2020-02-25',
             '2020-01-19','2020-01-19','2020-01-19', '2020-01-19'],
    'category': ['Category A']*4 + ['Category B']*4 + ['Category C']*4
})

df['date'] = pd.to_datetime(df['date'], format="%Y/%m/%d").dt.date
df['new_start_date'] = pd.to_datetime(df['new_start_date'], format="%Y/%m/%d").dt.date

################
# SOLUTION BELOW
################

# Convert DF to one row per category, with desired start and end dates
new_df = df[['category', 'new_start_date']].drop_duplicates().reset_index(drop=True)

new_df['extra_days'] = df.groupby('category')['date'].count().reset_index(drop=True) - 1
new_df['end_date'] = new_df.apply(lambda row: row.new_start_date + pd.Timedelta(f'{row.extra_days} days'), axis=1)

# Create list of days between start- and end-date, then transpose(explode) to get one row per date
new_df['dates'] = new_df.apply(lambda row: pd.date_range(row.new_start_date, row.end_date, freq='D'), axis=1)
final_df = new_df.explode('dates').loc[:, ['category', 'new_start_date', 'dates']].rename(columns={'dates':'new_date'})

ВЫХОД:

>>> final_df
>>> final_df
     category new_start_date   new_date
0  Category A     2020-03-01 2020-03-01
0  Category A     2020-03-01 2020-03-02
0  Category A     2020-03-01 2020-03-03
0  Category A     2020-03-01 2020-03-04
1  Category B     2020-02-25 2020-02-25
1  Category B     2020-02-25 2020-02-26
1  Category B     2020-02-25 2020-02-27
1  Category B     2020-02-25 2020-02-28
2  Category C     2020-01-19 2020-01-19
2  Category C     2020-01-19 2020-01-20
2  Category C     2020-01-19 2020-01-21
2  Category C     2020-01-19 2020-01-22
0 голосов
/ 06 февраля 2020

Создает новую последовательность, однако она начинается за день до дня начала. Если у кого-то есть какие-либо предложения, это будет оценено.

cat_list = df.category.unique()

newdf = pd.DataFrame()

for i in cat_list:
    df2 = df[(df['category'] == i )]
    new_start_date = df2['new_start_date'].max()
    future_window = len(df2['date'])
    df2['new_date'] = pd.date_range(new_start_date+pd.Timedelta(1,unit='d'),periods=future_window)
    newdf = newdf.append(df2)

print(newdf)

          date    category new_start_date   new_date
0   2019-01-01  category A     2020-03-01 2020-03-02
1   2019-01-02  category A     2020-03-01 2020-03-03
2   2019-01-03  category A     2020-03-01 2020-03-04
3   2019-01-04  category A     2020-03-01 2020-03-05
4   2019-05-20  category B     2020-02-25 2020-02-26
5   2019-05-21  category B     2020-02-25 2020-02-27
6   2019-05-22  category B     2020-02-25 2020-02-28
7   2019-05-23  category B     2020-02-25 2020-02-29
8   2019-03-01  category C     2020-01-19 2020-01-20
9   2019-03-02  category C     2020-01-19 2020-01-21
10  2019-03-03  category C     2020-01-19 2020-01-22
11  2019-03-04  category C     2020-01-19 2020-01-23
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...