Scikit-learn: 熊猫进,熊猫出?

创建于 2015-10-22  ·  59评论  ·  资料来源: scikit-learn/scikit-learn

目前,可以使用 Pandas 数据框作为大多数 sklearn 拟合/预测/转换方法的输入,但您会得到一个 numpy 数组。 能够以您放入的相同格式获取数据真是太好了。

这并不是很简单,因为如果您的 Dataframe 包含非数字列,那么中间的 numpy 数组将导致 sklearn 失败,因为它们将是dtype=object ,而不是dtype=float 。 这可以通过使用 Dataframe->ndarray 转换器来解决,它将非数字数据映射到数字数据(例如表示类/类别的整数)。 sklearn-pandas已经这样做了,虽然它目前没有inverse_transform ,但应该不难添加。

我觉得像这样的转换在 sklearn 中_真的_很有用 - 这是任何处理具有多种数据类型的数据集的人都会发现有用的东西。 将这样的东西放入 sklearn 需要什么?

最有用的评论

当给定DataFrame s 时,我所有的转换器都返回DataFrame DataFrame s。
当我将 300 列DataFrame输入Pipeline并收到 500 列ndarray ,我无法有效地从中学到很多东西,例如feature_selection ,因为我没有列名了。 例如,如果mutual_info_classif告诉我只有第 30 列和第 75 列很重要,那么我无法弄清楚如何简化我的原始Pipeline用于生产。
因此,将我的数据保存在DataFrame对我的用例至关重要。
谢谢你。

所有59条评论

Scikit-learn 旨在使用非常通用的输入格式。 也许 scikit-learn 周围的世界发生了很大变化,因为 Pandas 集成变得更加重要。 它仍然可以在很大程度上由第三方包装器提供。

但除了更广泛的问题之外,我认为您应该尝试举例说明标准估算器的 Pandas 友好输出将如何不同并对可用性产生影响。 我能想到的例子:

  • 所有方法都可以从输入复制索引
  • 转换器应该输出适当命名的列
  • multiclass predict_proba 可以用类名标记列

是的,在我的头顶上:

  • 该索引可能非常有用,例如用于创建定时滞后变量(例如滞后 1 天,在有一些缺失天数的每日数据上)
  • sklearn 回归器可以透明地与分类数据一起使用(传递混合数据框,使用 LabelBinarizer 转换分类列,逆变换返回)。
  • sklearn-pandas 已经提供了一个很好的接口,它允许你传递一个数据帧,并且只使用数据的一个子集,并任意转换单个列。

如果这一切都在转换中,那么它不会真正影响 sklearn 默认情况下的工作方式。

我不认为它可以很好地实现为变压器。 这将是
一个或多个元估计器或混合。 我认为他们最初应该
外部实施并证明有用

从2015年10月22日17:40,naught101 [email protected]写道:

是的,在我的头顶上:

  • 该索引非常有用,例如用于创建定时滞后
    变量(例如滞后 1 天,在有一些缺失天数的每日数据上)
  • sklearn 回归器可以透明地用于分类数据
    (传递混合数据框,使用 LabelBinarizer 转换分类列,
    inverse_transform 它回来)。
  • sklearn-pandas 已经提供了一个很好的界面,允许你
    传递一个数据帧,并且只使用数据的一个子集,并且任意
    转换单个列。

如果这一切都在转换中,那么它并不会真正影响 sklearn
默认情况下工作。


直接回复此邮件或在 GitHub 上查看
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -150123228
.

使“pandas in”更好是列转换器 PR #3886 背后的想法。 也许我应该更仔细地研究 sklearn-pandas 已经在做什么。 我不完全确定最好的前进方式是什么。

另一件不错的事情是在转换中保留列名/在进行特征选择时选择它们。 我没有发现我们现在讨论的问题。 也许@jnothman记得。 我真的很喜欢那个,尽管它需要对输入验证进行大手术才能保留列名:-/

相关 #4196

虽然它需要大手术和输入验证才能
保留列名:-/

不仅是输入验证:每个转换都必须描述它的内容
对输入列进行处理。

是的,但我认为那会很好;)

一个问题是,我们是否只希望在管道中或在任何地方都可以这样做。 如果我们将其限制为管道,则输入验证手术将不会那么大。 但我不确定它会有多大用处。

你总是可以做一个只有一件事的管道,对吧? 因此,我们通过首先限制为管道来处理所有情况(尽管它在 1 个对象的限制中很棘手)...

