Python - 将周末值提前到星期一

Python - Push forward weekend values to Monday

提问人:Bruno Di Franco Albuquerque 提问时间:3/23/2022 最后编辑:Bruno Di Franco Albuquerque 更新时间:3/23/2023 访问量:433

问:

我有一个数据帧(称为 df),如下所示:

df

我正在尝试获取所有周末的“交易量”值(列“WEEKDAY”=5(星期六)或 6(星期日)的值)并将它们求和到随后的星期一(WEEKDAY=0)。

我尝试了一些东西,但没有真正起作用,从最后三行中举个例子:

What I have

我所期待的是:

What I expect

若要重现该问题,请执行以下操作:

!wget https://raw.githubusercontent.com/brunodifranco/TCC/main/volume_por_dia.csv

df = pd.read_csv('volume_por_dia.csv').sort_values('Datas',ascending=True)
df['Datas'] = pd.to_datetime(df['Datas'])
df = df_volume_noticias.set_index('Datas')
df['WEEKDAY'] = df.index.dayofweek
df
Python DataFrame 日期时间 数据操作

评论

0赞 Community 3/23/2022
请提供足够的代码,以便其他人可以更好地理解或重现问题。

答:

0赞 Lukas Hestermeyer 3/17/2023 #1

这使用 pd.shift 解决了您的问题。

import pandas as pd
df['prior_volume'] = df.Volume.shift(1)
df['prior_volume2'] = df.Volume.shift(2)
df.loc[df['WEEKDAY'] == 0, 'Volume'] = df.loc[df['WEEKDAY'] == 0, 'prior_volume'] + \
    df.loc[df['WEEKDAY'] == 0, 'prior_volume2'] + \
    df.loc[df['WEEKDAY'] == 0, 'Volume']
df = df[df['WEEKDAY'].isin(range(5))]
df = df[['Volume', 'WEEKDAY']]
df.head(10)

其结果:enter image description here

0赞 alph 3/17/2023 #2

我使用.groupby来解决问题。

import pandas as pd

df = pd.read_csv('volume_por_dia.csv')
df['Datas'] = pd.to_datetime(df['Datas'])
df['WEEKDAY'] = df['Datas'].dt.dayofweek
df['index'] = df['Datas']

# Group df by date, setting frequency as week 
#(beginning Tue - so that Sat and Sun will be added to the next Mon)
df_group = df.groupby([pd.Grouper(key = 'Datas', freq='W-MON'), \
 'WEEKDAY', 'index']).agg({'Volume': 'sum'})

# In each group, add days 5, 6 (Sat and Sun) to day 0 (Mon)
df_group.loc[(slice(None), 0), 'Volume'] += \
df_group.loc[(slice(None), [5, 6]), 'Volume'].groupby(level=0).sum()

# In the grouped data, remove Sat and Sun
df_group = df_group.reset_index()
df_group = df_group[df_group['WEEKDAY'] != 5]
df_group = df_group[df_group['WEEKDAY'] != 6]

# Remove volume data from original df, and merge with volume from df_group 
df = df.drop(['Volume'], axis=1)
df = pd.merge(df,df_group[['index','Volume']],on='index', how='left')
df = df.dropna(subset=['Volume'])
df = df.drop(['index'], axis=1)

# Optional: sort dates in ascending order
df = df.sort_values(by=['Datas'])

print (df)
0赞 Rahul Vishwakarma 3/19/2023 #3

您可以简单地循环这些行,并从周五开始继续累积交易量,并更新周日交易量中的值。然后,只需删除星期五和星期六的行。

values = df.values

volume_accumulated = 0
for idx, row in enumerate(values):
  if row[1] in (5, 6):
    volume_accumulated += row[0]
  elif row[1] == 0:
    volume_accumulated += row[0]
    df["Volume"][idx] = volume_accumulated
  else:
    volume_accumulated = 0

df = df[~df["WEEKDAY"].isin([5, 6])]
0赞 Salvatore Daniele Bianco 3/21/2023 #4

