如何使用ColumnTransformer进行数据准备

在将机器学习模型拟合到原始数据之前,必须使用数据转换来准备数据。

这是必需的,以确保您最好地将预测建模问题的结构暴露给学习算法。

当所有输入变量都是同一类型时,应用诸如缩放或编码分类变量之类的数据转换非常简单。当您拥有包含混合类型且希望选择性地将数据转换应用于部分而非全部输入特征的数据集时,这可能会很棘手。

幸运的是,scikit-learn Python机器学习库提供了 ColumnTransformer,它允许您选择性地将数据转换应用于数据集中的不同列。

在本教程中,您将了解如何使用ColumnTransformer将数据转换选择性地应用于具有混合数据类型的 数据集中的列。

完成本教程后,您将了解:

  • 使用数据转换处理具有混合数据类型的 数据集时所面临的挑战。
  • 如何定义、拟合和使用ColumnTransformer来选择性地将数据转换应用于列。
  • 如何处理具有混合数据类型的实际数据集,并使用ColumnTransformer将不同的转换应用于分类和数值数据列。

通过我的新书《机器学习数据准备》,其中包括分步教程和所有示例的Python源代码文件,快速启动您的项目

让我们开始吧。

  • 更新于2020年12月:修复了API示例中的一个小拼写错误。
Use the ColumnTransformer for Numerical and Categorical Data in Python

在Python中使用ColumnTransformer处理数值和分类数据
照片由Kari拍摄,部分权利保留。

教程概述

本教程分为三个部分;它们是:

  1. 转换不同数据类型的挑战
  2. 如何使用ColumnTransformer
  3. Abalone回归数据集的数据准备

转换不同数据类型的挑战

在建模之前准备数据很重要。

这可能包括替换缺失值、缩放数值以及对分类数据进行独热编码。

可以使用scikit-learn库执行数据转换;例如,可以使用SimpleImputer类替换缺失值,可以使用MinMaxScaler类缩放数值,以及使用OneHotEncoder对分类变量进行编码。

例如

也可以使用Pipeline将不同转换的序列链接在一起,例如先进行缺失值填充,然后进行缩放。

例如

在输入数据的不同列上执行不同的数据准备技术是非常普遍的。

例如,您可能希望使用中位数填充缺失的数值,然后进行缩放,并使用最常见的值填充缺失的分类值,然后对类别进行独热编码。

传统上,这需要您分离数值和分类数据,然后在这些特征组上手动应用转换,然后再将列合并在一起,以便拟合和评估模型。

现在,您可以使用ColumnTransformer为您执行此操作。

想开始学习数据准备吗?

立即参加我为期7天的免费电子邮件速成课程(附示例代码)。

点击注册,同时获得该课程的免费PDF电子书版本。

如何使用ColumnTransformer

ColumnTransformer 是 scikit-learn Python 机器学习库中的一个类,它允许您选择性地应用数据准备转换。

例如,它允许您将特定的转换或一系列转换应用于仅数值列,并将另一系列转换应用于仅分类列。

要使用ColumnTransformer,您必须指定一个转换器列表。

每个转换器都是一个三元素元组,定义转换器的名称、要应用的转换以及要应用它的列索引。例如

  • (名称,对象,列)

例如,下面的ColumnTransformer将OneHotEncoder应用于第0列和第1列。

下面的示例将SimpleImputer与中位数填充应用于数值列0和1,并将SimpleImputer与最常见值填充应用于分类列2和3。

默认情况下,列表中未指定的任何列都将从数据集中删除;可以通过设置“remainder”参数来更改此行为。

remainder=’passthrough’设置为意味着列表中未指定的列将不经过转换而通过,而不是被删除。

例如,如果第0列和第1列是数值型的,而第2列和第3列是分类型的,并且我们只想转换分类数据并将数值列不变地传递,我们可以定义ColumnTransformer如下:

定义转换器后,就可以使用它来转换数据集。

例如

ColumnTransformer也可以用于Pipeline中,在拟合模型到准备好的数据之前,选择性地准备数据集的列。

这是最可能的用例,因为它确保在拟合模型时以及在进行预测时(例如,通过交叉验证在测试数据集上评估模型或将来对新数据进行预测时)自动执行转换。

例如

现在我们熟悉了如何配置和使用ColumnTransformer,让我们来看一个实际的示例。

Abalone回归数据集的数据准备

鲍鱼数据集是一个标准的机器学习问题,它涉及根据鲍鱼的测量值来预测鲍鱼的年龄。

您可以在此处下载数据集并了解更多信息

该数据集有4177个样本,8个输入变量,目标变量是整数。

通过预测平均值,一个朴素模型可以获得约2.363(标准差0.092)的平均绝对误差(MAE),该误差通过10折交叉验证进行评估。

我们可以将其建模为具有支持向量机模型(SVR)的回归预测建模问题。