+1。 从管道开始听起来不错,并在下一步中涵盖所有变压器。

我还有一个带有 pandas 和 sklearn 集成的 impl,它可以通过inverse_transform恢复列信息(虽然很脏......)

http://pandas-ml.readthedocs.org/en/latest/sklearn.html

• 索引非常有用,例如用于创建定时滞后变量
(例如滞后 1 天,在缺少一些天数的每日数据上)

我有点笨,但不是在谈论样本中的某些内容
这里的方向,而不是特征方向?

• sklearn 回归器可以透明地用于分类数据(通过
混合数据框,使用 LabelBinarizer 转换分类列,
将其逆变换回来)。

• sklearn-pandas 已经提供了一个很好的界面,允许您通过
dataframe,只使用数据的一个子集,任意变换
个别列。

好的,但这只是一个接收 Pandas 的转换器的级别,
并给出一个数据矩阵,不是吗? 而不是尝试一个
修改 scikit-learn 的所有对象(这是一个有风险的
努力),我们可以先实现这个转换器(我相信
@amueller有这个想法)。

这里的样本方向,而不是特征方向?

是的。

好的,但这一切都在一个变压器的层面上,它接收 Pandas,并给出一个数据矩阵,不是吗?

是的,这就是我开始的想法。 我会对处理Xy作为数据帧的包装器感到非常满意。 我看不出有什么明显的理由去搞砸 sklearn 的内部结构。

OK, but that's all at the level of one transformer that takes Pandas in,
and gives a data matrix out, isn't it?

是的,这就是我开始的想法。 我会很高兴
将 X 和 y 作为数据帧处理的包装器。 我没有看到明显的原因
搞砸sklearn的内部结构。

然后我们在同一页面上。 我确实认为@amueller有关于
这个,我们可能会看到一些讨论,也许很快就会编写代码。

另一件不错的事情是在转换中保留列名/在进行特征选择时选择它们。 我没有发现我们现在讨论的问题。

5172

