将pandas时间间隔转换为字符串(以及反向转换)

16

我对 Python 相对较新,并且正在尝试准备一些数据以训练一个 RandomForest。由于各种原因,我们希望数据是离散的,所以有一些连续变量需要被离散化。我在 pandas 中找到了 qcut 函数,它似乎可以做到我想要的 - 我可以设置一定数量的 bins,它会将变量离散化为那么多个 bins,尽可能保持每个 bin 中的计数平衡。

然而,pandas.qcut 的输出是一组区间(Intervals),而 scikit-learn 中的 RandomForest 分类器需要一个字符串。我发现可以使用 .astype(str) 将一个区间转换为字符串。以下是我正在进行的一个快速示例:

```python # 示例代码 import pandas as pd from sklearn.ensemble import RandomForestClassifier # 创建一个示例 DataFrame df = pd.DataFrame({'age': [20, 25, 30, 35, 40], 'income': [50000, 60000, 70000, 80000, 90000]})
# 将 'age' 变量离散化为 3 个 bins df['age_range'] = pd.qcut(df['age'], q=3)
# 将 'age_range' 转换为字符串类型 df['age_range_str'] = df['age_range'].astype(str)
# 创建一个随机森林分类器 rf = RandomForestClassifier()
# 使用离散化后的 'age_range_str' 变量拟合模型 rf.fit(df[['age_range_str', 'income']], ['low', 'low', 'medium', 'high', 'high']) ```
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 是传递给随机森林的变量之一。

然而,这个系统的目的是训练一个 RandomForest,并将其保存到文件中,然后允许某人在以后的某个日期加载它并为新的测试实例进行分类,该实例在训练时不可用。由于分类器是基于离散化数据训练的,因此在使用新的测试实例之前,需要将其离散化。因此,我想能够读取一个新实例,应用已经建立的离散化方案,将其转换为一个字符串,然后通过随机森林运行它。但是,我卡在了如何“应用离散化方案”的最佳方法上。

有没有简单的方法来处理这个问题?我假设没有直接将字符串转换回区间的简单方法。我可以从离散化中获取所有 Interval 值的列表(例如:cuts.unique()),并在测试时间应用它,但这将要求在随机森林旁边保存/加载一个离散化字典,这似乎很笨拙,并且我担心会遇到重新创建分类变量的问题(主要来自 R,它对分类变量的格式非常特别)。或者是否有其他我没看到的解决办法?

3个回答

4

qcut函数的labels参数中使用,或者使用pandas Categorical函数生成变量的类别。然后,您可以使用一种编码方式,例如标签编码序数编码将类别(如果您习惯于R,则为因子)转换为数字值,以便Forest能够使用。

然后,该过程如下:

cutting => categoricals => encoding

现在,您不再需要手动完成它。

最后,一些梯度提升树库支持分类变量,但这并非万能解决方案,将取决于您的目标。请查看catboostlightgbm


那么,当我获得一个新的测试实例并需要将其转换为相同的类别时,我该怎么办呢?我希望不需要将完整的训练数据传递给测试脚本,因此我需要存储“cutting => categoricals”转换,或者能够从随机森林中以某种方式恢复该转换。我的问题并不是qcut输出间隔 - 我只是试图找到最干净的方法来在以后重新应用离散化。如果我的问题没有表述清楚,我很抱歉。 - Amanda
我提到的编码器都支持“inverse_transform”,这正是您想要的。否则,您可以将映射保存为字典。 - Nathan Furnal

0
对于未来的搜索者而言,使用scikit-learn中的转换器而非pandas有其优点。在这种情况下,KBinsDiscretizerqcut的scikit等效版本。 它可以用于管道中,这将处理将先前学习的离散化应用于未见数据,无需单独存储离散化字典或往返字符串转换。以下是一个示例:
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://dev59.com/1r7pa4cB1Zd3GeqP2Zua#65296110,并且要么使用FunctionTransformer,要么编写自己的Transformer以进行管道集成。

-2

虽然这可能不是最干净的方法,但将字符串转换回时间间隔确实是可行的:

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]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接