正在交换数据框的列:当我按名称识别和分配列时,为什么我的循环会切换列值?

Columns of Data Frame are Being Swapped: Why is my loop switching the column values when I identify and assign the columns by name?

我需要有关我将在下面粘贴的特定代码的帮助。我正在使用 Dean De Cock 收集的 Ames Housing 数据集。 我正在使用 Python 笔记本并通过 Anaconda 的 Jupyter Lab 2.1.5 进行编辑。 下面的代码应该替换所有 np.nan 或“None”值。因为某些原因, 在 for 循环中重复调用一个手工制作的函数后,结果数据框的列被交换了。

注意:我知道我可以使用“输入器”来完成此操作。我计划 select 数字和对象类型特征,分别估算它们然后将它们放回一起。作为旁注,有什么方法可以做到这一点,同时让我使用文本显示或以其他方式验证手动输出的详细信息?

在有问题的单元格中,流程是:

  1. 获取并分配数据框中的数据点个数df_train
  2. 获取并分配一个序列,其中列出了 df_train 中空值的计数。语法是 sr_null_counts = df_train.isnull().sum().
  3. 创建一个空列表,其中附加了 5% 的值等于 null 的要素的名称。它们稍后会被丢弃, 在 for 循环之外。起初我以为这是问题所在,因为命令将 df_train 的列就地删除 曾经在for循环中。
  4. 重复调用一个手工制作的函数来估算 df_train 的空值不超过行数 5% 的列。

我使用了一个具有 for 循环和嵌套 try-except 语句的函数来:

  1. 接受一个系列,如果系列是数据框中的一列,则可以选择接受该系列的名称。它分配传递的系列的副本 到局部变量。
  2. 按照确切的顺序,(a) 尝试用传递的系列的平均值替换所有空值(NaNNone)。 (b) 如果失败,尝试用系列的中位数替换所有空值。 (c) 如果仍然失败,用系列的模式替换所有空值。
  3. Return 已替换所有空值的系列编辑副本。它还应该打印出告诉我什么功能的字符串 已修改,汇总统计数据用于 replace/impute 缺失值。

最后一行是删除所有标记为缺失值超过 5% 的列。

完整代码如下:

将主数据帧拆分为训练集和测试集。

完整数据集已通过 df_housing = pd.read_csv(sep = '\t', filepath_or_buffer = "AmesHousing.tsv") 加载。

def make_traintest(df, train_fraction = 0.7, random_state_val = 88):
    df = df.copy()
    df_train = df.sample(frac = train_fraction, random_state = random_state_val)    
    bmask_istrain = df.index.isin(df_train.index.values)
    df_test = df.loc[ ~bmask_istrain ]
    
    return {
        "train":df_train,
        "test":df_test
        }

dict_traintest = make_traintest(df = df_housing)
df_train = dict_traintest["train"]
df_test = dict_traintest["test"]

获取具有空值的列列表

lst_have_nulls = []
for feature in df_housing.columns.values.tolist():
    nullcount = df_housing[feature].isnull().sum()
    if nullcount > 0:
        lst_have_nulls.append(feature)
        print(feature, "\n=====\nNull Count:\t", nullcount, '\n', df_housing[feature].value_counts(dropna = False),'\n*****')

手办函数定义:

def impute_series(sr_values, feature_name = ''):
    sr_out = sr_values.copy()
    try:        
        sr_out.fillna(value = sr_values.mean())
        print("Feature", feature_name, "imputed with mean:", sr_values.mean())
    except Exception as e:
        print("Filling NaN values with mean of feature", feature_name, "caused an error:\n", e)
        try:
            sr_out.fillna(value = sr_values.median())
            print("Feature", feature_name, "imputed with median:", sr_values.median())
        except Exception as e:
            print("Filling NaN values with median for feature", feature_name, "caused an error:\n", e)
            sr_out.fillna(value = sr_values.mode())
            print("Feature", feature_name, "imputed with mode:", sr_values.mode())            
    
    return sr_out

For 循环

获取空值的计数,定义要删除的空列列表以允许追加,并重复 执行以下操作:对于 lst_have_nulls 中的每一列,检查该列是否具有等于、小于或大于 5% 的缺失值。 如果更多,请将该列附加到 lst_drop。否则,调用手工制作的插补函数。在 for 循环之后,将所有列放入 lst_drop,就地

我哪里做错了?如果您需要整个笔记本,我已将其上传到 Kaggle。这里有一个link。 https://www.kaggle.com/joachimrives/ames-housing-public-problem