注意:我想知道是否只想将最外层的估算器包装在一个整体中以向用户提供此功能。 我认为答案是:不,人们也想包装原子转换器,以允许在管道中使用数据帧感知转换器(为什么不呢?)。 如果不将此作为 mixin 实现,我认为您会遇到不必要的参数前缀问题或克隆问题(如#5080)。

:+1:

只是想扔掉我正在使用的解决方案:

def check_output(X, ensure_index=None, ensure_columns=None):
    """
    Joins X with ensure_index's index or ensure_columns's columns when avaialble
    """
    if ensure_index is not None:
        if ensure_columns is not None:
            if type(ensure_index) is pd.DataFrame and type(ensure_columns) is pd.DataFrame:
                X = pd.DataFrame(X, index=ensure_index.index, columns=ensure_columns.columns)
        else:
            if type(ensure_index) is pd.DataFrame:
                X = pd.DataFrame(X, index=ensure_index.index)
    return X

然后我围绕 sklearn 的估计器创建包装器,这些估计器在转换的输出上调用这个函数,例如,

from sklearn.preprocessing import StandardScaler as _StandardScaler 
class StandardScaler(_StandardScaler):
    def transform(self, X):
        Xt = super(StandardScaler, self).transform(X)
        return check_output(Xt, ensure_index=X, ensure_columns=X)

需要使用输入数据帧 X 索引的分类器可以只使用它的索引(正如所指出的那样对时间序列很有用)。

这种方法的好处是与现有的 sklearn 设计完全兼容,同时还保持计算速度(数据帧上的数学运算和索引比 numpy 数组慢 10 倍,http://penandpants.com/2014/09/05 /performance-of-pandas-series-vs-numpy-arrays/)。 不幸的是,将大量繁琐的工作添加到可以使用它的每个估算器中。

也许只需要用这个魔法制作一个 Pipeline 变体......

在2016年1月在02:30 15日,院长Wyatte [email protected]写道:

只是想扔掉我正在使用的解决方案:

def check_output(X, ensure_index=None, ensure_columns=None):
"""
可用时将 X 与 ensure_index 的索引或 ensure_columns 的列连接起来
"""
如果 ensure_index 不是 None:
如果 ensure_columns 不是 None:
如果 type(ensure_index) 是 pd.DataFrame 并且 type(ensure_columns) 是 pd.DataFrame:
X = pd.DataFrame(X, index=ensure_index.index, columns=ensure_columns.columns)
别的:
如果 type(ensure_index) 是 pd.DataFrame:
X = pd.DataFrame(X, index=ensure_index.index)
返回 X

然后我围绕调用这个函数的 sklearn 的估计器创建包装器
在变换的输出上,例如,

从 sklearn.preprocessing 导入 StandardScaler 作为 _StandardScaler
类 MinMaxScaler(_MinMaxScaler):
def变换(自我,X):
Xt = super(MinMaxScaler, self).transform(X)
返回 check_output(Xt, ensure_index=X, ensure_columns=X)

需要使用输入数据帧 X 索引的分类器可以
使用它的索引(正如所指出的那样对时间序列有用)。

这种方法的好处是完全兼容
现有 sklearn 设计,同时也保持计算速度
(数据帧上的数学运算和索引比 numpy 慢 10 倍
数组)。 不幸的是,为每个估计器添加大量繁琐的工作
可以利用它。


直接回复此邮件或在 GitHub 上查看
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171674105
.

或者只是包装管道/估算器的东西,不是吗?

我真的不明白为什么你会调用一个像“check_*”这样的函数,而它所做的不仅仅是检查......

2016年1月14日上午10时45分44秒CST,乔尔Nothman [email protected]写道:

也许只需要用这个魔法制作一个 Pipeline 变体......

2016 年 1 月 15 日 02:30,Dean Wyatte通知@github.com
写道:

只是想扔掉我正在使用的解决方案:

def check_output(X, ensure_index=None, ensure_columns=None):
"""
将 X 与 ensure_index 的索引或 ensure_columns 的列连接起来
有空时
"""
如果 ensure_index 不是 None:
如果 ensure_columns 不是 None:
如果 type(ensure_index) 是 pd.DataFrame 并且
type(ensure_columns) 是 pd.DataFrame:
X = pd.DataFrame(X, index=ensure_index.index,
列=ensure_columns.columns)
别的:
如果 type(ensure_index) 是 pd.DataFrame:
X = pd.DataFrame(X, index=ensure_index.index)
返回 X

然后我围绕 sklearn 的估计器创建包装器,调用它
功能
在变换的输出上,例如,

从 sklearn.preprocessing 导入 StandardScaler 作为 _StandardScaler
类 MinMaxScaler(_MinMaxScaler):
def变换(自我,X):
Xt = super(MinMaxScaler, self).transform(X)
返回 check_output(Xt, ensure_index=X, ensure_columns=X)

需要使用输入数据帧 X 索引的分类器可以
只是
使用它的索引(正如所指出的那样对时间序列有用)。

这种方法的好处是完全兼容
现有的 sklearn 设计,同时也保持速度
计算
(数据帧上的数学运算和索引比数据帧慢 10 倍
麻木的
数组)。 不幸的是,添加到每个
估计器
可以利用它。


直接回复此邮件或在 GitHub 上查看

https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171674105
.


直接回复此邮件或在 GitHub 上查看:
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171697542

使用 K-9 邮件从我的 Android 设备发送。 请原谅我的简短。

我不确定 Pipeline 是否是开始的正确位置,因为所有列名继承都是特定于估计器的,例如,缩放器应该继承输入数据帧的列名,而像 PCA 这样的模型不应该。 特征选择估计器应该继承特定的列名,但这是另一个问题,可能与 #2007 更相关。

在转换过程中所有数组的 n_rows 是否总是被保留? 如果是这样,仅继承输入的索引(如果存在)似乎是安全的,但我不确定获取具有默认列名(例如 [0, 1, 2, 3, ...])的数据框是从最终用户的角度来看,比当前的行为要好,但是如果使用显式包装器/元估计器,那么至少用户会知道会发生什么。

另外,同意 check_* 是一个糟糕的名字——我在我的函数中做了更多的验证,只是去掉了数据帧逻辑在这里发布。

我认为管道将是开始的地方,尽管我们需要向所有适当地映射列名称的估计器添加一些东西。

变压器应该输出适当命名的列@naught101

尽管需要对输入验证进行大手术才能保留列名:-/ @amueller

不仅是输入验证:每个转换都必须描述它对输入列的作用。 @GaelVaroquaux

有没有人想过如何在转换器之间传递名称的机制,以及如何跟踪出处? 一个人会在哪里存放这个?

我的一个朋友@cbrummitt也有类似的问题,他的设计矩阵的每一列都是一个函数形式(例如 x^2、x^3、x_1^3x_2^2,表示为 sympy 表达式),并且他有变换器其行为类似于 PolynomialFeatures,可以采用函数形式并基于此生成更多函数形式。 但是他使用 sympy 来获取旧表达式并生成新表达式,并且将表达式存储为字符串标签并没有削减它,并且当您对函数转换进行分层时会变得复杂。 他可以在管道之外完成所有这些工作,但随后他无法从 GridSearch 等中受益。

我想我们问题的更一般版本是,您如何获得一些从变压器传递到变压器的信息,而不是数据本身? 如果没有管道全局状态或让每个转换器 / 估计器了解先前的状态,或让每个步骤返回多个内容或其他内容,我就无法想出一个好方法。

然后我们还想出了修改管道以跟踪这一点的想法,您必须更改 _fit() 和 _transform() 以及其他一些内容。 这似乎是我们最好的选择。

这听起来很疯狂,但感觉就像我们真的希望我们的数据矩阵是 sympy 表达式,并且每次转换都会生成新的表达式? 这太糟糕了, check_array() 阻止了它的发生,并且会使管道中的其他步骤变得愤怒。

有关当前想法,请参阅 #6425。

你想要的只是一个映射,对于每个转换器(包括一个管道
转换器),从输入特征名称到输出特征名称(或一些
转换的结构化表示,我怀疑它更多
工程比我们要得到)。 这就是#6425 提供的。

2016 年 10 月 8 日 03:42,Andreas Mueller通知@github.com
写道:

见 #6425 https://github.com/scikit-learn/scikit-learn/issues/6425
目前的想法。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -252301608,
或静音线程
https://github.com/notifications/unsubscribe-auth/AAEz65fBsMwqmkDq3DEjMSUC-W-nfn9zks5qxnZxgaJpZM4GThGc
.

我们会研究一下,谢谢!

有人可以提供有关此问题的世界状况的一般更新吗?

pandas DataFrame支持会永远是 YMMV 的事情吗?
关于什么是/不被认为与熊猫一起使用是安全的指南DataFrame而不仅仅是ndarray会有所帮助。 也许类似于以下内容(制作示例表):

模块/类别|可以安全地使用pandas DataFrame
--|--
sklearn.pipeline|安全
sklearn.feature_selection|安全
回归量|YMMV
sklearn.feature_extraction|不安全,没有实施计划
等等|...

现在,除了“尝试一下,看看它是否抛出异常”之外,我不确定其他方法。

我们已经测试了一些手工编码的示例,这些示例似乎可以很好地接受 Pandas DataFrame,但是当我们决定需要进行看似微不足道的管道组件交换时,我们不禁想到这将不可避免地停止工作......在这一点上,一切都像一个神秘的堆栈跟踪中的纸牌屋一样倒塌。

我最初的想法是创建一个替换管道对象,该对象可以使用 Pandas DataFrame自动生成标准 scikit-learn 组件的包装器,将输入/输出DataFrame对象转换为 numpy ndarray对象根据需要。 这样我就可以编写自己的自定义选择器/转换器,这样我就可以使用 Pandas DataFrame原语,但这似乎有点笨手笨脚。 尤其如此,如果我们即将为他们提供“官方”支持。

我一直在关注一些不同的 PR,但很难了解哪些被放弃和/或哪些反映了当前的想法:
例子:

6425(在此线程中引用了上面的 2016 年 10 月)

9012(与 sklearn-pandas 明显重叠,但注释为实验性的?)

3886(被#9012取代?)

这取决于您所说的“可以安全地使用 Pandas DataFrame”是什么意思。 如果你的意思是一个只包含浮点数的 DataFrame,我们保证一切都会正常。 如果任何地方甚至只有一个字符串,也不会起作用。

我认为任何 scikit-learn 估计器为任何非平凡(甚至是平凡)操作返回数据帧都是可能永远不会发生的事情(尽管它希望如此)。

9012 会发生并变得稳定,PR 是第一次迭代(或第 10 次迭代,如果您计算未合并的迭代;)

6425 可能会发生,尽管它与熊猫并不完全相关。

3886 确实被 #9012 取代

功能 #6425 当前已实现(对于某些转换器和
可扩展到其他人)通过 singledispatch in
https://codecov.io/gh/TeamHG-Memex/eli5了解它的价值。

2017 年 6 月 21 日 13:25,Andreas Mueller [email protected]写道:

9012 https://github.com/scikit-learn/scikit-learn/pull/9012

发生并将变得稳定,PR 是第一次迭代。

6425 https://github.com/scikit-learn/scikit-learn/issues/6425

可能会发生,尽管它并不完全与熊猫有关。

3886 https://github.com/scikit-learn/scikit-learn/pull/3886确实是

由 #9012 取代
https://github.com/scikit-learn/scikit-learn/pull/9012


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment-309952467
或静音线程
https://github.com/notifications/unsubscribe-auth/AAEz61lgGBW1AoukPm_87elBjF2NGOUwks5sGI0-gaJpZM4GThGc
.

哦,当我说“如果你的意思是一个只包含浮点数的 DataFrame,我们保证一切都会正常。” 我的意思是基于位置的列索引。 假设训练和测试集列按位置相同。

这取决于您所说的“可以安全地使用 Pandas DataFrame”是什么意思。 如果你的意思是一个只包含浮点数的 DataFrame,我们保证一切都会正常。 如果任何地方甚至只有一个字符串,也不会起作用。

我认为这对我们来说已经足够了。

在到达 scikit-learn 组件(例如选择器或模型)之前,我们正在使用自定义组件的管道(围绕现有工具的薄包装器,不是管道友好的)通过编码/缩放将混合类型(字符串、浮点数和整数)转换为浮点数。

当给定DataFrame s 时,我所有的转换器都返回DataFrame DataFrame s。
当我将 300 列DataFrame输入Pipeline并收到 500 列ndarray ,我无法有效地从中学到很多东西,例如feature_selection ,因为我没有列名了。 例如,如果mutual_info_classif告诉我只有第 30 列和第 75 列很重要,那么我无法弄清楚如何简化我的原始Pipeline用于生产。
因此,将我的数据保存在DataFrame对我的用例至关重要。
谢谢你。

@sam-s 我完全同意。 在“短期”内,这将通过https://github.com/scikit-learn/scikit-learn/pull/13307https://github.com/scikit-learn/enhancement_proposals/pull/18 解决

你不会得到一个熊猫数据框,但你会得到列名来创建一个。

不过,你能举一个更具体的例子吗? 因为如果所有转换器都返回数据帧,事情应该可以工作(或者比上面的建议更容易工作)。

通过https://github.com/pandas-dev/pandas/issues/27211轻微更新
这让我的希望落空。 看起来我们不能相信有零拷贝往返,因此包装和解包到 Pandas 将导致大量成本。

通过 pandas-dev/pandas#27211 进行的轻微更新让我的希望落空。 看起来我们不能相信有零拷贝往返,因此包装和解包到 Pandas 将导致大量成本。

是的,但我想一旦我们涵盖了功能和示例道具(行名称和“索引”是一种示例道具),现在将涵盖现在有点需要熊猫的大多数相关用例,对吗?

@adrinjalali我不确定你所说的“最相关的用例有点需要熊猫”是什么意思。 我认为这个问题主要不是因为支持 Pandas 在 scikit-learn 中实现功能,而是为了让 scikit-learn 更容易地集成到基于 Pandas 的工作流程中。

出于好奇,是否有一个时间框架可以让 Pandas 的兼容性得到改善? 我对 Pandas 尤其感兴趣 -> Pandas out for StandardScaler

我有一个用例,我需要在Pipeline每个步骤中保存熊猫数据帧。 例如,管道具有 1) 特征选择步骤基于数据过滤特征,2) 数据转换步骤,3) 另一个特征选择步骤以过滤特定的特征列名称或原始索引,4) 标准化,5) 分类。