查看数据,您可以看到前几行如下:

我们可以看到第一列是分类的,其余列是数值的。

我们可以对第一列进行独热编码,对剩余的数值列进行归一化,这可以通过ColumnTransformer实现。

首先,我们需要加载数据集。我们可以使用read_csv() Pandas函数直接从URL加载数据集,然后将数据分为两个数据框:一个用于输入,一个用于输出。

加载数据集的完整示例列在下面。

注意:如果您在从URL加载数据集时遇到问题,可以下载名为“abalone.csv”的CSV文件,并将其放在Python文件所在的同一目录中,然后按如下方式修改read_csv()的调用:

运行示例,我们可以看到数据集已正确加载,并分为八个输入列和一个目标列。

接下来,我们可以使用select_dtypes()函数选择匹配不同数据类型的列索引。

我们感兴趣的是在Pandas中标记为‘float64’或‘int64’的数值列的列表,以及在Pandas中标记为‘object’或‘bool’类型的分类列的列表。

然后,我们可以在ColumnTransformer中使用这些列表对分类变量进行独热编码,这些变量应该只有第一列。

我们还可以使用数值列列表来归一化剩余数据。

接下来,我们可以定义我们的SVR模型,并定义一个Pipeline,该Pipeline首先使用ColumnTransformer,然后拟合模型到准备好的数据集。

最后,我们可以使用10折交叉验证来评估模型,并计算平均绝对误差,该误差在管道的10次评估中进行平均。

将所有这些结合起来,完整的示例如下所示。

运行该示例,使用10折交叉验证评估数据准备管道。

注意:您的结果可能因算法或评估程序的随机性或数值精度的差异而有所不同。请考虑运行示例几次并比较平均结果。

在这种情况下,我们实现了约1.4的平均MAE,这比基线得分2.3要好。

您现在有了一个在具有混合数据类型的 数据集上使用ColumnTransformer的模板,您可以在未来的项目中进行使用和调整。

进一步阅读

如果您想深入了解,本节提供了更多关于该主题的资源。

API

总结

在本教程中,您学习了如何使用ColumnTransformer将数据转换选择性地应用于具有混合数据类型的 数据集中的列。

具体来说,你学到了:

  • 使用数据转换处理具有混合数据类型的 数据集时所面临的挑战。
  • 如何定义、拟合和使用ColumnTransformer来选择性地将数据转换应用于列。
  • 如何处理具有混合数据类型的实际数据集,并使用ColumnTransformer将不同的转换应用于分类和数值数据列。

你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。

掌握现代数据准备!

Data Preparation for Machine Learning

在几分钟内准备好您的机器学习数据

...只需几行python代码

在我的新电子书中探索如何实现
机器学习数据准备

它提供**自学教程**,并附有关于以下内容的**完整工作代码**:
*特征选择*、*RFE*、*数据清洗*、*数据转换*、*缩放*、*降维*,以及更多...

将现代数据准备技术引入
您的机器学习项目


查看内容

