Pandas fillna() inplace 参数在不使用三重 For 循环的情况下无法工作

Pandas fillna() inplace Parameter Isn't Working Without Using a Triple For Loop

我正在尝试将 DataFrame 分成四个部分,并使用 fillna() 为每个部分估算四舍五入的平均值。我有两列,main_campusdegree_type 我想过滤,每列都有两个唯一值。所以在它们之间我应该能够将 DataFrame 过滤成两组。

我首先使用三重 for 循环(见下文)执行此操作,这似乎可行,但当我尝试以更优雅的方式执行此操作时,我收到 SettingWithCopy 警告,我不能'使用.loc.copy()无法修复,即使inplace设置为True也不会填充缺失值。这是后一种方法的代码:

# Imputing mean values for main campus BA students
df[(df.main_campus == 1) &
            (df.degree_type == 'BA')] = df[(df.main_campus == 1) &
            (df.degree_type == 'BA')].fillna(
                df[(nulled_data.main_campus == 1) &
                            (df.degree_type == 'BA')
                            ].mean(),
                     inplace=True)
            
# Imputing mean values for main campus BS students
df[(df.main_campus == 1) &
            (df.degree_type == 'BS')] = df[(df.main_campus == 1) &
            (df.degree_type == 'BS')].fillna(
                df[(df.main_campus == 1) &
                            (df.degree_type == 'BS')
                            ].mean(),
                     inplace=True)
            
# Imputing mean values for downtown campus BA students
df[(df.main_campus == 0) &
            (df.degree_type == 'BA')] = df[(df.main_campus == 0) &
            (df.degree_type == 'BA')].fillna(
                df[(df.main_campus == 0) &
                            (df.degree_type == 'BA')
                            ].mean(),
                     inplace=True)

# Imputing mean values for downtown campus BS students          
df[(df.main_campus == 0) &
            (df.degree_type == 'BS')] = df[(df.main_campus == 0) &
            (df.degree_type == 'BS')].fillna(
                df[(df.main_campus == 0) &
                            (df.degree_type == 'BS')
                            ].mean(),
                     inplace=True)      

我应该提到之前的代码经历了几次迭代,在不将其设置回切片的情况下进行尝试,有和没有 inplace,等等。

下面是使用三重 for 循环的代码:

imputation_cols = [# all the columns I want to impute]

for col in imputation_cols:

  for i in [1, 0]:

    for path in ['BA', 'BS']:

      group = ndf.loc[((df.main_campus == i) &
                               (df.degree_type == path)), :]
      
      group = group.fillna(value=round(group.mean()))

      df.loc[((df.main_campus == i) &
                               (df.degree_type == path)), :] = group

值得一提的是,我认为在三重 for 循环代码中使用 group 变量也是为了帮助填充的 NaN 值实际设置回 DataFrame,但我需要仔细检查这个。

有人知道这里发生了什么吗?

解决此类问题的一个好方法是简化您的代码。简化您的代码可以更容易地找到警告的来源:

group1 = (df.main_campus == 1) & (df.degree_type == 'BA')
group2 = (df.main_campus == 1) & (df.degree_type == 'BS')
group3 = (df.main_campus == 0) & (df.degree_type == 'BA')
group4 = (df.main_campus == 0) & (df.degree_type == 'BS')

# Imputing mean values for main campus BA students
df.loc[group1, :] = df.loc[group1, :].fillna(df.loc[group1, :].mean())  # repeat for other groups

现在你可以更清楚地看到问题了。您正在尝试将 df 的平均值写回 df。 Pandas 发出警告,因为您用于计算平均值的切片可能与更改后的数据帧不一致。在您的情况下,它会产生正确的结果。但是您的数据框的一致性存在风险。

你可以通过预先计算平均值来解决这个问题:

group1_mean = df.loc[group1, :].mean()
df.loc[group1, :] = df.loc[group1, :].fillna(group1_mean)

在我看来,这使代码更加清晰。但是您仍然有四个组(group1、group2、...)。使用循环的明确标志:

from itertools import product

for campus, degree in product([1, 0], ['BS', 'BA']):
    group = (df.main_campus == campus) & (df.degree_type == degree)
    group_mean = df.loc[group, :].mean()
    df.loc[group, :] = df.loc[group, :].fillna(group_mean)

我已经使用 itertools 中的 product 来摆脱丑陋的嵌套循环。它与您的“不优雅”的第一个解决方案非常相似。所以你第一次就差一点了。

我们最终得到了四行代码和一个循环。我敢肯定,使用一些 pandas 魔法,您可以将其转换为一行。但是,从现在开始的一周或一个月或一年后,您仍然会理解这四行。此外,阅读您的代码的其他人将很容易理解它。 可读性很重要。


免责声明:我无法测试代码,因为您没有提供示例数据框。所以我的代码可能会因为打字错误而抛出错误。 minimal reproducible example 使回答问题变得容易得多。下次您 post 关于 SO 的问题时请考虑这一点。