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

我有一个DataFrame,其中包含一个数据集,供以后的机器学习使用。它具有功能列(其中约有500列)和4列目标。目标之间以越来越大的粒度方式相互关联(例如,fault / no_fault,fault-where,fault-group,fault-exact)。
DataFrame具有很多NaN值,因为它是通过OUTER连接由2个单独的数据集编译而成-一些行已满,另一些具有来自一个数据集的数据,但没有其他数据集-请参见下图,非常抱歉编辑。

python - 基于群体的 Pandas 和菲娜-LMLPHP

无论如何,Sci-kit Learn的SimpleImputer()Transformer并没有给我我所追求的ML结果,而且我认为也许我应该基于目标进行插补,例如计算每个列中每个目标的可用样本的中位数,并将其估算。然后检查是否还有NaN值,如果存在,请移至tar_3(向下一级粒度),还计算中值,并针对每个目标,每个列估算该值。依此类推,直到没有NaN为止。

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

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()


尽管我对该代码产生的结果感到满意,但它有2个缺点,我不能忽略:
1)即使我的〜1000个小样本x〜500列数据集也要花费很多时间。
2)对于当前正在使用的每个目标值,它对每列中的所有NaN均值相同的中值。我宁愿它用一点杂音来插补某些东西,以防止只是简单地重复数据(也许是从该列中该目标值的正态分布中随机选择的值?)。

据我所知,Sci-Kit Learn或Pandas中没有现成的工具可以更有效地完成此任务。但是,如果有-有人可以指出我正确的方向吗?另外,我愿意就如何增强此代码来解决我的两个问题提出建议。

更新:

我提到的代码生成示例DataFrame:

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)

关于python - 基于群体的 Pandas 和菲娜,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56151936/

10-10 21:52