Python 熊猫及;基于组的fillna

Python 熊猫及;基于组的fillna,python,pandas,scikit-learn,imputation,Python,Pandas,Scikit Learn,Imputation,我有一个有趣的问题,我已经在表面上解决了这个问题,但是我想增强和改进我的实现 我有一个DataFrame,它保存了一个数据集,用于以后的机器学习。它有功能列(约500列)和4列目标。目标以越来越精细的方式相互关联(例如,故障/无故障、故障位置、故障组、故障精确)。 DataFrame有相当多的NaN值,因为它是通过外部联接由两个独立的数据集编译而成的-一些行已满,其他行有来自一个数据集的数据,但另一个数据集的数据,等等-请参见下面的pic,对于糟糕的编辑,我深表歉意 无论如何,Sci kit

我有一个有趣的问题,我已经在表面上解决了这个问题,但是我想增强和改进我的实现

我有一个DataFrame,它保存了一个数据集,用于以后的机器学习。它有功能列(约500列)和4列目标。目标以越来越精细的方式相互关联(例如,故障/无故障、故障位置、故障组、故障精确)。 DataFrame有相当多的NaN值,因为它是通过外部联接由两个独立的数据集编译而成的-一些行已满,其他行有来自一个数据集的数据,但另一个数据集的数据,等等-请参见下面的pic,对于糟糕的编辑,我深表歉意

无论如何,Sci kit Learn的SimpleImputer()Transformer没有给我我想要的ML结果,我想也许我应该根据目标进行插补,例如,计算每列中每个目标可用样本的中值,然后插补这些结果。然后检查是否还有任何NaN值,如果有,移动到tar_3(向下一级粒度),同时计算中值,并根据每个目标、每个列输入该值。以此类推,直到没有南了

我已经用下面的代码实现了这一点,我完全理解这是一个笨拙的代码,需要永远执行:

tar_list = ['tar_4', 'tar_3', 'tar_2', 'tar_1']

for tar in tar_list:

    medians = df.groupby(by = tar).agg('median')
    print("\nFilling values based on {} column granularity.".format(tar))

    for col in [col for col in df.columns if col not in tar_list]:

        print(col)
        uniques = sorted(df[tar].unique())

        for class_name in uniques:

            value_to_fill = medians.loc[class_name][col]
            print("Setting NaNs for target {} in column {} to {}".format(class_name, col, value_to_fill))
            df.loc[df[tar] == class_name, col] = df.loc[df[tar] == class_name, col].fillna(value = value_to_fill)
    print()
虽然我对这段代码产生的结果感到满意,但它有两个缺点,我不能忽视: 1) 即使在我的~1000个样本x ~500个列的小数据集上执行,也要花费很长时间。 2) 它根据当前正在处理的目标值,对每列中的所有NaN输入相同的中值。我更喜欢用一点杂音来插补一些东西,以防止数据的简单重复(可能是从该列中针对该目标值的正态分布中随机选择的值?)

据我所知,Sci Kit Learn或Pandas中没有现成的工具可以更有效地完成这项任务。但是,如果有,有人能给我指出正确的方向吗?或者,我愿意听取关于如何改进此代码以解决我的两个问题的建议

更新:

生成我提到的示例数据帧的代码:

df = pd.DataFrame(np.random.randint(0, 100, size=(vsize, 10)), 
              columns = ["col_{}".format(x) for x in range(10)], 
              index = range(0, vsize * 3, 3))

df_2 = pd.DataFrame(np.random.randint(0,100,size=(vsize, 10)), 
                columns = ["col_{}".format(x) for x in range(10, 20, 1)], 
                index = range(0, vsize * 2, 2))

df = df.merge(df_2, left_index = True, right_index = True, how = 'outer')

df_tar = pd.DataFrame({"tar_1": [np.random.randint(0, 2) for x in range(vsize * 3)], 
                   "tar_2": [np.random.randint(0, 4) for x in range(vsize * 3)], 
                   "tar_3": [np.random.randint(0, 8) for x in range(vsize * 3)], 
                   "tar_4": [np.random.randint(0, 16) for x in range(vsize * 3)]})

df = df.merge(df_tar, left_index = True, right_index = True, how = 'inner')
试试这个:

tar_list = ['tar_4', 'tar_3', 'tar_2', 'tar_1']
cols = [col for col in df.columns if col not in tar_list]
# since your dataframe may not have continuous index
idx = df.index

for tar in tar_list:
    medians = df[cols].groupby(by = df[tar]).agg('median')
    df.set_index(tar, inplace=True)
    for col in cols:
        df[col] = df[col].fillna(medians[col])
    df.reset_index(inplace=True)

df.index = idx
使用样本数据大约花费了1.5秒:

np.random.seed(2019)
len_df=1000
num_cols = 500
df = pd.DataFrame(np.random.choice(list(range(10))+[np.nan],
                                   size=(len_df, num_cols),
                                   p=[0.05]*10+[0.5]),
                  columns=[str(x) for x in range(num_cols)])

for i in range(1,5):
    np.random.seed(i)
    df[f'tar_{i}'] = np.random.randint(i*4, (i+1)*4, len_df)

试着提出一个问题。人们很可能会更快地回答问题,原因有二:1。他们可以复制并粘贴您的示例数据帧(我们无法复制图片)。2如果你包括一个预期的输出,我们可以直观地看到发生了什么,而不是阅读大量的文本和代码。你是对的,我已经更新了我的问题,将生成示例数据框的代码包括进来,就像我提到的那样。这非常有效。伟大的代码!但有一个问题-目前我只是将每个列中的medians值设置为nan,分别对应于“tar”值。有没有一个简单的方法,我可以修改这个代码,给这些值引入一些噪声?例如,如果X列有3个NaN值,总共包含10个值,而不是用7个值的中值替换这3个NaN,我希望将这3个NAN中的每一个都替换为一个不同的值+/-0-3 x标准偏差,与本列中7个值的中值相比。@Greem666我不确定我是否完全理解您的问题。但是看起来你可以为每列计算
NaN
的比率,为那些满足比率条件的列修改
中间值,然后赋值。我想到的一个例子如下:col_1是用该列中非NaN值的中值填充其NaN。有3个非NaN值和2个NaN值。非NaN列的中位数=5,标准误差=1。该列中的第一个NaN被赋值为5+(3*1),第二个NaN被赋值为5-(2*1)。这样,两个NaN都将填充属于该列中非NaN值分布的值。目前,脚本用相同的值(在本例中为中间值)填充colu_1中的所有NaN。我想我现在理解了这个问题,但我也相信它值得一个新问题。好吧,那我就问一个新问题。