步骤 3) 我相信目前在 sklearn 中是不可能的,即使使用 numpy 数组输入,因为当数据达到 3) 时,原始特征索引是没有意义的,因为在 1) 中有一个特征选择步骤。 如果 Pandas 数据帧保存在管道中,它会起作用,因为我可以在 3) 中按列名进行过滤。

我认为即使使用 numpy 数组输入也没有办法做到这一点,我错了吗?

你是对的,它不受支持,支持它并非易事。 与您的用例相关,我们正在努力沿管道传递功能名称(如您在上面链接的 PR 和提案中所见)。 一旦完成,这应该会对您的案件有所帮助。 我不确定它是否有帮助,但你也可以看看https://github.com/scikit-learn-contrib/sklearn-pandas

你是对的,它不受支持,支持它并非易事。 与您的用例相关,我们正在努力沿管道传递功能名称(如您在上面链接的 PR 和提案中所见)。 一旦完成,这应该会对您的案件有所帮助。

感谢您的确认,是的,能够传递特征名称(或其他特征属性)以适应方法并在每个特征选择步骤中将它们正确切片对于这个用例来说是很好的。

我不确定它是否有帮助,但你也可以看看https://github.com/scikit-learn-contrib/sklearn-pandas

早些时候我通读了他们的文档,也许我没有看到它,但他们的大部分(或全部)功能现在在 scikit-learn 0.21 中已经过时了sklearn.compose.ColumnTransformer ? 此外,它们似乎不支持熊猫,因为它在转换后看起来像 numpy 数组。

