数据集:我有1GB的股票数据集,其日期范围之间的值。日期范围没有重叠,数据集按(ticker,start_date)排序。
>>> df.head()
start_date end_date val
ticker
AAPL 2014-05-01 2014-05-01 10.0000000000
AAPL 2014-06-05 2014-06-10 20.0000000000
GOOG 2014-06-01 2014-06-15 50.0000000000
MSFT 2014-06-16 2014-06-16 None
TWTR 2014-01-17 2014-05-17 10.0000000000
目标:我想解压缩数据框,以便我有个别日期而不是日期范围。例如,AAPL行将从仅2行变为7行:
>>> AAPL_decompressed.head()
val
date
2014-05-01 10.0000000000
2014-06-05 20.0000000000
2014-06-06 20.0000000000
2014-06-07 20.0000000000
2014-06-08 20.0000000000
我希望有一个很好的优化方法,例如重新采样的pandas可以在几行中完成。
答案 0 :(得分:7)
多了几行,但我认为这会导致你提出的问题:
从您的数据框开始:
In [70]: df
Out[70]:
start_date end_date val row
ticker
AAPL 2014-05-01 2014-05-01 10 0
AAPL 2014-06-05 2014-06-10 20 1
GOOG 2014-06-01 2014-06-15 50 2
MSFT 2014-06-16 2014-06-16 NaN 3
TWTR 2014-01-17 2014-05-17 10 4
首先,我将此数据框重新整理为包含一个date
列的数据框(因此,对于start_date
和end_date
的每个日期,每行重复两次(我添加一个名为{的计数器列{1}}):
row
根据这个新的数据框架,我可以按In [60]: df['row'] = range(len(df))
In [61]: starts = df[['start_date', 'val', 'row']].rename(columns={'start_date': 'date'})
In [62]: ends = df[['end_date', 'val', 'row']].rename(columns={'end_date':'date'})
In [63]: df_decomp = pd.concat([starts, ends])
In [64]: df_decomp = df_decomp.set_index('row', append=True)
In [65]: df_decomp.sort_index()
Out[65]:
date val
ticker row
AAPL 0 2014-05-01 10
0 2014-05-01 10
1 2014-06-05 20
1 2014-06-10 20
GOOG 2 2014-06-01 50
2 2014-06-15 50
MSFT 3 2014-06-16 NaN
3 2014-06-16 NaN
TWTR 4 2014-01-17 10
4 2014-05-17 10
和ticker
对其进行分组,并在每个组row
上应用每日resample
(使用方法'垫'向前填充)
fillna
最后一个命令是删除现在多余的In [66]: df_decomp = df_decomp.groupby(level=[0,1]).apply(lambda x: x.set_index('date').resample('D').fillna(method='pad'))
In [67]: df_decomp = df_decomp.reset_index(level=1, drop=True)
索引级别
当我们访问AAPL行时,它会提供您想要的输出:
row
答案 1 :(得分:1)
我认为你可以分五步完成:
1)过滤自动收报机列以找到您想要的库存
2)使用pandas.bdate_range
建立start
和end
之间的日期范围列表
3)使用reduce
4)重新索引新过滤的数据框
5)使用方法pad
以下是代码:
>>> import pandas as pd
>>> import datetime
>>> data = [('AAPL', datetime.date(2014, 4, 28), datetime.date(2014, 5, 2), 90),
('AAPL', datetime.date(2014, 5, 5), datetime.date(2014, 5, 9), 80),
('MSFT', datetime.date(2014, 5, 5), datetime.date(2014, 5, 9), 150),
('AAPL', datetime.date(2014, 5, 12), datetime.date(2014, 5, 16), 85)]
>>> df = pd.DataFrame(data=data, columns=['ticker', 'start', 'end', 'val'])
>>> df_new = df[df['ticker'] == 'AAPL']
>>> df_new.name = 'AAPL'
>>> df_new.index = df_new['start']
>>> df_new.index.name = 'date'
>>> df_new.index = df_new.index.to_datetime()
>>> from functools import reduce #for py3k only
>>> new_index = [pd.bdate_range(**d) for d in df_new[['start','end']].to_dict('record')]
>>> new_index_flat = reduce(pd.tseries.index.DatetimeIndex.append, new_index)
>>> df_new = df_new.reindex(new_index_flat)
>>> df_new = df_new.fillna(method='pad')
>>> df_new
ticker start end val
2014-04-28 AAPL 2014-04-28 2014-05-02 90
2014-04-29 AAPL 2014-04-28 2014-05-02 90
2014-04-30 AAPL 2014-04-28 2014-05-02 90
2014-05-01 AAPL 2014-04-28 2014-05-02 90
2014-05-02 AAPL 2014-04-28 2014-05-02 90
2014-05-05 AAPL 2014-05-05 2014-05-09 80
2014-05-06 AAPL 2014-05-05 2014-05-09 80
2014-05-07 AAPL 2014-05-05 2014-05-09 80
2014-05-08 AAPL 2014-05-05 2014-05-09 80
2014-05-09 AAPL 2014-05-05 2014-05-09 80
2014-05-12 AAPL 2014-05-12 2014-05-16 85
2014-05-13 AAPL 2014-05-12 2014-05-16 85
2014-05-14 AAPL 2014-05-12 2014-05-16 85
2014-05-15 AAPL 2014-05-12 2014-05-16 85
2014-05-16 AAPL 2014-05-12 2014-05-16 85
[15 rows x 4 columns]
希望它有所帮助!
答案 2 :(得分:0)
这是一种愚蠢的方式 - 我发布这个糟糕的答案(记住 - 我不能代码:-))因为我是大熊猫的新手并且不介意有人改进它。
这将读取最初发布数据的文件 - 然后创建stock_id和end_date的多索引。下面的get_val函数采用整个帧,例如,一个自动收报机。 ' AAPL',以及日期并使用index.searchsorted,其行为类似于C ++中的map :: upper_bound - 即如果要插入,则查找将插入日期的索引 - 即找到最接近的结束日期,但是在有问题的日期之后 - 这将具有我们想要的值,并使用get_val返回该值。
然后我根据&AAPL'的stock_id,使用此Multiindex从系列中获取横截面。然后我们形成一个空列表,用于使用' AAPL'的密钥从多索引中展平日期元组列表。这些日期成为系列的索引和值。然后我将这个系列映射到get_val以获取所需的股票价格。
我知道这可能是错的......但是......乐于学习。
我不会惊讶地发现有一种简单的方法可以像这样使用一些填充插值方法来扩充数据帧......
stocks=pd.read_csv('stocks2.csv', parse_dates=['start_date', 'end_date'], index_col='ticker')
mi=zip(stocks.index, pd.Series(zip(stocks['start_date'],stocks['end_date'].values)).map(lambda z: tuple(pd.date_range(start=z[0], end=z[1]))).values)
mi=pd.MultiIndex.from_tuples(mi)
ticker='AAPL'
s=pd.Series(index=mi,data=0)
s=list(s.xs(key=ticker).index)
l=[]
map(lambda x: l.extend(x), s)
s=pd.Series(index=l,data=l)
stocks_byticker=stocks[stocks.index==ticker].set_index('end_date')
print(s.map(lambda x: stocks_byticker.ix[stocks_byticker.index.searchsorted(x), 'val']))
2014-05-01 10
2014-06-05 20
2014-06-06 20
2014-06-07 20
2014-06-08 20
2014-06-09 20
2014-06-10 20
答案 3 :(得分:0)
这是一种稍微更通用的方法来扩展joris的良好答案,但允许它与任意数量的其他列一起使用:
| id | car_name | service_name | done_date |
|----|-----------|----------------|------------|
| 1 | porsche_1 | oil_change | 2017-01-04 |
| 2 | porsche_1 | oil_change | 2017-04-15 |
| 3 | porsche_1 | oil_change | 2017-07-12 |
| 4 | benz_1 | oil_change | 2017-01-14 |
| 5 | benz_1 | oil_change | 2017-05-24 |
| 6 | porsche_1 | tire_check | 2017-01-04 |
| 7 | porsche_1 | tire_check | 2017-06-11 |
| 8 | porsche_1 | replace_wipers | 2017-01-04 |
| 9 | porsche_2 | oil_change | 2017-05-01 |
| 10 | porsche_2 | oil_change | 2017-08-20 |