我对 Python 比较陌生,正在尝试准备一些数据来训练随机森林。 由于各种原因,我们希望数据是离散的,因此有一些连续变量需要离散化。 我在 pandas 中发现了
qcut
,它似乎可以实现我想要的功能 - 我可以设置多个 bin,它会将变量离散化到多个 bin 中,试图使每个 bin 中的计数保持均匀。
但是,
pandas.qcut
的输出是一个Intervals列表,而scikit-learn中的RandomForest分类器需要一个字符串。 我发现我可以使用 .astype(str)
将间隔转换为字符串。 这是我正在做的事情的一个简单示例:
import pandas as pd
from random import sample
vals = sample(range(0,100), 100)
cuts = pd.qcut(vals, q=5)
str_cuts = pd.qcut(vals, q=5).astype(str)
然后 str_cuts 是传递到随机森林的变量之一。
但是,该系统的目的是训练随机森林,将其保存到文件中,然后允许某人在以后加载它并获得新测试实例的分类,这在训练时不可用。 由于分类器是根据离散数据进行训练的,因此新的测试实例在使用之前需要进行离散化。 所以我想要做的是在一个新实例中读取,对其应用已经建立的离散化方案,将其转换为字符串,然后在随机森林中运行它。 然而,我对“应用离散化方案”的最佳方法感到困惑。
有没有简单的方法来处理这个问题? 我认为没有直接的方法可以将字符串转换回间隔。 我可以从离散化中获取所有间隔值的列表(例如:
cuts.unique()
)并在测试时应用它,但这需要在随机森林旁边保存/加载离散化字典,这看起来很笨重,我担心尝试重新创建分类变量时遇到问题(主要来自 R,它对分类变量的格式非常挑剔)。 或者还有另一种我没有看到的解决方法吗?
在
labels
中使用
qcut
参数并使用 pandas Categorical
。
其中任何一个都可以帮助您为变量创建类别而不是间隔。然后,您可以使用某种形式的编码,例如 Label Encoding 或 Ordinal Encoding 将类别(如果您习惯使用 R,则为因子)转换为森林能够使用的数值。
然后流程进行:
cutting => categoricals => encoding
而且您不再需要手动完成。
最后,一些梯度增强树库支持分类变量,尽管这不是灵丹妙药,并且取决于您的目标。请参阅 catboost 和 lightgbm。
对于未来的搜索者来说,使用 scikit-learn 中的转换器而不是 pandas 有好处。 在本例中,
KBinsDiscretizer
是 scikit 中的 qcut
。
它可以在管道中使用,管道将处理将先前学习的离散化应用到未见过的数据,而不需要单独存储离散化字典或往返字符串转换。 这是一个例子:
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import KBinsDiscretizer
pipeline = make_pipeline(KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='quantile'),
RandomForestClassifier())
X, y = make_classification()
X_train, X_test, y_train, y_test = train_test_split(X, y)
pipeline.fit(X_train, y_train)
predictions = pipeline.predict(X_test)
如果您真的需要在pandas
IntervalIndex
和字符串之间来回转换,您可能需要按照此答案中所述进行一些解析:https://stackoverflow.com/a/65296110/3945991并使用 FunctionTransformer
或编写您自己的 Transformer
进行管道集成。
要将 pandas 间隔 转换为字符串,一个好方法是利用 pandas 间隔的 'left' 和 'right' 属性:
intervalString=f"{interval.left} - {interval.right}"
虽然这可能不是最干净的方法,但将字符串转换回间隔确实是可能的:
import pandas as pd
str_intervals = [i.replace("(","").replace("]", "").split(", ") for i in str_cuts]
original_cuts = [pd.Interval(float(i), float(j)) for i, j in str_intervals]