输入:

!wget https://raw.githubusercontent.com/brunodifranco/TCC/main/volume_por_dia.csv

import pandas as pd
import numpy as np

df = pd.read_csv('volume_por_dia.csv').sort_values('Datas',ascending=True)
df['Datas'] = pd.to_datetime(df['Datas'])
df.set_index('Datas', inplace=True)
df['WEEKDAY'] = df.index.dayofweek

我假设索引日期已排序,索引是唯一的,并且没有丢失的日期。我不能做出的一些假设是:Datas

  • 对于每个星期一,我都有上一个周末的完整交易量,这可能是错误的,因为数据帧可能从周日开始,而我的周末交易量不完整;
  • 对于每个周末,我都会有一个下一个星期一,这可能是错误的,因为数据帧可能会在周六或周日完成。

出于这些原因,在计算周末交易量之前,我首先提取第一个星期六和最后一个星期一的日期:

first_saturday = df.index[df.WEEKDAY==5][0]
last_monday = df.index[df.WEEKDAY==0][-1]

现在,我可以提取周末卷,确保我始终有周六-周日的一对,并且对于这些一对中的每一个,数据帧中都存在下一个星期一:

df_weekend = df.loc[
    (df.WEEKDAY.isin([5,6]))&
    (df.index<=last_monday)&
    (df.index>=first_saturday)
]
df_weekend

现在,由于我有一对周六至周日的交易量,我可以通过以下方式计算总和:

weekend_volumes = pd.Series(
    df_weekend.Volume.values.reshape(-1,2).sum(axis=1), #sum of volume couples
    index = df_weekend.index[1::2]+pd.Timedelta("1d"), #date of the following monday
    name="weekend_volume"
).reindex(df.index).fillna(0) #zero weekend-volume for days that are not mondays 
weekend_volumes

最后,将周末交易量添加到起始交易量中:

df["Volume"] = df.Volume+weekend_volumes

我在下面附上了 df 的最后 25 行:

# 2022-02-18    16.0    4
# 2022-02-19    2.0     5
# 2022-02-20    1.0     6
# 2022-02-21    10.0    0
# 2022-02-22    43.0    1
# 2022-02-23    36.0    2
# 2022-02-24    38.0    3
# 2022-02-25    28.0    4
# 2022-02-26    5.0     5
# 2022-02-27    3.0     6
# 2022-02-28    14.0    0
# 2022-03-01    10.0    1
# 2022-03-02    16.0    2
# 2022-03-03    18.0    3
# 2022-03-04    11.0    4
# 2022-03-05    8.0     5
# 2022-03-06    2.0     6
# 2022-03-07    32.0    0
# 2022-03-08    18.0    1
# 2022-03-09    32.0    2
# 2022-03-10    24.0    3
# 2022-03-11    18.0    4
# 2022-03-12    4.0     5
# 2022-03-13    1.0     6
# 2022-03-14    10.0    0
1赞 sid_pro_quo 3/21/2023 #5

在此处添加 2 个解决方案:

  1. 使用(Lukas Hestermeyer 早些时候指出;我添加了一个简化版本)pd.shift

  2. 使用滚动窗口(这实际上是单行)

两种解决方案都假设;

  1. Dates按升序排序(如果没有,则应在继续之前进行排序)
  2. 每个周末(周六和周日)的记录都由周一的记录接替。如果数据错误,需要添加其他检查

第 1 部分 |数据准备:

import pandas as pd
import numpy as np

# STEP 1: Create DF
Datas = [
    '2019-07-02',
    '2019-07-03',
    '2019-07-04',
    '2019-07-05',
    '2019-07-06',
    '2019-07-07',
    '2019-07-08',
    '2022-03-10',
    '2022-03-11',
    '2022-03-12',
    '2022-03-13',
    '2022-03-14'
]

Volume = [17, 30, 20, 21, 5, 10, 12, 24, 18, 4, 1, 5]
WEEKDAY = [1, 2, 3, 4, 5, 6, 0, 3, 4, 5, 6, 0]

