Pandas DataFrame - 根据其他列的值填充NaN列

3

我有一个包含多年数据的宽数据框:

df = pd.DataFrame(index=pd.Index([29925, 223725, 280165, 813285, 956765], name='ID'),
                  columns=pd.Index([1991, 1992, 1993, 1994, 1995, 1996, '2010-2012'], name='Year'),
                  data = np.array([[np.NaN, np.NaN, 16, 17, 18, 19, np.NaN],
                                   [16, 17, 18, 19, 20, 21, np.NaN],
                                   [np.NaN, np.NaN, np.NaN, np.NaN, 16, 17, 31],
                                   [np.NaN, 22, 23, 24, np.NaN, 26, np.NaN],
                                   [36, 36, 37, 38, 39, 40, 55]]))

Year     1991  1992  1993  1994  1995  1996  2010-2012
ID                                                    
29925     NaN   NaN  16.0  17.0  18.0  19.0        NaN
223725   16.0  17.0  18.0  19.0  20.0  21.0        NaN
280165    NaN   NaN   NaN   NaN  16.0  17.0       31.0
813285    NaN  22.0  23.0  24.0   NaN  26.0        NaN
956765   36.0  36.0  37.0  38.0  39.0  40.0       55.0

每行中的值是每个人的年龄,每个人都有一个唯一的ID。我想要根据每行现有的年龄值,在每年的每行中填充此数据帧中的NaN
例如,ID 29925 1993 是16岁,我们知道他们在1992 是15岁,在1991 是14岁,因此我们希望替换列1992 1991 29925 NaN。同样地,我希望根据29925 的现有年龄值替换2010-2012 列中的NaN。假设29925 1996 2010-2012 列增加了15岁。如何以最快的方式为整个数据框架执行此操作 - 即对所有ID?

最后一行的第36个是35,对吧?不应该有两个36... - zabop
你能解释一下最后一行吗?如果你的数据可能很混乱,以至于年龄不总是每年增加1岁(或者只在1996-2010-2012年间增加14岁而不是15岁),那么你想如何解决这些情况呢?在这种情况下,我可以想象你可能会有像“35 NaN 36”这样的间隙,这就变得无法确定“NaN”应该变成35还是36了... - ALollz
大家好,感谢你们的回复。我们可以争论最后一行应该是35还是36,因为这些数据来自一个假设性调查,那个人在1991年和1992年都可能已经是36岁了,例如他们在1991年12月和1992年1月都回答了调查,而他们的生日是在6月。至于其他“NaN”的情况,应根据一般规则填写+1或-1,具体取决于年份。 - MI MA
1个回答

2

# imports we need later
import numpy as np
import pandas as pd

这不是特别高效的方法,但它可以运行。我将省略您的最后一列,以使事情更加系统化。
数据框 df:
df = pd.DataFrame(index=pd.Index([29925, 223725, 280165, 813285, 956765], name='ID'),
                  columns=pd.Index([1992, 1992, 1993, 1994, 1995, 1996], name='Year'),
                  data = np.array([[np.NaN, np.NaN, 16, 17, 18, 19],
                                   [16, 17, 18, 19, 20, 21],
                                   [np.NaN, np.NaN, np.NaN, np.NaN, 16, 17],
                                   [np.NaN, 22, 23, 24, np.NaN, 26],
                                   [35, 36, 37, 38, 39, 40]]))

enter image description here

计算每个人的出生日期:
dob=[]
for irow, row in enumerate(df.iterrows()):
    dob.append(np.asarray([int(each) for each in df.columns]) - np.asarray(df.iloc[irow,:]))

或者,如果你喜欢列表推导式 comprehensions
dob = [np.asarray([int(each) for each in df.columns]) - np.asarray(df.iloc[irow,:]) for irow, row in enumerate(df.iterrows())]

现在 dob 是这样的:
[array([  nan,   nan, 1977., 1977., 1977., 1977.]),
 array([1976., 1975., 1975., 1975., 1975., 1975.]),
 array([  nan,   nan,   nan,   nan, 1979., 1979.]),
 array([  nan, 1970., 1970., 1970.,   nan, 1970.]),
 array([1956., 1956., 1956., 1956., 1956., 1956.])]

使用np.uniqueremove nans创建一个更简单的 dob 列表:
dob_filtered=[np.unique(each[~np.isnan(each)])[0] for each in dob]

dob_filtered现在看起来像这样:

[1977.0, 1975.0, 1979.0, 1970.0, 1956.0]

将这个列表Attach到数据框中:
df['dob']=dob_filtered

使用“dob”列填充“df”的NaN值:
for irow, row in enumerate(df.index):
    for icol, col in enumerate(df.columns[:-2]):
        df.loc[row,col] = col - df['dob'][row]

删除dob列(仅为获取原始列,否则不重要):

df.drop(['dob'],axis=1)

获取:
Year    1992    1992    1993    1994    1995    1996
ID                      
29925   15.0    15.0    16.0    17.0    18.0    19.0
223725  17.0    17.0    18.0    19.0    20.0    21.0
280165  13.0    13.0    14.0    15.0    16.0    17.0
813285  22.0    22.0    23.0    24.0    25.0    26.0
956765  36.0    36.0    37.0    38.0    39.0    40.0

ie

enter image description here


1
我已经思考了很长时间,老实说,创建一个出生年份的DataFrame确实是高效完成此任务的关键。一旦你有了dobfffilbffill,你实际上可以避免很多迭代。 - ALollz
我也觉得这个问题很有趣;如果你能绕过迭代使它更有效率,我很感兴趣! - zabop
请翻译以下与编程有关的内容,从英文到中文。只返回翻译后的文本:(可以编辑我的答案或添加您自己的答案) - zabop
dob 是关键...如果 df 有很多行,从头构建整个 df 可能更快:df_new = pd.DataFrame([[col - dob for col in df.columns[:-2]] for dob in df['dob']], index=df.index, columns=df.columns[:-2]) ...然后添加2010-2012年不变的数据。 - RichieV

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接