通过 Python 中的 pandas 将每日股票数据转换为每周数据

问题描述 投票:0回答:8

我有一个

DataFrame
存储每日数据,如下所示:

Date              Open        High         Low       Close   Volume
2010-01-04   38.660000   39.299999   38.509998   39.279999  1293400   
2010-01-05   39.389999   39.520000   39.029999   39.430000  1261400   
2010-01-06   39.549999   40.700001   39.020000   40.250000  1879800   
2010-01-07   40.090000   40.349998   39.910000   40.090000   836400   
2010-01-08   40.139999   40.310001   39.720001   40.290001   654600   
2010-01-11   40.209999   40.520000   40.040001   40.290001   963600   
2010-01-12   40.160000   40.340000   39.279999   39.980000  1012800   
2010-01-13   39.930000   40.669998   39.709999   40.560001  1773400   
2010-01-14   40.490002   40.970001   40.189999   40.520000  1240600   
2010-01-15   40.570000   40.939999   40.099998   40.450001  1244200   

我打算做的是将其合并到每周的数据中。分组后:

  1. Date应为每周一(此时应考虑节假日情况,周一非交易日,应采用本周第一个交易日作为Date)。
  2. 开盘应该是周一(或本周的第一个交易日)开盘。
  3. 收盘价应为周五(或本周最后一个交易日)收盘价。
  4. High 应该是本周交易日的最高点。
  5. 最低价应该是本周交易日的最低最低价。
  6. 交易量应为本周所有交易日交易量的总和。

应该是这样的:

Date              Open        High         Low       Close   Volume
2010-01-04   38.660000   40.700001   38.509998   40.290001  5925600   
2010-01-11   40.209999   40.970001   39.279999   40.450001  6234600   

目前,我的代码片段如下,我应该使用哪个函数将基于每日的数据映射到预期的基于每周的数据?非常感谢!

import pandas_datareader.data as web

start = datetime.datetime(2010, 1, 1)
end = datetime.datetime(2016, 12, 31)
f = web.DataReader("MNST", "yahoo", start, end, session=session)
print f
python pandas yahoo-finance
8个回答
43
投票

您可以按如下所示

resample
(每周)、
offset
(轮班)和
apply
聚合规则[已更新以反映pandas 1.1中的更改]:

logic = {'Open'  : 'first',
         'High'  : 'max',
         'Low'   : 'min',
         'Close' : 'last',
         'Volume': 'sum'}
    
df = pd.read_clipboard(parse_dates=['Date'], index_col=['Date'])
df = df.resample('W').apply(logic)

# changes in Pandas 1.1
from pandas.tseries.frequencies import to_offset
df.index = df.index - to_offset('6D')

获得:

                 Open       High        Low      Close   Volume
Date                                                           
2010-01-04  38.660000  40.700001  38.509998  40.290001  5925600
2010-01-11  40.209999  40.970001  39.279999  40.450001  6234600

22
投票

一般来说,假设您有指定形式的数据框,则需要执行以下步骤:

  1. Date
    放入索引
  2. resample
    索引。

您所面临的是对不同列应用不同函数的情况。 参见

您可以通过多种方式重新采样。例如您可以取值的平均值或计数等。检查pandas 重新采样

您还可以应用自定义聚合器(检查相同的链接)。 考虑到这一点,您的案例的代码片段可以如下所示:

f['Date'] = pd.to_datetime(f['Date'])
f.set_index('Date', inplace=True)
f.sort_index(inplace=True)

def take_first(array_like):
    return array_like[0]

def take_last(array_like):
    return array_like[-1]

output = f.resample('W',                                 # Weekly resample
                    how={'Open': take_first, 
                         'High': 'max',
                         'Low': 'min',
                         'Close': take_last,
                         'Volume': 'sum'}, 
                    loffset=pd.offsets.timedelta(days=-6))  # to put the labels to Monday

output = output[['Open', 'High', 'Low', 'Close', 'Volume']]

此处,

W
表示每周重新采样,默认情况下从周一到周日。为了将标签保持为星期一,使用
loffset
。 有几个预定义的日期说明符。看一下 pandas offsets。您甚至可以定义自定义偏移量(参见)。

回到重采样方法。在这里,对于

Open
Close
,您可以指定自定义方法来获取第一个值等,并将函数句柄传递给
how
参数。

这个答案基于这样的假设:数据似乎是每日的,即每天只有 1 个条目。此外,非工作日没有数据。即周六和周日。因此,将本周的最后一个数据点作为周五的数据点就可以了。如果您愿意,可以使用工作周而不是“W”。此外,对于更复杂的数据,您可能需要使用