(我想知道在特征选择中支持 Pandas 是否会中断
很多...)

只需简单地检查代码,就会在很多地方任意进行各种检查,例如使用https://github.com/scikit-learn/scikit-learn/blob/939fa3cccefe708db7a81c5248db32a1d600bf8d/sklearn/utils/validation.py# L619

此外,许多操作以 numpy 的方式使用索引,pandas 数据框不会接受。

保持大熊猫进出是 IMO 日常数据科学的必要条件,但 scikit-learn 的设计方式似乎使其难以实施。

保持大熊猫进出是 IMO 日常数据科学的必要条件,但是
scikit-learn 的设计方式似乎很难做到
实施的。

好的数字很难在 Pandas 数据帧上实现。 他们只是
不是为了那个,特别是对于多元操作(数值
跨列操作)。

机器学习主要是多元数字。

好的数字很难在 Pandas 数据帧上实现。 它们只是不是为了那个,特别是对于多变量操作(跨列的数值操作)。 机器学习主要是多元数字。

这个决定应该留给用户吗? 根据我在过去两年中广泛使用 scikit-learn 的经验,我认为缺少的两个核心和重要功能是许多 ML 用例必须具备的功能,即支持传递样本和特征元数据。 完整的 Pandas 数据框支持是处理其中一些问题的自然而优雅的方式。

