如何解决使用一年中离散天数的窗口,pandas 数据框使用 groupby 为 Nans 进行插值 分析
下面的小型可重复示例设置了一个长度为 100 年的数据帧,其中包含一些随机生成的值。然后插入 3 个 100 天的缺失值范围。使用这个小例子,我试图整理将使用一年中那一天的平均值(因此使用 .groupby)和条件来填充缺失天数的 Pandas 命令。例如,如果缺少 4 月 12 日,如何更改最后一行代码,以便仅使用最接近 4 月 12 日的 10 个来填充缺失值?换句话说,1920 年缺失的 4 月 12 日值将使用 1915 年至 1925 年之间的 4 月 12 日平均值来填补; 2000 年缺失的 4 月 12 日值将用 1995 年至 2005 年之间的平均 4 月 12 日值填充,等等。我尝试在脚本的最后一行中将 .rolling() 添加到 lambda 函数,但在我的尝试。
奖励问题:下面的例子从 1918 年到 2018 年。例如,如果一个值在 1919 年 4 月 12 日缺失,如果使用 10 个 4 月 12 日来填充缺失值,即使窗口不能因为它接近时间序列的开头,所以不要“集中”在缺少的那一天。当缺失值接近时间序列的开头和结尾时,上述第一个问题是否有足够灵活的解决方案,可以仍然使用至少 10 个值?
import pandas as pd
import numpy as np
import random
# create 100 yr time series
dates = pd.date_range(start="1918-01-01",end="2018-12-31").strftime("%Y-%m-%d")
vals = [random.randrange(1,50,1) for i in range(len(dates))]
# Create some arbitrary gaps
vals[100:200] = vals[9962:10062] = vals[35895:35995] = [np.nan] * 100
# Create dataframe
df = pd.DataFrame(dict(
list(
zip(["Date","vals"],[dates,vals])
)
))
# confirm missing vals
df.iloc[95:105]
df.iloc[35890:35900]
# set a date index (for use by groupby)
df.index = pd.DatetimeIndex(df['Date'])
df['Date'] = df.index
# Need help restricting the mean to the 10 nearest same-days-of-the-year:
df['vals'] = df.groupby([df.index.month,df.index.day])['vals'].transform(lambda x: x.fillna(x.mean()))
解决方法
这两个部分都回答了
- 构建一个 DF
dfr
,这是您想要的计算 -
lambda
函数返回一个字典{year:val,...}
- 确保以合理的方式命名索引
- 用
dict
展开apply(pd.Series)
- 通过将年份列重新放入索引来重塑
-
merge()
使用原始 DF 构建 DF。 vals 列包含NaN
0 列是要填充的值 - 终于
fillna()
# create 100 yr time series
dates = pd.date_range(start="1918-01-01",end="2018-12-31")
vals = [random.randrange(1,50,1) for i in range(len(dates))]
# Create some arbitrary gaps
vals[100:200] = vals[9962:10062] = vals[35895:35995] = [np.nan] * 100
# Create dataframe - simplified from question...
df = pd.DataFrame({"Date":dates,"vals":vals})
df[df.isna().any(axis=1)]
ystart = df.Date.dt.year.min()
# generate rolling means for month/day. bfill for when it's start of series
dfr = (df.groupby([df.Date.dt.month,df.Date.dt.day])["vals"]
.agg(lambda s: {y+ystart:v for y,v in enumerate(s.dropna().rolling(5).mean().bfill())})
.to_frame().rename_axis(["month","day"])
)
# expand dict into columns and reshape to by indexed by month,day,year
dfr = dfr.join(dfr.vals.apply(pd.Series)).drop(columns="vals").rename_axis("year",axis=1).stack().to_frame()
# get df index back,plus vals & fillna (column 0) can be seen alongside each other
dfm = df.merge(dfr,left_on=[df.Date.dt.month,df.Date.dt.day,df.Date.dt.year],right_index=True)
# finally what we really want to do - fill tha NaNs
df.fillna(dfm[0])
分析
- 1918 年 4 月 11 日采用 NaN,默认值为 22,因为它是从 1921 年回填的
- (12+2+47+47+2)/5 == 22
dfm.query("key_0==4 & key_1==11").head(7)
key_0 | key_1 | key_2 | 日期 | vals | 0 | |
---|---|---|---|---|---|---|
100 | 4 | 11 | 1918 | 1918-04-11 00:00:00 | nan | 22 |
465 | 4 | 11 | 1919 | 1919-04-11 00:00:00 | 12 | 22 |
831 | 4 | 11 | 1920 | 1920-04-11 00:00:00 | 2 | 22 |
1196 | 4 | 11 | 1921 | 1921-04-11 00:00:00 | 47 | 27 |
1561 | 4 | 11 | 1922 | 1922-04-11 00:00:00 | 47 | 36 |
1926 | 4 | 11 | 1923 | 1923-04-11 00:00:00 | 2 | 34.6 |
2292 | 4 | 11 | 1924 | 1924-04-11 00:00:00 | 37 | 29.4 |
我不知道我对你的问题的意图有多深。我采取的方法是满足两个要求
- 需要任意数量的平均值
- 使用这些平均值填写 NA
我已经解决了 简而言之,我不是用前后日期填充 NA,而是用从连续任意年数中提取的平均值填充 NA。
import pandas as pd
import numpy as np
import random
# create 100 yr time series
dates = pd.date_range(start="1918-01-01",end="2018-12-31").strftime("%Y-%m-%d")
vals = [random.randrange(1,1) for i in range(len(dates))]
# Create some arbitrary gaps
vals[100:200] = vals[9962:10062] = vals[35895:35995] = [np.nan] * 100
# Create dataframe
df = pd.DataFrame(dict(
list(
zip(["Date","vals"],[dates,vals])
)
))
df['Date'] = pd.to_datetime(df['Date'])
df['mm-dd'] = df['Date'].apply(lambda x:'{:02}-{:02}'.format(x.month,x.day))
df['yyyy'] = df['Date'].apply(lambda x:'{:04}'.format(x.year))
df = df.iloc[:,1:].pivot(index='mm-dd',columns='yyyy')
df.columns = df.columns.droplevel(0)
df['nans'] = df.isnull().sum(axis=1)
df['10n_mean'] = df.iloc[:,:-1].sample(n=10,axis=1).mean(axis=1)
df['10n_mean'] = df['10n_mean'].round(1)
df.loc[df['nans'] >= 1]
yyyy 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 ... 2011 2012 2013 2014 2015 2016 2017 2018 nans 10n_mean
mm-dd
02-29 NaN NaN 34.0 NaN NaN NaN 2.0 NaN NaN NaN ... NaN 49.0 NaN NaN NaN 32.0 NaN NaN 76 21.6
04-11 NaN 43.0 12.0 28.0 29.0 28.0 1.0 38.0 11.0 3.0 ... 17.0 35.0 8.0 17.0 34.0 NaN 5.0 33.0 3 29.7
04-12 NaN 19.0 38.0 34.0 48.0 46.0 28.0 29.0 29.0 14.0 ... 41.0 16.0 9.0 39.0 8.0 NaN 1.0 12.0 3 21.3
04-13 NaN 33.0 26.0 47.0 21.0 26.0 20.0 16.0 11.0 7.0 ... 5.0 11.0 34.0 28.0 27.0 NaN 2.0 46.0 3 21.3
04-14 NaN 36.0 19.0 6.0 45.0 41.0 24.0 39.0 1.0 11.0 ... 30.0 47.0 45.0 14.0 48.0 NaN 16.0 8.0 3 24.7
df_mean = df.T.fillna(df['10n_mean'],downcast='infer').T
df_mean.loc[df_mean['nans'] >= 1]
yyyy 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 ... 2011 2012 2013 2014 2015 2016 2017 2018 nans 10n_mean
mm-dd
02-29 21.6 21.6 34.0 21.6 21.6 21.6 2.0 21.6 21.6 21.6 ... 21.6 49.0 21.6 21.6 21.6 32.0 21.6 21.6 76.0 21.6
04-11 29.7 43.0 12.0 28.0 29.0 28.0 1.0 38.0 11.0 3.0 ... 17.0 35.0 8.0 17.0 34.0 29.7 5.0 33.0 3.0 29.7
04-12 21.3 19.0 38.0 34.0 48.0 46.0 28.0 29.0 29.0 14.0 ... 41.0 16.0 9.0 39.0 8.0 21.3 1.0 12.0 3.0 21.3
04-13 21.3 33.0 26.0 47.0 21.0 26.0 20.0 16.0 11.0 7.0 ... 5.0 11.0 34.0 28.0 27.0 21.3 2.0 46.0 3.0 21.3
04-14 24.7 36.0 19.0 6.0 45.0 41.0 24.0 39.0 1.0 11.0 ... 30.0 47.0 45.0 14.0 48.0 24.7 16.0 8.0 3.0 24.7
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。