更新:测试 Anvar 的答案并进行更改后问题仍然存在

当我尝试 Anvar Kurmukov 的代码时,我的数据框列值仍然被交换。我所做的更改是将 intfloat 添加到要检查的数据类型列表中。更改在 for 循环内: if dtype in [np.int64, np.float64, int, float].

可能是我在完整笔记本中的另一部分代码有问题。我需要通过从顶部逐个单元调用 df_train.info() 来检查它的位置。我在自己做的笔记本public中测试了代码。它在单元格 128 中。出于某种原因,在 运行 Anvar 的代码之后,df_train.info() 方法返回了这个:

    1st Flr SF  2nd Flr SF  3Ssn Porch  Alley   Bedroom AbvGr   Bldg Type   Bsmt Cond   Bsmt Exposure   Bsmt Full Bath  Bsmt Half Bath  ... Roof Style  SalePrice   Screen Porch    Street  TotRms AbvGrd   Total Bsmt SF   Utilities   Wood Deck SF    Year Built  Year Remod/Add
1222    1223    534453140   70  RL  50.0    4882    Pave    NaN IR1 Bnk ... 0   0   0   0   0   NaN NaN NaN 0   87000
1642    1643    527256040   20  RL  81.0    13870   Pave    NaN IR1 HLS ... 52  0   0   174 0   NaN NaN NaN 0   455000
1408    1409    905427050   50  RL  66.0    21780   Pave    NaN Reg Lvl ... 36  0   0   144 0   NaN NaN NaN 0   185000
1729    1730    528218050   60  RL  65.0    10237   Pave    NaN Reg Lvl ... 72  0   0   0   0   NaN NaN NaN 0   178900
1069    1070    528180110   120 RL  58.0    10110   Pave    NaN IR1 Lvl ... 48  0   0   0   0   NaN NaN NaN 0   336860

tl;dr 而不是 try: except 你应该简单地使用 if 并检查列的数据类型;您不需要遍历列。

drop_columns = df.columns[df.isna().sum() / df.shape[0] > 0.05]
df.drop(drop_columns, axis=1)

num_columns = []
cat_columns = []

for col, dtype in df.dtypes.iteritems():
    if dtype in [np.int64, np.float64]:
        num_columns.append(col)
    else:
        cat_columns.append(col)
        
df[num_columns] = df[num_columns].fillna(df[num_columns].mean())
df[cat_columns] = df[cat_columns].fillna(df[cat_columns].mode())

关于 make_traintest 函数的简短评论:我会简单地 return 2 个单独的 DataFrame 而不是字典或使用 sklearn.model_selection.train_test_split.

更新。您可以检查列中 NaN 值的数量,但如果您的唯一目标是估算 NaN,则没有必要这样做。

回答

我找到了为什么我的专栏被调换的答案。他们实际上并没有被交换。最初的问题是我没有将“订单”列设置为索引列。为了解决我 PC 笔记本电脑上的问题,我只是将以下参数和值添加到 pd.read_csvindex_col = "Order"。这解决了我本地笔记本上的问题。然而,当我在 Kaggle notebook 上尝试时,它并没有解决问题

我第一次在笔记本上使用的 Ames Housing 数据集的 版本 - 出于某种原因 - 也是列交换的原因。

Anvar 的代码很好。您可以测试我编写的代码,但为了安全起见,请遵循 Anvar 的代码。我的还在测试中

测试完成

我修改了我在问题中链接的 Kaggle 笔记本。我使用了我在 PC 上实际使用的数据集。当我这样做时,Anvar Kurmukov 的回答给出的代码运行完美。我测试了自己的代码,看起来不错,但在尝试之前测试两个版本。我只使用 head() 查看了数据集并手动检查了列输入。如果你想检查笔记本,这里是: https://www.kaggle.com/joachimrives/ames-housing-public-problem/

为了测试数据集是否有问题,我创建了数据框。一个是直接从我上传到 Kaggle 的本地文件中提取的。另一个使用我用作输入的 Ames Iowa Housing 数据集的当前版本。这些列与它们的预期输入正确地“对齐”。为了找到预期的列值,我使用了这个来源: http://jse.amstat.org/v19n3/decock/DataDocumentation.txt

下面是我交换数据集时得到的不同结果的截图:

使用我的本地文件的上传副本:

与原始 AmesHousing.csv 来自笔记本版本 1:

我使用的数据集导致了 Kaggle Notebook 上的 Column-swap

https://www.kaggle.com/marcopale/housing