这些核心功能对于保持用户群和吸引新用户非常重要。 否则我会看到像 mlr3 这样的库最终成熟并吸引用户远离 sklearn,因为我知道它们确实(或将)完全支持数据帧和元数据。

这个决定应该留给用户吗?

好吧,用户没有实现算法。

否则我会看到像 mlr3 这样的库最终成熟并且
吸引用户远离 sklearn,因为我知道他们会(或将会)
完全支持数据帧和元数据。

mlr3 在 R 中,数据帧与熊猫数据帧完全不同。
也许这使得实施起来更容易。

我同意更好地支持特征名称和异构数据
类型很重要。 我们正在努力寻找好的技术解决方案
这不会导致性能损失和过于复杂的代码。

这个决定应该留给用户吗?
好吧,用户没有实现算法。
否则我会看到像 mlr3 这样的库最终成熟并吸引用户远离 sklearn,因为我知道它们确实(或将)完全支持数据帧和元数据。
mlr3 在 R 中,数据帧与熊猫数据帧完全不同。 也许这使得实施起来更容易。 我同意更好地支持特征名称和异构数据类型很重要。 我们正在努力寻找不会导致性能损失和代码过于复杂的良好技术解决方案。

我认为您坚持使用 numpy 数组并至少支持传递功能名称甚至更好的多功能元数据的方法适用于许多用例。 为了传递训练样本元数据,您已经在**fit_params支持它,而且我知道正在努力改进设计。 但我在https://github.com/scikit-learn/enhancement_proposals/pull/16中提到,在某些用例中,您还需要将测试样本元数据传递给transform方法,目前不支持.

mlr3 在 R 中,数据帧与熊猫数据帧完全不同。

生命科学研究中的计算科学家通常对 Python 和 R 都非常熟悉,并且将两者结合使用(包括我自己)。 我很确定 scikit-learn 用户群中有很大一部分是生命科学研究人员。

目前在 R 恕我直言中可用的成熟 ML 库在提供精心设计的 API 和使 ML 的实用部分非常简单(管道、超参数搜索、评分等)方面甚至不接近 scikit-learn 而在 R使用这些库,您几乎必须自己编写代码。 但我认为 mlr3 是未来 scikit-learn 的一大竞争,因为他们正在以正确的方式从头开始设计它。