groupby
对每周数据进行分组,然后处理其中的时间索引。

顺便说一句,解决方案的要点可以在以下位置找到: https://gist.github.com/prithwi/339f87bf9c3c37bb3188


12
投票

我有完全相同的问题,并在这里找到了一个很好的解决方案。

https://www.techtrekking.com/how-to-convert-daily-time-series-data-into-weekly-and-monthly-using-pandas-and-python/

每周代码发布在下面。

import pandas as pd
import numpy as np

print('*** Program Started ***')

df = pd.read_csv('15-06-2016-TO-14-06-2018HDFCBANKALLN.csv')

# ensuring only equity series is considered
df = df.loc[df['Series'] == 'EQ']

# Converting date to pandas datetime format
df['Date'] = pd.to_datetime(df['Date'])
# Getting week number
df['Week_Number'] = df['Date'].dt.week
# Getting year. Weeknum is common across years to we need to create unique index by using year and weeknum
df['Year'] = df['Date'].dt.year

# Grouping based on required values
df2 = df.groupby(['Year','Week_Number']).agg({'Open Price':'first', 'High Price':'max', 'Low Price':'min', 'Close Price':'last','Total Traded Quantity':'sum'})
# df3 = df.groupby(['Year','Week_Number']).agg({'Open Price':'first', 'High Price':'max', 'Low Price':'min', 'Close Price':'last','Total Traded Quantity':'sum','Average Price':'avg'})
df2.to_csv('Weekly_OHLC.csv')
print('*** Program ended ***')

3
投票

使用最近的 pandas API 添加到 @Stefan 的答案,因为

loffset
自版本 1.1.0 以来已被弃用,后来被删除。

df = pd.read_clipboard(parse_dates=['Date'], index_col=['Date'])
logic = {'Open'  : 'first',
         'High'  : 'max',
         'Low'   : 'min',
         'Close' : 'last',
         'Volume': 'sum'}

dfw = df.resample('W').apply(logic)
# set the index to the beginning of the week
dfw.index = dfw.index - pd.tseries.frequencies.to_offset("6D")

3
投票

2022 年更新解决方案

import pandas as pd
from pandas.tseries.frequencies import to_offset

df = pd.read_csv('your_ticker.csv')

logic = {'<Open>'  : 'first',
         '<High>'  : 'max',
         '<Low>'   : 'min',
         '<Close>' : 'last',
         '<Volume>': 'sum'}
df['<DTYYYYMMDD>'] = pd.to_datetime(df['<DTYYYYMMDD>'])
df = df.set_index('<DTYYYYMMDD>')
df = df.sort_index()
df = df.resample('W').apply(logic)
df.index = df.index - pd.tseries.frequencies.to_offset("6D")

1
投票

一开始我根据上述答案使用 df.resample() ,但是当错过一周时它会填充 NaN ,对此不满意,经过一番研究,我使用 groupby() 而不是 resample() 。谢谢你的分享。

我的原始数据是:

           c      date        h        l        o
260  6014.78  20220321  6053.90  5984.79  6030.43
261  6052.59  20220322  6099.53  5995.22  6012.17
262  6040.86  20220323  6070.85  6008.26  6059.11
263  6003.05  20220324  6031.73  5987.40  6020.00
264  5931.33  20220325  6033.04  5928.72  6033.04
265  5946.98  20220328  5946.98  5830.93  5871.35
266  5900.04  20220329  5958.71  5894.82  5950.89
267  6003.05  20220330  6003.05  5913.08  5913.08
268  6033.04  20220331  6059.11  5978.27  5993.92
269  6126.91  20220401  6134.74  5975.66  6006.96
270  6149.08  20220406  6177.77  6106.05  6126.91
271  6134.74  20220407  6171.25  6091.71  6130.83
272  6151.69  20220408  6160.82  6096.93  6147.78
273  6095.62  20220411  6166.03  6072.15  6164.73
274  6184.28  20220412  6228.62  6049.99  6094.32
275  6119.09  20220413  6180.37  6117.79  6173.85
276  6188.20  20220414  6201.24  6132.13  6150.38
277  6173.85  20220415  6199.93  6137.35  6137.35
278  6124.31  20220418  6173.85  6108.66  6173.85
279  6065.63  20220419  6147.78  6042.16  6124.31

我不在乎日期是不是星期一,所以我没有处理它,代码是:

