对于数据集DF,我想添加从辅助数据集TEMP获取的温度。
TEMP没有所有日期。需要对缺失日期进行插值,以便为缺失值填写最新的可用值。对于前。如果2019-2-20缺少温度,但在2019-2-19可用,则将填写此值。这可以通过使用pd.DataFrame(x.asfreq('D')).ffill().reset_index(drop=False)
来完成。
当每天有多个温度测量值时,应使用加权平均值。
在当前代码中,使用isin
预先选择了日期以限制数据大小。
该代码有效,但如果数据变大,则不是最佳选择,特别是如果需要填充来自数千个地点和日期的温度数据并将其合并到DF时。我正在寻找有关计时/内存的更好的解决方案。基于itertools,应用,生成器表达式或其他任何东西。
在下面,我展示了一个带有代码的虚拟小可复制示例。
模块:
import numpy as np
import pandas as pd
假想数据:
DF = pd.DataFrame({'date': ['2019-01-01', '2019-01-11', '2019-01-13', '2019-01-14', '2019-01-22', '2019-02-14'], 'place':['A', 'A','A','A','B','C']})
TEMP = pd.DataFrame({'date':['2019-01-10', '2019-01-14', '2019-01-20', '2019-01-20', '2019-01-22', '2019-01-23', '2019-01-24', '2019-02-14', '2019-02-14'], 'place':['A', 'A', 'A', 'A', 'B', 'B', 'B', 'C', 'C'], 'temp': [5, 7, 9, 10, 10, 2, 0, 10, 11], 'quantity': [3,5,2,1,1,2,2,1,3]})
DF['date'] = pd.to_datetime(DF['date'])
TEMP['date'] = pd.to_datetime(TEMP['date'])
代码:
if 'temp' in DF.columns:
del DF['temp']
else: print('No variable temp found in DF')
def filltemps(dat1, dat2):
"""dat1: TEMP
dat2: DF"""
global totmp
places = dat2['place'].unique()
mx=len(places)
totmp = pd.DataFrame(columns=['date', 'temp', 'place'])
for i in range(mx):
mp=[]
dd1=[]
nsp = pd.DataFrame(dat1[ (dat1['place']==places[i]) ])
nsp = nsp[['date', 'quantity', 'temp']]
prod = lambda w,z: w*z
nsp['sumn'] = prod(nsp['temp'], nsp['quantity'])
wavg = lambda y,x: y/x
c3 = wavg(nsp.groupby('date')['sumn'].agg('sum'), nsp.groupby('date')['quantity'].agg('sum'))
mp = pd.DataFrame(c3.asfreq('D')).ffill().reset_index(drop=False)
mp.columns = ['date', 'temp']
mp['place'] = np.array([places[i]] * len(mp))
mp['date'] = pd.to_datetime(mp['date'])
dd1 = dat2.loc[dat2['place']==places[i], ['date']]
mp = mp[ mp['date'].isin(list(pd.to_datetime(dd1['date']))) ]
totmp = pd.concat([totmp, mp])
return totmp
通过%timeit TEMP2 = filltemps(TEMP, DF)
计时显示每个循环116 ms±401 µs(平均±标准偏差,共运行7次,每个循环10个循环)
在TEMP2中,可以看到,如预期的那样,计算了位置C的加权平均值:
TEMP2 = filltemps(TEMP, DF)
TEMP2将与原始DF合并。请注意,如果没有更早的数据,则可能会缺少值。
DF = pd.merge(DF, TEMP2, how='left', on=['date', 'place'])
DF,预期结果应如下所示:
非常感谢您的指导和帮助!
答案 0 :(得分:1)
这里是filltemps
的替代品,在我的计算机上的运行速度提高了10倍。它还根据要求使用apply
:)
def interpolate_temps(temp_data, other_data):
""" Interpolate temperature by place and location on a daily frequency.
Parameters
----------
temp_data : pd.DataFrame
with columns:
date : datetime
place : categorical
temp : numerical
quantity : numerical
other_Data : pd.DataFrame
with columns:
date : datetime
place : categorical
Returns
-------
pd.DataFrame
With date and place in the index and a temp column
"""
def weighted_average(df, col1, col2):
"""Average of col1 weighted by col2"""
return (df[col1] * df[col2]).sum() / df[col2].sum()
results = []
for place in temp_data.place.unique():
dates_needed = other_data[other_data.place == place].date.unique()
result = (temp_data[temp_data.place==place]
.groupby('date')
.apply(weighted_average, 'temp', 'quantity')
.reindex(dates_needed, method='ffill')
.to_frame('temp')
.assign(place=place)
.set_index('place', append=True)
)
results.append(result)
return pd.concat(results)
它遵循与您相同的基本结构:
遍历每个地方。
按日期获取温度的加权平均值。
用ffill
填充缺少的值。
重新添加位置作为列。
加入每个地方的结果
时间上的主要进步是在运行ffill
之前将日期选择为特定地点所需的日期。这也应该使其具有更高的内存效率,因为我们没有创建那么多命名中间对象。在您的代码中出现问题的最大地方是totmp = pd.concat([totmp, mp])
,您在其中创建了len(place)
不同版本的totmp
,而在interpolate_temps
中,我们一直等到{{ 1}}。
pd.concat(results)
的结果在索引中具有interpolate_temps
和date
,因此您可以像这样将其加入:
place
或重置索引并像以前一样进行合并。
我唯一的其他建议是使用更详细的变量名。我从来没有真正弄清楚其中的许多含义,这使得理解您的代码变得更加困难。
答案 1 :(得分:1)
计算TEMP中的加权平均值。按地点在TEMP中填写缺失的日期,然后将结果数据框与DF合并。
.imgSmall {
width: 70px;
}
/* The Modal (background) */
.modal {
/* Hidden by default */
position: fixed;
/* Stay in place */
z-index: 1;
/* Sit on top */
padding-top: 100px;
/* Location of the box */
left: 0;
top: 0;
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: lightblue;
}
/* Modal Content (image) */
.modal-content {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
}
/* Caption of Modal Image */
#caption {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
text-align: center;
color: #ccc;
padding: 10px 0;
height: 150px;
}
/* Add Animation */
.modal-content,
#caption {
-webkit-animation-name: zoom;
-webkit-animation-duration: 0.6s;
animation-name: zoom;
animation-duration: 0.6s;
}
@-webkit-keyframes zoom {
from {
-webkit-transform: scale(0)
}
to {
-webkit-transform: scale(1)
}
}
@keyframes zoom {
from {
transform: scale(0)
}
to {
transform: scale(1)
}
}
/* The Close Button */
.close {
position: absolute;
top: 15px;
right: 35px;
color: #f1f1f1;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
cursor: pointer;
}
答案 2 :(得分:0)
我的解决方案类似于Vaishali那个人,但我想指出asfreq
的一个陷阱。
让我们从头开始。我们计算加权平均值:
TEMP.groupby(['date', 'place']).apply(lambda x: np.round(np.average(x['temp'],weights=x['quantity']), 2)).reset_index(name = 'temp')
现在我们计算整个日期范围:
rng = pd.date_range(DF.date.min(), DF.date.max(), freq='D')
我们使用此数据范围为温度重新编制索引:
TEMP = TEMP.groupby('place')['date', 'temp'].apply(lambda x: x.set_index('date').reindex(rng).ffill()).reset_index().rename(columns={'level_1': 'date'})
与as_freq
相比,我们现在还将处理温度时间序列比位置序列“短”的情况。
最后,我们可以将所有内容放在一起:
DF.merge(TEMP, on=['date', 'place'])
如果我们通过更改最后日期来认真修改输入内容:
DF = pd.DataFrame({'date': ['2019-01-01', '2019-01-11', '2019-01-13', '2019-01-14', '2019-01-22', '2019-02-15'], 'place':['A', 'A','A','A','B','C']})
Ruthger的解决方案提供了:
date place temp
1 2019-01-11 A 5.0
3 2019-01-13 A 5.0
4 2019-01-14 A 7.0
0 2019-01-22 B 10.0
瓦沙里:
date place temp
0 2019-01-01 A NaN
1 2019-01-11 A 5.0
2 2019-01-13 A 5.0
3 2019-01-14 A 7.0
4 2019-01-22 B 10.0
5 2019-02-15 C NaN
Joerg:
0 2019-01-01 A NaN
1 2019-01-11 A 5.00
2 2019-01-13 A 5.00
3 2019-01-14 A 7.00
4 2019-01-22 B 10.00
5 2019-02-15 C 10.75
简而言之:
%timeit Ruthger(TEMP, DF)
526 ms ± 36.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit Vaishali(TEMP, DF)
12.1 ms ± 135 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit Joerg(TEMP, DF)
14.9 ms ± 872 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)