好的数字很难在 Pandas 数据帧上实现。 他们只是
不是为了那个,特别是对于多元操作(数值
跨列操作)。

也许我遗漏了一些东西,但是是否可以解开 DataFrame (使用df.values ),进行计算然后包装回一个新的 DataFrame ?

这基本上是我在步骤之间手动执行的操作,也是唯一阻止使用Pipeline事情。

也许我遗漏了一些东西,但不是可以解开包装吗?
DataFrame(使用 df.values),进行计算,然后换回一个新的
数据帧?

一般来说不:它可能不起作用(异构列),它会
导致大量内存拷贝。

一般不:它可能不起作用(异构列)

我认为 Column Transformers 等可以单独处理。

它会导致大量的内存副本。

我知道有困难的设计和实现选择,这是一个合理的论点。

但是,我不明白您为什么会争辩说改进 sklearn 支持列元数据的方式不是一个好主意。

允许例如摄取具有特征的 df,由于预测器添加一列,进行更多数据操作,进行另一个预测,所有这些在管道中都是有用的,因为它会(例如)允许超参数优化以更好的集成和优雅的方式。

使用或不使用 Pandas 只是一个建议,因为它是最常见、最简单和最流行的数据操作方式,而且我认为重写比他们所做的更多没有任何好处。

在优化性能时,由用户决定不使用此工作流程。

把事情留给用户来决定需要清楚地解释
供用户选择。 大多数用户不阅读文档
解释这样的选择。 许多人会尝试他们认为可行的方法,然后
当他们发现它很慢时放弃,没有意识到这是他们的选择
daraframe 使它如此。

所以我们需要小心谨慎。 但我们确实需要继续解决
这是一个高度优先事项。

我认为最好的解决方案是支持进出样本和特征属性的熊猫数据帧,并正确地将它们传递和切片到训练和测试拟合/转换中。 这将解决大多数用例,同时将数据矩阵 X 的速度保持为 numpy 数组。

这个论点中遗漏的一个重要点是,pandas 正在朝着数据的柱状表示发展,在这种情况下, np.array(pd.DataFrame(numpy_data))将有两个 _guaranteed_ 内存副本。 这就是为什么在我们需要速度时只保留数据框并使用values并不容易。

这个论点中遗漏的一个重要点是,pandas 正在朝着数据的柱状表示发展,在这种情况下, np.array(pd.DataFrame(numpy_data))将有两个 _guaranteed_ 内存副本。 这就是为什么在我们需要速度时只保留数据框并使用values并不容易。

我希望我在上一篇文章中说得很清楚。 我相信 scikit-learn 目前不需要支持 X 数据的 Pandas 数据帧,将它们保留为快速的 numpy 数组。 但是可以解决许多用例的是通过元数据的 Pandas 数据帧框架的完全支持,即样本属性和特征属性。 即使对于内存副本,这也不应该是性能负担,因为这两个数据结构与 X 相比很小,并且实际上只会对它们进行子集化。

是的,这些更改在许多用例中确实有帮助,我们正在努力解决这些问题。 但这个问题不止于此: https :

@hermidalc您是否建议我们让X成为一个 numpy 数组,并在其他数据帧对象中分配元数据?

@hermidalc您是否建议我们让X成为一个 numpy 数组,并在其他数据帧对象中分配元数据?

是的,完全支持样本属性和特征属性作为 Pandas 数据框。 已经在其他 PR 和问题中讨论了示例属性和功能名称,例如这里 #9566 和 #14315

我已经阅读了这个问题,看起来这里有两个主要的障碍:

  1. https://github.com/pandas-dev/pandas/issues/27211
  2. 大熊猫不处理 ND 数组。

您是否考虑过添加对 xarrays 的支持? 他们没有熊猫的那些限制。

X = np.arange(10).reshape(5, 2)
assert np.asarray(xr.DataArray(X)) is X
assert np.asarray(xr.Dataset({"data": (("samples", "features"), X)}).data).base is X.base

有一个名为sklearn-xarrayhttps ://phausamann.github.io/sklearn-xarray/content/wrappers.html,它包装了 scikit 估计器以将 xarrays 作为输入和输出处理,但似乎没有维护年。 但是,我想知道包装器是否是通往这里的方式。

xarray 正在积极考虑中。 它正在这里进行原型设计和工作: https :

(在我们完成 0.23 版本后,我会回到它)