data['Date'] = pd.to_datetime(data['date'], format="%Y%m%d")
# Refer to: https://www.techtrekking.com/how-to-convert-daily-time-series-data-into-weekly-and-monthly-using-pandas-and-python/
# and here: https://stackoverflow.com/a/60518425/5449346
# and this: https://github.com/pandas-dev/pandas/issues/11217#issuecomment-145253671
logic = {'o'  : 'first',
         'h'  : 'max',
         'l'   : 'min',
         'c' : 'last',
         'Date': 'first',
         }
data = data.groupby([data['Date'].dt.year, data['Date'].dt.week]).agg(logic)
data.set_index('Date', inplace=True)

结果是,在 2022.01.31 上 resample() 不会产生 NaN:

                  l        o        h        c
Date
2021-11-29  6284.68  6355.09  6421.59  6382.47
2021-12-06  6365.52  6372.04  6700.62  6593.70
2021-12-13  6445.06  6593.70  6690.19  6450.28
2021-12-20  6415.07  6437.24  6531.12  6463.31
2021-12-27  6463.31  6473.75  6794.50  6649.77
2022-01-04  6625.00  6649.77  7089.18  7055.27
2022-01-10  6804.93  7055.27  7181.75  6808.84
2022-01-17  6769.73  6776.25  7098.30  6919.67
2022-01-24  6692.80  6906.63  7048.76  6754.08
2022-02-07  6737.13  6811.45  7056.58  7023.98
2022-02-14  6815.36  7073.53  7086.57  6911.85
2022-02-21  6634.12  6880.56  6904.03  6668.02
2022-02-28  6452.88  6669.33  6671.93  6493.30
2022-03-07  5953.50  6463.31  6468.53  6228.62
2022-03-14  5817.90  6154.30  6205.15  6027.82
2022-03-21  5928.72  6030.43  6099.53  5931.33
2022-03-28  5830.93  5871.35  6134.74  6126.91
2022-04-06  6091.71  6126.91  6177.77  6151.69
2022-04-11  6049.99  6164.73  6228.62  6173.85
2022-04-18  6042.16  6173.85  6173.85  6065.63

0
投票

不是直接答案,但假设这些列是日期(表格的转置),没有丢失日期。

'''sum up daily results in df to weekly results in wdf'''
wdf = pd.DataFrame(index = df.index)
for i in range(len(df.columns)):
    if (i!=0) & (i%7==0):
        wdf['week'+str(i//7)]= df[df.columns[i-7:i]].sum(axis = 1)

0
投票

上述代码中有一个限制,如果周一有假期,则不会获取该数据,因此我创建了一个函数来克服该限制。希望有帮助。将日期名称命名为“星期一”或您想要的任何名称

def convert_to_weekly(df,day):
    #Initialising dataframe to store converted data
    converted_data = pd.DataFrame()        
    converted_data_temp = pd.DataFrame({"Date":[0],"Open":[0],"High":[0],"Low":[0],"Close":[0],"Volume":[0]})
    
    #Converting Date to Datetime and setting that as index
    df['Date str'] = df['Date']
    df['Date'] = df['Date'].map(lambda a : datetime.datetime.strptime(a, '%d/%m/%Y'))
    df.set_index('Date', inplace=True)
    df.sort_index(inplace=True)
    
    #Finding first date which is our start Day
    start_date = df.index[0]
    for i in range(0,6):
        start_date = start_date + timedelta(days=i)
        day_name = start_date.strftime("%A")

        if(day_name == day):
            break
    
    #Taking a week back to include middle data
    start_date = start_date - timedelta(days=7)
    
    while(start_date < df.index[-1]):
        
        stop_date = start_date + timedelta(days=7)
        
        data_temp = df[start_date:stop_date - timedelta(days=1)]
        
        if(data_temp.size == 0):
            start_date = start_date + timedelta(days=7)
            continue
        
        data_temp = data_temp.reset_index()
        
        converted_data_temp['Date'] = data_temp['Date str'].iloc[0]
        converted_data_temp['Open'] = data_temp['Open'].iloc[0]
        converted_data_temp['High'] = data_temp['High'].max()
        converted_data_temp['Low'] = data_temp['Low'].min()
        converted_data_temp['Close'] = data_temp['Close'].iloc[-1]
        try:
            converted_data_temp['Volume'] = data_temp['Volume'].sum()
        except:
            pass
        
        converted_data = pd.concat([converted_data, converted_data_temp])
        
        start_date = start_date + timedelta(days=7)
    
    converted_data = converted_data.reset_index()
    converted_data = converted_data.drop(['index'],axis=1)
    return converted_data

df_w = Convert_to_weekly(df, "星期一")

© www.soinside.com 2019 - 2024. All rights reserved.