dic = {'Datas': Datas, 'Volume': Volume, 'WEEKDAY': WEEKDAY}

df['Datas'] = pd.to_datetime(df['Datas'])
df = df.set_index('Datas')

df = pd.DataFrame(dic)

第 2 部分 |解决 方案:

解决方案 1 [pd.shift] :

# STEP 1: add shifts
df['shift_1'] = df['Volume'].shift(1)
df['shift_2'] = df['shift_1'].shift(1)

# STEP 2: sum Volume with shifts where weekday==0
cols_to_sum = ['Volume', 'shift_1', 'shift_2']
df['Volume'] = df[['WEEKDAY'] + cols_to_sum].apply(lambda x: int(x[1]) if x[0] else int(x[1] + x[2] + x[3]), axis=1)
df = df.drop(['shift_1', 'shift_2'], axis=1)
df

解决方案 2 [滚动窗口] :

# use rolling window of size 3 to sum where weekday == 0
df['Volume'] = np.where(
    df['WEEKDAY'] == 0,  
    df['Volume'].rolling(window=3, center=False).sum(), 
    df['Volume']
)
df

第 3 部分 |删除周末记录:

df = df.loc[~df['WEEKDAY'].isin([5, 6])]
df
0赞 Brener Ramos 3/21/2023 #6

例如,如果您认为几周从星期二开始,那么问题就会变得更简单。您只需要获取周末的值并将其求和为该周的星期一(这将是周末后的星期一)。这将自动处理您的数据可能在周末开始/结束的情况。

import numpy as np
import pandas as pd
np.random.seed(1)

# Sample data
dates = pd.date_range('2018-02-05', '2018-07-22', freq='D')
volume = np.random.randint(1, 50, len(dates))
df = pd.DataFrame(dict(Datas=dates, Volume=volume))
df = df.set_index('Datas')

# Week starting from Tuesday
week = ((df.index - pd.DateOffset(days=1)).isocalendar().week).values

def add_weekend_to_monday(week): 
    monday = week.index.weekday == 0
    weekend = week.index.weekday >= 5
    week[monday] += week[weekend].sum() 
    return week

df['Volume'] = df.groupby(week)['Volume'].apply(add_weekend_to_monday)
0赞 Cyber 3/22/2023 #7

根据您提供的代码,您已将 CSV 文件加载到 DataFrame df 中,将“Datas”列转换为日期时间,按日期升序对 DataFrame 进行排序,并将“Datas”列设置为索引。

您还创建了一个新列“WEEKDAY”,其中包含索引中每个日期的星期几(0-6,其中 0 表示星期一,6 表示星期日)。

import pandas as pd

# Load the data and convert the 'Datas' column to a datetime
df = pd.read_csv('https://raw.githubusercontent.com/brunodifranco/TCC/main/volume_por_dia.csv')
df['Datas'] = pd.to_datetime(df['Datas'])

# Set the 'Datas' column as the index
df.set_index('Datas', inplace=True)

# Compute the sum of weekend days (Saturday and Sunday) and assign it to the next following Monday
weekend_sum = df.loc[df.index.weekday.isin([5,6])]['Volume'].resample('W-MON').sum()
df.loc[weekend_sum.index, 'Volume'] += weekend_sum.values

解释:

  1. df.loc[df.index.weekday.isin([5,6])]选择索引(即日期)位于星期六或星期日(分别为工作日 5 或 6)的行。

  2. ['Volume'].resample('W-MON').sum()计算从周一开始的每周至少包含一个周末的“交易量”列的总和。结果是一个序列,其中索引包含每周的开始日期,值包含相应的总和。

  3. df.loc[weekend_sum.index, 'Volume'] += weekend_sum.values将计算的总和分配给下一个星期一。它选择与具有周末总和的周的开始日期相对应的行,并将相应的总和添加到“交易量”列中。请注意,运算符用于修改原始 DataFrame df。(weekend_sum.index)(weekend_sum.values)+=