69条对*《如何使用ColumnTransformer进行数据准备》*的回复

  1. Venkatesh Gandi 2020年1月20日 上午7:06 #

    哇,这个带有管道的功能似乎很神奇。我真的很想知道管道能做什么(管道的力量)。我看了管道(pipeline())的文档,它不像你解释得那么简单 🙂 我真的很喜欢你的解释方式。你有没有写过关于管道介绍的博客,可以从简单的开始解释复杂的管道?如果有,请分享链接。

    如果没有,您能否分享一些我们可以了解更多关于sklearn管道的参考资料?

  2. rahul malik 2020年4月22日 上午8:18 #

    我跟着你的例子,我有一个数据集,有3个特征列和1个标签列,它们都是分类的,没有数值。我打印了X,y,它们似乎是正确的,但输出是MAE: nan (nan)。你能建议我哪里做错了什么吗?

    • Jason Brownlee 2020年4月22日 上午10:12 #

      也许检查一下你的输入数据中是否有nan?

  3. Grzegorz Kępisty 2020年4月30日 下午4:47 #

    sklearn的一个非常有用的实用程序!

    我想知道如何获取可以应用于ColumnTransformer的转换的完整列表,我找到的最佳参考如下:
    https://scikit-learn.cn/stable/modules/preprocessing.html#
    你是否知道这个主题更广泛的来源?

    此致!

    • Jason Brownlee 2020年5月1日 上午6:32 #

      这是一个很好的开始。

      不,但我写了和计划了很多关于这个主题的教程。准备好!

  4. ashar138 2020年5月19日 下午2:57 #

    喜欢这个。让你意识到ColumnTransformer能让你的生活变得多么轻松。
    但我没弄懂的是,转换是按顺序应用的吗?

    因为我想先使用Imputer,然后进行归一化/独热编码。


    transformers =[ (imputer cat) , (imputer num) , (normalize num), (onehot cat)]

    我假设这可以做到?

    • Jason Brownlee 2020年5月20日 上午6:19 #

      当然可以!

      是的,您可以对不同子集的功能应用不同的序列。

      • ashar138 2020年5月20日 下午5:58 #

        结果证明你不能 :\)
        即使Imputer是第一个转换,它也会抛出错误,处理NaN值。
        需要先使用Imputer,然后再次使用Minmax

        • Jason Brownlee 2020年5月21日 上午6:12 #

          我在这里提供了一个关于使用columntransformer进行缺失值填充和顺序转换的例子
          https://machinelearning.org.cn/results-for-standard-classification-and-regression-machine-learning-datasets/

          特别是自动导入示例。

          • ashar138 2020年5月26日 下午1:12 #

            好的,我现在有点糊涂了…

            所以我们做的是创建一个Pipeline,将其馈送到ColumnTransformer,然后再将其馈送到另一个Pipeline。

            但是,那么我们还需要Pipeline做什么呢?例如,

            trans1 = ColumnTransformer([('catimp', SimpleImputer(strategy='most_frequent'), cat_var),
            ('enc', OneHotEncoder(handle_unknown='ignore'), cat_var),
            ('imp', SimpleImputer(strategy= 'median'),num_var )],
            remainder='passthrough')

            steps = [('c', Pipeline(steps=[('s',SimpleImputer(strategy='most_frequent')),
            ('oe',OneHotEncoder(handle_unknown='ignore'))]), cat_var),
            ('n', SimpleImputer(strategy='median'), num_var)]

            trans2 = ColumnTransformer(transformers=steps, remainder='passthrough')

            为什么trans1和trans2不同?

          • Jason Brownlee 2020年5月26日 下午1:24 #

            好问题。你不需要使用它,它只是我们拥有的另一种工具。

            每组特征都可以用一个管道进行准备。

            我们还可以有一个主管道,其中包含数据准备和建模 — 然后使用交叉验证来评估它。

          • ashar138 2020年5月26日 下午1:45 #

            我明白了…我需要花更多时间来理解它…

            实际上,我下面分享的例子产生了2个不同的结果。不使用管道的那个会报NaN错误。也许管道对于按顺序执行是必需的,而ColumnTransformer不这样做?

            我观察到的另一个问题是在转换训练和验证数据集时。
            结果的训练数据集返回一个scipy.sparse.csr.csr_matrix,而验证数据只返回一个ndarray。

            我减少了sparse_threshold,但这会在预测验证数据集时导致特征不匹配。

            总之,我已经打扰您够了,我会想办法解决的 🙂

          • Jason Brownlee 2020年5月27日 上午7:41 #

            像独热编码这样的转换默认会创建一个稀疏矩阵,您可以设置一个参数强制它们创建一个密集矩阵。

  5. Manideep 2020年5月27日 上午4:45 #

    如果我必须为一组分类列使用简单的 imputter 和 onehotencoder,您能告诉我该怎么做吗?

  6. MS 2020年7月1日 上午1:47 #

    numerical_x = reduced_df.select_dtypes(include=[‘int64’, ‘float64’]).columns
    categorical_x = reduced_df.select_dtypes(include=[‘object’, ‘bool’]).columns

    t = [((‘le’,LabelEncoder(),categorical_x),(‘ohe’,OneHotEncoder(),categorical_x),
    (‘catimp’, SimpleImputer(strategy=’most_frequent’),categorical_x)),
    ((‘num’,SimpleImputer(strategy=’median’),numerical_x),(‘sts’,StandardScaler(), numerical_x))]

    col_transform = ColumnTransformer(transformers=t)
    dt= DecisionTreeClassifier()
    pl= Pipeline(steps=[(‘prep’,col_transform), (‘dt’, dt)])
    pl.fit(reduced_df,y_train)
    pl.score(reduced_df,y_train)

    上面的代码出现此错误=>

    names, transformers, _ = zip(*self.transformers)
    ValueError: not enough values to unpack (expected 3, got 2)

    你能帮帮我吗?

  7. Volkan Yurtseven 2020年7月1日 上午5:50 #

    在“这是最有可能的用例,因为它确保了在拟合模型和进行预测时,转换会自动在原始数据上执行,例如在通过交叉验证在测试数据集上评估模型或将来对新数据进行预测时..”这一段之后,您说
    model.fit(train_X, train_y),
    但我认为应该是
    pipeline.fit(train_X, train_y)

  8. Navish 2020年7月11日 上午12:04 #

    你好,
    感谢这篇精彩的文章。
    有三个澄清

    1. 如果我需要在预处理中执行多于一个的转换(例如,填充然后转换),我是否需要将每个单独的组合放入一个管道中?它们不能直接放入列转换器吗?

    2. 我想我们也可以对数据框进行多次拆分?不只是两次?例如,如果我们想对某些分类列进行标签编码,对其他分类列进行“most_frequent”填充和独热编码,对一些数值列进行“median”填充,然后对其他数值列进行缩放?

    3. 最后,如果我们想删除某些列 – 是否建议在任何管道/转换器之外首先这样做?

    • Jason Brownlee 2020年7月11日 上午6:17 #

      正确,一个管道。

      是的,任何您喜欢的子集。

      嗯,好问题。大概 – 凭直觉来说是合理的。

  9. Navish 2020年7月12日 上午4:06 #

    谢谢您的回复。

    我一直在阅读 Pipeline 和 ColumnTransformer 的全部语法。
    其中有一个明确的 drop 选项,您可以在其中指定要删除的特定列。为了使验证/测试/生产数据预处理更容易,使用该选项可能更好。
    这样您就可以在删除要保留的列时使用“passthrough”选项。

  10. Nalla 2020年7月27日 上午2:13 #

    嗨,Jason,

    我有一些澄清。

    1. 如果我正在使用 SMOTE,并且我更喜欢使用 imblearn pipeline。
    在 imblearn pipeline 中是否有方法进行 imputer,或者我们应该依赖 sklearn pipeline?

    2. 如何集成 imblearn pipeline 和 sklearn,或者这样做是否明智?

    谢谢,
    Nalla

    • Jason Brownlee 2020年7月27日 上午5:50 #

      是的,您可以在 imbalanced learn pipeline 中使用任何您喜欢的数据预处理。

      像使用 sklearn pipeline 一样使用 imbalanced learn pipeline 即可。

      • Nalla 2020年7月29日 上午1:56 #

        非常感谢您的澄清,但我还想详细了解一些事情。

        sklearn 和 imblearn pipeline 之间有什么区别或相似之处?

        据我所知 – SMOTE 只能在 imblearn pipeline 中执行,不能在 sklearn 中执行。
        但其他功能如何工作?
        您能否提供一个例子来说明两者(相似性和差异性)- 如果您有的话。

        以及将上述两个管道集成的可能性(即)
        imblearn 和 sklearn?
        上述是否可能?例如,如果我注意到数据集不平衡,因此我决定使用 imblearn pipeline – 是否有任何方法可以添加 sklearn pipeline 进行 Imputer 或在 imblearn 中进行 SMOTE 后的缩放?

        • Jason Brownlee 2020年7月29日 上午5:54 #

          imblearn pipeline 还允许您使用数据采样方法 – 例如,可以更改行数的方法。

          这是唯一的显著区别。

  11. Binyamin 2020年8月23日 下午11:39 #

    我正在寻找应用缩放器和独热编码,这样我就不会丢失我的数据框的索引。我偶然发现了您的博客,其中演示了列转换器,这正是我需要的。然而,这种方法似乎有一个缺点,即您会丢失特征的名称。Column transformer 返回一个稀疏矩阵,经过缩放和编码。

    有没有办法检索列名并重新创建数据帧?这在模型解释时会很有帮助。

    感谢您的时间,您的博客太棒了

    • Jason Brownlee 2020年8月24日 上午6:25 #

      谢谢!

      将特征名称保存在单独的数组中。不需要将该信息放在模型中。

  12. Diego 2020年9月13日 上午2:34 #

    亲爱的 Jason,

    假设我们有一个包含 5 个特征的数据集。
    我们仅将 column_transformer 应用于第 2 和第 3 列,并将其余列传递。
    应用管道后,新列的顺序将是:2,3,1,4,5(首先是经过转换的列,然后是其余的列)。
    最后,数据集的列顺序发生了变化,为什么?有没有办法避免这种情况?

    非常感谢

    • Jason Brownlee 2020年9月13日 上午6:09 #

      有关系吗?您只需要模型的预测输出。行顺序不变。

      • Diego 2020年9月14日 上午12:42 #

        确实是个好观点:)

        我认为这只在其中一个步骤是 RFE 或 RFECV,并且我们想知道选择了哪些特征时才重要。

        谢谢!!

        • Jason Brownlee 2020年9月14日 上午6:50 #

          抱歉 Diego,也许我遗漏了什么。这会如何重要?

          • Diego 2020年9月15日 上午4:40 #

            嗨,Jason,
            抱歉,也许我没说清楚。

            假设我们有特征 1、2、3、4 和 5 作为管道的一部分。
            步骤 1 仅对特征 4 和 5 使用 MinMaxScaler(使用 column_transformer)。
            步骤 2 是 RFE 或 RFECV。
            步骤 3 是一个模型。

            在步骤 1 之后,新的列顺序是 4,5,1,2,3。
            如果步骤 2(RFE)说它只保留了前两列:选择了哪两列?4 和 5,还是 1 和 2?
            我认为是 4 和 5,因为那时列已经被重新排序了。

            这发生在我身上,我不敢相信 RFE 会选择那些特征,但当我想到它们被重新排序了,那就说得通了。

            我希望现在更清楚了。
            非常感谢您的帮助。

          • Jason Brownlee 2020年9月15日 上午5:29 #

            我看不到任何问题。

            RFE 选择什么并不重要,因为您正在评估建模管道。您正在回答“此管道的性能如何?”这个问题,而不是“RFE 会选择哪些特征?”。如果您想知道后者,可以在隔离的情况下研究它。

            您允许 RFE 根据训练数据选择特征,就像您允许模型根据训练数据拟合内部参数一样。您不会问“模型内部系数的值是什么?”,因为当您关注模型技能时,这实际上并不重要。

            话虽如此,您仍然可以访问管道中的 RFE 模型或保留对其的引用,并报告从单次运行中选择的特征。

      • Richard 2021年3月10日 上午5:22 #

        Hi, 一个重要的情况是 LightGBM 和分类特征。使用 LightGBM 的 sklearn API,分类特征作为 .fit() 的参数指定。由于 DataFrame 在转换期间(例如使用 StandardScaler())被转换为 numpy 数组,因此最好使用整数列表指定分类特征。列的重新排序会成为一个“难以发现”的 bug。

  13. Diego 2020年9月16日 下午11:33 #

    嗨,Jason,

    是的。你说得对。我应该比较一个完整的管道与另一个管道,而不考虑选择了哪些变量。
    从那个角度来看,这是有道理的。

    感谢您的解释和奉献!

  14. Keone 2020年11月18日 上午11:37 #

    有没有办法通过 ColumnTransformer 进行 inverse_transform?

  15. Zineb 2020年12月30日 下午8:54 #

    嗨,Jason,

    在第二段的第二个例子中,我认为您想要 pipeline.fit_transform 而不是 scaler.fit_transform!

    • Zineb 2020年12月30日 下午8:56 #

      “转换不同数据类型的挑战”部分

    • Jason Brownlee 2020年12月31日 上午5:25 #

      谢谢!已修复。

  16. SULAIMAN KHAN 2021年2月15日 下午10:38 #

    model.fit(X_train,y_train,validation_data=(X_test,y_test),epochs=10,batch_size=64)
    #############
    InvalidArgumentError: 找到 2 个根错误。
    (0) Invalid argument: indices[55,1] = 170355 is not in [0, 5000)
    [[node sequential_3/embedding_3/embedding_lookup (defined at :9) ]]
    (1) Invalid argument: indices[55,1] = 170355 is not in [0, 5000)
    [[node sequential_3/embedding_3/embedding_lookup (defined at :9) ]]
    [[Adam/Adam/update/AssignSubVariableOp/_35]]
    0 次成功操作。
    0 derived errors ignored. [Op:__inference_train_function_12567]

    Errors may have originated from an input operation.
    Input Source operations connected to node sequential_3/embedding_3/embedding_lookup
    sequential_3/embedding_3/embedding_lookup/11365 (defined at /usr/lib/python3.6/contextlib.py:81)

    Input Source operations connected to node sequential_3/embedding_3/embedding_lookup
    sequential_3/embedding_3/embedding_lookup/11365 (defined at /usr/lib/python3.6/contextlib.py:81)

    Function call stack
    train_function -> train_function

    如何修复此错误?

  17. SULAIMAN KHAN 2021年2月15日 下午10:41 #

    我组合了我所有的数值列和分类列。它们在损失函数中出现错误。

    • Jason Brownlee 2021年2月16日 上午6:06 #

      也许检查一下数据预处理步骤的输出,以确认数据的形状和值符合您的预期?

  18. Leen 2021年4月17日 下午2:07 #

    你好 Jason,

    很棒的教程。能否将 Column Transformer 作为 param_grid 的一部分?我想使用分类编码机制转换一些列,但有多种机制值得关注:Target Encoding、Weights of Evidence 编码、catboost 编码等。因此,我想让每种编码机制成为 grid search param_grid 中的一个超参数。您认为这可能吗?

    • Jason Brownlee 2021年4月18日 上午5:51 #

      嗯,您可能需要手动运行您的网格搜索循环。

  19. JG 2021年4月19日 下午6:05 #

    嗨,Jason,

    我认为 ColumnTransformer() 是一个非常强大的模块,可以全局应用,但可以区分对数据集中每个特征的转换。
    我认为它不仅在 Pipeline 步骤内有用,而且可以作为独立模块使用,以便对特征进行内部分析。

    非常感谢这个教程!
    此致,

  20. Leo 2021年5月7日 上午11:48 #

    谢谢 Jason!

    我很难将 columnTransform(或 pipeline)应用于以下一组特征:(i) 一个基于 NLP 的特征(需要 count vectorizer 或 tf-idf fit-transform);以及 (ii) 其他需要标准缩放的简单特征 – 您是否曾遇到过类似问题并且知道如何解决?

    count vectorizer(或 tf-idf)输出稀疏矩阵(而其余保持为 pandas 系列/df)……我应该对它们都使用 numpy 数组吗?

    再次感谢!

    • Jason Brownlee 2021年5月8日 上午6:31 #

      也许让您的 columntransformer 与最小的数据集或管道一起工作,然后逐步添加元素,直到您找出问题的原因。

  21. Leo 2021年5月9日 上午6:09 #

    使用 FeatureUnion 和 ColumnSelector(来自 mlxtended)已解决!

    cat_pipe = Pipeline([(‘selector’, ColumnSelector(cat_features)),
    (‘imputer’, SimpleImputer(strategy=’constant’, fill_value=’missing’)),
    (‘encoder’, OneHotEncoder(handle_unknown=’ignore’, sparse=False))])

    num_pipe = Pipeline([(‘selector’, ColumnSelector(num_features)),
    (‘imputer’, SimpleImputer(strategy=’mean’)),
    (‘scaler’, MinMaxScaler())])

    text_pipe = Pipeline([(‘selector’, ColumnSelector(text_features,drop_axis=True)),
    (‘tf’, TfidfVectorizer(preprocessor=lambda x: x,tokenizer=lambda x: x))])

    preprocessor = FeatureUnion(transformer_list=[(‘cat’, cat_pipe),
    (‘num’, num_pipe),
    (‘nlp’, text_pipe)])

  22. JG 2021年7月3日 下午9:43 #

    嗨,Jason,

    感谢教程!

    永远是一份有价值的代码,可以进行其他实验!

    我的实验

    ColumnTransformer() API 是否可以按列或特征执行不同的转换(太棒了!)并且有一个 remainder= 参数可以设置为“passthrough”。好的。

    但我对 Sklearn API 的抱怨是转换是连续执行的,所以如果你决定例如先执行第 3 列的转换,然后再执行第 2 列…那么第 2 列就被第 3 列转换后的结果替换了。

    此外,即使你决定对一个备用列进行“passthrough”而不进行转换,在两次交替的列转换之间,ColumnTransformer() 会用后续的转换替换之前的列,并将(未转换的)列留在列系列的末尾……

    这种行为使得 ColumnTransformer 的工作方式非常令人困惑,并且会浪费你时间处理一些可以通过良好 API 设计来避免的问题 :-((
    除非有其他参数可以指定以避免这种错误的列修改。

    总之,正如我之前所说,感谢您的代码,您可以预见这种行为。

    此致,

    • Jason Brownlee 2021年7月4日 上午6:03 #

      同意。

      将 ColumnTransformers 堆叠到管道中并按顺序执行一个子集/操作可能更容易。

  23. Vishwanath reddy 2021年7月22日 上午4:43 #

    非常感谢
    非常有帮助

  24. Cyanide Lancer 2021年12月22日 下午9:48 #

    一个基本问题,为什么您没有考虑将数据集分成训练和测试的特征和标签?

  25. Bobby K 2022年5月8日 下午1:13 #

    好东西!

    您成功地在复杂性和可用性之间取得了良好的平衡,结果易于阅读,易于遵循的示例,可以打开一个更广阔的领域供人们按照自己的节奏探索。

    • James Carmichael 2022年5月9日 上午11:03 #

      感谢您的反馈和支持 Bobby!

  26. Giovanna 2023年4月30日 上午1:33 #

    你好,

    我一直在到处寻找答案,但运气不佳。我正在训练一个管道,该管道由数值和分类数据转换管道以及一个列转换器管道组成。

    num_pipeline = Pipeline([
    (‘std_scaler’, StandardScaler()),
    (‘imputer’, IterativeImputer(random_state=seed, max_iter=100,
    estimator=DecisionTreeRegressor(max_features=’sqrt’,
    random_state=seed)))])

    cat_pipeline = Pipeline([(‘imputer’, SimpleImputer(strategy=’constant’, fill_value=’Missing’)),
    (‘encoding’, OneHotEncoder(handle_unknown=’ignore’, sparse=False))])

    data_pipeline = ColumnTransformer([
    (‘numerical’, num_pipeline, num_feats),
    (‘categorical’, cat_pipeline, cat_feats)])

    我遇到的问题是,我将使用此 data_pipeline 对训练数据进行 fit_transform,并使用 joblib dump 保存此训练好的管道,以便使用 .transform() 对验证数据进行转换,以及在生产环境中进行推理。到目前为止一切顺利,但是,当我的训练数据比较大的时候,保存的文件会非常大,加载时间也很长(例如:训练了 140 万行,保存的管道为 49GB)。

    我想知道我有什么方法可以使这个保存的训练数据转换管道更轻量化。有什么建议吗?

  27. Pascal 2023年8月7日 下午10:20 #

    嗨,感谢您写下这些示例。
    在您的代码示例的最后一行:我不太确定,但在这里使用

    std(scores)

    可以吗?

    这是因为值之前设置为绝对值。因此,由于“零边界”,值之间的距离可能会有所不同。所以如果我之前有一个值 -0.1,均值为 0.1,那么距离 = 0.2;但在我应用 absolute() 之后,该值现在是 0.1,与 0.1 的距离为 0。这会影响标准差,不是吗?

    所以您需要在应用 absolute() 之前,从分数中获取标准差,或者我错了?

  28. Pascal 2023年8月10日 下午7:14 #

    这并没有回答我的问题……看看这个

    在取绝对值之前的数值序列
    0,1 -0,1 0,5 -0,2 -0,5 -0,7 0,8
    这里的标准差:0.530498418

    在取绝对值之前的数值序列
    0,1 0,1 0,5 0,2 0,5 0,7 0,8
    这里的标准差:0.285356919

  29. neelanshuni 2024年3月3日 下午9:57 #

    嗨,我在 fit_transform 时遇到一个错误,这是我的代码

    import pandas as pd
    import numpy as np
    from sklearn.preprocessing import OneHotEncoder, StandardScaler, OrdinalEncoder
    from sklearn.compose import ColumnTransformer
    import category_encoders as ce

    df = pd.read_csv(‘gurgaon_properties_post_feature_selection_v2.csv’)
    X=df.drop(columns=[‘price’])
    Y_t=np.log1p(Y)

    t = [
    (‘num’, StandardScaler(), [‘bedRoom’, ‘bathroom’, ‘built_up_area’, ‘servant room’, ‘store room’]),
    (‘cat’, OrdinalEncoder(), columns_to_encode),
    (‘cat1′,OneHotEncoder(drop=’first’,sparse_output=False),[‘agePossession’]),
    (‘target_enc’, ce.TargetEncoder(), [‘sector’])
    ]
    transformer = ColumnTransformer(transformers=t, remainder=’passthrough’)
    transformer.fit_transform(X)

    得到以下错误
    —————————————————————————
    TypeError Traceback (most recent call last)
    Cell In[40], line 1
    —-> 1 transformer.fit_transform(X)

    File ~\anaconda3\lib\site-packages\sklearn\utils\_set_output.py:142, in _wrap_method_output..wrapped(self, X, *args, **kwargs)
    140 @wraps(f)
    141 def wrapped(self, X, *args, **kwargs)
    –> 142 data_to_wrap = f(self, X, *args, **kwargs)
    143 if isinstance(data_to_wrap, tuple)
    144 # only wrap the first output for cross decomposition
    145 return (
    146 _wrap_data_with_container(method, data_to_wrap[0], X, self),
    147 *data_to_wrap[1:],
    148 )

    File ~\anaconda3\lib\site-packages\sklearn\compose\_column_transformer.py:727, in ColumnTransformer.fit_transform(self, X, y)
    724 self._validate_column_callables(X)
    725 self._validate_remainder(X)
    –> 727 result = self._fit_transform(X, y, _fit_transform_one)
    729 if not result
    730 self._update_fitted_transformers([])

    File ~\anaconda3\lib\site-packages\sklearn\compose\_column_transformer.py:658, in ColumnTransformer._fit_transform(self, X, y, func, fitted, column_as_strings)
    652 transformers = list(
    653 self._iter(
    654 fitted=fitted, replace_strings=True, column_as_strings=column_as_strings
    655 )
    656 )
    657 try
    –> 658 return Parallel(n_jobs=self.n_jobs)(
    659 delayed(func)(
    660 transformer=clone(trans) if not fitted else trans,
    661 X=_safe_indexing(X, column, axis=1),
    662 y=y,
    663 weight=weight,
    664 message_clsname=”ColumnTransformer”,
    665 message=self._log_message(name, idx, len(transformers)),
    666 )
    667 for idx, (name, trans, column, weight) in enumerate(transformers, 1)
    668 )
    669 except ValueError as e
    670 if “Expected 2D array, got 1D array instead” in str(e)

    File ~\anaconda3\lib\site-packages\sklearn\utils\parallel.py:63, in Parallel.__call__(self, iterable)
    58 config = get_config()
    59 iterable_with_config = (
    60 (_with_config(delayed_func, config), args, kwargs)
    61 for delayed_func, args, kwargs in iterable
    62 )
    –> 63 return super().__call__(iterable_with_config)

    File ~\anaconda3\lib\site-packages\joblib\parallel.py:1051, in Parallel.__call__(self, iterable)
    1048 if self.dispatch_one_batch(iterator)
    1049 self._iterating = self._original_iterator is not None
    -> 1051 while self.dispatch_one_batch(iterator)
    1052 pass
    1054 if pre_dispatch == “all” or n_jobs == 1
    1055 # The iterable was consumed all at once by the above for loop.
    1056 # No need to wait for async callbacks to trigger to
    1057 # consumption.

    File ~\anaconda3\lib\site-packages\joblib\parallel.py:864, in Parallel.dispatch_one_batch(self, iterator)
    862 return False
    863 else
    –> 864 self._dispatch(tasks)
    865 return True

    File ~\anaconda3\lib\site-packages\joblib\parallel.py:782, in Parallel._dispatch(self, batch)
    780 with self._lock
    781 job_idx = len(self._jobs)
    –> 782 job = self._backend.apply_async(batch, callback=cb)
    783 # A job can complete so quickly than its callback is
    784 # called before we get here, causing self._jobs to
    785 # grow. To ensure correct results ordering, .insert is
    786 # used (rather than .append) in the following line
    787 self._jobs.insert(job_idx, job)

    File ~\anaconda3\lib\site-packages\joblib\_parallel_backends.py:208, in SequentialBackend.apply_async(self, func, callback=None)
    206 def apply_async(self, func, callback=None)
    207 “””Schedule a func to be run”””
    –> 208 result = ImmediateResult(func)
    209 if callback
    210 callback(result)

    File ~\anaconda3\lib\site-packages\joblib\_parallel_backends.py:572, in ImmediateResult.__init__(self, batch)
    569 def __init__(self, batch)
    569 # Don’t delay the application, to avoid keeping the input
    570 # arguments in memory
    –> 571 self.results = batch()

    File ~\anaconda3\lib\site-packages\joblib\parallel.py:263, in BatchedCalls.__call__(self)
    259 def __call__(self)
    260 # Set the default nested backend to self._backend but do not set the
    261 # change the default number of processes to -1
    262 with parallel_backend(self._backend, n_jobs=self._n_jobs)
    –> 263 return [func(*args, **kwargs)
    264 for func, args, kwargs in self.items]

    File ~\anaconda3\lib\site-packages\joblib\parallel.py:263, in (.0)
    259 def __call__(self)
    260 # Set the default nested backend to self._backend but do not set the
    261 # change the default number of processes to -1
    262 with parallel_backend(self._backend, n_jobs=self._n_jobs)
    –> 263 return [func(*args, **kwargs)
    264 for func, args, kwargs in self.items]

    文件~\anaconda3\lib\site-packages\sklearn\utils\parallel.py:123, in _FuncWrapper.__call__(self, *args, **kwargs)
    121 config = {}
    122 with config_context(**config)
    –> 123 return self.function(*args, **kwargs)

    文件~\anaconda3\lib\site-packages\sklearn\pipeline.py:893, in _fit_transform_one(transformer, X, y, weight, message_clsname, message, **fit_params)
    891 with _print_elapsed_time(message_clsname, message)
    892 if hasattr(transformer, “fit_transform”)
    –> 893 res = transformer.fit_transform(X, y, **fit_params)
    894 else
    895 res = transformer.fit(X, y, **fit_params).transform(X)

    File ~\anaconda3\lib\site-packages\sklearn\utils\_set_output.py:142, in _wrap_method_output..wrapped(self, X, *args, **kwargs)
    140 @wraps(f)
    141 def wrapped(self, X, *args, **kwargs)
    –> 142 data_to_wrap = f(self, X, *args, **kwargs)
    143 if isinstance(data_to_wrap, tuple)
    144 # only wrap the first output for cross decomposition
    145 return (
    146 _wrap_data_with_container(method, data_to_wrap[0], X, self),
    147 *data_to_wrap[1:],
    148 )

    文件~\anaconda3\lib\site-packages\category_encoders\utils.py:458, in SupervisedTransformerMixin.fit_transform(self, X, y, **fit_params)
    451 “””
    452 利用目标值的编码器必须确保训练数据是通过
    453 transform(X, y)
    454 进行转换,而不是通过
    455 transform(X)
    456 “””
    457 if y is None
    –> 458 raise TypeError(‘fit_transform() 缺少参数:“y”’)
    459 return self.fit(X, y, **fit_params).transform(X, y)

    TypeError: fit_transform() 缺少参数:“y”

    • James Carmichael 2024 年 3 月 4 日 上午 1:26 #

      您好 neelanshuni…您可能希望在 Google Colab 中尝试您的实现,以确定您的本地 Python 环境是否存在问题。否则,请确保没有因复制粘贴代码而导致的问题。

留下回复

Machine Learning Mastery 是 Guiding Tech Media 的一部分,Guiding Tech Media 是一家领先的数字媒体出版商,专注于帮助人们了解技术。访问我们的公司网站以了解更多关于我们的使命和团队的信息。