我也对这个功能很感兴趣。
它将解决无限的问题。 目前这是我正在使用的解决方案。
我在sklearn.preprocessing模块周围写了一个包装器,我称之为sklearn_wrapper

因此,而不是从导入sklearn.preprocessing我进口从sklearn_wrapper
例如:

# this
from sklearn.preprocessing import StandardScaler 
# becomes 
from sklearn_wrapper import StandardScaler

下面介绍这个模块的实现。 试试看,让我知道你们的想法

from functools import wraps
from itertools import chain

import pandas as pd
from sklearn import preprocessing, compose, feature_selection, decomposition
from sklearn.compose._column_transformer import _get_transformer_list

modules = (preprocessing, feature_selection, decomposition)


def base_wrapper(Parent):
    class Wrapper(Parent):

        def transform(self, X, **kwargs):
            result = super().transform(X, **kwargs)
            check = self.check_out(X, result)
            return check if check is not None else result

        def fit_transform(self, X, y=None, **kwargs):
            result = super().fit_transform(X, y, **kwargs)
            check = self.check_out(X, result)
            return check if check is not None else result

        def check_out(self, X, result):
            if isinstance(X, pd.DataFrame):
                result = pd.DataFrame(result, index=X.index, columns=X.columns)
                result = result.astype(X.dtypes.to_dict())
            return result

        def __repr__(self):
            name = Parent.__name__
            tmp = super().__repr__().split('(')[1]
            return f'{name}({tmp}'

    Wrapper.__name__ = Parent.__name__
    Wrapper.__qualname__ = Parent.__name__

    return Wrapper


def base_pca_wrapper(Parent):
    Parent = base_wrapper(Parent)

    class Wrapper(Parent):
        @wraps(Parent)
        def __init__(self, *args, **kwargs):
            self._prefix_ = kwargs.pop('prefix', 'PCA')
            super().__init__(*args, **kwargs)

        def check_out(self, X, result):
            if isinstance(X, pd.DataFrame):
                columns = [f'{self._prefix_}_{i}' for i in range(1, (self.n_components or X.shape[1]) + 1)]
                result = pd.DataFrame(result, index=X.index, columns=columns)
            return result

    return Wrapper


class ColumnTransformer(base_wrapper(compose.ColumnTransformer)):

    def check_out(self, X, result):
        if isinstance(X, pd.DataFrame):
            return pd.DataFrame(result, index=X.index, columns=self._columns[0]) if self._remainder[1] == 'drop' \
                else pd.DataFrame(result, index=X.index, columns=X.columns). \
                astype(self.dtypes.iloc[self._remainder[-1]].to_dict())


class SelectKBest(base_wrapper(feature_selection.SelectKBest)):

    def check_out(self, X, result):
        if isinstance(X, pd.DataFrame):
            return pd.DataFrame(result, index=X.index, columns=X.columns[self.get_support()]). \
                astype(X.dtypes[self.get_support()].to_dict())


def make_column_transformer(*transformers, **kwargs):
    n_jobs = kwargs.pop('n_jobs', None)
    remainder = kwargs.pop('remainder', 'drop')
    sparse_threshold = kwargs.pop('sparse_threshold', 0.3)
    verbose = kwargs.pop('verbose', False)
    if kwargs:
        raise TypeError('Unknown keyword arguments: "{}"'
                        .format(list(kwargs.keys())[0]))
    transformer_list = _get_transformer_list(transformers)
    return ColumnTransformer(transformer_list, n_jobs=n_jobs,
                             remainder=remainder,
                             sparse_threshold=sparse_threshold,
                             verbose=verbose)


def __getattr__(name):
    if name not in __all__:
        return

    for module in modules:
        Parent = getattr(module, name, None)
        if Parent is not None:
            break

    if Parent is None:
        return

    if module is decomposition:
        Wrapper = base_pca_wrapper(Parent)
    else:
        Wrapper = base_wrapper(Parent)

    return Wrapper


__all__ = [*[c for c in preprocessing.__all__ if c[0].istitle()],
           *[c for c in decomposition.__all__ if c[0].istitle()],
           'SelectKBest']


def __dir__():
    tmp = dir()
    tmp.extend(__all__)
    return tmp

https://github.com/koaning/scikit-lego/issues/304通过热修复 sklearn.pipeline.FeatureUnion 提供了另一种解决方案

此页面是否有帮助?
0 / 5 - 0 等级