【问题标题】:Pandas DataFrame - Fill NaNs of columns based on values of other columnsPandas DataFrame - 根据其他列的值填充列的 NaN
【发布时间】:2020-11-14 15:29:06
【问题描述】:

我有几年的广泛数据框架:

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 299251993 中是 16,我们知道它们在 1992 中是 15,在 1991 中是 14,因此我们想将 NaN 替换为 @ 列中的 29925 987654329@ 和1991。同样,我想根据29925 的现有年龄值替换2010-2012 列中的NaN。假设299252010-2012 列中的1996 大15 岁。对整个数据框(即所有 ID)执行此操作的最快方法是什么?

【问题讨论】:

  • 最后一行前 36 是 35,是吗?不应该有两个 36...
  • 是的,你能明确地解释一下最后一行吗?如果您的数据有可能是混乱的,以至于年龄并不总是每年增加 1 岁(或者从 1996-2010-2012 年仅增加 14 岁而不是 15 岁),您想如何解决这种情况?在那种情况下,我可以想象你可能会有像 35 NaN 36 这样的空白,并且不可能知道 NaN 应该变成 35 还是 36...
  • 嗨,谢谢你们的回复,我们可以说最后一行应该是 35 还是 36 是未知的。数据来自假设调查,该人可能已经 36 次回复1991 年和 1992 年的调查,例如他们在 1991 年 12 月和 1992 年 1 月再次做出回应,他们的生日是 6 月。对于其他NaNs的情况,按照年份+1或-1的一般规则填写。

标签: python pandas dataframe nan


【解决方案1】:

# 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]]))

计算每个人的出生日期:

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 列填写dfNaNs:

for irow, row in enumerate(df.index):
    for icol, col in enumerate(df.columns[:-2]):
        df.loc[row,col] = col - df['dob'][row]

Deletedob列(只是为了获取原始列而已,否则不重要):

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

【讨论】:

  • 我一直在考虑这个问题,老实说,创建​​一个出生年份的 DataFrame 确实是高效执行此操作的关键。一旦你拥有dob 和 fffil 并 bffill 它,你实际上可以绕过很多迭代。
  • 我发现这个问题也很有趣;如果您可以通过绕过迭代来提高效率,我很感兴趣!
  • (随时编辑我的答案或使用我的部分添加您自己的答案)
  • 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 不变
猜你喜欢
  • 2019-09-30
  • 1970-01-01
  • 2021-12-20
  • 2015-10-05
  • 1970-01-01
  • 2019-11-21
  • 1970-01-01
  • 2023-02-09
  • 1970-01-01
相关资源
最近更新 更多