Python-特征工程秘籍-全-
Python 特征工程秘籍(全)
原文:
annas-archive.org/md5/3be0ee95713353c7de9e7e13bc64211b译者:飞龙
第一章:前言
<st c="632">pandas</st> <st c="643">scikit-learn</st>
本书面向对象
本书涵盖内容
为了最大限度地利用这本书
-
<st c="6438">category-encoders == 2.6.3</st> -
<st c="6465">Feature-engine == 1.8.0</st> -
<st c="6489">featuretools == 1.31.0</st> -
<st c="6512">matplotlib==3.8.3</st> -
<st c="6530">nltk=3.8.1</st> -
<st c="6541">numpy==1.26.4</st> -
<st c="6555">pandas==2.2.1</st> -
<st c="6569">scikit-learn==1.5.0</st> -
<st c="6589">scipy==1.12.0</st> -
<st c="6603">seaborn==0.13.2</st> -
<st c="6619">tsfresh==0.20.0</st>
*
下载示例代码文件
使用的约定
<st c="7736">文本中的代码</st><st c="7931">year</st><st c="7937">month</st><st c="7948">quarter</st>
date = "2024-05-17"
rng_hr = pd.date_range(date, periods=20, freq="h")
rng_month = pd.date_range(date, periods=20, freq="ME")
df = pd.DataFrame({"date1": rng_hr, "date2": rng_month})
pip install yellowbrick
章节
准备就绪
如何操作…
工作原理…
还有更多…
另请参阅
取得联系
分享你的想法
下载这本书的免费 PDF 副本
扫描二维码或访问以下 链接

-
提交您的购买证明 证明 -
这就完了! 我们将直接将您的免费 PDF 和其他福利发送到您的 电子邮件
第二章:填充缺失数据
缺失数据——即某些观测值的值缺失——是大多数数据源中不可避免的问题。一些机器学习模型的实现可以开箱即用处理缺失数据。为了训练其他模型,我们必须移除带有缺失数据的观测值或将它们转换为允许的值。
用统计估计替换缺失数据的行为称为填充。任何填充技术的目标都是生成一个完整的数据集。有多种填充方法。我们根据数据是否随机缺失、缺失值的比例以及我们打算使用的机器学习模型来选择使用哪种方法。在本章中,我们将讨论几种填充方法。
本章将涵盖以下食谱:
-
移除带有缺失数据的观测值
-
执行均值或中位数填充
-
填充分类变量
-
用任意数字替换缺失值
-
为填充寻找极端值
-
标记填充值
-
实施前向和后向填充
-
执行插值
-
通过链式方程进行多元填充
-
使用最近邻估计缺失数据
技术要求
在本章中,我们将使用 Python 库 Matplotlib、pandas、NumPy、scikit-learn 和 Feature-engine。如果你需要安装 Python,免费的 Anaconda Python 发行版(
<st c="1470">feature-engine</st>可以使用pip安装,如下所示:
pip install feature-engine
如果你使用 Anaconda,你可以使用conda安装<st c="1588">feature-engine</st>:
conda install -c conda-forge feature_engine
注意
本章中的食谱是在出版时使用的 Python 库的最新版本中创建的。<st c="1819">requirements.txt</st>
我们将使用
我已下载并修改了如下笔记本中的数据:
我们还将使用位于 Facebook 的 Prophet GitHub 仓库中的
我已修改了如下笔记本中的数据:
删除具有缺失数据的观察值
如何操作...
-
让我们导入 <st c="3956">pandas</st>, <st c="3964">matplotlib</st>,以及来自 scikit-learn 的训练/测试分割函数 : import matplotlib.pyplot as plt import pandas as pd from sklearn.model_selection import train_test_split -
让我们加载并显示在《技术要求》部分中描述的数据集: data = pd.read_csv("credit_approval_uci.csv") data.head()在以下图像中,我们看到了数据的前 5 行 :

-
让我们像通常准备训练机器学习模型数据那样进行操作: : 通过将数据分为训练集和测试集: X_train, X_test, y_train, y_test = train_test_split( data.drop("target", axis=1), data["target"], test_size=0.30, random_state=42, ) -
现在让我们绘制一个条形图,显示训练集和测试集中每个变量的缺失数据比例: : fig, axes = plt.subplots( 2, 1, figsize=(15, 10), squeeze=False) X_train.isnull().mean().plot( kind='bar', color='grey', ax=axes[0, 0], title="train") X_test.isnull().mean().plot( kind='bar', color='black', ax=axes[1, 0], title="test") axes[0, 0].set_ylabel('Fraction of NAN') axes[1, 0].set_ylabel('Fraction of NAN') plt.show()前面的代码块返回以下条形图 ,显示了训练集(顶部)和测试集(底部)中每个变量的缺失数据比例:

-
现在 ,我们将删除任何变量中存在缺失值的观测值: train_cca = X_train.dropna() test_cca = X_test.dropna()
<st c="5916">dropna()</st><st c="6060">data.dropna(subset=["A3", "A4"])</st>
-
让我们打印并比较原始和完整 案例数据集的大小: print(f"Total observations: {len(X_train)}") print(f"Observations without NAN: {len(train_cca)}")我们从训练集中删除了超过 200 个具有缺失数据的观测值,如下面的输出所示: <st c="6381">Total observations: 483</st> <st c="6405">Observations without NAN: 264</st> -
从训练集和测试集中删除观测值后,我们需要对齐 目标变量: y_train_cca = y_train.loc[train_cca.index] y_test_cca = y_test.loc[test_cca.index]现在,数据集和目标变量包含没有 缺失数据的行。 -
要使用 <st c="6746">feature-engine</st>删除具有缺失数据的观测值,让我们导入所需的变压器: from feature_engine.imputation import DropMissingData -
让我们设置填充器以自动 找到具有 缺失数据的变量: cca = DropMissingData(variables=None, missing_only=True) -
让我们拟合变压器,以便它找到具有 缺失数据的变量: cca.fit(X_train) -
让我们检查变压器发现的具有 NAN 的变量: cca.variables_之前的命令返回了具有 缺失数据的变量名称: <st c="7241">['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A14']</st> -
让我们删除训练集和 测试集中具有缺失数据的行: train_cca = cca.transform(X_train) test_cca = cca.transform(X_test)使用 <st c="7454">train_cca.isnull().sum()</st>来证实完整 案例数据集中没有缺失数据。 -
<st c="7551">DropMissingData</st>可以在从训练集中删除缺失数据后自动调整目标: train_c, y_train_c = cca.transform_x_y( X_train, y_train) test_c, y_test_c = cca.transform_x_y(X_test, y_test)
<st c="7802">nan</st>
<st c="7956">DropMissingData(variables=['A3', 'A4'])</st><st c="8017">nan</st> <st c="8058">DropMissingData(threshold=0.95)</st>
工作原理 ...
<st c="8246">pandas</st> <st c="8252">isnull()</st> <st c="8266">mean()</st> <st c="8355">isnull()</st> <st c="8414">True</st> <st c="8423">False</st> <st c="8480">mean()</st>
<st c="8580">pandas</st> <st c="8586">plot.bar()</st> <st c="8703">nan</st>
<st c="8827">dropna()</st>
<st c="8936">DropMissingData()</st><st c="9074">fit()</st> <st c="9097">transform()</st> <st c="9154">nan</st> <st c="9184">transform_x_y()</st><st c="9231">nan</st>
另请参阅
<st c="9322">DropMissingData()</st> <st c="9450">Pipeline</st>
执行均值或中位数填充
<st c="10132">pandas</st><st c="10140">scikit</st><st c="10148">learn</st><st c="10155">feature-engine</st>
如何做...
-
首先,我们将导入 <st c="10446">pandas</st>以及从 <st c="10496">scikit-learn</st>和 <st c="10514">feature-engine</st>中所需的函数和类: import pandas as pd from sklearn.model_selection import train_test_split from sklearn.impute import SimpleImputer from sklearn.compose import ColumnTransformer from feature_engine.imputation import MeanMedianImputer -
让我们加载我们在 技术 要求 部分中准备的数据集: data = pd.read_csv("credit_approval_uci.csv") -
让我们将数据分割成训练集和测试集及其 相应的目标: X_train, X_test, y_train, y_test = train_test_split( data.drop("target", axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们通过排除 对象类型的变量来创建一个包含数值变量的列表: numeric_vars = X_train.select_dtypes( exclude="O").columns.to_list()如果您执行 <st c="11251">numeric_vars</st>,您将看到数值变量的名称: <st c="11316">['A2', 'A3', 'A8', 'A11', '</st>``<st c="11343">A14', 'A15']</st>。 -
让我们将变量的中位数存储在 一个字典中: median_values = X_train[ numeric_vars].median().to_dict()
<st c="11645">mean()</st> <st c="11663">median()</st>
<st c="11688">median_values</st><st c="11765">{'A2': 28.835, 'A3': 2.75, 'A8': 1.0, 'A11': 0.0, 'A14': 160.0, '</st>``<st c="11830">A15': 6.0}.</st>
-
让我们用中值替换缺失数据 : X_train_t = X_train.fillna(value=median_values) X_test_t = X_test.fillna(value=median_values)如果您在填充后执行 <st c="11996">X_train_t[numeric_vars].isnull().sum()</st>,数值变量中的缺失值数量应该是 0 。
<st c="12130">pandas</st> <st c="12137">fillna()</st> <st c="12260">inplace</st> <st c="12281">True</st><st c="12288">X_train.fillna(value=median_values, inplace</st><st c="12331">=True)</st>
<st c="12396">scikit-learn</st>
-
让我们设置填充器 以用中值替换缺失数据 : imputer = SimpleImputer(strategy="median")
<st c="12556">SimpleImputer()</st> <st c="12584">imputer =</st> <st c="12593">SimpleImputer(strategy = "</st>``<st c="12620">mean")</st>
-
我们通过使用 <st c="12692">ColumnTransformer()</st>来限制填充仅限于数值变量: ct = ColumnTransformer( [("imputer", imputer, numeric_vars)], remainder="passthrough", force_int_remainder_cols=False, ).set_output(transform="pandas")
<st c="12895">numpy</st> <st c="12909">pandas</st> <st c="12931">极坐标</st> <st c="13018">numpy</st>
-
让我们将填充器 拟合到训练集,以便它学习中值 值: ct.fit(X_train) -
让我们查看学到的 中值: ct.named_transformers_.imputer.statistics_前一个命令返回每个变量的中值 : array([ 28.835, 2.75, 1., 0., 160., 6.]) -
让我们用中值替换缺失值 : X_train_t = ct.transform(X_train) X_test_t = ct.transform(X_test) -
让我们显示生成的 训练集: print(X_train_t.head())以下图像显示了生成的 DataFrame:

最后,让我们使用 <st c="14295">feature-engine</st>
-
让我们设置插补器,以用中位数替换数值变量中的缺失数据:
imputer = MeanMedianImputer( imputation_method="median", variables=numeric_vars, )
注意
要执行均值插补,将 <st c="14523">imputation_method</st> <st c="14544">"mean"</st><st c="14563">MeanMedianImputer()</st> <st c="14677">variables</st>
-
将插补器拟合,以便它学习中位数值:
imputer.fit(X_train) -
检查学习到的中位数:
imputer.imputer_dict_前一个命令返回字典中的中位数值:
{<st c="14951">'A2': 28.835, 'A3': 2.75, 'A8': 1.0, 'A11': 0.0, 'A14': 160.0, 'A15': 6.0}</st> -
最后,让我们用中位数替换缺失值:
X_train = imputer.transform(X_train) X_test = imputer.transform(X_test)
<st c="15156">Feature-engine</st> 的 <st c="15174">MeanMedianImputer()</st> <st c="15204">DataFrame</st><st c="15292">X_train[numeric</st><st c="15307">_vars].isnull().mean()</st>
如何工作...
在这个配方中,我们使用 <st c="15429">pandas</st>、<st c="15449"> 和 <st c="15451">feature-engine</st>
我们使用 scikit-learn 的 <st c="15540">train_test_split()</st> <st c="15687">random_state</st>
要使用pandas填充缺失数据,在第 5 步中,我们创建了一个字典,其中数值变量名称作为键,它们的平均值作为值。这些平均值是从训练集中学习的,以避免数据泄露。为了替换缺失数据,我们应用了pandas的fillna()到训练和测试集,传递包含每个变量的中值的字典作为参数。
要使用scikit-learn用中位数替换缺失值,我们使用了SimpleImputer(),其strategy设置为"median"。为了限制填充仅限于数值变量,我们使用了ColumnTransformer()。将remainder参数设置为passthrough,我们使ColumnTransformer()返回训练集中看到的所有变量在转换输出中;填充的变量随后是那些未转换的变量。
注意
ColumnTransformer()会更改输出中变量的名称。转换后的变量显示前缀imputer,而未更改的变量显示前缀remainder。
在第 8 步中,我们将列转换器的输出设置为pandas以获得一个 DataFrame 作为结果。默认情况下,ColumnTransformer()返回numpy数组。
注意
从版本 1.4.0 开始,scikit-learn转换器可以返回numpy数组、pandas DataFrame 或polar frames 作为transform()方法的输出结果。
使用fit(),SimpleImputer()学习训练集中每个数值变量的中位数并将它们存储在其statistics_属性中。使用transform(),它用中位数替换了缺失值。
<st c="17448">MeanMedianImputer()</st> <st c="17477">imputation_method</st> <st c="17502">median</st><st c="17609">variables</st> <st c="17635">fit()</st><st c="17723">imputer_dict_</st> <st c="17761">transform()</st>
插补分类变量
<st c="18119">scikit-learn</st> <st c="18136">feature-engine</st>
如何做到这一点...
-
让我们导入 <st c="18444">pandas</st>以及从 <st c="18495">scikit-learn</st>和 <st c="18512">feature-engine</st>中所需的函数和类: import pandas as pd from sklearn.model_selection import train_test_split from sklearn.impute import SimpleImputer from sklearn.compose import ColumnTransformer from feature_engine.imputation import CategoricalImputer -
让我们加载我们在 技术 要求 部分中准备的数据集: data = pd.read_csv("credit_approval_uci.csv") -
让我们将数据集分割成训练集和测试集及其 相应的目标: X_train, X_test, y_train, y_test = train_test_split( data.drop("target", axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们将分类变量 存储在一个列表中: categorical_vars = X_train.select_dtypes( include="O").columns.to_list() -
让我们将变量的最频繁类别存储在一个 字典中: frequent_values = X_train[ categorical_vars].mode().iloc[0].to_dict() -
让我们用频繁的类别 替换缺失值: X_train_t = X_train.fillna(value=frequent_values) X_test_t = X_test.fillna(value=frequent_values)
<st c="19503">fillna()</st> <st c="19640">X_train.fillna(value=frequent_valu</st><st c="19674">es, inplace=True)</st>
-
要用特定字符串替换缺失数据,请创建一个包含分类变量名称作为键和任意字符串作为 值的插补字典: imputation_dict = {var: "no_data" for var in categorical_vars}现在,我们可以使用这个字典和第 *6 步 中的代码 来替换缺失数据。
<st c="20020">pandas</st> <st c="20026">value_counts()</st> <st c="20117">X_train["A1"].value_counts()</st>
<st c="20218">scikit-learn</st>
-
让我们设置插补器以找到每个变量的最频繁类别: imputer = SimpleImputer(strategy='most_frequent')
<st c="20360">SimpleImputer()</st>
-
让我们将插补限制在 分类变量 : ct = ColumnTransformer( [("imputer",imputer, categorical_vars)], remainder="passthrough" ).set_output(transform="pandas")
<st c="20784">SimpleImputer()</st> <st c="20812">imputer =</st> <st c="20822">SimpleImputer(strategy="constant",</st> <st c="20857">fill_value="missing")</st>
-
将插补器拟合到训练集,以便它学习最频繁的值: ct.fit(X_train) -
让我们看看插补器学到的最频繁值: : ct.named_transformers_.imputer.statistics_上一条命令返回每个变量的最频繁值 : <st c="21153">array(['b', 'u', 'g', 'c', 'v', 't', 'f', 'f', 'g'], dtype=object)</st> -
最后,让我们用频繁的类别替换缺失值: X_train_t = ct.transform(X_train) X_test_t = ct.transform(X_test)确保通过 执行 <st c="21414">X_train_t.head()</st>. 来检查生成的 DataFrames。
<st c="21441">ColumnTransformer()</st> <st c="21535">imputer</st> <st c="21579">remainder</st>
<st c="21641">feature-engine</st>
-
让我们设置插补器 以用最频繁的值替换分类变量中的缺失数据: imputer = CategoricalImputer( imputation_method="frequent", variables=categorical_vars, )
将 <st c="21871">variables</st> <st c="21898">None</st><st c="21904">CategoricalImputer()</st>
-
将填充器拟合到训练集,以便它学习最频繁的类别:
imputer.fit(X_train)
要使用特定字符串填充分类变量,将 <st c="22273">imputation_method</st> 设置为 <st c="22294">missing</st>,并将 <st c="22306">fill_value</st> 设置为所需的字符串。
-
让我们检查一下学习到的类别:
imputer.imputer_dict_我们可以在以下输出中看到包含最频繁值的字典:
<st c="22482">{'A1': 'b',</st> <st c="22494">'A4': 'u',</st> <st c="22505">'A5': 'g',</st> <st c="22516">'A6': 'c',</st> <st c="22527">'A7': 'v',</st> <st c="22538">'A9': 't',</st> <st c="22549">'A10': 'f',</st> <st c="22561">'A12': 'f',</st> <st c="22573">'A13': 'g'}</st> -
最后,让我们用频繁类别替换缺失值:
X_train_t = imputer.transform(X_train) X_test_t = imputer.transform(X_test)如果你想要使用
<st c="22819">CategoricalImputer()</st>》用字符串或最频繁的值填充数值变量,将 <st c="22849">ignore_format</st>参数设置为<st c="22876">True</st>。
<st c="22881">CategoricalImputer()</st>
工作原理...
在这个菜谱中,我们使用最频繁的类别或任意字符串替换了分类变量中的缺失值。<st c="23093">pandas</st>、<st c="23099">scikit-learn</st>、<st c="23101">feature-engine</st>。
在步骤 5中,我们使用变量名作为键,频繁类别作为值创建了一个字典。<st c="23292">mode()</st>,并且为了返回一个字典,我们使用了 pandas 的 <st c="23343">to_dict()</st>。为了替换缺失数据,我们使用了 <st c="23391">pandas</st> 的 <st c="23397">fillna()</st>,传递包含变量及其频繁类别的字典作为参数。<st c="23612">.iloc[0]</st>
为了使用scikit-learn替换缺失值,我们使用了SimpleImputer(),其strategy设置为most_frequent。为了将插补限制为分类变量,我们使用了ColumnTransformer()。将remainder设置为passthrough后,ColumnTransformer()将返回训练集中所有变量的结果,作为transform()方法的输出。
注意
ColumnTransformer()更改输出中变量的名称。转换后的变量显示前缀imputer,而未更改的变量显示前缀remainder。
使用fit()函数,SimpleImputer()学习变量最频繁的类别并将它们存储在其statistics_属性中。使用transform()函数,它用学习到的参数替换了缺失数据。
SimpleImputer()和ColumnTransformer()默认返回 NumPy 数组。我们可以通过set_output()参数更改此行为。
为了使用feature-engine替换缺失值,我们使用了CategoricalImputer(),其imputation_method设置为frequent。使用fit(),转换器学习并存储了最频繁的类别在其imputer_dict_属性中的字典中。使用transform(),它用学习到的参数替换了缺失值。
<st c="24814">SimpleImputer()</st><st c="24831">CategoricalImputer()</st> <st c="24946">ignore_format</st> <st c="24973">True</st><st c="24997">feature-engine</st> <st c="25122">scikit-learn</st> <st c="25172">ColumnTransformer()</st>
用任意数替换缺失值
<st c="25385">999</st><st c="25390">9999</st><st c="25399">-1</st>
<st c="26106">pandas</st><st c="26111">s</st><st c="26115">scikit-learn</st><st c="26129">feature-engine</st>
如何操作...
-
导入 <st c="26239">pandas</st>和所需的函数 和类: import pandas as pd from sklearn.model_selection import train_test_split from sklearn.impute import SimpleImputer from feature_engine.imputation import ArbitraryNumberImputer -
让我们加载在 技术 要求 部分中描述的数据集: data = pd.read_csv("credit_approval_uci.csv") -
让我们将 数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( data.drop("target", axis=1), data["target"], test_size=0.3, random_state=0, )我们将选择大于分布最大值的任意值。 -
让我们找到四个 数值变量的最大值: X_train[['A2','A3', 'A8', 'A11']].max()之前的 命令返回以下输出: <st c="26994">A2 76.750</st> <st c="27004">A3 26.335</st> <st c="27014">A8 28.500</st> <st c="27024">A11 67.000</st> <st c="27061">99</st> for the imputation because it is bigger than the maximum values of the numerical variables in *<st c="27158">step 4</st>*. -
让我们复制原始的 DataFrame: X_train_t = X_train.copy() X_test_t = X_test.copy() -
现在,我们将缺失值替换为 <st c="27299">99</st>: X_train_t[["A2", "A3", "A8", "A11"]] = X_train_t[[ "A2", "A3", "A8", "A11"]].fillna(99) X_test_t[["A2", "A3", "A8", "A11"]] = X_test_t[[ "A2", "A3", "A8", "A11"]].fillna(99)
<st c="27546">pandas</st> <st c="27552">fillna()</st><st c="27591">imputation_dict = {"A2": -1, "A3": -</st><st c="27627">1, "A8": 999, "</st>``<st c="27643">A11": 9999}</st>
<st c="27682">scikit-learn</st>
-
让我们设置 <st c="27748">填充器</st>以替换缺失值为 <st c="27782">99</st>: imputer = SimpleImputer(strategy='constant', fill_value=99)
<st c="27905">SimpleImputer()</st> <st c="27930">99</st>
-
让我们将 <st c="27997">填充器</st>拟合到包含要填充变量的训练集切片中 : vars = ["A2", "A3", "A8", "A11"] imputer.fit(X_train[vars]) -
在所需变量中将缺失值替换为 <st c="28161">99</st>: X_train_t[vars] = imputer.transform(X_train[vars]) X_test_t[vars] = imputer.transform(X_test[vars])继续执行 <st c="28349">X_test_t[["A2", "A3", "</st>``<st c="28372">A8", "A11"]].isnull().sum()</st>来检查缺失值的数量。 为了完成,让我们使用 填充缺失值 功能 <st c="28448">f</st><st c="28449">eature-engine</st>. -
让我们设置 <st c="28481">填充器</st>以替换 <st c="28499">缺失值</st>为 <st c="28520">99</st>在 4 个特定变量中: imputer = ArbitraryNumberImputer( arbitrary_number=99, variables=["A2", "A3", "A8", "A11"], )
<st c="28641">注意</st>
<st c="28646">ArbitraryNumberImputer()</st> <st c="28768">variables</st> <st c="28791">None</st>
-
最后,让我们将缺失值替换为 <st c="28839">99</st>: X_train = imputer.fit_transform(X_train) X_test = imputer.transform(X_test)
<st c="28991">ArbitraryNumberImputer()</st> <st c="29028">ArbitraryNumberImputer(imputater_dict = {"A2": -1, "A3": -1, "A8": 999, "</st>``<st c="29101">A11": 9999})</st>
它是如何工作的...
<st c="29332">pandas</st><st c="29340">sc</st><st c="29342">ikit-learn</st><st c="29355">featur</st><st c="29365">e-engine</st>
<st c="29493">max()</st><st c="29509">99</st> <st c="29605">pandas</st> <st c="29611">fillna()</st>
<st c="29682">scikit-learn</st><st c="29708">SimpleImputer()</st><st c="29734">策略</st> <st c="29750">constant</st><st c="29784">fill_value</st> <st c="29774">99</st> <st c="29937">transform()</st>
<st c="29987">feature-engine</st> <st c="30010">ArbitraryValueImputer()</st><st c="30056">99</st> <st c="30123">fit_transform()</st> <st c="30195">transform()</st>
为填充确定极端值
<st c="31314">pandas</st> <st c="31325">feature-engine</st>
如何做...
-
让我们导入 <st c="31444">pandas</st>和所需的功能 和类: import pandas as pd from sklearn.model_selection import train_test_split from feature_engine.imputation import EndTailImputer -
让我们加载我们在 技术 要求 部分 中描述的数据集:data = pd.read_csv("credit_approval_uci.csv") -
让我们将数值变量捕获到一个列表中,排除 目标: numeric_vars = [ var for var in data.select_dtypes( exclude="O").columns.to_list() if var !="target" ] -
让我们将数据分为训练集和测试集,只保留 数值变量: X_train, X_test, y_train, y_test = train_test_split( data[numeric_vars], data["target"], test_size=0.3, random_state=0, ) -
我们现在将确定 四分位数间距: IQR = X_train.quantile(0.75) - X_train.quantile(0.25)我们可以通过执行 <st c="32245">IQR</st>或 <st c="32252">print(IQR)</st>来可视化 四分位数间距值: <st c="32264">A2 16.4200</st> <st c="32274">A3</st> <st c="32278">6.5825</st> <st c="32284">A8 2.8350</st> <st c="32294">A11 3.0000</st> <st c="32305">A14 212.0000</st> <st c="32318">A15 450.0000</st> <st c="32331">dtype: float64</st> -
让我们创建一个包含变量名称和 插补值 的字典:imputation_dict = ( X_train.quantile(0.75) + 1.5 * IQR).to_dict()
<st c="32762">imputation_dict = (X_train.mean() + 3 * X_train.std()).to_dict()</st>
-
最后,让我们替换 缺失的数据: X_train_t = X_train.fillna(value=imputation_dict) X_test_t = X_test.fillna(value=imputation_dict)
<st c="33061">value = X_train[var].quantile(0.25) - 1.5 * IQR</st> <st c="33112">value = X_train[var].mean() – 3 * X_train[var].std()</st>
-
让我们设置 <st c="33240">imputer</st>来使用 IQR 邻近规则估计分布右侧的值: imputer = EndTailImputer( imputation_method="iqr", tail="right", fold=3, variables=None, )
<st c="33507">imputation_method="Gaussian"</st><st c="33541">left</st> <st c="33549">right</st> <st c="33562">tail</st>
-
让我们将 <st c="33678">EndTailImputer()</st>拟合到训练集,以便它学习插补的值: imputer.fit(X_train) -
让我们检查 学习到的值: imputer.imputer_dict_之前的命令 返回一个包含用于插补 每个变量的值 的字典: <st c="33927">{'A2': 88.18,</st> <st c="33941">'A3': 27.31,</st> <st c="33954">'A8': 11.504999999999999,</st> <st c="33980">'A11': 12.0,</st> <st c="33993">'A14': 908.0,</st> <st c="34007">'A15': 1800.0}</st> -
最后,让我们替换 缺失的值: X_train = imputer.transform(X_train) X_test = imputer.transform(X_test)
<st c="34219">X_train[[</st><st c="34228">'A2','A3', 'A8', 'A11', '</st>``<st c="34254">A14', 'A15']].isnull().mean()</st>
它是如何工作的...
pandas和feature-engine在数值变量的分布末端替换了缺失值
<st c="34569">quantile()</st> <st c="34617">pandas</st> <st c="34623">mean()</st> <st c="34635">std()</st> <st c="34690">fillna()</st>
<st c="34762">EndTailImputer()</st> <st c="34784">feature-engine</st><st c="34807">distribution</st> <st c="34823">iqr</st> <st c="34889">tail</st> <st c="34901">right</st> <st c="34992">fit()</st><st c="35083">imputer_dict_</st> <st c="35113">transform()</st>
标记插补值
<st c="35451">1</st> <st c="35456">True</st> <st c="35506">0</st> <st c="35511">False</st> <st c="35778">pandas</st><st c="35786">scikit-learn</st><st c="35800">和</st>
如何操作...
-
让我们导入所需的库、函数和类: import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.impute import SimpleImputer from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from feature_engine.imputation import( AddMissingIndicator, CategoricalImputer, MeanMedianImputer ) -
让我们加载并分割 在 *技术 * *要求 *部分中描述的 数据集:data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop("target", axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们将变量名捕获到一个 列表中: varnames = ["A1", "A3", "A4", "A5", "A6", "A7", "A8"] -
让我们为缺失指示器创建名称并将它们存储到一个 列表中: indicators = [f"{var}_na" for var in varnames]如果我们执行 <st c="36761">indicators</st>,我们将看到我们将用于新变量的名称: <st c="36830">['A1_na', 'A3_na', 'A4_na', 'A5_na', 'A6_na', '</st>``<st c="36877">A7_na', 'A8_na']</st>。 -
让我们复制原始的 DataFrame: X_train_t = X_train.copy() X_test_t = X_test.copy() -
让我们添加 缺失指示器: X_train_t[indicators] =X_train[ varnames].isna().astype(int) X_test_t[indicators] = X_test[ varnames].isna().astype(int)
<st c="37190">True</st> <st c="37199">False</st> <st c="37226">0</st> <st c="37232">1</st><st c="37242">astype(int)</st>
-
让我们检查 结果 DataFrame: X_train_t.head()我们可以在以下图像的 DataFrame 右侧看到新添加的变量: (图像)

-
设置插补器,为每个具有 缺失数据的变量添加二进制指示器: imputer = AddMissingIndicator( variables=None, missing_only=True ) -
将插补器拟合到训练集,以便它找到具有 缺失数据的变量: imputer.fit(X_train)
<st c="38202">imputer.variables_</st>
-
最后,让我们添加 缺失指示器: X_train_t = imputer.transform(X_train) X_test_t = imputer.transform(X_test)到目前为止,我们只是添加了缺失指示器。 但我们变量中仍然有缺失数据。 我们需要用数字来替换它们。 在本教程的剩余部分,我们将结合使用缺失指示器与均值和 众数插补。 -
让我们创建一个 管道,将缺失指示器添加到分类和数值变量中,然后使用最频繁的类别对分类变量进行插补,使用均值对数值变量进行插补: pipe = Pipeline([ ("indicators", AddMissingIndicator(missing_only=True)), ("categorical", CategoricalImputer( imputation_method="frequent")), ("numerical", MeanMedianImputer()), ])
<st c="39031">feature-engine</st> <st c="39046">填充器会自动识别数值或类别变量。</st> <st c="39115">因此在这种情况下不需要切片数据或传递变量名作为参数到</st> <st c="39172">这个转换器的参数中。</st>
-
<st c="39227">让我们添加指示器并填充</st><st c="39264">缺失值:</st>X_train_t = pipe.fit_transform(X_train) X_test_t = pipe.transform(X_test)
<st c="39353">注意</st>
使用 <st c="39358">Use</st> <st c="39363">X_train_t.isnull().sum()</st> <st c="39387">来验证没有数据缺失。</st> <st c="39434">执行</st> <st c="39442">X_train_t.head()</st> <st c="39458">以查看</st> <st c="39480">结果数据集。</st>
<st c="39499">最后,让我们添加缺失指示器,并利用 scikit-learn 同时用均值和最频繁的类别分别填充数值和类别变量,</st> <st c="39657">实现填充:</st>
-
<st c="39680">让我们创建一个包含</st><st c="39713">数值和</st><st c="39735">类别变量</st><st c="39735">名称的列表:</st>numvars = X_train.select_dtypes( exclude="O").columns.to_list() catvars = X_train.select_dtypes( include="O").columns.to_list() -
<st c="39885">让我们设置一个管道来执行均值和频繁类别填充,同时标记</st><st c="39977">缺失数据:</st>pipe = ColumnTransformer([ ("num_imputer", SimpleImputer( strategy="mean", add_indicator=True), numvars), ("cat_imputer", SimpleImputer( strategy="most_frequent", add_indicator=True), catvars), ]).set_output(transform="pandas") -
<st c="40218">现在,让我们执行</st><st c="40240">填充:</st>X_train_t = pipe.fit_transform(X_train) X_test_t = pipe.transform(X_test)
<st c="40329">确保探索</st> <st c="40350">X_train_t.head()</st> <st c="40367">以熟悉</st> <st c="40393">管道的输出。</st>
<st c="40411">它是如何工作的...</st>
<st c="40427">要使用 pandas 添加缺失</st> <st c="40442">指示器</st> <st c="40453">,我们使用了</st> <st c="40476">isna()</st> <st c="40482">,它创建了一个新向量,如果存在缺失值则分配值为</st> <st c="40534">True</st> <st c="40538">,否则为</st> <st c="40571">False</st> <st c="40576">。</st> <st c="40588">我们使用了</st> <st c="40596">astype(int)</st> <st c="40607">将布尔向量转换为具有值</st> <st c="40671">1</st> <st c="40672">和</st> <st c="40676">0</st> <st c="40677">的二进制向量。</st>
<st c="40678">要使用</st> <st c="40711">feature-engine</st> <st c="40725">添加缺失指示器,我们使用了</st> <st c="40735">AddMissingIndicator()</st> <st c="40756">。使用</st> <st c="40763">fit()</st> <st c="40768">转换器找到了具有缺失数据的变量。</st> <st c="40824">使用</st> <st c="40829">transform()</st> <st c="40840">它将缺失指示器添加到训练集和测试集的右侧。</st>
<st c="40977">nan</st> <st c="41062">AddMissingIndicator()</st><st c="41085">CategoricalImputer()</st><st c="41111">MeanMedianImputer()</st> <st c="41140">pipeline</st><st c="41154">fit()</st> <st c="41176">pipeline</st> <st c="41231">nan</st> <st c="41328">transform()</st> <st c="41360">pipeline</st>
<st c="41588">Scikit-learn 的
<st c="41815">scikit-learn</st><st c="41858">SimpleImputer()</st><st c="41977">在两种情况下,我们将 <st c="42016">参数设置为<st c="42034">以添加缺失指标。</st><st c="42077">SimpleImputer()</st> <st c="42098">ColumnTransformer()</st> <st c="42177">然后我们使用 <st c="42199">和 <st c="42215">方法从
<st c="42477">X_train_t.head()</st><st c="42658">num_imputer__A2</st> <st c="42728">cat_imputer__A12</st>
还有更多...
<st c="42829">MissingIndicator()</st>
实现前向和后向填充
如何操作...
-
让我们导入 <st c="44023">pandas</st>和 <st c="44034">matplotlib</st>: import matplotlib.pyplot as plt import pandas as pd -
让我们加载我们在 *技术要求 部分中描述的航空乘客数据集,并显示时序的前五行: df = pd.read_csv( "air_passengers.csv", parse_dates=["ds"], index_col=["ds"], ) print(df.head())我们在以下输出中看到时序数据: : <st c="44389">y</st> <st c="44391">ds</st> <st c="44393">1949-01-01 112.0</st> <st c="44410">1949-02-01 118.0</st> <st c="44427">1949-03-01</st> <st c="44438">132.0</st> <st c="44444">1949-04-01 129.0</st> <st c="44461">1949-05-01 121.0</st>
-
让我们绘制时序图以查找任何明显 的数据间隙: ax = df.plot(marker=".", figsize=[10, 5], legend=None) ax.set_title("Air passengers") ax.set_ylabel("Number of passengers") ax.set_xlabel("Time")前面的代码返回以下图表,其中我们看到了数据缺失的时间间隔:

-
让我们通过将任何区间内最后观察到的值携带到下一个有效值来填充缺失数据: : df_imputed = df.ffill()您可以通过执行 df_imputed.isnull().sum() 来验证缺失数据的缺失: -
现在让我们绘制完整的数据集,并将用于 插补的值叠加为虚线: ax = df_imputed.plot( linestyle="-", marker=".", figsize=[10, 5]) df_imputed[df.isnull()].plot( ax=ax, legend=None, marker=".", color="r") ax.set_title("Air passengers") ax.set_ylabel("Number of passengers") ax.set_xlabel("Time")前面的代码返回以下图表,其中我们看到了用于替换 <st c="45647">nan</st>的值,并以虚线形式叠加在连续时间序列线之间 :

-
或者,我们可以使用 后向填充 来填充缺失数据:df_imputed = df.bfill()如果我们绘制插补数据集,并将插补值叠加到我们 在第 5 步 所做的那样,我们将看到以下图表:

它是如何工作的...
<st c="46672">pandas</st> <st c="46679">ffill()</st>
<st c="46934">pandas</st> <st c="46941">bfill()</st>
<st c="47223">ffill()</st> <st c="47235">bfill()</st> <st c="47406">limit</st> <st c="47411">参数来限制插补到任何间隔中的最大数据点数。<st c="47452">ffill(limit=10)</st>
执行插值
如何做...
-
让我们导入 <st c="48215">pandas</st>和 <st c="48226">matplotlib</st>: import matplotlib.pyplot as plt import pandas as pd -
让我们加载在 技术 要求 部分中描述的时间序列数据: df = pd.read_csv( "air_passengers.csv", parse_dates=["ds"], index_col=["ds"], )
-
让我们通过 线性插值来插补缺失数据: df_imputed = df.interpolate(method="linear")
<st c="48743">方法</st> <st c="48767">时间</st>
<st c="48853">df_imputed.isnull().sum()</st>
-
现在让我们绘制完整的数据集,并将用于 插补的值以虚线形式叠加: ax = df_imputed.plot( linestyle="-", marker=".", figsize=[10, 5]) df_imputed[df.isnull()].plot( ax=ax, legend=None, marker=".", color="r") ax.set_title("Air passengers") ax.set_ylabel("Number of passengers") ax.set_xlabel("Time")前面的代码返回以下图表,其中我们看到了用于替换 <st c="49297">nan</st>的值,并以虚线形式绘制在时间序列的连续线之间:

-
或者,我们可以通过进行样条插值来插补缺失数据。 我们将使用二次多项式: df_imputed = df.interpolate(method="spline", order=2)如果我们绘制插补数据集,并将插补值叠加,就像我们在 步骤 4 中所做的那样,我们将看到以下图表:

它是如何工作的...
<st c="50360">pandas</st> <st c="50367">interpolate()</st> <st c="50464">method</st> <st c="50474">linear</st><st c="50482">interpolate()</st>
<st c="50725">method</st> <st c="50735">time</st>
<st c="50823">method</st> <st c="50833">spline</st> <st c="50844">order</st> <st c="50853">2</st>
<st c="50855">pandas</st> <st c="50862">interpolate()</st> <st c="50882">scipy.interpolate.interp1d</st> <st c="50913">scipy.interpolate.UnivariateSpline</st>
另请参阅
通过链式方程执行多元插补
-
首先,它对每个有缺失数据的变量执行简单的单变量插补。 例如, 中位数插补。 -
接下来,它选择一个特定的变量,比如, <st c="52233">var_1</st>,并将缺失值重新设置为缺失。 -
它训练一个模型来预测 <st c="52314">var_1</st>,使用其他变量作为 输入特征。 -
最后,它用模型的输出替换 <st c="52408">var_1</st>的缺失值。
如何做...
-
让我们导入所需的 Python 库、类、 和函数: import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.linear_model import BayesianRidge from sklearn.experimental import ( enable_iterative_imputer ) from sklearn.impute import ( IterativeImputer, SimpleImputer ) -
让我们从描述在 *技术 * *要求 *部分的 数据集中加载一些数值变量:variables = [ "A2", "A3", "A8", "A11", "A14", "A15", "target"] data = pd.read_csv( "credit_approval_uci.csv", usecols=variables) -
让我们将数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( data.drop("target", axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们使用贝叶斯回归创建一个 MICE 插补器,指定迭代周期数并设置 <st c="54108">random_state</st>以实现可重复性: imputer = IterativeImputer( estimator= BayesianRidge(), max_iter=10, random_state=0, ).set_output(transform="pandas")
<st c="54264">IterativeImputer()</st> <st c="54385">initial_strategy</st>
-
让我们拟合 <st c="54641">IterativeImputer()</st>,以便它训练 估计器 来预测每个变量中的缺失值: imputer.fit(X_train)
<st c="54833">IterativeImputer()</st>
-
最后,让我们在训练集和 测试集中填充缺失值: X_train_t = imputer.transform(X_train) X_test_t = imputer.transform(X_test)
<st c="55065">X_train_t.isnull().sum()</st>
-
让我们设置 scikit-learn 的 <st c="55267">SimpleImputer()</st>以执行均值插补,然后转换 数据集: imputer_simple = SimpleImputer( strategy="mean").set_output(transform="pandas") X_train_s = imputer_simple.fit_transform(X_train) X_test_s = imputer_simple.transform(X_test) -
现在让我们绘制 变量 <st c="55552">A3</st>在 MICE 插补后的直方图,然后是相同变量在 均值插补后的直方图: fig, axes = plt.subplots( 2, 1, figsize=(10, 10), squeeze=False) X_test_t["A3"].hist( bins=50, ax=axes[0, 0], color="blue") X_test_s["A3"].hist( bins=50, ax=axes[1, 0], color="green") axes[0, 0].set_ylabel('Number of observations') axes[1, 0].set_ylabel('Number of observations') axes[0, 0].set_xlabel('A3') axes[1, 0].set_xlabel('A3') axes[0, 0].set_title('MICE') axes[1, 0].set_title('Mean imputation') plt.show()在下面的图中,我们可以看到均值插补扭曲了变量分布,更多的观测值趋向于 平均值:

它是如何工作的...
在这个菜谱中,我们使用 <st c="56529">IterativeImputer()</st> 从 <st c="56553">scikit-learn</st> 进行多元插补。当我们拟合模型时,<st c="56590">IterativeImputer()</st> 执行了我们在菜谱介绍中描述的步骤。也就是说,它使用均值对所有变量进行插补。然后它选择一个变量,将其缺失值重新设置为缺失。最后,它拟合一个贝叶斯回归器来根据其他变量估计该变量。它对每个变量重复此过程。这是插补的一个周期。我们将它设置为重复此过程 10 次。到这个过程的最后,<st c="57048">IterativeImputer()</st> 训练了一个贝叶斯回归器,用于根据数据集中的其他变量预测每个变量的值。使用 <st c="57188">transform()</st>,它使用这些贝叶斯模型的预测来插补缺失数据。
<st c="57275">Iter</st><st c="57280">ativeImputer()</st> 只能基于数值变量对数值变量中的缺失数据进行插补。如果你想使用分类变量作为输入,你需要先对它们进行编码。然而,请注意,它只会执行回归。因此,它不适合估计离散或分类变量中的缺失数据。
参见
要了解更多关于 MICE 的信息,请查看以下资源:
-
使用一系列回归模型进行多重插补缺失值的多元技术:
www.researchgate.net/publication/244959137 -
链式方程多重插补:是什么以及它是如何工作的? : www.jstatsoft.org/article/download/v045i03/550
使用最近邻估计缺失数据

如何做到这一点...
-
让我们导入所需的库、类、 和函数: import matplotlib.pyplot as plt import pandas as pd from sklearn.model_selection import train_test_split from sklearn.impute import KNNImputer -
让我们加载在 技术要求 部分中描述的数据集(仅一些 数值变量): variables = [ "A2", "A3", "A8", "A11", "A14", "A15", "target"] data = pd.read_csv( "credit_approval_uci.csv", usecols=variables, ) -
让我们将数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( data.drop("target", axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们设置缺失值填充器 以用其最近五个邻居的加权平均值替换缺失数据 : imputer = KNNImputer( n_neighbors=5, weights="distance", ).set_output(transform="pandas")
-
找到 最近邻: imputer.fit(X_train) -
用邻居显示的值的加权平均值替换缺失值: X_train_t = imputer.transform(X_train) X_test_t = imputer.transform(X_test)
它是如何工作的...
在这个方法中,我们用每个观测值的 k 近邻的平均值替换了<st c="60938">KNNImputer()</st> <st c="61179">transform()</st>
第三章:0 2
编码分类变量
<st c="146">业主</st> <st c="185">owner</st>和<st c="190">非业主</st> <st c="235">婚姻状况</st> <st c="278">从未结婚</st><st c="293">已婚</st><st c="302">离婚</st><st c="316">丧偶</st><st c="412">学生成绩</st> <st c="452">A</st><st c="455">B</st><st c="458">C</st><st c="465">Fail</st> <st c="488">A</st> <st c="518">Fail</st> <st c="541">这些被称为 <st c="712">变量,其值为<st c="748">、<st c="760">、<st c="769">等。</st>
分类变量的值通常编码为字符串。为了训练大多数机器学习模型,我们需要将这些字符串转换为数字。将字符串替换为数字的行为称为
本章将涵盖以下食谱:
-
通过独热编码创建二进制变量
-
对频繁类别进行独热编码
-
用计数或观察值的频率替换类别
-
用序数替换类别
-
根据目标值进行序数编码
-
实现目标均值编码
-
使用证据权重进行编码
-
对稀有或不频繁类别进行分组
-
进行二元编码
技术要求
<st c="1587">Matplotlib</st><st c="1599">pandas</st><st c="1607">NumPy</st><st c="1614">scikit-learn</st><st c="1628">feature-engine</st>
<st c="1827">feature-engine</st> <st c="1865">pip</st>
pip install feature-engine
<st c="1935">feature-engine</st> <st c="1955">conda</st>
conda install -c conda-forge feature_engine
<st c="2041">pip</st>
pip install category_encoders
在编码分类变量之前,你可能想要填充它们的缺失数据。
通过独热编码创建二进制变量
<st c="3223">1</st> <st c="3256">0</st>
<st c="3337">Smoker</st> <st c="3376">Smoker</st> <st c="3387">Non-Smoker</st>

<st c="3575">Smoker</st> <st c="3628">Smoker</st><st c="3661">1</st> <st c="3703">Non-Smoker</st><st c="3740">1</st>
<st c="3778">Color</st> <st c="3824">red</st><st c="3829">blue</st><st c="3839">green</st><st c="3883">red</st><st c="3888">blue</st><st c="3898">green</st><st c="3949">1</st> <st c="4011">0</st>
<st c="4124">Smoker</st><st c="4166">Smoker</st> <st c="4178">Non-Smoker</st><st c="4282">Color</st> <st c="4331">red</st><st c="4336">blue</st><st c="4346">green</st>
-
如果观察结果是红色,它将被 <st c="4511">红色</st>变量捕获( <st c="4525">红色</st>= <st c="4532">1</st>, <st c="4535">蓝色</st>= <st c="4542">0</st>) -
如果观察结果是蓝色,它将被 <st c="4599">蓝色</st>变量捕获( <st c="4614">红色</st>= <st c="4621">0</st>, <st c="4624">蓝色</st>= <st c="4631">1</st>) -
如果观察结果是绿色,它将被 <st c="4704">红色</st>和 <st c="4712">蓝色</st>( <st c="4718">红色</st>= <st c="4724">0</st>, <st c="4727">蓝色</st>= <st c="4734">0</st>)
-
在训练决策树时,由于它们不会同时评估整个特征空间 , -
当递归选择特征时 -
当确定变量中每个类别的 重要性时
<st c="5188">pandas</st><st c="5196">scikit-learn</st><st c="5210">featur</st><st c="5220">e-engine</st>
如何做到这一点...
-
导入 <st c="5312">pandas</st>和 <st c="5327">train_test_split</st>函数 从 <st c="5358">scikit-learn</st>: import pandas as pd from sklearn.model_selection import train_test_split -
让我们加载信用 批准数据集: data = pd.read_csv("credit_approval_uci.csv") -
让我们将数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们 检查 <st c="5765">A4</st>变量的 唯一类别: X_train["A4"].unique()我们可以在以下输出中看到 <st c="5833">A4</st>的独特值: <st c="5923">A4</st> into *<st c="5930">k-1</st>* binary variables using <st c="5958">pandas</st> and then inspect the first five rows of the resulting DataFrame:dummies = pd.get_dummies(
X_train["A4"], drop_first=True)dummies.head()
<st c="6113">pandas</st><st c="6122">get_dummies()</st><st c="6193">dummy_na</st> <st c="6224">dummy_na=True</st><st c="6337">drop_first=False</st>
<st c="6444">Missing l u y</st>
<st c="6458">596 False</st> <st c="6468">False True False</st>
<st c="6485">303 False False True False</st>
<st c="6512">204 False False</st> <st c="6529">False True</st>
<st c="6539">351 False False False True</st>
<st c="6566">118 False False True False</st>
-
现在,让我们 将所有分类变量编码为 k-1 二进制值: X_train_enc = pd.get_dummies(X_train, drop_first=True) X_test_enc = pd.get_dummies(X_test, drop_first=True)
<st c="6773">pandas</st><st c="6783">get_dummies()</st><st c="6953">columns</st>
-
让我们检查一下生成的 DataFrame 的前五行: resulting DataFrame: X_train_enc.head()
<st c="7097">get_dummies()</st> <st c="7145">A1</st>

<st c="8116">pandas</st><st c="8126">get_dummies()</st><st c="8281">get_dummies()</st> <st c="8462">scikit-learn</st> <st c="8478">feature-engine</st>
<st c="8526">scikit-learn</st>
-
让我们 从 <st c="8600">scikit-learn</st>导入编码器 和 <st c="8577">ColumnTransformer</st>: from sklearn.preprocessing import OneHotEncoder from sklearn.compose import ColumnTransformer -
让我们创建一个包含分类变量名称的列表: cat_vars = X_train.select_dtypes( include="O").columns.to_list() -
让我们设置编码器以创建 k-1 个二进制变量: encoder = OneHotEncoder(drop="first", sparse_output=False)
<st c="9004">drop</st> <st c="9022">None</st><st c="9078">drop</st> <st c="9096">if_binary</st>
-
让我们 将编码限制在 分类变量 :ct = ColumnTransformer( [("encoder", encoder, cat_vars)], remainder="passthrough", force_int_remainder_cols=False, ).set_output(transform="pandas") -
让我们拟合编码器,使其识别要编码的类别: ct.fit(X_train) -
让我们检查将用二进制变量表示的类别: ct.named_transformers_["encoder"].categories_该转换器将为以下类别添加二进制变量:

<st c="10244">scikit-learn</st><st c="10261">OneHotEncoder()</st> <st c="10503">handle_unknown</st> <st c="10531">ignore</st><st c="10539">error</st><st c="10546">或</st>
-
让我们 编码分类变量: X_train_enc = ct.transform(X_train) X_test_enc = ct.transform(X_test)确保通过执行 <st c="10727">X_test_enc.head()</st>来检查结果。 -
为了熟悉输出,让我们打印结果 DataFrame 的变量名称: ct.get_feature_names_out()在以下图像中,我们看到转换后的 DataFrame 中的变量名称:

<st c="11779">ColumnTransformer()</st> <st c="11919">encoder</st> <st c="11991">remainder</st>
<st c="12063">featu</st><st c="12068">re-engine</st>
-
让我们 导入 编码器 来自 <st c="12115">f</st>``<st c="12116">eature-engine</st>: from feature_engine.encoding import OneHotEncoder -
让我们设置编码器,使其返回 *<st c="12227">k-1</st>*二元变量: ohe_enc = OneHotEncoder(drop_last=True)
<st c="12293">feature-engine</st>的<st c="12327">默认情况下编码所有分类变量。</st> <st c="12374">要编码变量的子集,请传递变量名称列表:</st><st c="12482">)。</st> <st c="12486">要编码数值变量,将 <st c="12538">参数设置为
-
让我们将编码器拟合到训练集,以便它学习要编码的类别和变量: 要编码的: ohe_enc.fit(X_train)
*<st c="12746">k-1</st>**<st c="12788">k</st> <st c="12807">drop_last_binary</st> <st c="12837">True</st>
-
让我们探索将要 编码的变量: ohe_enc.variables_Transformer 找到了并存储了对象或分类类型的变量,如下面的输出所示: 以下输出: ['A1', 'A4', 'A5', 'A6', 'A7', 'A9', 'A10', 'A12', 'A13'] -
让我们 探索将创建虚变量的类别: ohe_enc.encoder_dict_以下 字典包含将在每个变量中编码的类别: <st c="13269">{'A1': ['a', 'b'],</st> <st c="13288">'A4': ['u', 'y', 'Missing'],</st> <st c="13317">'A5': ['g', 'p', 'Missing'],</st> <st c="13346">'A6': ['c', 'q', 'w', 'ff', 'm', 'i', 'e', 'cc', 'x', 'd', 'k', 'j', 'Missing', 'aa'],</st> <st c="13433">'A7': ['v', 'ff', 'h', 'dd', 'z', 'bb', 'j', 'Missing', 'n'],</st> <st c="13495">'A9': ['t'],</st> <st c="13508">'A10': ['t'],</st> <st c="13522">'A12': ['t'],</st> <st c="13536">'A13': ['g', 's']}</st> -
让我们在训练集和测试集中编码分类变量: X_train_enc = ohe_enc.transform(X_train) X_test_enc = ohe_enc.transform(X_test)如果我们执行 <st c="13713">X_train_enc.head()</st>,我们将看到以下 DataFrame:

<st c="14325">A4</st> <st c="14368">A4_u</st><st c="14374">A4_y</st>
<st c="14468">ohe_enc.get</st><st c="14489">_feature_names_out()</st>
它的工作原理...
<st c="14607">pandas</st><st c="14615">scikit-learn</st><st c="14629">feature-engine</st>
<st c="14648">pandas</st><st c="14658">get_dummies()</st>
<st c="14976">pandas</st> <st c="15228">scikit-learn</st> <st c="15245">feature-engine</st>
<st c="15260">OneHotEncoder()</st> <st c="15282">scikit-learn</st> <st c="15298">feature-engine</st> <st c="15418">fit()</st><st c="15430">transform()</st><st c="15443">scikit-learn</st> <st c="15500">feature-engine</st>
<st c="15619">scikit-learn</st> <st c="15632">’s</st> <st c="15636">OneHotEncoder()</st> <st c="15651">默认情况下编码所有变量。</st> <st c="15686">要限制编码为</st> <st c="15714">分类变量,我们</st> <st c="15740">使用了</st> <st c="15745">ColumnTransformer()</st> <st c="15764">。我们将</st> <st c="15787">transform()</st> <st c="15798">的输出</st> <st c="15802">设置为</st> <st c="15808">pandas</st> <st c="15841">,以获得结果数据作为</st> <st c="15841">DataFrame。</st>
<st c="15853">注意</st>
<st c="15858">One-hot 编码适用于线性模型。</st> <st c="15907">它还扩展了特征空间。</st> <st c="15942">如果你的数据集包含许多分类变量或高度基数变量,你可以通过仅编码最频繁的类别来限制二进制变量的数量。</st> <st c="16119">你可以使用</st> <st c="16159">scikit-learn</st> <st c="16171">和</st> <st c="16176">feature-engine</st> <st c="16190">自动完成此操作,正如我们在</st> <st c="16213">Performing one-hot encoding of freq</st><st c="16248">uent</st> <st c="16254">categories</st> <st c="16264">recipe.</st> <st c="16213">执行频繁类别 one-hot 编码</st> <st c="16254">recipe.</st> <st c="16264">配方</st> <st c="16213">中描述的。</st>
<st c="16272">还有更多...</st>
<st c="16288">我们还可以使用 Category Encoders Python</st> <st c="16361">库</st> <st c="16370">https://</st><st c="16378">contrib.scikit-learn.org/category_encoders/onehot.html</st> <st c="16433">。</st>
<st c="16434">为了限制二进制变量的数量,我们可以选择要编码的类别和要忽略的类别;查看以下</st> <st c="16579">文章</st> <st c="16588">https://www.blog.trainindata.com/one-hot-encoding-</st><st c="16638">categorical-variables</st><st c="16660">/</st><st c="16662">。</st>
<st c="16663">执行频繁类别 one-hot 编码</st>
<st c="16714">One-hot 编码</st> <st c="16732">使用二进制变量表示每个变量的类别。</st> <st c="16791">因此,高度基数变量或具有多个分类特征的数据集的 one-hot 编码可以显著扩展特征空间。</st> <st c="16935">这反过来可能会增加使用机器学习模型的计算成本或降低其性能。</st> <st c="17053">为了减少二进制变量的数量,我们可以对最频繁的类别执行 one-hot 编码。</st> <st c="17160">对顶级类别进行 one-hot 编码</st> <st c="17177">相当于将剩余的、较少出现的类别视为一个单一的、独特的类别。</st>
在这个示例中,我们将使用 <st c="17291">pandas</st>、<st c="17380">Scikit-l</st><st c="17396">earn</st>、<st c="17403">和</st> <st c="17407">feature-engine</st> <st c="17421"> 来实现最流行类别的 one-hot 编码。
如何操作...
-
导入所需的 Python 库、函数、 和类: import pandas as pd import numpy as np from sklearn.model_selection import train_test_split -
让我们加载 Credit Approval 数据集并将其分为训练集和 测试集: data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, )
-
让我们检查 <st c="18084">A6</st>变量的唯一类别: X_train["A6"].unique()以下输出显示了 <st c="18141">A6</st>的唯一值: <st c="18343">A6</st>, sort them in decreasing order, and then display the five most frequent categories:X_train["A6"].value_counts().sort_values(
ascending=False).head(5)<st c="18496">We can</st> <st c="18503">see the five most frequent categories and the number of observations per category in the</st> <st c="18593">following output:</st>A6 c 93 q 56 w 48 i 41 ff 38 使用 步骤 4 中的代码在一个列表推导式中将 <st c="18717">A6</st>放入一个列表中: top_5 = [x for x in X_train[ <st c="18816">" A6"].value_counts().sort_values(</st> **<st c="18851">ascending=False).head(5).index ]</st>**
*****
```py
X_train_enc = X_train.copy()
X_test_enc = X_test.copy()
for label in top_5:
X_train_enc[f"A6_{label}"] = np.where(
X_train["A6"] == label, 1, 0)
X_test_enc[f"A6_{label}"] = np.where(
X_test["A6"] == label, 1, 0)
```
+ <st c="19178">让我们显示</st> <st c="19192">原始和编码变量</st> `<st c="19203">A6</st>`<st c="19249">,在</st> <st c="19258">训练集</st>中的</st> `<st c="19201">前 10</st>` <st c="19209">行:</st>
```py
X_train_enc[["A6"] + [f"A6_{
label}" for label in top_5]].head(10)
```
<st c="19335">在</st> *<st c="19353">步骤 7</st>*<st c="19359">的输出中,我们可以看到</st> `<st c="19376">A6</st>` <st c="19378">变量,后面跟着</st> <st c="19405">二元变量:</st>
```py
<st c="19422">A6 A6_c A6_q</st> <st c="19436">A6_w A6_i A6_ff</st>
<st c="19451">596 c 1 0 0 0 0</st>
<st c="19467">303 q</st> <st c="19474">0 1 0 0 0</st>
<st c="19483">204 w 0 0 1 0</st> <st c="19498">0</st>
<st c="19499">351 ff 0 0 0 0 1</st>
<st c="19515">118 m 0</st> <st c="19524">0 0 0 0</st>
<st c="19531">247 q 0 1 0 0 0</st>
<st c="19547">652</st> <st c="19551">i 0 0 0 1 0</st>
<st c="19563">513 e 0 0</st> <st c="19574">0 0 0</st>
<st c="19579">230 cc 0 0 0 0 0</st>
<st c="19677">scikit-learn</st>.
```
+ <st c="19690">让我们导入</st> <st c="19704">编码器:</st>
```py
from sklearn.preprocessing import OneHotEncoder
```
+ <st c="19764">让我们设置</st> <st c="19775">编码器以编码至少</st> `<st c="19829">39</st>` <st c="19831">个观察值中显示的类别,并将编码的类别数量限制为</st> `<st c="19890">5</st>`<st c="19894">:</st>
```py
encoder = OneHotEncoder(
min_frequency=39,
max_categories=5,
sparse_output=False,
).set_output(transform="pandas")
```
+ <st c="20010">最后,让我们</st> <st c="20025">将转换器拟合到两个高基数变量,然后转换</st> <st c="20100">数据:</st>
```py
X_train_enc = encoder.fit_transform(X_train[
['A6', 'A7']])
X_test_enc = encoder.transform(X_test[['A6', 'A7']])
```
<st c="20222">如果你执行</st> `<st c="20238">X_train_enc.head()</st>` <st c="20256">,你会看到</st> <st c="20272">生成的 DataFrame:</st>****
****
<st c="20843">feature-engine</st>
-
让我们设置 单热编码器,以对 <st c="20943">A6</st>和 <st c="20950">A7</st>变量的前五个最频繁类别进行编码: From feature_engine.encoding import OneHotEncoder ohe_enc = OneHotEncoder( top_categories=5, variables=["A6", "A7"] )
-
让我们将编码器拟合到训练集,以便它学习并存储 <st c="21271">A6</st>和 <st c="21278">A7</st>的最频繁类别: ohe_enc.fit(X_train) -
最后,让我们在训练集和测试集中对 <st c="21326">A6</st>和 <st c="21333">A7</st>进行编码: X_train_enc = ohe_enc.transform(X_train) X_test_enc = ohe_enc.transform(X_test)您可以通过执行 <st c="21524">X_train_enc.head()</st>来查看转换后的 DataFrame 中的新二进制变量。您还可以通过执行 <st c="21623">ohe_enc.encoder_dict_</st>来找到编码器学习到的前五个类别。
它是如何工作的...
<st c="21715">A6</st> <st c="21780">pandas</st><st c="21789">unique()</st><st c="21862">pandas</st><st c="21871">value_counts()</st><st c="21904">pandas</st> <st c="22096">pandas</st><st c="22104">sort_values()</st><st c="22191">pandas</st><st c="22200">head()</st><st c="22365">where()</st><st c="22424">1</st> <st c="22469">0</st>
<st c="22481">我们在</st> <st c="22506">scikit-learn</st> <st c="22521">和</st> <st c="22527">scikit-learn</st> <st c="22539">中的</st> <st c="22544">feature-engine</st> <st c="22558">的</st> <st c="22566">通过单热编码创建二元变量</st> <st c="22616">食谱中讨论了如何使用</st> <st c="22625">这里,我将只强调编码最频繁类别所需的参数。</st>
<st c="22714">使用</st> <st c="22724">scikit-learn</st> <st c="22750">来编码频繁类别,我们将</st> <st c="22775">min_frequency</st> <st c="22788">参数设置为</st> <st c="22802">39</st> <st c="22804">。因此,在少于</st> <st c="22843">39</st> <st c="22845">个观测值中显示的类别将被组合成一个额外的二元变量</st> <st c="22907">,称为</st> <st c="22914">infrequent_sklearn</st> <st c="22932">。</st>
使用<st c="22933">特征工程</st> <st c="22969">feature-engine</st> <st c="22983">,我们将</st> <st c="22996">top_categories</st> <st c="23010">参数设置为</st> <st c="23024">5</st> <st c="23025">。因此,该转换器只为 5 个最频繁的类别创建了二元变量。</st> <st c="23116">较少出现的类别将在所有</st> <st c="23147">二元变量中显示为</st> <st c="23154">0</st> <st c="23155">。</st>
<st c="23184">还有更多...</st>
<st c="23200">这个食谱基于 2009 年知识发现与数据挖掘杯赛(</st> <st c="23253">知识发现与数据挖掘</st> <st c="23282">(</st> <st c="23283">KDD</st> <st c="23286">) <st c="23282">)的获奖解决方案,</st> <st c="23306">Winning the KDD Cup Orange Challenge with Ensemble Selection</st> <st c="23366">(http://proceedings.mlr.press/v7/niculescu09/niculescu09.pdf),其中作者将单热编码限制为每个变量的 10 个最频繁类别。</st>
<st c="23527">用计数或观测值的频率替换类别</st>
与“计数”或“观测值的计数或频率”编码相比,我们用该类别的计数或观测值中显示该类别的比例来替换类别。
<st c="24143">注意</st>
<st c="24284">信息丢失</st>。
<st c="24370">pandas</st> <st c="24376">和</st>
如何做到这一点...
<st c="24455">pandas</st> <st c="24498">feature-engine</st>
-
让我们从 <st c="24536">进口</st>开始:import pandas as pd from sklearn.model_selection import train_test_split from feature_engine.encoding import CountFrequencyEncoder -
让我们加载信用批准数据集并将其分为训练集和 <st c="24748">测试集</st>:data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们 <st c="24951">使用计数或观测频率”来捕获</st>A7 <st c="25050">变量</st>的每个类别的观测数 ,并在一个字典中:counts = X_train["A7"].value_counts().to_dict()
<st c="25161">执行</st> <st c="25169">X_train["A7"].value_counts(normalize=True).to_dict()</st>
<st c="25237">print(counts)</st><st c="25304">A7</st>
<st c="25308">{'v': 277, 'h': 101, 'ff': 41, 'bb': 39, 'z': 7, 'dd': 5, 'j': 5, 'Missing': 4, 'n': 3, 'o': 1}</st>
-
让我们用 <st c="25436">A7</st>中的计数来替换 <st c="25472">数据集</st>中的类别:X_train_enc = X_train.copy() X_test_enc = X_test.copy() X_train_enc["A7"] = X_train_enc["A7"].map(counts) X_test_enc["A7"] = X_test_enc["A7"].map(counts)继续执行 <st c="25680">X_train_enc.head()</st>来验证类别已经被计数所替换: 要将此过程应用于多个变量,我们可以 <st c="25822">使用</st><st c="25826">feature-engine</st>。 -
让我们设置编码器,使其使用观测计数来编码所有分类变量: count_enc = CountFrequencyEncoder( encoding_method="count", variables=None, )
<st c="26026">CountFrequencyEncoder()</st> <st c="26050">将自动找到并编码训练集中的所有分类变量。</st> <st c="26233">要使用频率进行编码,请使用</st> <st c="26271">encoding_method="frequency"</st> `
-
让我们将编码器拟合到训练集,以便它存储每个变量每个类别的观测数: count_enc.fit(X_train) -
编码器自动找到了分类变量。 <st c="26497">让我们检查</st>`它们: count_enc.variables_<st c="26539">之前的命令返回了训练集中分类变量的名称:</st><st c="26625">['A1', 'A4', 'A5', 'A6', 'A7', 'A9', 'A10', 'A12', 'A13']</st> -
<st c="26683">让我们按变量打印每个类别的观测数:</st><st c="26748">count_enc.encoder_dict_</st><st c="26772">之前的属性存储了将要用于替换类别的映射:</st>

<st c="27366">图 2.7 – 包含每个变量的每个类别的观测数字典;这些值将用于编码分类变量</st>
-
<st c="27522">最后,让我们用“每个类别的观测数或频率”替换训练集和测试集中的类别:</st>X_train_enc = count_enc.transform(X_train) X_test_enc = count_enc.transform(X_test)
<st c="27726">通过执行 <st c="27779"> 来查看结果。编码器返回了 数据框,其中分类变量的字符串被替换为观测数的计数,使得变量准备好在机器学习模型中使用。</st>
<st c="27970">工作原理...</st>
<st c="27986">在这个配方中,我们使用 和 将类别替换为观测数。</st>
使用 <st c="28089">pandas</st> 的 <st c="28105">value_counts()</st>,我们确定了变量 <st c="28182">A7</st> 的每个类别的观测数,并且使用 <st c="28204">pandas</st> 的 <st c="28213">to_dict()</st>,我们将这些值捕获在一个“每个类别的观测数或频率”的字典中,其中每个键是一个唯一的类别,每个值是该类别的观测数。 使用 <st c="28412">pandas</st> 的 <st c="28421">map()</st> 和这个字典,我们在训练集和测试集中将类别替换为观测数。
<st c="28541">注意</st>
<st c="28546">编码的观测数应该从训练集中获得,以避免数据泄露。</st> 注意,测试集中的新类别将没有相应的映射,因此将被替换为 <st c="28763">。为了避免这种情况,请使用 <st c="28799">。或者,您可以将 <st c="28839">替换为<st c="28846">。</st>
使用 <st c="28879">feature-engine</st> 进行计数编码时,我们使用了 <st c="28903">CountFrequencyEncoder()</st> 并将 <st c="28935">encoding_method</st> 设置为 <st c="28954">'count'</st>。我们将 <st c="28975">variables</st> 参数设置为 <st c="29001">None</st>,以便编码器自动找到数据集中的所有类别变量。使用 <st c="29097">fit()</st>,转换器找到了类别变量,并将每个类别的观测计数存储在 <st c="29206">encoder_dict_</st> 属性中。使用 <st c="29236">transform()</st>,转换器将类别替换为计数,返回一个 <st c="29318">pandas</st> <st c="29324">DataFrame</st>。
注意
如果测试集中有在训练集中不存在的类别,编码器默认会引发错误。您可以使其忽略它们,在这种情况下,它们将显示为 <st c="29528">nan</st>,或者将它们编码为 <st c="29551">0</st>。
参见
您还可以使用 Python 库 Category Encoders 执行计数和频率编码:contrib.scikit-learn.org/category_encoders/count.html。
要查看计数编码的一些有用应用,请参阅这篇文章:https://。
替换类别为序数
序数编码包括将类别替换为从
在这个菜谱中,我们将使用 <st c="30339">pandas</st>、<st c="30348">scikit-learn</st>、<st c="30360">and</st> <st c="30366">feature-engine</st> 进行序数编码。
如何做到这一点...
-
导入 <st c="30459">pandas</st>和数据 分割函数: import pandas as pd from sklearn.model_selection import train_test_split -
让我们加载 Credit Approval 数据集并将其分为训练集和 测试集: data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
为了 编码 <st c="30847">A7</st>变量,让我们创建一个类别到整数的字典对: : ordinal_mapping = {k: i for i, k in enumerate( X_train["A7"].unique(), 0) }如果我们执行 <st c="31004">print(ordinal_mapping)</st>,我们将看到将替换每个类别的数字: <st c="31083">{'v': 0, 'ff': 1, 'h': 2, 'dd': 3, 'z': 4, 'bb': 5, 'j': 6, 'Missing': 7, 'n': 8, 'o': 9}</st> -
现在,让我们在 DataFrame 的副本中替换类别: : X_train_enc = X_train.copy() X_test_enc = X_test.copy() X_train_enc["A7"] = X_train_enc["A7"].map(ordinal_mapping) X_test_enc["A7"] = X_test_enc["A7"].map(ordinal_mapping)执行 <st c="31430">print(X_train["A7"].head())</st>以查看上一操作的 结果。 接下来,我们将使用 scikit-learn `执行顺序编码: -
让我们导入所需的 类: from sklearn.preprocessing import OrdinalEncoder from sklearn.compose import ColumnTransformer
<st c="31712">OrdinalEncoder()</st> <st c="31734">LabelEncoder()</st> <st c="31754">scikit-learn</st>
-
让我们设置 编码器: enc = OrdinalEncoder() -
让我们创建一个包含要编码的分类变量的列表: : cat_vars = X_train.select_dtypes(include="O").columns.to_list() -
让我们将编码限制在 分类变量 上: ct = ColumnTransformer( [("encoder", enc, cat_vars)], remainder="passthrough", force_int_remainder_cols=False, ).set_output(transform="pandas")
<st c="32286">remainder</st> <st c="32299">"passthrough"</st> <st c="32325">ColumnTransformer()</st>
-
让我们将编码器拟合到训练集,以便它创建并存储类别到数字的表示: : ct.fit(X_train)
<st c="32534">ct.named_transformers_["encoder"].categories_</st>
-
现在,让我们对训练集和 测试集中的分类变量进行编码: X_train_enc = ct.transform(X_train) X_test_enc = ct.transform(X_test)执行 <st c="32798">X_train_enc.head()</st>以检查生成的 DataFrame。
<st c="32859">ColumnTransformer()</st> <st c="32925">encoder</st> <st c="33001">remainder</st>
现在,让我们使用<st c="33018">。</st> <st c="33032">ordinal encoding</st> <st c="33055">feature-engine</st>
-
让我们导入编码器:
。 。 from feature_engine.encoding import OrdinalEncoder -
让我们设置编码器,使其在
步骤 7 中指定的分类变量中用任意整数替换类别: 。 enc = OrdinalEncoder( encoding_method="arbitrary", variables=cat_vars, )
<st c="33356">feature-engine</st><st c="33375">OrdinalEncoder()</st> <st c="33480">None</st>
-
让我们将编码器拟合到训练集中,以便它学习并存储类别到整数的映射:
。 。 。 。 enc.fit(X_train)
<st c="33818">注意</st>
分类到整数的映射存储在<st c="33875">encoder_dict_</st> <st c="33922">enc.encoder_dict_</st>
-
最后,让我们将训练集和测试集中的分类变量进行编码:
。 。 X_train_enc = enc.transform(X_train) X_test_enc = enc.transform(X_test)
<st c="34098">feature-engine</st> <st c="34122">pandas</st>
它是如何工作的...
在这个方法中,我们将类别替换为任意分配的整数
我们使用了<st c="34372">pandas</st><st c="34381">unique()</st> <st c="34516">’</st>
<st c="34572">接下来,我们使用</st> <st c="34617">scikit-learn</st> <st c="34629">的</st> <st c="34633">OrdinalEncoder()</st> <st c="34650">进行了序数编码,并使用</st> <st c="34659">ColumnTransformer()</st> <st c="34678">将编码限制为分类变量。</st> <st c="34730">使用</st> <st c="34735">fit()</st> <st c="34740">,转换器根据训练集中的类别创建了类别到整数的映射。</st> <st c="34841">使用</st> <st c="34846">transform()</st> <st c="34857">,类别被替换为整数。</st> <st c="34903">通过将</st> <st c="34918">remainder</st> <st c="34927">参数设置为</st> <st c="34941">passthrough</st> <st c="34952">,我们使</st> <st c="34962">ColumnTransformer()</st> <st c="34981">将未编码的变量连接到编码特征之后。</st>
使用<st c="35065">feature-engine</st>进行序数编码时,我们使用了<st c="35123">OrdinalEncoder()</st>,表示整数应通过<st c="35209">encoding_method</st>任意分配,并在<st c="35280">variables</st> <st c="35289">参数</st>中传递一个包含要编码变量的列表。<st c="35305">fit()</st><st c="35398">encoder_dict_</st> <st c="35411">属性</st>中。<st c="35428">映射</st>随后被<st c="35460">transform()</st> <st c="35471">方法</st>使用,以替换训练集和测试集中的类别,并返回 DataFrames。
<st c="35554">注意</st>
<st c="35559">当测试集中的类别不在训练集中时,它将没有映射到数字。</st> <st c="35667">OrdinalEncoder()</st> <st c="35683">来自</st> <st c="35689">scikit-learn</st> <st c="35701">和</st> <st c="35706">feature-engine</st> <st c="35720">默认情况下会引发错误。</st> <st c="35753">然而,它们可以选择用用户定义的值或
<st c="35858">scikit-learn</st> <st c="35871">的</st> <st c="35875">OrdinalEncoder()</st> <st c="35891">可以限制编码到具有最小频率的类别。</st> <st c="35964">feature-engine</st> <st c="35978">的</st> <st c="35982">OrdinalEncoder()</st> <st c="35998">可以根据目标
<st c="36093">还有更多...</st>
您还可以使用来自 Category Encoders 的 <st c="36155">OrdinalEncoder()</st>
基于目标值执行序数编码
在上一个
这种编码方法在分类变量和响应之间创建了一个单调关系,因此使变量更适合用于线性模型。
在这个配方中,我们将使用 <st c="37108">pandas</st> <st c="37119">feature-engine</st>
如何做到这一点...
首先,让我们导入必要的 Python 库并准备好数据集:
-
导入所需的 Python 库、函数和类:
import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split -
让我们加载 Credit Approval 数据集并将其分为训练集和测试集:
data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们在
<st c="37715">A7</st>中确定每个类别的平均目标值,然后按从最低到最高的目标值对类别进行排序: y_train.groupby(X_train["A7"]).mean().sort_values()以下是在先前的命令的输出:
<st c="37914">A7</st> <st c="37917">o 0.000000</st> <st c="37928">ff 0.146341</st> <st c="37940">j 0.200000</st> <st c="37951">dd 0.400000</st> <st c="37963">v 0.418773</st> <st c="37974">bb 0.512821</st> <st c="37986">h 0.603960</st> <st c="37997">n 0.666667</st> <st c="38008">z 0.714286</st> <st c="38019">Missing 1.000000</st> <st c="38036">Name: target, dtype: float64</st> -
现在,让我们重复在
步骤 3 中的计算,但这次,让我们保留有序的 类别名称: ordered_labels = y_train.groupby( X_train["A7"]).mean().sort_values().index要显示先前的命令的输出,我们可以执行
<st c="38306">print(ordered_labels)</st>: <st c="38330">Index(['o', 'ff', 'j', 'dd', 'v', 'bb', 'h', 'n', 'z', 'Missing'],</st><st c="38397">dtype='object', name='A7')</st>。 -
让我们创建一个类别到整数的字典,使用我们在 步骤 4 中创建的有序列表: ordinal_mapping = { k: i for i, k in enumerate(ordered_labels, 0) }我们可以通过执行 print(ordinal_mapping) `来可视化前面代码的结果: <st c="38847">A7</st> in a copy of the datasets:X_train_enc = X_train.copy()
X_test_enc = X_test.copy()
X_train_enc["A7"] = X_train_enc["A7"].map(
ordinal_mapping)X_test_enc["A7"] = X_test_enc["A7"].map(
ordinal_mapping)
<st c="39164">np.nan</st>
<st c="39267">A7</st>
-
让我们绘制 <st c="39382">A7</st>变量每个类别的均值目标响应: y_train.groupby(X_train["A7"]).mean().plot() plt.title("Relationship between A7 and the target") plt.ylabel("Mean of target") plt.show()我们可以从以下图中看到 <st c="39596">A7</st>类别与目标之间的非单调关系:

-
让我们绘制编码变量中每个类别的均值目标值: y_train.groupby(X_train_enc["A7"]).mean().plot() plt.title("Relationship between A7 and the target") plt.ylabel("Mean of target") plt.show()编码变量与目标之间存在单调关系——目标均值目标值越高,分配给 的类别 的数字就越高:

<st c="40393">feature-engine</st>
-
让我们导入 编码器: from feature_engine.encoding import OrdinalEncoder -
接下来,让我们设置编码器,使其根据目标均值值将整数分配给数据集中所有分类变量: ordinal_enc = OrdinalEncoder( encoding_method="ordered", variables=None)
<st c="40699">OrdinalEncoder()</st> <st c="40864">variables</st> <st c="40919">ignore_format=True</st>
-
让我们将编码器拟合到训练集,以便它找到类别变量,然后存储 类别和 整数映射: ordinal_enc.fit(X_train, y_train) -
最后,让我们在训练集和 测试集中将类别替换为数字: X_train_enc = ordinal_enc.transform(X_train) X_test_enc = ordinal_enc.transform(X_test)
它是如何工作的...
[使用feature-engine进行编码时,我们使用了OrdinalEncoder(),将encoding_method设置为ordered。我们将参数变量设置为None,以便编码器自动检测数据集中的所有分类变量。 [使用fit(),编码器找到了分类变量,并根据目标均值值将数字分配给它们的类别。 [分类变量的名称和类别到数字对的字典分别存储在variables_和encoder_dict_属性中。 [最后,使用transform(),我们在训练集和测试集中将类别替换为数字,返回pandas DataFrame。]
另请参阅
要使用 Category Encoders 实现此菜谱,请访问本书的 GitHub 仓库:
实现目标均值编码
**均值编码** 或 **目标编码** 将每个类别映射到目标属性的概率估计值。如果目标是二进制,则数值映射是目标在给定类别值条件下的后验概率。如果目标是连续的,则数值表示是给定类别值的目标的期望值。]
在其最简单形式中,每个类别的数值表示是通过特定类别组的目标变量的平均值给出的。<st c="43913">城市</st> <st c="43951">伦敦</st><st c="43959">曼彻斯特</st><st c="43975">布里斯托尔</st><st c="44052">0</st> <st c="44058">1</st><st c="44086">伦敦</st> <st c="44112">0.3</st><st c="44154">曼彻斯特</st> <st c="44184">0.2</st><st c="44301">伦敦</st><st c="44309">曼彻斯特</st><st c="44325">布里斯托尔</st>
在数学术语中,如果目标是二进制,则替换值,



当类别组较大时,
权重因子,

在这里,
在scikit-learn和feature-engine中,

在这里,
注意
<st c="47505">scikit-learn</st> <st c="47522">feature-engine</st>
如何操作...
-
导入 <st c="47591">pandas</st>以及数据 `分割函数 :import pandas as pd from sklearn.model_selection import train_test_split -
让我们加载 Credit Approval 数据集并将其分为训练集和 测试集: data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们从 <st c="48000">scikit-learn</st>导入转换器 : from sklearn.preprocessing import TargetEncoder from sklearn.compose import ColumnTransformer -
让我们 创建一个包含分类变量名称的列表: cat_vars = X_train.select_dtypes( include="O").columns.to_list() -
让我们设置编码器,使其使用目标方差来确定权重因子,正如菜谱开头所述: enc = TargetEncoder(smooth="auto", random_state=9) -
让我们将插补限制在 分类变量: ct = ColumnTransformer( [("encoder", enc, cat_vars)], remainder="passthrough", ).set_output(transform="pandas") -
让我们拟合编码器并转换 数据集: X_train_enc = ct.fit_transform(X_train, y_train) X_test_enc = ct.transform(X_test)通过执行 <st c="48755">X_train_enc.head()</st>` 来查看结果。
<st c="48784">fit_transform()</st> <st c="48810">scikit-learn</st><st c="48826">TargetEncoder()</st> <st c="48872">fit().transform()</st><st c="48896">fit_transform()</st>
<st c="49176">feature-engine</st>
-
让我们导入 编码器: from feature_engine.encoding import MeanEncoder -
让我们设置目标均值编码器以编码所有分类变量,同时 应用平滑处理: mean_enc = MeanEncoder(smoothing="auto", variables=None)
<st c="49426">MeanEncoder()</st> <st c="49502">auto</st>
-
让我们将变压器拟合到训练集,以便它学习并存储每个类别的平均目标值 每个变量: mean_enc.fit(X_train, y_train) -
最后,让我们对训练集和 测试集进行编码: X_train_enc = mean_enc.transform(X_train) X_test_enc = mean_enc.transform(X_test)
<st c="49957">encoder_dict_</st> <st c="50017">mean_enc.encoder_dict_.</st>
它是如何工作的…
<st c="50139">scikit-learn</st> <st c="50156">feature-engine</st>
<st c="50187">scikit-learn</st><st c="50209">TargetEncoder()</st><st c="50238">smooth</st> <st c="50279">auto</st><st c="50408">fit()</st><st c="50499">transform()</st>
<st c="50554">TargetEncoder()</st><st c="50575">fit()</st> <st c="50600">transform()</st> <st c="50650">fit_transform()</st><st c="50776">fit_transform()</st> <st c="50932">encodings_</st> <st c="50972">fit()</st> <st c="50982">fit_transform()</st><st c="51052">transform()</st> <st c="51139">fit()</st> <st c="51148">fit_transform()</st>
<st c="51277">feature-engine</st><st c="51295">MeanEncoder()</st>可以返回错误,用
<st c="51462">MeanEncoder(),</st> <st c="51489">smoothing</st> <st c="51512">auto</st><st c="51523">fit()</st><st c="51634">transform()</st><st c="51699">pandas</st>
还有更多...


<st c="52786">TargetEncoder()</st>的一个很好的替代方案,因为,在 Category Encoders 实现的目标编码中,我们需要优化两个参数而不是一个(正如我们在<st c="52934">feature-engine</st> <st c="52948">和</st> <st c="52953">scikit-learn</st> <st c="52965">中做的那样),以控制<st c="52979">平滑度。</st>
<st c="53046">MEstimateEncoder()</st>实现此编码方法,请访问本书的 GitHub
使用证据权重进行编码

-
WoE = 0 if p(positive) / p(negative) = 1 ; 即,如果结果 是随机的 -
WoE > 0 if p(positive) > p(negative) -
如果 0 ,则 WoE < p(negative) > p(positive)

<st c="55079">pandas</st> <st c="55090">feature-engine</st>
如何操作...
-
导入所需的库 和函数: import numpy as np import pandas as pd from sklearn.model_selection import train_test_split -
让我们加载 信用批准数据集并将其分为训练集和 测试集: data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们求出目标值的逆,以便计算 负案例: neg_y_train = pd.Series( np.where(y_train == 1, 0, 1), index=y_train.index ) -
让我们确定目标变量取值为 <st c="55833">1</st>或 <st c="55837">0</st>的观测数: total_pos = y_train.sum() total_neg = neg_y_train.sum() -
现在,让我们计算 WOE 公式的分子和分母,这是我们之前在这个菜谱中讨论过的: pos = y_train.groupby( X_train["A1"]).sum() / total_pos neg = neg_y_train.groupby( X_train["A1"]).sum() / total_neg -
现在,让我们 计算每个类别的 WOE: woe = np.log(pos/neg)我们可以通过执行 print(woe) 来显示类别与 WOE 对的序列: <st c="56276">A1</st> <st c="56278">Missing 0.203599</st> <st c="56295">a 0.092373</st> <st c="56306">b -0.042410</st> <st c="56375">A1</st> with the WoE in a copy of the datasets:X_train_enc = X_train.copy()
X_test_enc = X_test.copy()
X_train_enc["A1"] = X_train_enc["A1"].map(woe)
X_test_enc["A1"] = X_test_enc["A1"].map(woe)
<st c="56565">You can inspect the e</st><st c="56587">ncoded variable by</st> <st c="56607">executing</st> `<st c="56617">X_train_enc["A1"].head()</st>`<st c="56641">.</st><st c="56642">Now, let’s perform WoE encoding</st> <st c="56675">using</st> `<st c="56681">feature-engine</st>`<st c="56695">.</st> -
让我们导入 编码器: <st c="56722">from feature_engine.encoding import WoEEncoder</st> -
接下来,让我们设置编码器以编码三个 分类变量: woe_enc = WoEEncoder(variables = ["A1", "A9", "A12"])
对于罕见类别,可能会发生
-
让我们将 转换器拟合到训练集,以便它学习并存储不同类别的 WoE: woe_enc.fit(X_train, y_train)
-
最后,让我们对训练集和 测试集中的三个分类变量进行编码: X_train_enc = woe_enc.transform(X_train) X_test_enc = woe_enc.transform(X_test)
<st c="57527">feature-engine</st> <st c="57551">pandas</st>
它是如何工作的...
<st c="57744">pandas</st> <st c="57755">feature-engine</st>
<st c="57794">pandas</st><st c="57803">sum()</st> <st c="57813">groupby()</st> <st c="57827">numpy</st><st c="57836">log()</st>
<st c="57950">feature-engine</st><st c="57978">WoEEncoder()</st><st c="58036">fit()</st> <st c="58064">tra</st><st c="58067">nsform()</st>
另请参阅
分组罕见或不常见类别
<st c="59068">Rare</st> <st c="59076">Other</st>
<st c="59141">pandas</st> <st c="59152">feature-engine</st>
如何操作...
-
导入必要的 Python 库、函数和类: import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from feature_engine.encoding import RareLabelEncoder -
让我们加载 Credit Approval 数据集并将其分为训练集和 测试集: data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们 捕获 A7 中每个类别的观测值比例 在一个变量中: freqs = X_train["A7"].value_counts(normalize=True)我们可以看到以下输出中 A7 每个类别的观测值比例,以小数表示,在执行 <st c="59989">print(freqs)</st>之后: <st c="60003">v 0.573499</st> <st c="60013">h 0.209110</st> <st c="60024">ff 0.084886</st> <st c="60036">bb 0.080745</st> <st c="60048">z 0.014493</st> <st c="60059">dd 0.010352</st> <st c="60071">j 0.010352</st> <st c="60082">Missing 0.008282</st> <st c="60099">n 0.006211</st> <st c="60110">o 0.002070</st> <st c="60234">z</st>, <st c="60237">dd</st>, <st c="60241">j</st>, <st c="60244">Missing</st>, <st c="60253">n</st>, and <st c="60260">o</st> are rare categories. -
让我们创建一个包含在超过 5%的 观测中存在的类别名称的列表: frequent_cat = [ x for x in freqs.loc[freqs > 0.05].index.values]如果我们执行 <st c="60467">print(frequent_cat)</st>,我们将看到 A7 的 频繁类别:<st c="60646">Rare</st> string in a copy of the datasets:X_train_enc = X_train.copy()
X_test_enc = X_test.copy()
X_train_enc["A7"] = np.where(X_train["A7"].isin(
frequent_cat), X_train["A7"], "Rare")X_test_enc["A7"] = np.where(X_test["A7"].isin(
frequent_cat), X_test["A7"], "Rare") -
让我们确定编码变量的观测值百分比: X_train["A7"].value_counts(normalize=True)我们可以看到不频繁的标签现在已经被重新组合到 <st c="61099">Rare</st>类别中: <st c="61113">v 0.573499</st> <st c="61124">h 0.209110</st> <st c="61135">ff 0.084886</st> <st c="61147">bb 0.080745</st> <st c="61159">Rare 0.051760</st> <st c="61236">feature-engine</st>. -
让我们 创建一个稀疏标签编码器,将出现在观测值不到 5%的类别组合在一起,前提是分类变量有超过四个 不同值: rare_encoder = RareLabelEncoder(tol=0.05, n_categories=4) -
<st c="61486">让我们拟合编码器,以便它找到分类变量,然后学习它们的最高频率类别:</st>rare_encoder.fit(X_train)
<st c="61625">注意</st>
<st c="61630">在拟合过程中,转换器将发出警告,表明许多分类变量少于四个类别,因此它们的值不会被分组。</st> <st c="61796">转换器只是通知你这种情况正在发生。</st>
我们可以通过执行<st c="61920">rare_encoder.encoder_dict_</st><st c="61997">executing</st> <st c="62007">rare_encoder.variables_</st>
-
最后,让我们将训练集和测试集中的稀有标签分组:
X_train_enc = rare_encoder.transform(X_train) X_test_enc = rare_encoder.transform(X_test)
现在我们已经将稀有标签分组,我们准备对分类变量进行编码,就像我们在本章前面的食谱中所做的那样。
<st c="62326">它的工作原理...</st>
<st c="62342">在这个食谱中,我们使用 <st c="62404">和<st c="62423">将不频繁的类别分组。</st>
我们确定了<st c="62488">A7</st> <st c="62506">pandas</st><st c="62515">value_counts()</st> <st c="62545">normalize</st> <st c="62568">True</st><st c="62708">where()</st><st c="62741">A7</st><st c="62839">pandas</st><st c="62848">isin()</st><st c="62908">Rare</st>
<st c="62988">feature-engine</st><st c="63006">RareLabelEncoder()</st><st c="63037">tol</st> <st c="63044">0.05</st><st c="63129">n_categories</st> <st c="63145">4</st><st c="63228">fit()</st><st c="63348">transform</st><st c="63357">()</st><st c="63418">Rare</st>
执行二进制编码
<st c="63808">1</st> <st c="63850">1-0</st><st c="63863">2</st> <st c="63870">0-1</st><st c="63883">3</st> <st c="63890">1-1</st><st c="63907">0</st> <st c="63914">0-0</st>

<st c="64316">颜色</st>
如何做到这一点...
-
导入所需的 Python 库、函数、 和类: import pandas as pd from sklearn.model_selection import train_test_split from category_encoders.binary import BinaryEncoder -
让我们加载 Credit Approval 数据集并将其分为训练集和 测试集: data = pd.read_csv("credit_approval_uci.csv") X_train, X_test, y_train, y_test = train_test_split( data.drop(labels=["target"], axis=1), data["target"], test_size=0.3, random_state=0, ) -
让我们检查 A7中的唯一类别: X_train["A7"].unique()在以下输出中,我们可以看到 <st c="66162">A7</st>有 10 个不同的类别: <st c="66315">A7</st>:encoder = BinaryEncoder(cols=["A7"],
drop_invariant=True)
<st c="66382">BinaryEncoder()</st><st c="66555">cols</st>
-
让我们将转换器拟合到训练集中,以便它计算所需的二进制变量数量并创建变量到二进制 代码表示: encoder.fit(X_train) -
最后,让我们将 <st c="66770">A7</st>编码到训练集和测试集中: X_train_enc = encoder.transform(X_train) X_test_enc = encoder.transform(X_test)我们可以 通过执行 <st c="66951">print(X_train_enc.head())</st>来显示转换后的训练集的前几行,它返回以下输出:

<st c="67449">A7</st><st c="67463">A7_0</st><st c="67469">A7_1</st><st c="67475">A7_2</st><st c="67485">A</st><st c="67486">7_3</st>
它是如何工作的...
在这个食谱中,我们使用了 Category Encoders 包进行二进制编码。<st c="67668">BinaryEncoder()</st> <st c="67698">A</st><st c="67699">7</st> <st c="67720">fit()</st> <st c="67734">BinaryEncoder()</st> <st c="67825">transform()</st> <st c="67869">A7</st>
第四章:3
变换数值变量
<st c="507">0</st>
-
使用对数函数变换变量 -
使用倒数函数变换变量 -
使用平方根来 变换变量 -
使用 幂变换 -
执行 Box-Cox 变换 s -
执行 Yeo-Johnson 变换 s
使用对数函数变换变量
<st c="2138">收入</st>
准备中
如何做到这一点...
-
导入所需的 Python 库 和数据集: import numpy as np import pandas as pd import matplotlib.pyplot as plt import scipy.stats as stats from sklearn.datasets import fetch_california_housing -
让我们 将加利福尼亚住房数据集加载到一个 pandas DataFrame: X, y = fetch_california_housing(return_X_y=True, as_frame=True) -
让我们通过绘制直方图来探索数据集中所有变量的分布 使用 pandas: X.hist(bins=30, figsize=(12, 12)) plt.show()在以下输出中,我们可以看到 <st c="4084">MedInc</st>变量显示出轻微的右偏斜分布,变量 AveRooms <st c="4167">和</st>Population <st c="4182">严重右偏斜,而</st>HouseAge <st c="4225">变量在其</st>范围内 `的值分布得更加均匀:

-
为了 评估 变换对变量分布的影响 ,我们将创建一个函数,该函数接受 DataFrame 和变量名作为输入,并在 Q-Q 图旁边绘制直方图: def diagnostic_plots(df, variable): plt.figure(figsize=(15,6)) plt.subplot(1, 2, 1) df[variable].hist(bins=30) plt.title(f"Histogram of {variable}") plt.subplot(1, 2, 2) stats.probplot(df[variable], dist="norm", plot=plt) plt.title(f"Q-Q plot of {variable}") plt.show() -
让我们 用 步骤 4 中的函数绘制 <st c="5257">MedInc</st>变量的分布图: diagnostic_plots(X, "MedInc")以下输出显示 <st c="5367">MedInc</st>具有右偏斜的分布:

-
首先,让我们复制原始 DataFrame: X_tf = X.copy()我们已经创建了一个副本,这样我们就可以修改副本中的值,而不是原始 DataFrame 中的值,这对于本食谱的其余部分是必需的。
<st c="5891">X_tf = X</st> <st c="5925">copy()</st><st c="5942">X_tf</st> <st c="6062">X_tf</st> <st c="6088">X</st>
-
让我们 创建一个包含我们想要转换的变量的列表: vars = ["MedInc", "AveRooms", "AveBedrms", "Population"] -
让我们使用 NumPy 对步骤 7 中的变量应用对数变换,并将转换后的变量捕获在新 DataFrame 中: X_tf[vars] = np.log(X[vars])
<st c="6645">1</st> <st c="6653">X_tf[vars] = np.log(X[vars] +</st> <st c="6683">1)</st>
-
让我们使用 步骤 4 中的诊断函数检查变换后 <st c="6719">MedInc</st>的分布: diagnostic_plots(X_tf, "MedInc")在以下输出中,我们可以看到对数变换返回了一个分布更均匀的变量,在 Q-Q 图 中更好地逼近理论正态分布:

<st c="7477">scikit-learn</st>
-
让我们 导入 <st c="7504">FunctionTransformer()</st>: from sklearn.preprocessing import FunctionTransformer在我们继续之前,我们需要复制原始数据集,就像我们在 步骤 6 中所做的那样。 -
我们将设置变压器以应用对数并能够将转换后的变量还原到其 原始表示: transformer = FunctionTransformer(np.log, inverse_func=np.exp)
<st c="7885">FunctionTransformer()</st> <st c="7935">validate=False</st><st c="8028">validate</st> <st c="8040">True</st><st c="8095">fit</st>
-
让我们将 从 步骤 7 转换正变量: X_tf[vars] = transformer.transform(X[vars])
<st c="8553">set_output</st> <st c="8623">ColumnTransformer()</st>
-
现在让我们将转换回原始 变量表示: X_tf[vars] = transformer.inverse_transform(X_tf[vars])如果你通过执行 <st c="8901">diagnostic_plots(X_tf, "MedInc")</st>来检查分布,你应该看到一个与 第 5 步 返回的相同的图表。
<st c="9094">transformer = FunctionTransformer(lambda x: np.log(x +</st> <st c="9149">1)</st><st c="9151">)</st>
-
让我们导入 的 <st c="9240">LogTransformer()</st>: from feature_engine.transformation import LogTransformer -
我们将设置转换器以转换第 7 步 的变量,然后拟合转换器到 数据集: lt = LogTransformer(variables = vars) lt.fit(X)
<st c="9489">变量</st> <st c="9519">None</st><st c="9525">LogTransformer()</st> <st c="9608">fit()</st>
-
最后,让我们 转换 数据: X_tf = lt.transform(X)<st c="9752">X_tf</st>是 <st c="9775">X</st>DataFrame 的一个副本,其中第 7 步 的变量已用对数进行转换。 -
我们还可以将转换后的变量转换回它们的 原始表示: X_tf = lt.inverse_transform(X_tf)如果你检查第 17 步 后的变量的分布,它们应该与原始数据的分布相同。
<st c="10092">Feature-engine 有一个专门的转换器,在应用对数变换之前向变量添加常数值。</st>
它是如何工作的...
在这个方法中,我们使用 NumPy、scikit-learn 和 Feature-engine 对正变量子集应用了对数变换
为了比较变换对变量分布的影响,我们创建了一个诊断函数来在 Q-Q 图旁边绘制直方图<st c="10633">scipy.stats.probplot()</st><st c="10820">dist</st> <st c="10838">norm</st> <st c="10866">matplotlib</st> <st c="10912">plot</st> <st c="10927">plt</st>
通过使用<st c="10940">plt.figure()</st> <st c="10957">figsize</st><st c="11011">plt.subplot()</st><st c="11056">一</st> <st c="11069">两</st> <st c="11139">plt.subplot()</st>
为了测试该函数,我们在变换之前为<st c="11440">MedInc</st> <st c="11501">MedInc</st>
<st c="11700">np.log()</st><st c="11894">MedInc</st>
<st c="12137">FunctionTransformer()</st> <st c="12242">np.log()</st> <st c="12316">exp()</st> <st c="12356">FunctionTransfomer()</st><st c="12387">transform()</st> <st c="12504">inverse_transform()</st>
<st c="12625">LogTransformer()</st> <st c="12703">variables</st> <st c="12728">fit()</st><st c="12816">transform()</st><st c="12840">np.log()</st> <st c="12906">inverse_transform()</st>
还有更多……
<st c="13161">LogCpTransformer()</st><st c="13181">LogCpTransformer()</st>
-
将相同的常数添加到 所有变量 -
自动识别并添加所需的最小值以使 变量为正 -
将用户定义的不同值添加到 不同的变量中。
<st c="13436">LogCpTransformer()</st>
使用倒数函数转换变量
<st c="14259">NumPy</st><st c="14266">scikit-learn</st><st c="14284">Feature-engine</st>
如何操作...
-
导入所需的 Python 库 和数据: import numpy as np import pandas as pd import matplotlib.pyplot as plt import scipy.stats as stats from sklearn.datasets import fetch_california_housing -
让我们加载 加利福尼亚 住房数据集: X, y = fetch_california_housing(return_X_y=True, as_frame=True) -
为了评估变量分布,我们将创建一个函数,该函数接受 DataFrame 和变量名作为输入,并在 Q-Q 图旁边绘制直方图: def diagnostic_plots(df, variable): plt.figure(figsize=(15,6)) plt.subplot(1, 2, 1) df[variable].hist(bins=30) plt.title(f"Histogram of {variable}") plt.subplot(1, 2, 2) stats.probplot(df[variable], dist="norm", plot=plt) plt.title(f"Q-Q plot of {variable}") plt.show() -
现在,让我们绘制 <st c="15240">AveOccup</st>变量的分布,该变量指定了房屋的平均占用情况: diagnostic_plots(X, "AveOccup")变量 <st c="15348">AveOccup</st>显示了一个非常强的 右偏分布,如下面的输出所示:

<st c="15772">AveOccup</st> <st c="16073">data = fetch_california_housing()</st> <st c="16119">print(data.DESCR)</st>
-
首先,让我们复制原始 DataFrame,这样我们就可以修改副本中的值,而不是原始数据,这对于接下来的 这个菜谱 是必需的: X_tf = X.copy()
<st c="16415">X_tf = X</st> <st c="16449">copy()</st> <st c="16537">X_tf</st> <st c="16564">X</st>
-
让我们 将倒数变换应用到 <st c="16623">AveOccup</st>变量上: X_tf["AveOccup"] = np.reciprocal(X_tf["AveOccup"]) -
让我们使用我们在第 3 步中创建的诊断函数检查变换后的 <st c="16729">AveOccup</st>变量分布: diagnostic_plots(X_tf, "AveOccup")
<st c="16890">AveOccup</st>
<st c="17083">AveOccup</st>

-
让我们导入 FunctionTransformer() : from sklearn.preprocessing import FunctionTransformer -
让我们通过传递 <st c="17611">np.reciprocal</st>作为参数来设置转换器: transformer = FunctionTransformer(np.reciprocal)
<st c="17707">FunctionTransformer()</st>
-
现在,让我们 复制原始数据集并转换变量: X_tf = X.copy() X_tf["AveOccup"] = transformer.transform( X["AveOccup"])您可以使用 步骤 3 中的函数来检查转换的效果。
<st c="18127">transform()</st> <st c="18253">inverse_transform</st> <st c="18288">FunctionTransformer()</st> <st c="18313">np.reciprocal</st>
<st c="18388">feature-engine</st>
-
让我们导入 的 <st c="18421">ReciprocalTransformer()</st>: from feature_engine.transformation import ReciprocalTransformer -
让我们设置转换器以修改 <st c="18554">AveOccup</st>变量,并将其拟合到 数据集: rt = ReciprocalTransformer(variables=»AveOccup») rt.fit(X)
<st c="18675">变量</st> <st c="18704">None</st><st c="18840">0</st>
-
让我们 转换数据集中选择的变量: X_tf = rt.transform(X)<st c="18964">ReciprocalTransformer()</st>将返回一个包含原始变量的新 pandas DataFrame,其中在 步骤 12 中指示的变量通过 以下 倒数函数进行转换。
如何工作...
<st c="19546">AveOccup</st>
<st c="19783">np.reciprocal()</st> <st c="19854">变换后,</st> <st c="19880">AveOccup</st>
<st c="20063">np.reciprocal()</st> <st c="20099">FunctionTransformer()</st><st c="20126">transform()</st> <st c="20153">np.reciprocal()</st>
<st c="20216">FunctionTransformer()</st> <st c="20271">ColumnTransformer()</st>
<st c="20406">ReciprocalTransformer()</st> <st c="20472">fit()</st><st c="20541">transform()</st><st c="20578">np.reciprocal()</st>
<st c="20653">ReciprocalTransformer()</st> <st c="20777">inverse_transform()</st>
<st c="20889">reciprocal()</st> <st c="21022">Pipeline</st>
FunctionTransformer()<st c="21106">ReciprocalTransformer()<st c="21129">之间的区别在于,前者可以应用任何用户指定的变换,而后者仅应用倒数函数。</st><st c="21344">另一方面,Feature-engine 的转换器返回 pandas DataFrame,并且可以在不使用</st>
使用平方根变换变量
<st c="21864">平方根变换是一种幂变换形式,其中指数为</st> **<st c="21951">1/2</st>** <st c="21954">,并且仅对</st>
<st c="22115">换句话说,它是一种计数分布。</st>
如何实现...
<st c="22672">泊松分布</st>
-
让我们首先导入必要的库: import numpy as np import pandas as pd import scipy.stats as stats -
让我们 创建一个包含两个变量的 DataFrame,这两个变量分别从均值为 <st c="22909">2</st>和 <st c="22915">3</st>的泊松分布中抽取,并且 <st c="22936">10000</st>个观测值: df = pd.DataFrame() df["counts1"] = stats.poisson.rvs(mu=3, size=10000) df["counts2"] = stats.poisson.rvs(mu=2, size=10000) -
让我们创建一个函数,它接受一个 <st c="23117">DataFrame</st>和一个变量名作为输入,并在 Q-Q 图旁边绘制一个条形图,显示每个值的观测数: def diagnostic_plots(df, variable): plt.figure(figsize=(15,6)) plt.subplot(1, 2, 1) df[variable].value_counts().sort_index(). plot.bar() plt.title(f"Histogram of {variable}") plt.subplot(1, 2, 2) stats.probplot(df[variable], dist="norm", plot=plt) plt.title(f"Q-Q plot of {variable}") plt.show() -
让我们使用第 3 步中的函数为数据中的一个变量创建条形图和 Q-Q 图: diagnostic_plots(df, "counts1")在这里,我们可以看到输出中的泊松分布:

-
现在,让我们 <st c="23937">复制</st><st c="23952">数据集:</st>df_tf = df.copy() -
让我们将平方根转换应用于 <st c="23998">两个变量:</st>df_tf[["counts1", "counts2"]] = np.sqrt( df[["counts1","counts2"]]) -
让我们将值四舍五入到两位小数,以便更好地可视化: df_tf[["counts1", "counts2"]] = np.round( df_tf[["counts1", "counts2"]], 2) -
让我们绘制转换后 <st c="24286">counts1</st>的分布图: diagnostic_plots(df_tf, "counts1")我们看到方差更加 稳定 ,因为 Q-Q 图中的点更紧密地遵循 45 度对角线:

<st c="24775">应用</st> <st c="24796">平方根</st> <st c="24812">转换</st> <st c="24817">scikit-learn</st>
-
让我们导入 <st c="24844">FunctionTransformer()</st>并将其设置为执行平方根转换: from sklearn.preprocessing import FunctionTransformer transformer = FunctionTransformer( np.sqrt).set_output(transform="pandas")
<st c="25145">transformer = FunctionTransformer(func=lambda x:</st> <st c="25194">np.round(np.sqrt(x), 2))</st>
-
让我们复制数据并转换 <st c="25264">变量:</st>df_tf = df.copy() df_tf = transformer.transform(df)像我们在第 8 步中做的那样,检查转换的结果: 要使用 Feature-engine 应用平方根,我们使用指数为 0.5 的 <st c="25469">PowerTransformer()</st>: from feature_engine.transformation import PowerTransformer root_t = PowerTransformer(exp=1/2) -
接下来,我们将转换器拟合到 <st c="25640">数据:</st>root_t.fit(df)
<st c="25770">root_t.variables_</st>
-
最后,让我们对数据进行变换: 。 df_tf = root_t.transform(df)<st c="25852">PowerTransformer()</st>返回一个包含变换变量的 panda DataFrame。
它是如何工作的…
<st c="26067">sqrt()</st> <st c="26124">FunctionTransformer()</st> <st c="26227">PowerTransformer()</st><st c="26442">transform()</st>
使用幂变换
如何操作...
-
导入所需的 Python 库和类: import numpy as np import pandas as pd from sklearn.datasets import fetch_california_housing from sklearn.preprocessing import FunctionTransformer from feature_engine.transformation import PowerTransformer -
让我们将加利福尼亚住房数据集加载到一个 pandas DataFrame 中: X, y = fetch_california_housing( return_X_y=True, as_frame=True) -
为了评估变量分布,我们将创建一个函数,该函数接受 DataFrame 和变量名作为输入,并在 Q-Q 图旁边绘制直方图: def diagnostic_plots(df, variable): plt.figure(figsize=(15,6)) plt.subplot(1, 2, 1) df[variable].hist(bins=30) plt.title(f"Histogram of {variable}") plt.subplot(1, 2, 2) stats.probplot(df[variable], dist="norm", plot=plt) plt.title(f"Q-Q plot of {variable}") plt.show() -
让我们使用之前的函数绘制 <st c="28487">Population</st>变量的分布图: diagnostic_plots(X, "Population")在之前命令返回的图表中,我们可以看到 <st c="28633">Population</st>严重向右倾斜:

<st c="29026">Population</st>
-
让我们将需要变换的变量放入 一个列表中: variables = ["MedInc", "Population"] -
让我们复制 DataFrame,然后对从 步骤 5 ,(其中指数为 <st c="29375">0.3</st>) 的变量应用幂变换。X_tf = X.copy() X_tf[variables] = np.power(X[variables], 0.3)
<st c="29453">np.power()</st>
-
让我们检查 <st c="29629">Population</st>的分布变化: diagnostic_plots(X_tf, "Population")如之前命令返回的图表所示, <st c="29735">Population</st>现在在值范围内分布更均匀,并且更接近正态分布的量级:

-
让我们设置 <st c="30178">FunctionTransformer()</st>并使用指数转换,指数为 0.3 : transformer = FunctionTransformer( lambda x: np.power(x,0.3)) -
让我们复制 DataFrame 并将变量从 步骤 5 转换: X_tf = X.copy() X_tf[variables] = transformer.transform(X[variables])这就完成了 – 我们现在可以检查变量分布。 最后,让我们使用 Feature-engine 执行指数转换。 -
让我们设置 <st c="30608">PowerTransformer()</st>并使用指数 <st c="30647">0.3</st>来转换变量,从 步骤 5 。然后,我们将它拟合到 数据: power_t = PowerTransformer(variables=variables, exp=0.3) power_t.fit(X)
<st c="30847">PowerTransformer()</st>
-
最后,让我们转换这些 两个变量: X_tf = power_t.transform(X)
它是如何工作的...
<st c="31336">power()</st> <st c="31480">FunctionTransformer()</st><st c="31527">lambda</st> <st c="31550">0.</st><st c="31552">3</st> <st c="31632">PowerTransformer()</st>
<st c="31808">transform()</st> <st c="31843">FunctionTransformer()</st> <st c="32072">ColumnTransformer()</st><st c="32110">PowerTransformer()</st>
执行 Box-Cox 转换


如何做到这一点...
-
导入所需的 Python 库 和类: import numpy as np import pandas as pd import scipy.stats as stats from sklearn.datasets import fetch_california_housing from sklearn.preprocessing import PowerTransformer from feature_engine.transformation import BoxCoxTransformer -
让我们将加利福尼亚住房数据集加载到一个 pandas DataFrame: X, y = fetch_california_housing( return_X_y=True, as_frame=True) -
让我们 删除 <st c="33874">纬度</st>和 <st c="33887">经度</st>变量: X.drop(labels=["Latitude", "Longitude"], axis=1, inplace=True) -
让我们用直方图检查变量分布: 直方图: X.hist(bins=30, figsize=(12, 12), layout=(3, 3)) plt.show()在以下输出中,我们可以看到 <st c="34134">中位数收入</st>变量显示出轻微的右偏分布, <st c="34208">平均房间数</st>和 <st c="34221">人口</st>变量严重右偏,而 <st c="34266">房屋年龄</st>变量在其范围内显示出值的均匀分布:

-
让我们 将变量名捕获到一个列表中,因为我们将在下一步中使用这些变量: 以下步骤: variables = list(X.columns) -
让我们创建一个函数,该函数将为数据中的所有变量绘制 Q-Q 图,每行三个图: 每个图: def make_qqplot(df): plt.figure(figsize=(10, 6), constrained_layout=True) for i in range(6): # location in figure ax = plt.subplot(2, 3, i + 1) # variable to plot var = variables[i] # q-q plot stats.probplot((df[var]), dist="norm", plot=plt) # add variable name as title ax.set_title(var) plt.show() -
现在,让我们使用前面的函数显示 Q-Q 图: 函数: make_qqplot(X)通过查看以下图表,我们可以证实变量不是正态分布的:

-
让我们设置 <st c="36083">PowerTransformer()</st>以应用 Box-Cox 转换并将其拟合到数据中,以便找到最优的 *λ 参数: transformer = PowerTransformer( method="box-cox", standardize=False, ).set_output(transform="pandas") transformer.fit(X)
<st c="36532">PowerTransformer()</st>
-
现在,让我们转换 数据集: X_tf = transformer.transform(X)
<st c="36638">PowerTransformer()</st> <st c="36691">lambdas_</st> <st c="36736">transformer.lambdas_</st>
-
让我们使用直方图检查转换数据的分布: X_tf.hist(bins=30, figsize=(12, 12), layout=(3, 3)) plt.show()在以下输出中,我们可以看到变量的 值在其范围内分布得更均匀:

-
现在,让我们返回转换变量的 Q-Q 图: make_qqplot(X_tf)在以下输出中,我们可以看到,经过转换后,变量更接近理论正态分布: :

-
让我们设置 <st c="38242">BoxCoxTransformer()</st>以转换数据集中的所有变量,并将其拟合到 数据: bct = BoxCoxTransformer() bct.fit(X) -
现在,让我们继续转换 变量: X_tf = bct.transform(X)转换返回一个包含 修改后的变量 的 pandas DataFrame。
<st c="38532">scikit-learn 的 PowerTransformer()</st> <st c="38624">Feature-engine 的 BoxCoxTransformer()</st> <st c="38742">variables</st> <st c="38802">variables</st> <st c="38832">None</st><st c="38898">fit()</st>
-
Box-Cox 变换的最优 lambda 值存储在 <st c="38981">lambda_dict_</st>属性中。 让我们 检查它们: bct.lambda_dict_上一条命令的输出如下: 以下: <st c="39094">{'MedInc': 0.09085449361507383,</st> <st c="39126">'HouseAge': 0.8093980940712507,</st> <st c="39158">'AveRooms': -0.2980048976549959,</st> <st c="39191">'AveBedrms': -1.6290002625859639,</st> <st c="39225">'Population': 0.235767</st><st c="39248">57812051324,</st> <st c="39261">'AveOccup': -0.4763032278973292}</st>
它是如何工作的...
<st c="39421">PowerTransformer()</st> <st c="39579">ox-cox</st> <st c="39746">lambdas_</st> <st c="39787">transform()</st>
<st c="39946">set_output()</st> <st c="40031">the</st>
<st c="40140">BoxCoxTransformer()</st><st c="40168">variables</st> <st c="40201">None</st><st c="40299">fit()</st><st c="40415">lambda_dict_</st><st c="40469">transform()</st> <st c="40506">BoxCoxTransformer()</st>
还有更多...
执行 Yeo-Johnson 转换
-
![<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">mml:mfracmml:mrowmml:msupmml:mrowmml:mo(</mml:mo>mml:miX</mml:mi>mml:mo+</mml:mo>mml:mn1</mml:mn>mml:mo)</mml:mo></mml:mrow>mml:mrowmml:miλ</mml:mi></mml:mrow></mml:msup>mml:mo-</mml:mo>mml:mn1</mml:mn></mml:mrow>mml:mrowmml:miλ</mml:mi></mml:mrow></mml:mfrac></mml:math>]()
; 如果 λ ≠ 0 且 X >= 0 -
ln(X + 1); 如果 λ = 0 且 X >= 0 -
![−(−X+1)2−λ−12−λ]()
; 如果 λ ≠ 2 且 X < 0 -
-ln(-X + 1); 如果 λ = 2 且 X < 0
如何操作...
-
<st c="42052">导入所需的 Python 库</st><st c="42090">和类:</st>import numpy as np import pandas as pd import scipy.stats as stats from sklearn.datasets import fetch_california_housing from sklearn.preprocessing import PowerTransformer from feature_engine.transformation import YeoJohnsonTransformer -
<st c="42338">让我们将加利福尼亚住房数据集加载到 pandas DataFrame 中,然后删除</st><st c="42423">纬度</st><st c="42431">和</st><st c="42436">经度</st><st c="42445">变量:</st>X, y = fetch_california_housing( return_X_y=True, as_frame=True) X.drop(labels=[«Latitude», «Longitude»], axis=1, inplace=True)
<st c="42584">注意</st>
<st c="42589">我们可以使用直方图和 Q-Q 图来评估变量分布,就像我们在</st> * *<st c="42687">步骤 7</st> <st c="42688">的</st> *
<st c="42738">现在,让我们</st> <st c="42750">使用 scikit-learn 应用 Yeo-Johnson 变换:</st>
-
<st c="42805">让我们使用</st><st c="42819">PowerTransformer()</st><st c="42837">和</st><st c="42847">yeo-johnson</st><st c="42858">变换</st><st c="42847">设置:</st>transformer = PowerTransformer( method="yeo-johnson", standardize=False, ).set_output(transform="pandas") -
<st c="42981">让我们</st><st c="42988">将转换器拟合到</st><st c="43011">数据:</st>transformer.fit(X)
<st c="43039">注意</st>
<st c="43044">*<st c="43049">λ</st>* 参数应该从训练集中学习,然后用于转换训练集和测试集。</st> <st c="43150">因此,在拟合</st> <st c="43227">PowerTransformer()</st> <st c="43245">之前,请记住将您的数据分为训练集和测试集。</st>
-
<st c="43246">现在,让我们转换</st><st c="43268">数据集:</st>X_tf = transformer.transform(X)
<st c="43312">注意</st>
<st c="43317">PowerTransformer()</st> <st c="43336">将学习到的参数存储在其</st> <st c="43374">lambda_</st> <st c="43381">属性中,您可以通过执行</st> <st c="43417">transformer.lambdas_</st> <st c="43447">来返回它。</st>
-
<st c="43448">让我们使用直方图检查转换数据的分布:</st><st c="43505">:</st>X_tf.hist(bins=30, figsize=(12, 12), layout=(3, 3)) plt.show()<st c="43584">在以下输出中,我们可以看到变量的值在其范围内分布得更均匀:</st><st c="43678">:</st>

<st c="43883">图 3.14 – yeo-Johnson 变换后变量的直方图</st>
<st c="43961">最后,让我们使用 Feature-engine 实现 Yeo-Johnson 变换:</st> <st c="43986">:</st>
-
<st c="44038">让我们设置</st><st c="44043">YeoJohnsonTransformer()</st><st c="44076">来转换所有数值变量,然后将其拟合到</st><st c="44133">数据:</st>yjt = YeoJohnsonTransformer() yjt.fit(X)
<st c="44183">注意</st>
如果将<st c="44188">变量</st> <st c="44196">参数</st> <st c="44205">设置为</st> <st c="44230">None</st> <st c="44234">,转换器将选择并转换数据集中所有数值变量。</st> `
<st c="44406">PowerTransformer()</st> <st c="44525">fit()</st> <st c="44535">transform()</st>
-
让我们转换 变量: X_tf = yjt.transform(X) -
<st c="44662">YeoJohnsonTransformer()</st>将每个变量的最佳参数存储在其 <st c="44734">lambda_dict_</st>属性中,我们可以如下显示: yjt.lambda_dict_前一个命令返回以下字典: 以下字典: <st c="44862">{'MedInc': -0.1985098937827175,</st> <st c="44894">'HouseAge': 0.8081480895997063,</st> <st c="44926">'AveRooms': -0.5536698033957893,</st> <st c="44959">'AveBedrms': -4.3940822236920365,</st> <st c="44993">'Population': 0.2335</st><st c="45014">2363517075606,</st> <st c="45029">'AveOccup': -0.9013456270549428}</st>
它是如何工作的...
<st c="45247">scikit-learn</st> <st c="45264">Feature-engine</st>
<st c="45279">scikit-learn</st><st c="45296">PowerTransformer()</st> <st c="45415">yeo-johnson</st> <st c="45653">PowerTransformer()</st> <st c="45706">lambdas_</st> <st c="45747">transform()</st> <st c="45834">pandas</st>
之后,我们使用 Feature-engine 应用了 Yeo-Johnson 转换。<st c="45974">YeoJohnsonTransformer()</st> fit()<st c="46175">lambda_dict_</st><st c="46237">transform()</st> <st c="46257">Feature-engine 的</st> <st c="46274">YeoJohnnsonTransformer()</st>
还有更多...
第五章:4
执行变量离散化
离散化过程可以分为监督****的和非监督的。非监督离散化方法仅使用变量的分布来确定连续箱的界限。
在本章中,我们将讨论在成熟的开源库中广泛使用的监督和非监督离散化过程。
本章包含以下食谱:
-
执行等宽离散化
-
实现等频率离散化
-
将变量离散化到任意区间
-
使用k-means 聚类进行离散化
-
实现特征二值化
-
使用决策树进行离散化
技术要求
在本章中,我们将使用数值计算库pandas、numpy、matplotlib、scikit-learn和feature-engine。我们还将使用yellowbrick Python 开源库,您可以使用pip安装它:
pip install yellowbrick
更多关于yellowbrick的详细信息,请访问以下文档:
www.scikit-yb.org/en/latest/index.html
执行等宽离散化
等宽离散化包括将变量的观察值范围划分为k等宽的区间,其中**k由用户提供。

<st c="4265">pandas</st><st c="4273">scikit-learn</st><st c="4287">和
如何操作...
-
让我们导入所需的库和函数: import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split -
让我们加载加利福尼亚住房数据集的预测变量和目标变量: X, y = fetch_california_housing( return_X_y=True, as_frame=True)
-
让我们将数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0)接下来,我们将使用 <st c="5172">pandas</st>和配方开头描述的公式,将连续的 <st c="5130">HouseAge</st>变量分为 10 个区间: -
让我们捕获 HouseAge 的最小值和最大值: min_value = int(X_train["HouseAge"].min()) max_value = int(X_train["HouseAge"].max()) -
让我们 确定区间宽度,即变量的值范围除以箱子的数量: width = int((max_value - min_value) / 10)如果我们执行 <st c="5541">print(width)</st>,我们将获得 <st c="5570">5</st>,这是区间的 大小。 -
现在 我们需要定义区间限制并将它们存储在一个列表中: interval_limits = [i for i in range( min_value, max_value, width)]如果我们现在执行 <st c="5763">print(interval_limits)</st>,我们将看到 区间限制: <st c="5819">[1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51]</st> -
让我们将第一个和最后一个区间的限制范围扩展,以容纳测试集或未来数据源中可能找到的较小或较大的值: interval_limits[0] = -np.inf interval_limits[-1] = np.inf -
让我们复制一下 DataFrame,这样我们就不会覆盖原始的 DataFrame,我们将在食谱的后续步骤中需要它们: : train_t = X_train.copy() test_t = X_test.copy() -
让我们将 HouseAge 变量排序到我们在 步骤 6 中定义的区间中: train_t["HouseAge_disc"] = pd.cut( x=X_train["HouseAge"], bins=interval_limits, include_lowest=True) test_t["HouseAge_disc"] = pd.cut( x=X_test["HouseAge"], bins=interval_limits, include_lowest=True)
<st c="6552">include_lowest=True</st>
-
让我们打印离散化和原始变量的前 <st c="6763">5</st>个观测值: print(train_t[["HouseAge", "HouseAge_disc"]].head(5))在以下输出中,我们可以看到 <st c="6921">52</st>值被分配到 46-无穷区间, <st c="6977">43</st>值被分配到 41-46 区间,以此类推: <st c="7033">HouseAge HouseAge_disc</st> <st c="7056">1989 52.0 (46.0, inf]</st> <st c="7078">256 43.0 (41.0, 46.0]</st> <st c="7100">7887 17.0 (16.0, 21.0]</st> <st c="7123">4581</st> <st c="7128">17.0 (16.0, 21.0]</st> <st c="7146">1993 50.0 (46.0, inf]</st>
-
让我们绘制一个条形图,显示训练集和测试集中 <st c="7558">HouseAge</st>区间的观测比例: t1 = train_t["HouseAge_disc"].value_counts( normalize=True, sort=False) t2 = test_t["HouseAge_disc"].value_counts( normalize=True, sort=False) tmp = pd.concat([t1, t2], axis=1) tmp.columns = ["train", "test"] tmp.plot.bar(figsize=(8, 5)) plt.xticks(rotation=45) plt.ylabel("Number of observations per bin") plt.xlabel('Discretized HouseAge') plt.title("HouseAge") plt.show()在以下输出中,我们可以看到训练集和测试集中每个区间的观测比例大致相同,但不同 区间之间不同:

<st c="8386">feature-engine</st>
-
首先,让我们导入 离散化器: from feature_engine.discretisation import EqualWidthDiscretiser -
让我们设置离散化器,将三个连续变量排序到八个区间中: variables = ['MedInc', 'HouseAge', 'AveRooms'] disc = EqualWidthDiscretiser( bins=8, variables=variables)
<st c="8798">EqualWidthDiscretiser()</st> <st c="9141">feature-engine</st> <st c="9159">category</st> <st c="9167">encoders</st> <st c="9245">return_object</st> <st c="9262">True</st><st c="9342">return_boundaries</st> <st c="9363">True</st>
-
让我们将离散化器拟合到训练集,以便它为每个变量学习切分点: disc.fit(X_train)拟合后,我们可以通过执行 <st c="9534">binner_dict_</st>属性中的 <st c="9560">print(disc.binner_dict_)</st>来检查切分点。
<st c="9600">feature-engine</st>
-
让我们对训练集和测试集中的变量进行离散化: train_t = disc.transform(X_train) test_t = disc.transform(X_test)<st c="9872">EqualWidthDiscretiser()</st>返回一个 DataFrame,其中选定的变量被离散化。 如果我们运行 <st c="9973">test_t.head()</st>, 我们将看到以下输出,其中原始值 <st c="10050">MedInc</st>, <st c="10058">HouseAge</st>,和 <st c="10072">AveRooms</st>被替换为 区间数值:

-
现在,让我们 用每个区间的观测值比例制作条形图,以更好地理解等宽离散化的影响: plt.figure(figsize=(6, 12), constrained_layout=True) for i in range(3): # location of plot in figure ax = plt.subplot(3, 1, i + 1) # the variable to plot var = variables[i] # determine proportion of observations per bin t1 = train_t[var].value_counts(normalize=True, sort=False) t2 = test_t[var].value_counts(normalize=True, sort=False) # concatenate proportions tmp = pd.concat([t1, t2], axis=1) tmp.columns = ['train', 'test'] # sort the intervals tmp.sort_index(inplace=True) # make plot tmp.plot.bar(ax=ax) plt.xticks(rotation=0) plt.ylabel('Observations per bin') ax.set_title(var) plt.show()区间包含不同数量的观测值,如下面的图表所示:

图 4.3 – 切分后每个区间的观测比例的条形图
现在,让我们使用 scikit-learn 实现等宽切分。
-
让我们导入来自 scikit-learn 的类。
from sklearn.compose import ColumnTransformer from sklearn.preprocessing import kBinsDiscretizer -
让我们通过将其
<st c="12035">strategy</st>设置为<st c="12047">uniform</st>来设置等宽切分器。disc = KBinsDiscretizer( n_bins=8, encode='ordinal', strategy='uniform')
注意
<st c="12134">KBinsDiscretiser()</st>可以通过设置<st c="12197">encoding</st>为<st c="12209">'ordinal'</st>或通过设置<st c="12249">encoding</st>为<st c="12261">'onehot-dense'</st>来返回整数形式的区间。
-
让我们使用
<st c="12287">ColumnTransformer()</st>将切分限制为从步骤 13 选定的变量。<st c="12378">ct = ColumnTransformer(</st> <st c="12401">[("discretizer", disc, variables)],</st> <st c="12437">remainder="passthrough",</st> <st c="12462">).set_output(transform="pandas")</st>
注意
当<st c="12506">remainder</st>设置为<st c="12523">passthrough</st>时,<st c="12536">ColumnTransformer()</st>在转换后返回输入 DataFrame 中的所有变量。要仅返回转换后的变量,将<st c="12677">remainder</st>设置为<st c="12690">drop</st>。
-
让我们将切分器拟合到训练集,以便它学习区间限制。
ct.fit(X_train) -
最后,让我们在训练集和测试集中对选定的变量进行切分。
train_t = ct.transform(X_train) test_t = ct.transform(X_test)我们可以通过执行
ct.named_transformers_["discretizer"].bin_edges_来检查 transformer 学习到的切分点。
注意
<st c="13058">ColumnTransformer()</st>将<st c="13091">discretize</st>附加到已切分的变量上,并将<st c="13145">remainder</st>附加到未修改的变量上。
我们可以通过执行<st c="13225">test_</st><st c="13230">t.head()</st>来检查输出。
它是如何工作的……
在这个方法中,我们将变量值排序到等距的区间中。<st c="13360">pandas</st><st c="13449">max()</st> <st c="13464">方法找到了</st><st c="13429">变量的最大值和最小值。</st> <st c="13474">然后,我们将值范围除以任意分箱的数量来估计区间宽度。</st> <st c="13573">有了宽度和最小值、最大值,我们确定了区间界限并将它们存储在一个列表中。</st> <st c="13685">我们使用这个列表和 pandas 的</st><st c="13720">函数将变量值排序到</st>
<st c="13781">cut()</st> <st c="14119">我们的模型</st>时,适应未来数据源中可能出现的比数据集中看到的更小或更大的值。
<st c="14226">cut()</st> <st c="14390">labels</st> <st c="14410">None</st>
<st c="14510">value_counts()</st> <st c="14790">concat()</st><st c="14849">train</st> <st c="14859">test</st> <st c="14901">plot.bar()</st> <st c="14975">xticks()</st><st c="15028">xlabels()</st> <st c="15042">ylabel()</st><st c="15073">title()</st>
<st c="15130">feature-engine</st><st c="15154">EqualWidth</st> <st c="15262">fit()</st><st c="15337">transform()</st>
<st c="15385">EqualWidthDiscretiser()</st> <st c="15586">feature-engine</st> <st c="15604">category encoders</st> <st c="15687">return_object</st> <st c="15704">True</st>
<st c="15745">EqualWidthDiscretiser()</st>
我们在离散化后使用条形图来显示每个转换变量的每个区间的观测值比例。 我们可以看到,如果原始变量是偏斜的,那么条形图也是偏斜的。 注意到 <st c="16212">MedInc</st> <st c="16218">和</st> <st c="16223">AveRooms</st> <st c="16231">变量</st> 的某些区间,这些变量具有偏斜分布,包含非常少的观测值。 特别是,尽管我们想要为 <st c="16370">AveRooms</st> <st c="16378">》 创建八个桶,但只有足够的数据创建五个,并且大多数变量的值都被分配到</st> <st c="16481">第一个区间</st>。
最后,我们使用 <st c="16575">KBinsDiscretizer()</st> 从 scikit-learn 中将三个连续变量离散化成等宽桶。 <st c="16593">为了创建等宽桶,我们将</st> <st c="16652">strategy</st> <st c="16660">参数</st> <st c="16673">设置为</st> <st c="16680">uniform</st> <st c="16692">,转换器学习了区间的极限,并使用</st> <st c="16756">transform()</st>
我们使用了 <st c="16809">ColumnTransformer()</st> 来限制离散化只应用于选定的变量,并将转换输出设置为 pandas,以便在转换后获得一个 DataFrame。 <st c="16987">KBinsDiscretizer()</st> 可以返回作为序数的区间,正如我们在配方中所做的那样,或者作为 one-hot 编码的变量。 <st c="17115">通过</st> <st c="17156">encod</st><st c="17161">e</st> <st c="17163">参数</st> 可以修改这种行为。
参见也
为了比较等宽离散化与更复杂的方法,请参阅 Dougherty J, Kohavi R, Sahami M.
实现等频离散化
等宽离散化 <st c="17536">是直观且易于计算的。</st> 然而,如果变量是偏斜的,那么将会有许多空桶或只有少数值的桶,而大多数观测值将被分配到少数几个区间。 这可能会导致信息丢失。 通过自适应地找到区间切点,可以解决这个问题,使得每个区间包含相似比例的观测值。
<st c="18599">pandas</st><st c="18607">scikit-learn</st><st c="18625">feature-engine</st>
如何做到这一点...
-
让我们导入所需的 Python 库 和函数: import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split -
让我们将加利福尼亚住房数据集加载到 DataFrame 中: X, y = fetch_california_housing( return_X_y=True, as_frame=True)
为了避免数据泄露,我们将从训练集中确定区间边界或分位数
-
让我们将数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
让我们复制 DataFrame: train_t = X_train.copy() test_t = X_test.copy() -
我们将使用 pandas <st c="19420">qcut()</st>来获取 <st c="19463">HouseAge</st>变量的离散化副本,并将其存储为训练集中的新列,以及八个 等频率区间的边界: train_t["House_disc"], interval_limits = pd.qcut( x=X_train["HouseAge"], q=8, labels=None, retbins=True, )如果你执行 <st c="19712">print(interval_limits)</st>,你会看到以下区间限制: <st c="19778">array([ 1., 14., 18., 24., 29., 34., 37.,</st><st c="19820">44., 52.])</st>。 -
让我们 打印离散化和 原始变量的前五个观测值: print(train_t[["HouseAge", "House_disc"]].head(5))在以下输出中,我们看到 <st c="20005">52</st>值被分配到 44-52 区间, <st c="20055">43</st>值被分配到 37-44 区间,以此类推: <st c="20111">HouseAge House_disc</st> <st c="20131">1989 52.0 (44.0, 52.0]</st> <st c="20154">256 43.0 (37.0, 44.0]</st> <st c="20176">7887 17.0 (14.0, 18.0]</st> <st c="20199">4581 17.0 (14.0, 18.0]</st> <st c="20268">HouseAge</st> in the test set, using pandas <st c="20307">cut()</st> with the interval limits determined in *<st c="20352">step 5</st>*:test_t["House_disc"] = pd.cut(
x=X_test["HouseAge"], bins=interval_limits, include_lowest=True) -
让我们 制作一个条形图,展示训练集和 测试集中每个区间的观测值比例: # determine proportion of observations per bin t1 = train_t["House_disc"].value_counts( normalize=True) t2 = test_t["House_disc"].value_counts(normalize=True) # concatenate proportions tmp = pd.concat([t1, t2], axis=1) tmp.columns = ["train", "test"] tmp.sort_index(inplace=True) # plot tmp.plot.bar() plt.xticks(rotation=45) plt.ylabel("Number of observations per bin") plt.title("HouseAge") plt.show()<st c="20959">在下面的图表中,我们可以看到每个区间包含相似比例的观测值:</st>`。

<st c="21186">图 4.4 – 在等频率离散化后 HouseAge 每个区间的观测比例。</st>
<st c="21291">使用</st> <st c="21297">feature-engine</st> <st c="21311">,我们可以将等频率离散化应用于</st> <st c="21360">多个</st> <st c="21369">变量。</st>
-
<st c="21379">让我们</st>导入 离散化器: from feature_engine.discretisation import EqualFrequencyDiscretiser -
<st c="21477">让我们设置转换器将三个连续变量离散化成</st>八个区间: variables = ['MedInc', 'HouseAge', 'AveRooms'] disc = EqualFrequencyDiscretiser( q=8, variables=variables, return_boundaries=True)
<st c="21695">注意</st>
<st c="21700">使用</st> <st c="21706">return_boundaries=True</st> <st c="21728">,转换器将在离散化后返回区间边界。</st> <st c="21808">要返回区间编号,将其</st> <st c="21846">设置为</st> <st c="21849">False</st> <st c="21854">。</st>
-
<st c="21855">让我们将</st><st c="21870">离散化器拟合到训练集中,以便它学习区间限制:</st>`。 <st c="22048">disc.binner_dict_</st> attribute.
<st c="22076">注意</st>
<st c="22081">feature-engine</st>
-
<st c="22228">让我们将训练集和</st>测试集中的变量 进行转换: train_t = disc.transform(X_train) test_t = disc.transform(X_test) -
<st c="22353">让我们绘制条形图,以观察每个区间的观测值比例,更好地理解等频率离散化的影响:</st><st c="22457">。</st>plt.figure(figsize=(6, 12), constrained_layout=True) for i in range(3): # location of plot in figure ax = plt.subplot(3, 1, i + 1) # the variable to plot var = variables[i] # determine proportion of observations per bin t1 = train_t[var].value_counts(normalize=True) t2 = test_t[var].value_counts(normalize=True) # concatenate proportions tmp = pd.concat([t1, t2], axis=1) tmp.columns = ['train', 'test'] # sort the intervals tmp.sort_index(inplace=True) # make plot tmp.plot.bar(ax=ax) plt.xticks(rotation=45) plt.ylabel("Observations per bin") # add variable name as title ax.set_title(var) plt.show()<st c="23092">在下面的</st><st c="23110">图表中,我们可以看到区间具有相似比例的观测值:</st><st c="23127">。</st><st c="23170">。</st>`。

`
<st c="23835">现在,让我们使用 scikit-learn</st> <st c="23853">执行等频率离散化:</st> `
-
<st c="23906">让我们导入</st><st c="23920">转换器:</st>from sklearn.preprocessing import KBinsDiscretizer -
<st c="23987">让我们设置离散化器将变量排序成八个</st>等频率区间: disc = KBinsDiscretizer( n_bins=8, encode='ordinal', strategy='quantile') -
<st c="24141">让我们将</st><st c="24156">离散化器拟合到包含来自</st><st c="24226">步骤 10</st><st c="24233">的变量的训练集切片中,以便它学习区间限制:</st>`。 disc.fit(X_train[variables])
<st c="24301">注意</st>
<st c="24322">KBinsDiscretiser()</st> <st c="24604">ColumnTransformer()</st>
-
让我们复制一个 DataFrame,我们将存储 离散化变量: train_t = X_train.copy() test_t = X_test.copy() -
最后,让我们转换训练集和 测试集中的变量: train_t[variables] = disc.transform( X_train[variables]) test_t[variables] = disc.transform(X_test[variables])
它是如何工作的…
<st c="25194">qcut()</st> <st c="25279">HouseAge</st> <st c="25367">cut()</st> <st c="25387">HouseAge</st> <st c="25430">qcut()</st><st c="25450">cut()</st>
<st c="25938">EqualFrequencyDiscretiser()</st><st c="26020">fit()</st><st c="26094">binner_dict_</st> <st c="26123">transform()</st>
<st c="26185">EqualFrequencyDiscretiser()</st>
<st c="26610">return_object</st> <st c="26627">True</st> <st c="26656">feature-engine</st> <st c="26674">category encoders</st>
<st c="26854">scikit-learn</st><st c="26870">KBinsDiscretizer()</st><st c="26895">fit()</st><st c="26964">bin_edges_</st> <st c="26991">transform()</st><st c="27073">EqualFrequencyDiscretiser()</st><st c="27102">KBinsDiscretizer()</st>
<st c="27293">KbinsDiscretizer</st> <st c="27429">encode</st>
将变量离散化到任意区间 中 的
在这个菜谱中,我们将使用 <st c="27973">pan</st><st c="28058">das</st> <st c="28065">和</st> <st c="28070">feature-engine</st>
如何 做到这一点...
-
导入 Python 库和类: import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import fetch_california_housing -
让我们 将加利福尼亚住房数据集 加载到一个 <st c="28393">pandas</st>DataFrame: X, y = fetch_california_housing( return_X_y=True, as_frame=True) -
让我们 绘制 <st c="28506">人口</st>变量的直方图,以找出其 值范围: X["Population"].hist(bins=30) plt.title("Population") plt.ylabel("Number of observations") plt.show()人口 值在 0 和 大约 40,000 之间变化:

-
让我们 创建一个包含任意区间极限的列表,将上限设置为无穷大以适应 更大的值: intervals = [0, 200, 500, 1000, 2000, np.inf] -
让我们 创建一个包含区间极限的字符串列表: labels = ["0-200", "200-500", "500-1000", "1000-2000", ">2000"] -
让我们 复制数据集并 将 <st c="29229">人口</st>变量离散化到从 步骤 4 预定义的极限: X_t = X.copy() X_t[«Population_limits»] = pd.cut( X["Population"], bins=intervals, labels=None, include_lowest=True) -
现在,让我们 将 <st c="29420">人口</st>变量离散化到预定义的区间,并使用我们在 步骤 5 中定义的标签来命名区间,以便进行比较: X_t[«Population_range»] = pd.cut( X[„Population"], bins=intervals, labels=labels, include_lowest=True) -
让我们 检查原始和 离散化变量的前五行: X_t[['Population', 'Population_range', 'Population_limits']].head()在 DataFrame 的最后两列中,我们可以看到离散化变量:第一个以我们在 步骤 5 中创建的字符串作为值,第二个以 <st c="29963">区间极限</st>:<st c="29979">Population Population_range Population_limits</st> <st c="30025">0 322.0 200-500 (200.0, 500.0]</st> <st c="30056">1 2401.0 >2000 (2000.0, inf]</st> <st c="30085">2 496.0 200-500 (200.0, 500.0]</st> <st c="30116">3 558.0 500-1000 (500.0, 1000.0]</st> <st c="30149">4 565.0 500-1000 (500.0, 1000.0]</st>
<st c="30381">pandas</st>
-
最后,我们可以 计算并绘制每个区间内的 观察次数: X_t['Population_range' ].value_counts().sort_index().plot.bar() plt.xticks(rotation=0) plt.ylabel("Number of observations") plt.title("Population") plt.show()在下面的图中,我们可以看到每个区间的观察次数 有所变化:

<st c="30977">feature-engine</st>
-
让我们 导入 转换器: from feature_engine.discretisation import ArbitraryDiscretiser -
让我们创建一个字典,将变量作为键,将区间极限作为值: intervals = { "Population": [0, 200, 500, 1000, 2000, np.inf], "MedInc": [0, 2, 4, 6, np.inf]} -
让我们使用从 步骤 11 的极限来设置离散化器: discretizer = ArbitraryDiscretiser( binning_dict=intervals, return_boundaries=True) -
现在,我们可以继续对变量进行离散化 处理: X_t = discretizer.fit_transform(X)如果我们执行 <st c="31514">X_t.head()</st>,我们将看到以下输出,其中</st>人口 <st c="31580">和</st>中位收入 `变量已经被离散化:

<st c="32147">feature-engine</st> <st c="32286">Pipeline</st>
它是如何工作的...
在这个方法中,我们将一个变量的值排序到用户定义的区间中。首先,我们绘制了<st c="32430">Population</st>变量的直方图,以了解其值范围。接下来,我们任意确定了区间的限制,并将它们捕获在一个列表中。我们创建了包含 0-200、200-500、500-1000、1000-2000 以及超过 2,000 的区间,通过将上限设置为<st c="32711">np.inf</st>来设置。接下来,我们创建了一个包含区间名称的字符串列表。使用 pandas 的<st c="32792">cut()</st>,并传递包含区间限制的列表,我们将变量值排序到预定义的箱中。我们执行了两次命令;在第一次运行中,我们将<st c="32962">labels</st>参数设置为<st c="32981">None</st>,返回结果为区间限制。在第二次运行中,我们将<st c="33060">labels</st>参数设置为字符串列表。我们将返回的输出捕获在两个变量中:第一个变量显示区间限制作为值,第二个变量具有字符串作为值。最后,我们使用 pandas 的<st c="33311">value_counts()</st>对每个变量进行了观测次数的计数。
最后,我们使用<st c="33375">feature-engine</st>的<st c="33393">ArbitraryDiscretiser()</st>函数自动化了该过程。这个转换器接受一个字典作为键,其中包含要离散化的变量,以及作为值的列表,其中包含区间限制,然后使用 pandas 的<st c="33560">cut()</st>在底层对变量进行离散化。使用<st c="33615">fit()</st>,转换器不会学习任何参数,而是检查变量是否为数值型。使用<st c="33718">transform()</st>,它将变量进行离散化。
使用 k-means 聚类进行离散化
离散化过程的目标是找到一组切割点,将变量划分为几个具有良好类别一致性的小区间。为了创建将相似观测值分组在一起的分区,我们可以使用 k-means 等聚类算法。
在使用 k-means 聚类进行离散化时,分区是由 k-means 算法识别的聚类。
使用 k-means 进行离散化需要一个参数,即
在这个菜谱中,我们将使用 Python 库 <st c="35649">yellowbrick</st> <st c="35697">来决定最佳聚类数量,然后使用 scikit-learn 执行 k-means
如何操作...
-
导入所需的 Python 库 和类: import pandas as pd from sklearn.cluster import KMeans from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.preprocessing import KBinsDiscretizer from yellowbrick.cluster import KElbowVisualizer -
让我们将加利福尼亚住房数据集加载到一个 pandas DataFrame :X, y = fetch_california_housing( return_X_y=True, as_frame=True) -
k-means 最佳簇应该使用训练集来确定,因此让我们将数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
让我们创建一个包含要转换的变量的列表: : variables = ['MedInc', 'HouseAge', 'AveRooms'] -
让我们设置一个 k-means 聚类算法: k_means = KMeans(random_state=10) -
现在,使用 Yellowbrick 的可视化器和肘部方法,让我们找到每个变量的最佳簇数量: : for variable in variables: # set up a visualizer visualizer = KElbowVisualizer( k_means, k=(4,12), metric='distortion', timings=False) visualizer.fit(X_train[variable].to_frame()) visualizer.show()在 以下 图表中,我们看到前两个变量的最佳簇数量是六个,第三个是七个:

-
让我们设置一个使用 k-means 聚类创建六个分区并将簇作为 one-hot 编码变量返回的离散化器: : disc = KBinsDiscretizer( n_bins=6, encode="onehot-dense", strategy="kmeans", subsample=None, ).set_output(transform="pandas") -
让我们将离散化器拟合到包含要离散化变量的 DataFrame 切片中,以便它为每个变量找到簇: : disc.fit(X_train[variables])
<st c="38234">MedInc</st> <st c="38245">HouseAge</st> <st c="38278">AveRooms</st> <st c="38383">ColumnTransformer()</st>
-
让我们检查 切割点: disc.bin_edges_每个数组包含 <st c="38555">MedInc</st>、 <st c="38563">HouseAge</st>、 和 <st c="38577">AveRooms</st>的六个簇的切割点: <st c="38587">array([array([0.4999, 2.49587954, 3.66599029, 4.95730115, 6.67700141, 9.67326677, 15.0001]),</st> <st c="38679">array([1., 11.7038878, 19.88430419, 27.81472503, 35.39424098, 43.90930314, 52.]),</st> <st c="38761">array([0.84615385, 4.84568771, 6.62222005, 15.24138445, 37.60664483, 92.4473438, 132.53333333])], dtype=object)</st> -
让我们获取训练集测试集中变量的离散化形式: : train_features = disc.transform(X_train[variables]) test_features = disc.transform(X_test[variables])使用 <st c="39056">print(test_features)</st>,我们可以 检查离散化器返回的 DataFrame。 它包含 18 个二元变量,对应于每个三个数值变量返回的六个簇的 one-hot 编码转换: : 转换 : : <st c="39300">MedInc_0.0 MedInc_1.0 MedInc_2.0 MedInc_3.0 MedInc_4.0 MedInc_5.0 \</st> <st c="39368">14740 0.0 0.0</st> <st c="39383">1.0 0.0 0.0 0.0</st> <st c="39398">10101 0.0 0.0 0.0 1.0 0.0</st> <st c="39425">0.0</st> <st c="39428">20566 0.0 0.0 1.0 0.0 0.0 0.0</st> <st c="39458">2670 1.0</st> <st c="39468">0.0 0.0 0.0 0.0 0.0</st> <st c="39487">15709 0.0 0.0 0.0 1.0</st> <st c="39510">0.0 0.0</st> <st c="39517">HouseAge_0.0 HouseAge_1.0 HouseAge_2.0 HouseAge_3.0 HouseAge_4.0 \</st> <st c="39584">14740 0.0</st> <st c="39594">0.0 1.0 0.0 0.0</st> <st c="39610">10101 0.0 0.0 0.0 1.0 0.0</st> <st c="39636">20566 0.0 0.0 0.0 1.0 0.0</st> <st c="39662">2670 0.0 0.0 0.0</st> <st c="39679">0.0 1.0</st> <st c="39687">15709 0.0 0.0 1.0 0.0 0.0</st> <st c="39713">HouseAge_5.0</st> <st c="39726">AveRooms_0.0 AveRooms_1.0 AveRooms_2.0 AveRooms_3.0 \</st> <st c="39780">14740 0.0 0.0 1.0 0.0</st> <st c="39803">0.0</st> <st c="39806">10101 0.0 0.0 1.0 0.0 0.0</st> <st c="39832">20566 0.0 0.0</st> <st c="39847">1.0 0.0 0.0</st> <st c="39858">2670 0.0 0.0 1.0 0.0 0.0</st> <st c="39883">15709</st> <st c="39890">0.0 1.0 0.0 0.0 0.0</st> <st c="39909">AveRooms_4.0 AveRooms_5.0</st> <st c="39935">14740 0.0</st> <st c="39946">0.0</st> <st c="39949">10101 0.0 0.0</st> <st c="39963">20566 0.0 0.0</st> <st c="39977">2670 0.0 0.0</st> <st c="39990">15709 0.0 0.0</st>
<st c="40013">pandas</st> <st c="40146">ColumnTransformer()</st> remainder<st c="40285">"passthrough"</st>
它是如何工作的...
<st c="40473">KElbowVisualizer()</st>
<st c="40565">KBinsDiscretizer()</st><st c="40593">strategy</st> <st c="40605">kmeans</st> <st c="40653">n_bins</st> <st c="40675">fit()</st><st c="40763">transform()</st><st c="40845">encode</st> <st c="40855">"onehot-dense"</st><st c="41008">pandas</st>
另请参阅
-
k-means 离散化在 Palaniappan 和 Hong,OLAP 数据立方体中连续值维度的离散化 文章中有描述。 国际计算机科学和网络安全杂志,第 8 卷第 11 期,2008 年 11 月。 http://paper.i jcsns.org/07_book/200811/20081117.pdf 。 -
要了解更多关于肘部方法的信息,请访问 Yellowbrick 的文档和参考资料 在 https://www.scikit-yb.org/en/latest/api/cluster/elbow.html 。 -
要确定 k-means 聚类分析的拟合度,请查看 Yellowbrick 的其他可视化工具 在 https://www. scikit-yb.org/en/latest/api/cluster/index.html 。
实现特征二值化
为了更简单地表示这些稀疏或高度偏斜的变量,我们可以通过将所有大于 1 的值裁剪为 1 来对它们进行二值化。
<st c="43035">scikit-learn</st>
准备工作
我们将使用一个包含词袋的语料库,它可在 UCI 机器学习仓库(https://archive.ics.uci.edu/ml/datasets/Bag+of+Words)找到。
我下载并准备了一个小词袋,它代表了一个数据集的简化版本。
如何实现...
-
让我们导入所需的 Python 库、类和 数据集: import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.preprocessing import Binarizer -
让我们加载包含单词作为列和不同文本作为行的词袋数据集: : data = pd.read_csv("bag_of_words.csv") -
让我们 显示直方图以可视化变量的稀疏性: data.hist(bins=30, figsize=(20, 20), layout=(3,4)) plt.show()在以下直方图中,我们可以看到不同的单词在 大多数文档中 没有出现:

-
让我们 设置 <st c="44737">binarizer</st>将所有大于 1 的值裁剪为 1,并返回结果为 DataFrame: binarizer = Binarizer(threshold = 0) .set_output(transform="pandas") -
让我们二进制化 变量: data_t = binarizer.fit_transform(data)现在我们可以通过显示直方图,如 第 3 步 中所示,或者更好的方法,通过创建 条形图来探索二进制变量的分布。 -
让我们 创建一个条形图,显示每个变量中每个箱的观测数 : variables = data_t.columns.to_list() plt.figure(figsize=(20, 20), constrained_layout=True) for i in range(10): ax = plt.subplot(3, 4, i + 1) var = variables[i] t = data_t[var].value_counts(normalize=True) t.plot.bar(ax=ax) plt.xticks(rotation=0) plt.ylabel("Observations per bin") ax.set_title(var) plt.show()在下面的图中,我们可以看到二进制变量,其中大多数出现显示的是 <st c="45580">0</st>值:

它是如何工作的…
<st c="46899">Binarizer()</st> <st c="46987">1</st> <st c="47063">Binarizer()</st> <st c="47083">fit()</st> <st c="47093">transform()</st> <st c="47120">fit()</st> <st c="47151">transform()</st>
<st c="47187">Binarizer()</st> <st c="47281">pandas</st> <st c="47337">pandas</st>
使用决策树进行离散化

如何做到这一点...
-
让我们导入所需的 Python 库、类和数据集: import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.tree import plot_tree from feature_engine.discretisation import DecisionTreeDiscretiser -
让我们将加利福尼亚住房数据集加载到一个 <st c="49903">pandas</st>DataFrame 中,并将其分为训练集和 测试集: X, y = fetch_california_housing(return_X_y=True, as_frame=True) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
让我们创建一个包含要离散化变量名称的列表: variables = list(X.columns)[:-2]如果我们执行 <st c="50230">print(variables)</st>,我们将看到以下变量名称: <st c="50288">['MedInc'</st>, <st c="50299">'HouseAge'</st>, <st c="50311">'AveRooms'</st>, <st c="50323">'AveBedrms'</st>, <st c="50336">'</st>``<st c="50337">Population'</st>, <st c="50350">'AveOccup']</st>. -
让我们设置 转换器以离散化 从 步骤 3 中的变量。我们希望转换器根据三折交叉验证的负均方误差指标优化每个树的超参数的最大深度和每个叶子的最小样本数。我们希望离散化的输出是区间的范围: 作为离散化的输出,我们希望得到区间的范围: disc = DecisionTreeDiscretiser( bin_output="boundaries", precision=3, cv=3, scoring="neg_mean_squared_error", variables=variables, regression=True, param_grid={ "max_depth": [1, 2, 3], "min_samples_leaf": [10, 20, 50]}, ) -
让我们使用训练集来拟合离散化器,以便它为每个变量找到最佳的决策树: disc.fit(X_train, y_train)
<st c="51181">disc.binner_dict_</st><st c="51145">binner_dict_</st>
-
让我们对变量进行离散化,然后显示转换后训练集的前五行: train_t = disc.transform(X_train) test_t = disc.transform(X_test) train_t[variables].head()在以下 输出中,我们可以看到每个观测值被分配到的区间的范围:

-
我们不是返回区间限制,而是可以通过设置 将每个观测值分配到相应区间的转换器 来返回区间编号: disc = DecisionTreeDiscretiser( bin_output="bin_number", cv=3, scoring="neg_mean_squared_error", variables=variables, regression=True, param_grid={ "max_depth": [1, 2, 3], "min_samples_leaf": [10, 20, 50]}) -
我们现在可以拟合并转换训练集和 测试集: train_t = disc.fit_transform(X_train, y_train) test_t = disc.transform(X_test)如果你现在执行 <st c="53076">train_t[variables].head()</st>,你将看到整数结果而不是区间限制:

-
让我们设置 转换器以返回 预测值,然后将其拟合到训练集,最后转换 这两个数据集: disc = DecisionTreeDiscretiser( bin_output="prediction", precision=1, cv=3, scoring="neg_mean_squared_error", variables=variables, regression=True, param_grid= {"max_depth": [1, 2, 3], "min_samples_leaf": [10, 20, 50]}, ) train_t = disc.fit_transform(X_train, y_train) test_t = disc.transform(X_test) -
让我们探索在离散化前后 <st c="54012">AveRooms</st>变量的唯一值数量: X_test["AveRooms"].nunique(), test_t["AveRooms"].nunique()在以下输出中,我们可以看到决策树的预测值也是离散的或有限的,因为树包含有限数量的末端叶子节点; <st c="54290">7</st>,而原始 变量 包含超过 6000 个不同的值: (6034, 7) -
为了更好地理解树的结构,我们可以将其捕获到一个 变量 中:tree = disc.binner_dict_["AveRooms"].best_estimator_
<st c="54614">binner_dict_</st> <st c="54696">binner_dict_</st>
-
现在,我们可以显示 树结构: fig = plt.figure(figsize=(20, 6)) plot_tree(tree, fontsize=10, proportion=True) plt.show() -
在下面的图中,我们可以看到树根据房间数量的平均值将样本分配到不同的末端叶子节点所使用的值:

图 4.15 – 训练用于离散化 AveRooms 的决策树结构
-
为了总结这个方法,我们可以绘制三个变量每个区间的观测数:
plt.figure(figsize=(6, 12), constrained_layout=True) for i in range(3): ax = plt.subplot(3, 1, i + 1) var = variables[i] t1 = train_t[var].value_counts(normalize=True) t2 = test_t[var].value_counts(normalize=True) tmp = pd.concat([t1, t2], axis=1) tmp.columns = ["train", "test"] tmp.sort_index(inplace=True) tmp.plot.bar(ax=ax) plt.xticks(rotation=0) plt.ylabel("Observations per bin") ax.set_title(var) plt.show()我们可以在以下输出中看到每个区间的观测数:

图 4.16 – 使用决策树对变量进行离散化后的观测比例
如图中所示,使用决策树进行离散化在每个节点或区间返回不同比例的观测值。
它是如何工作的...
为了执行使用决策树的离散化,我们使用了feature-engine的DecisionTreeDiscretiser()。这个转换器使用每个变量作为输入来拟合决策树,并优化模型的超参数以找到基于性能指标的最佳分区。它自动找到最佳区间数量及其限制,返回结果可以是限制、区间编号或预测。
还有更多...
<feature-engine>的实现灵感来源于 KDD 2009 数据科学竞赛的获胜方案。获胜者通过基于连续特征的决策树预测创建了新特征。你可以在文章系列的第 27 页找到更多细节,文章标题为《用集成选择赢得 KDD Cup Orange 挑战》Winning the KDD Cup Orange Challenge with Ensemble Selection。
对于离散化技术的回顾,你可能觉得以下文章有用:
-
Dougherty 等人,《监督和非监督连续特征离散化,机器学习:第 12 届国际会议论文集》,1995 年,(
ai.stanford.edu/~ronnyk/disc.pdf)。 -
卢等人 《离散化:一种使能技术,数据挖掘与知识发现》 ,第 6 卷,第 393–423 页, 2002 年,( https://www.researchgate.net/publication/220451974_Discretization_An_Enabling_Technique ). -
加西亚等人 《离散化技术综述:监督学习中的分类和实证分析,IEEE 知识数据工程杂志》 第 25 卷第 4 期 , 2013 年,( https://ieeexplore.ieee.org/document/6152258 ).
第六章:5
处理异常值
-
使用箱线图和四分位数间距规则可视化异常值 。 -
使用均值和 标准差 -
使用中位数绝对偏差来 查找异常值 -
移除异常值 -
将异常值带回 可接受的范围 -
应用 Winsorization
技术要求
<st c="2626">numpy</st><st c="2633">pandas</st><st c="2641">matplotlib</st><st c="2653">seaborn</st><st c="2666">feature-engine</st>
使用箱线图和四分位距规则可视化异常值



如何做到这一点...
<st c="4461">seaborn</st>
-
让我们导入 Python 库和 数据集: import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import fetch_california_housing -
将默认背景从 <st c="4743">seaborn</st>(它使图表更美观,但这很主观, 当然): sns.set(style="darkgrid") -
从 scikit-learn 加载加利福尼亚房价数据集 : X, y = fetch_california_housing( return_X_y=True, as_frame=True) -
绘制 <st c="4985">MedInc</st>变量的箱线图以可视化其分布: plt.figure(figsize=(8, 3)) sns.boxplot(data=X["MedInc"], orient="y") plt.title("Boxplot") plt.show()在下面的箱线图中,我们识别出包含在四分位数范围(IQR)内的箱子,即第一和第三四分位数之间的观测值。 我们还可以看到触须。 在左边,触须延伸到 <st c="5371">MedInc</st>的最小值;在右边,触须延伸到第三四分位数加上 1.5 倍的四分位数范围。 超出右触须的值用点表示,可能 构成异常值:

-
现在让我们创建一个函数来 在直方图旁边绘制箱线图: def plot_boxplot_and_hist(data, variable): f, (ax_box, ax_hist) = plt.subplots( 2, sharex=True, gridspec_kw={"height_ratios": (0.50, 0.85)}) sns.boxplot(x=data[variable], ax=ax_box) sns.histplot(data=data, x=variable, ax=ax_hist) plt.show() -
让我们使用 前面的 函数来创建 <st c="6487">MedInc</st>变量的 绘图:plot_boxplot_and_hist(X, "MedInc")在下面的图中,我们可以看到箱线图与直方图中显示的变量分布之间的关系。 注意大多数 <st c="6685">MedInc</st>的观测值都位于 IQR 框内。</st>MedInc <st c="6746">的潜在异常值位于右尾,对应于收入异常高的个人:

-
让我们创建一个函数,该函数根据 IQR 接近规则返回限制: def find_limits(df, variable, fold): q1 = df[variable].quantile(0.25) q3 = df[variable].quantile(0.75) IQR = q3 - q1 lower_limit = q1 - (IQR * fold) upper_limit = q3 + (IQR * fold) return lower_limit, upper_limit
<st c="7577">quantile</st>
-
使用 步骤 7 中的函数,我们将计算 <st c="7683">MedInc</st>`的极端限制: lower_limit, upper_limit = find_limits( X, "MedInc", 1.5)如果我们现在执行 <st c="7768">lower_limit</st>和 <st c="7784">upper_limit</st>,我们将看到值 <st c="7820">-0.7063</st>和 <st c="7832">8.013</st>。下限超出了 <st c="7865">MedInc</st>的最小值,因此 在箱线图中,触须只延伸到最小值。 另一方面,上限与右侧 触须的界限相一致。
<st c="8080">1.5</st><st c="8128">3</st>
-
让我们显示 <st c="8215">HouseAge</st>变量的 箱线图和直方图:plot_boxplot_and_hist(X, "HouseAge")我们可以看到这个变量似乎不包含异常值,因此箱线图中的触须延伸到最小值和 最大值:

-
让我们 根据 IQR 邻近规则找到变量的界限: lower_limit, upper_limit = find_limits( X, "HouseAge", 1.5)
<st c="8642">lower_limit</st> <st c="8658">upper_limit</st><st c="8694">-10.5</st> <st c="8704">65.5</st>
它是如何工作的 …
<st c="8829">boxplot</st>
使用均值和标准差查找异常值
如何操作...
-
让我们导入 Python 库 和数据集: import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import load_breast_cancer -
从 scikit-learn 加载乳腺癌数据集: : X, y = load_breast_cancer( return_X_y=True, as_frame=True) -
创建一个函数来绘制 一个箱线图并放在 直方图旁边: def plot_boxplot_and_hist(data, variable): f, (ax_box, ax_hist) = plt.subplots( 2, sharex=True, gridspec_kw={"height_ratios": (0.50, 0.85)}) sns.boxplot(x=data[variable], ax=ax_box) sns.histplot(data=data, x=variable, ax=ax_hist) plt.show()
-
让我们绘制 <st c="11761">均值</st><st c="11766">平滑度</st>变量的分布: plot_boxplot_and_hist(X, "mean smoothness")在下面的箱线图中,我们看到变量的值显示出类似于 正态分布的分布,并且它有六个异常值 – 一个在左侧,五个在 右侧尾部:

-
创建一个函数,该函数返回均值加减 <st c="12185">倍数</st>的标准差,其中 <st c="12226">倍数</st>是函数的参数: def find_limits(df, variable, fold): var_mean = df[variable].mean() var_std = df[variable].std() lower_limit = var_mean - fold * var_std upper_limit = var_mean + fold * var_std return lower_limit, upper_limit -
使用该函数来识别 <st c="12527">均值</st><st c="12532">平滑度</st>变量的极端极限: lower_limit, upper_limit = find_limits( X, "mean smoothness", 3)如果我们现在执行 <st c="12637">lower_limit</st>或 <st c="12652">upper_limit</st>,我们将看到值 <st c="12688">0.0541</st>和 <st c="12699">0.13855</st>,对应于我们可以考虑一个值 为异常值的极限。
-
创建 一个布尔向量,用于标记超出在 步骤 6 中确定的限制值的观测值 outliers = np.where( (X[«mean smoothness»] > upper_limit) | (X[«mean smoothness»] < lower_limit), True, False )如果我们现在执行 <st c="13358">outliers.sum()</st>,我们将看到值 <st c="13396">5</st>,表示有五个异常值或观测值小于或大于使用均值和标准差找到的极端值。 根据这些限制,我们将比 IQR 规则少识别一个异常值。 -
让我们从 步骤 3 开始,在直方图中添加红色垂直线,以突出显示由 使用均值和标准差 确定的限制值: def plot_boxplot_and_hist(data, variable): f, (ax_box, ax_hist) = plt.subplots( 2, sharex=True, gridspec_kw={"height_ratios": (0.50, 0.85)}) sns.boxplot(x=data[variable], ax=ax_box) sns.histplot(data=data, x=variable, ax=ax_hist) plt.vlines( x=lower_limit, ymin=0, ymax=80, color='r') plt.vlines( x=upper_limit, ymin=0, ymax=80, color='r') plt.show() -
现在 让我们制作 这些图表: plot_boxplot_and_hist(X, "mean smoothness")在下面的图中,我们可以看到箱线图中 IQR 邻近规则观察到的限制值比均值和标准差确定的限制值更保守。 这就是为什么我们在箱线图中观察到六个潜在的异常值,但基于均值和标准差计算只有五个:

它是如何工作的…
<st c="15013">mean()</st> <st c="15024">std()</st><st c="15223">where()</st><st c="15236">where()</st> <st c="15387">True</st><st c="15411">False</st><st c="15443">sum()</st>
最后,我们比较了边界以确定由 IQR 邻近规则返回的异常值,我们在之前的菜谱中讨论了该规则,
使用中位数绝对偏差来寻找异常值

<st c="16340">xi</st> <st c="16370">X</st> <st c="16481">b</st> <st c="16578">b =</st> <st c="16582">1.4826</st>
<st c="16656">b</st>
如何操作...
-
让我们导入 Python 库 和数据集: import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import load_breast_cancer -
从 scikit-learn 加载 乳腺癌数据集 : X, y = load_breast_cancer( return_X_y=True, as_frame=True) -
创建一个 基于 MAD 返回限制的函数 : def find_limits(df, variable, fold): median = df[variable].median() center = df[variable] - median MAD = center.abs().median() * 1.4826 lower_limit = median - fold * MAD upper_limit = median + fold * MAD return lower_limit, upper_limit -
让我们使用这个函数来捕捉 <st c="17891">均值</st><st c="17896">平滑度</st>变量的极端限制: lower_limit, upper_limit = find_limits( X, "mean smoothness", 3)如果我们执行 <st c="17996">lower_limit</st>或 <st c="18011">upper_limit</st>,我们将看到值 <st c="18047">0.0536</st>和 <st c="18058">0.13812</st>,对应于我们可以考虑一个值 为异常值的限制。 -
让我们创建一个布尔向量,标记超出 限制的观测值: outliers = np.where( (X[«mean smoothness»] > upper_limit) | (X[«mean smoothness»] < lower_limit), True, False )如果我们 现在执行 <st c="18359">outliers.sum()</st>,我们将看到值 <st c="18397">5</st>,表示有五个异常值或观测值小于或大于 MAD 找到的极端值。 -
让我们 编写一个函数,在变量的直方图旁边绘制箱线图,突出显示在直方图中计算的 步骤 4 中的限制: def plot_boxplot_and_hist(data, variable): f, (ax_box, ax_hist) = plt.subplots( 2, sharex=True, gridspec_kw={"height_ratios": (0.50, 0.85)}) sns.boxplot(x=data[variable], ax=ax_box) sns.histplot(data=data, x=variable, ax=ax_hist) plt.vlines( x=lower_limit, ymin=0, ymax=80, color='r') plt.vlines( x=upper_limit, ymin=0, ymax=80, color='r') plt.show() -
现在让我们制作 图表: plot_boxplot_and_hist(X, "mean smoothness")在下面的图中,我们可以看到箱线图中 IQR 接近规则观察到的限制比使用 MAD 确定的限制更为保守。 MAD 返回 对称边界,而箱线图生成非对称边界,这对于高度 偏斜的分布 更为合适:

它是如何工作的…
<st c="20011">median()</st> <st c="20061">abs()</st><st c="20092">where()</st> <st c="20141">True</st> <st c="20233">False</st><st c="20265">sum()</st>
另请参阅
-
Rousseeuw PJ, Croux C. 中位数绝对偏差的替代方案 . 美国统计学会杂志, 1993。 http://www.jstor.org/stable/2291267 。 -
Leys C, et. al. 检测异常值:不要使用均值周围的标准差,而要使用中位数周围的绝对偏差 . 实验社会心理学杂志, 2013。 http://dx.doi.org/10.1016/j.jesp.2013.03.013 。 -
Thériault R, et. al. 检查你的异常值 **!使用 easystats 在 R 中识别统计异常值入门 . 行为研究方法, 2024。 https://doi. org/10.3758/s13428-024-02356-w 。
移除异常值
如何操作...
-
让我们导入 Python 库、函数、 和类: import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from feature_engine.outliers import OutlierTrimmer -
从 scikit-learn 加载加利福尼亚住房数据集,并将其分为训练集和 测试集: X, y = fetch_california_housing( return_X_y=True, as_frame=True) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
让我们创建一个函数,使用四分位数间距规则来确定我们将视为异常值的范围: 接近规则: def find_limits(df, variable, fold): q1 = df[variable].quantile(0.25) q3 = df[variable].quantile(0.75) IQR = q3 - q1 lower_limit = q1 - (IQR * fold) upper_limit = q3 + (IQR * fold) return lower_limit, upper_limit
-
使用 步骤 3 中的 *变量 <st c="23568">MedInc</st>的 限制:lower, upper = find_limits(X_train, "MedInc", 3)如果您执行 <st c="23649">print(lower_limit, upper_limit)</st>,您将看到上一个命令的结果: <st c="23729">(-</st>``<st c="23731">3.925900000000002, 11.232600000000001)</st>。 -
让我们保留训练集和测试集中那些值大于或等于( <st c="23872">ge</st>)下限的观测值: : inliers = X_train["MedInc"].ge(lower) train_t = X_train.loc[inliers] inliers = X_test["MedInc"].ge(lower) test_t = X_test.loc[inliers] -
让我们保留那些值低于或等于( <st c="24101">le</st>)上限的观测值: inliers = X_train["MedInc"].le(upper) train_t = X_train.loc[inliers] inliers = X_test["MedInc"].le(upper) test_t = X_test.loc[inliers]继续执行 ,然后执行 <st c="24280">X_train.shape</st>,接着执行 <st c="24306">train_t.shape</st>,以证实转换后的 DataFrame 在移除异常值后比原始 DataFrame 包含的观测值更少。 我们可以使用 <st c="24514">feature-engine</st>同时从多个变量中移除异常值。 -
设置一个转换器,使用 IQR 规则识别三个变量中的异常值: trimmer = OutlierTrimmer( variables = [«MedInc", "HouseAge", "Population"], capping_method="iqr", tail="both", fold=1.5, )
<st c="24741">OutlierTrimmer</st> <st c="24903">capping_method</st> <st c="24921">gaussian</st> <st c="24933">mad</st>
-
将转换器拟合到训练集,以便它学习这些限制: trimmer.fit(X_train)
<st c="25063">trimmer.left_tail_caps_</st><st c="25147">{'MedInc': -0.6776500000000012, 'HouseAge': -10.5, 'Population': -626.0}</st><st c="25234">trimmer.right_tail_caps_</st><st c="25300">{'MedInc': 7.984350000000001, 'HouseAge': 65.5, '</st>``<st c="25349">Population': 3134.0}</st>
-
最后,让我们从训练集和测试集中移除异常值: : X_train_t = trimmer.transform(X_train) X_test_t = trimmer.transform(X_test)为了完成这个食谱,让我们比较移除异常值前后变量的分布。 -
让我们创建 一个函数来在直方图上显示箱线图: def plot_boxplot_and_hist(data, variable): f, (ax_box, ax_hist) = plt.subplots( 2, sharex=True, gridspec_kw={"height_ratios": (0.50, 0.85)} ) sns.boxplot(x=data[variable], ax=ax_box) sns.histplot(data=data, x=variable, ax=ax_hist) plt.show()
-
让我们在移除 <st c="26071">MedInc</st>的异常值之前绘制其分布图: plot_boxplot_and_hist(X_train, "MedInc")在下面的图中,我们可以看到 <st c="26184">MedInc</st>是偏斜的,并且大于 8 的观测值被标记为异常值:

-
最后,让我们绘制移除 <st c="26371">MedInc</st>的异常值之后的分布图: plot_boxplot_and_hist(train_t, "MedInc")移除异常值后, <st c="26493">MedInc</st>似乎不那么偏斜,其值分布更 均匀:

它的工作原理...
<st c="27340">ge()</st> <st c="27349">le()</st> <st c="27526">loc</st>
<st c="27597">feature-engine</st> <st c="27622">OutlierTrimmer()</st> <st c="27708">OutlierTrimmer()</st> <st c="27868">capping_method</st>
通过改变我们乘以四分位距(IQR)、标准差或 MAD 的系数,可以使得识别异常值的方法更加或更加保守。使用 <st c="28055">OutlierTrimmer()</st>,我们可以通过 <st c="28128">fold</st> 参数来控制方法的强度。
将 <st c="28149">tails</st> 设置为 <st c="28162">"both"</st>,<st c="28170">OutlierTrimmer()</st> 找到并移除了变量分布两端的异常值。要仅移除一端的异常值,我们可以将 <st c="28316">"left"</st> 或 <st c="28322">"right"</st> 传递给 <st c="28341">tails</st> 参数。
<st c="28357">OutlierTrimmer()</st> 采用 scikit-learn 功能,使用 <st c="28422">fit()</st> 方法来学习参数,以及 <st c="28461">transform()</st> 来修改数据集。使用 <st c="28501">fit()</st>,转换器学习并存储了每个变量的界限。使用 <st c="28578">transform()</st>,它从数据中移除了异常值,返回 <st c="28640">pandas</st> 数据框。
参见
这是之前提到的研究,它将异常值分类为错误;有趣且随机:Leys C, et.al. 2019. 如何分类、检测和管理单变量和多变量异常值,重点在于预注册。国际社会心理学评论。doi.org/10.5334/irsp.289
将异常值带回可接受范围内
移除错误异常值可能是一个有效的策略。然而,这种方法可能会降低统计功效,特别是在许多变量都有异常值的情况下,因为我们最终移除了数据集的大部分内容。处理错误异常值的另一种方法是将其带回可接受范围内。在实践中,这意味着用四分位距(IQR)邻近规则、均值和标准差或 MAD 确定的某些阈值来替换异常值。在本食谱中,我们将使用 <st c="29572">pandas</st> 和 <st c="29583">feature-engine</st> 来替换异常值。
如何做...
我们将使用均值和标准差来查找异常值,然后使用 <st c="29711">pandas</st> 和 <st c="29722">feature-engine</st> 替换它们的值:
-
让我们导入所需的 Python 库和函数:
from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from feature_engine.outliers import Winsorizer -
从 scikit-learn 加载乳腺癌数据集并将其分为训练集和测试集:
X, y = load_breast_cancer( return_X_y=True, as_frame=True) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
让我们创建一个函数来使用均值和标准差查找异常值:
def find_limits(df, variable, fold): var_mean = df[variable].mean() var_std = df[variable].std() lower_limit = var_mean - fold * var_std upper_limit = var_mean + fold * var_std return lower_limit, upper_limit
注意
在
-
使用来自
步骤 3 的函数,让我们确定<st c="30977">平均平滑度</st>变量的极限,该变量大约遵循高斯分布:var = "worst smoothness" lower_limit, upper_limit = find_limits( X_train, var, 3) -
让我们复制原始数据集:
train_t = X_train.copy() test_t = X_test.copy() -
现在,在新数据框中使用
步骤 4 中的下限或上限替换异常值:train_t[var] = train_t[var].clip( lower=lower_limit, upper=upper_limit) test_t[var] = test_t[var].clip( lower=lower_limit, upper=upper_limit)为了证实异常值已被替换为在
步骤 4 中确定的值,执行<st c="31553">train_t["worst smoothness"].agg(["min", "max"])</st>以获得新的最大和最小值。它们应该与变量的最小和最大值一致,或者与步骤 4 中返回的极限一致。我们可以通过利用
<st c="31829">feature-engine</st>同时替换多个变量中的异常值。 -
让我们设置一个转换器来替换两个变量中的异常值,使用由均值和
<st c="31952">标准差</st>确定的极限:capper = Winsorizer( variables=[«worst smoothness», «worst texture»], capping_method="gaussian", tail="both", fold=3, )
注意
<st c="32096">Winsorizer</st> <st c="32260">capping_meth</st><st c="32272">od</st> <st c="32279">iqr</st> <st c="32286">mad</st>
-
让我们将变压器拟合到数据中,以便它学习 这些限制: capper.fit(X_train)通过执行 <st c="32408">capper.left_tail</st><st c="32424">_caps_</st>,我们可以可视化两个变量的下限: <st c="32490">{'worst smoothness': 0.06364743973736293, 'worst texture': 7.115307053129349}</st>。通过执行 <st c="32582">capper.right_tail_caps_</st>,我们可以看到变量的上限: <st c="32647">{'worst smoothness': 0.20149734880520967, 'worst</st><st c="32696">texture': 43.97692158753917}</st>。 -
最后,让我们用第 8 步 中的限制来替换异常值: X_train = capper.transform(X_train) X_test = capper.transform(X_test)如果我们现在执行 <st c="32880">train_t[capper.variables_].agg(["min", "max"])</st>,我们将看到转换后的 DataFrame 的最大值和最小值与变量的最大值和最小值或识别到的限制值相匹配,以先到者为准: worst smoothness worst texture min 0.071170 12.020000 max 0.201411 43.953738如果您计划限制变量,确保在替换异常值前后比较您模型的性能或分析结果。
它是如何工作的...
<st c="33378">clip()</st> worst smoothness
<st c="34048">feature-engine</st> <st c="34121">Winsorizer()</st> <st c="34292">capping_method</st>
<st c="34479">Winsorizer()</st><st c="34548">fold</st>
<st c="34569">tails</st> <st c="34582">"both"</st><st c="34590">Winsorizer()</st> <st c="34723">"left"</st> <st c="34733">"right"</st> <st c="34748">tails</st>
<st c="34764">Winsorizer()</st> <st c="34825">fit()</st> <st c="34864">transform()</st> <st c="34904">fit()</st><st c="34981">transform()</st>
另请参阅
<st c="35072">feature-engine</st> <st c="35092">ArbitraryOutlierCapper()</st>
应用 Winsorizer
如何做到这一点...
-
让我们导入所需的 Python 库 和函数: import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split -
从 scikit-learn 加载乳腺癌数据集 : X, y = load_breast_cancer( return_X_y=True, as_frame=True) -
将数据 分离为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0, ) -
在字典中捕获每个变量的第 5 和第 95 百分位数 : q05 = X_train.quantile(0.05).to_dict() q95 = X_train.quantile(0.95).to_dict() -
现在让我们一次性将所有变量的值替换为相应的百分位数 超过这些百分位数: train_t = X_train.clip(lower=q05, upper=q95) test_t = X_test.clip(lower=q05, upper=q95) -
让我们显示在截尾处理之前一个变量的最小值、最大值和平均值: var = 'worst smoothness' X_train[var].agg(["min", "max", "mean"])我们可以在以下输出中看到值: <st c="37705">min 0.071170</st> <st c="37718">max 0.222600</st> <st c="37731">mean 0.132529</st> <st c="37745">Name: worst smoothness, dtype: float64</st> -
显示截尾处理后的同一变量的最小值、最大值和平均值 : train_t[var].agg([„min", „max"])在以下输出中,我们可以看到最小值和最大值对应于百分位数。 然而,平均值与变量的原始平均值 非常相似: <st c="38081">min 0.096053</st> <st c="38094">max 0.173215</st> <st c="38107">mean 0.132063</st> <st c="38121">Name: worst smoothness, dtype: float64</st>
<st c="38251">feature-engine</st> <st c="38276">Winsorizer()</st>
<st c="38318">capper =</st> <st c="38328">Winsorizer(</st>
**<st c="38339">capping_method="quantiles",</st>**
****<st c="38367">tail="both",</st>**
****<st c="38380">fold=0.05,</st>**
**<st c="38391">)</st>
<st c="38422">fit()</st> <st c="38432">transform()</st>
它是如何工作的...
<st c="38834">quantiles()</st> <st c="38943">to_dict()</st> <st c="39114">clip()</st>
另请参阅
第七章:6
从日期和时间变量中提取特征
日期和时间变量包含有关日期、时间或两者的信息,在编程中,我们将它们统称为 <st c="177">datetime</st> 特征。出生日期、事件发生时间以及最后付款的日期和时间是 <st c="291">datetime</st> 变量的例子。
由于它们的性质,<st c="336">datetime</st> 特征通常具有高基数。这意味着它们包含大量唯一的值,每个值对应一个特定的日期和/或时间组合。我们通常不会在原始格式下使用 <st c="536">datetime</st> 变量作为机器学习模型的输入。相反,我们通过从这些变量中提取多个特征来丰富数据集。这些新特征通常具有较低的基数,并允许我们捕捉到有意义的信息,例如趋势、季节性和重要事件和倾向。
在本章中,我们将探讨如何通过利用 <st c="957">pandas</st> <st c="963">dt</st> 模块从日期和时间中提取特征,然后使用 <st c="1013">feature-engine</st> 自动化此过程。
本章将涵盖以下内容:
-
使用
<st c="1112">pandas</st>从日期中提取特征 -
使用
<st c="1154">pandas</st>从时间中提取特征 -
捕获
<st c="1192">datetime</st>变量之间的经过时间 -
在不同的时区中处理时间
-
使用
<st c="1297">feature-engine</st>自动化<st c="1264">datetime</st>特征提取
技术要求
在本章中,我们将使用 <st c="1368">pandas</st>、<st c="1376">numpy</st> 和 <st c="1387">feature-engine</st> Python 库。
使用 pandas 从日期中提取特征
<st c="1477">datetime</st> 变量的值可以是日期、时间或两者。我们将首先关注包含日期的变量。我们很少使用原始数据与机器学习算法一起使用。相反,我们提取更简单的特征,如年份、月份或星期几,这些特征允许我们捕捉到季节性、周期性和趋势等洞察。
<st c="1809">The</st> <st c="1814">pandas</st> <st c="1820">Python 库非常适合处理日期和时间。</st> <st c="1877">利用</st> <st c="1891">pandas</st> <st c="1897">dt</st> <st c="1900">模块,我们可以访问</st> <st c="1927">datetime</st> <st c="1935">属性,从而提取许多特征。</st> <st c="1992">然而,为了利用此功能,变量需要转换为支持这些操作的数据类型,例如</st> <st c="2120">datetime</st> <st c="2128">或</st> <st c="2132">timedelta</st> <st c="2141">.</st>
<st c="2142">注意</st>
<st c="2147">The</st> <st c="2152">datetime</st> <st c="2160">variables can be cast as objects, particularly when we load the data from a CSV file.</st> <st c="2247">为了提取本章中我们将讨论的日期和时间特征,有必要将变量重新转换为</st> <st c="2374">datetime</st> <st c="2382">.</st>
在这个菜谱中,我们将学习如何通过利用 <st c="2462">panda</st><st c="2467">s</st> <st c="2469">.</st> 从日期中提取特征。
<st c="2470">准备就绪</st>
<st c="2484">以下是一些我们可以从</st> <st c="2553">date</st> <st c="2557">部分</st> <st c="2570">datetime</st> <st c="2578">变量</st> <st c="2602">使用</st> <st c="2608">pandas</st> <st c="2614">直接提取的特征</st>:
-
<st c="2616">pandas.Series.dt.year</st> -
<st c="2637">pandas.Series.dt.quarter</st> -
<st c="2662">pandas.Series.dt.month</st> -
<st c="2685">pandas.Series.dt.isocalendar().week</st> -
<st c="2721">pandas.Series.dt.day</st> -
<st c="2742">pandas.Series.dt.day_of_week</st> -
<st c="2771">pandas.Series.dt.weekday</st> -
<st c="2796">pandas.Series.dt.dayofyear</st> -
<st c="2823">pandas.Series.dt.day_of_year</st>
<st c="2852">我们可以</st> <st c="2860">使用我们使用</st> <st c="2897">pandas</st> <st c="2903">获得的特征</st> <st c="2906">创建更多特征,例如学期或</st> <st c="2958">是否为周末。</st> <st c="2983">我们将在下一节中学习如何做到这一点。</st>
<st c="3033">How to do it...</st>
<st c="3049">为了继续这个菜谱,让我们导入</st> <st c="3093">pandas</st> <st c="3099">和</st> <st c="3104">numpy</st> <st c="3109">,并创建一个</st> <st c="3124">样本 DataFrame</st> <st c="3374">:</st>
-
<st c="3141">Let’s import</st><st c="3155">the libraries:</st>import numpy as np import pandas as pd -
<st c="3208">我们将从创建 20 个</st><st c="3236">datetime</st><st c="3244">值开始,从</st><st c="3267">2024-05-17</st><st c="3277">午夜开始,然后以 1 天的增量递增。</st><st c="3327">然后,我们将这些值捕获在一个</st><st c="3365">DataFrame</st><st c="3374">实例中,并显示前五行:</st>rng_ = pd.date_range( "2024-05-17", periods=20, freq="D") data = pd.DataFrame({"date": rng_}) data.head()在以下输出中,我们看到我们在
Step 2 中创建的包含日期的变量: :

我们可以通过执行 <st c="3768">data["date"].dtypes</st> 来检查变量的数据格式。如果变量被转换为对象,我们可以通过执行 <st c="3938">data["date_dt"] =</st> <st c="3956">pd.to_datetime(data["date"])</st> <st c="3984">.</st> 将其转换为 <st c="3909">datetime</st> 格式。
-
L et’s <st c="3991">extract the year pa</st><st c="4011">rt of the date in a</st>new column and display the top five rows of the resulting DataFrame: data["year"] = data["date"].dt.year data.head()我们在以下输出中看到新的 <st c="4164">year</st>`变量:

-
让我们从日期中提取年份的四分之一到一个新列,并显示前五行:
data["quarter"] = data["date"].dt.quarter data[["date", "quarter"]].head()我们在以下输出中看到新的 <st c="4589">quarter</st>`变量:

-
通过
<st c="4825">quarte</st><st c="4831">r</st><st c="4833">,我们现在可以创建<st c="4857">semester</st>`特征: data["semester"] = np.where(data["quarter"] < 3, 1, 2)
您可以使用 <st c="5002">pandas</st> <st c="5008">’</st> <st c="5011">unique()</st> <st c="5019">,例如,通过执行 <st c="5047">df["quarter"].unique()</st> <st c="5069"> 或 <st c="5073">df["semester"].unique()</st> <st c="5096">.</st> 来探索新变量的不同值。
-
让我们从日期中提取 <st c="5116">month</st>`部分到一个新列,并显示 DataFrame 的前五行: data["month"] = data["date"].dt.month data[["date", "month"]].head()我们在以下输出中看到新的 <st c="5287">month</st>`变量:

-
让我们从 <st c="5545">日期</st>中提取周数(一年有 52 周): data["week"] = data["date"].dt.isocalendar().week data[["date", "week"]].head()我们在 <st c="5646">下周</st>变量中看到 以下 输出:

-
让我们提取月份的天数,它可以取 <st c="5914">1</st>到 <st c="5920">31</st>之间的值,到一个新列中: data["day_mo"] = data["date"].dt.day data[["date", "day_mo"]].head()我们 看到 <st c="6023">day_mo</st>变量 在 以下 输出中:

-
让我们提取星期几,其值介于 <st c="6303">0</st>和 <st c="6309">6</st>(从星期一到星期日),在一个新列中,然后显示前几行: data["day_week"] = data["date"].dt.dayofweek data[["date", "day_mo", "day_week"]].head()我们在 <st c="6480">day_week</st>变量中看到 以下 输出:

-
使用来自 步骤 9 的变量,我们可以创建一个二元变量,表示是否是 周末: data["is_weekend"] = ( data[«date»].dt.dayofweek > 4).astype(int) data[["date", "day_week", "is_weekend"]].head()我们在 <st c="6943">看到</st>新的 <st c="6955">is_weekend</st>变量 在 以下 输出中:

<st c="7249">feature-engine</st>
<st c="7475">pandas</st><st c="7451">datetime</st> <st c="7547">预测</st><st c="7556">建模</st>非常有用。
它是如何工作的...
在这个菜谱中,我们<st c="7649">datetime</st> <st c="7682">pandas</st><st c="7680">dt</st> <st c="7786">pandas</st><st c="7795">date_range()</st> <st c="7924">periods</st> <st c="8040">freq</st> <st c="8117">D</st> <st c="8205">pandas</st> <st c="8211">DataFrame()</st>
<st c="8275">pandas</st><st c="8284">dt</st> <st c="8301">pandas</st> <st c="8309">datetime</st> <st c="8392">year</st><st c="8398">month</st><st c="8409">quarter</st> <st c="8549">where()</st> <st c="8595">quarter</st> <st c="8621">where()</st> <st c="8655">quarter</st> <st c="8699">3</st><st c="8727">1</st> <st c="8789">2</st>
<st c="8903">isocalender().week</st><st c="8923">day</st><st c="8932">dayofweek</st> <st c="9070">where()</st> <st c="9141">4</st><st c="9208">True</st> <st c="9227">False</st>
还有更多...
<st c="9459">pandas</st><st c="9468">dt</st>
-
<st c="9730">pandas.Series.dt.is_month_start</st> -
<st c="9762">pandas.Series.dt.is_month_end</st> -
<st c="9792">pandas.Series.dt.is_quarter_start</st> -
<st c="9826">pandas.Series.dt.is_quarter_end</st> -
<st c="9858">pandas.Series.dt.is_year_start</st> -
<st c="9889">pandas.Series.dt.is_year_end</st> -
<st c="9918">pandas.Series.dt.is_leap_year</st> -
<st c="9948">pandas.Series.dt.days_in_month</st>
<st c="10043">pd.dt.days_in_month</st> <st c="10091">1</st> <st c="10096">365</st><st c="10107">pd.dt</st><st c="10112">.dayofyear</st>
<st c="10152">pandas</st><st c="10161">datetime</st>
另请参阅
<st c="10351">date_ranges()</st> <st c="10321">datetime</st>
<st c="10480">pandas</st><st c="10489">dt</st>
使用 pandas 从时间中提取特征
<st c="11095">pandas</st> <st c="11111">NumPy</st>
准备就绪
<st c="11200">pandas</st><st c="11209">datetime</st>
-
<st c="11229">pandas.Series.dt.hour</st> -
<st c="11251">pandas.Series.dt.minute</st> -
<st c="11276">pandas.Series.dt.second</st>
如何操作...
<st c="11357">分钟</st><st c="11369">秒</st> <st c="11386">变量</st>
-
让我们导入 <st c="11484">pandas</st>和 <st c="11495">numpy</st>: import numpy as np import pandas as pd -
让我们首先创建 20 个 日期时间 观察值,从
<st c="11605">2024-05-17</st>午夜开始,然后以 1 小时、15 分钟和 10 秒的增量增加。接下来,我们将时间范围捕获到 DataFrame 中并显示前五行: rng_ = pd.date_range( "2024-05-17", periods=20, freq="1h15min10s") df = pd.DataFrame({"date": rng_}) df.head()在以下输出中,我们可以看到步骤 2中的变量,它包含一个日期部分和一个时间部分,并且值以 1 小时、15 分钟和 10 秒的间隔增加:


-
让我们提取小时、分钟和秒部分,并将它们捕获到三个新的列中,然后显示 DataFrame 的前五行:
df["hour"] = df["date"].dt.hour df["min"] = df["date"].dt.minute df["sec"] = df["date"].dt.second df.head()在以下输出中,我们可以看到我们在步骤 3中提取的三个时间特征:


注意
记住,<st c="12845">pandas</st><st c="12854">dt</st> 需要一个 <st c="12865">datetime</st> 对象才能工作。您可以使用 <st c="12964">pandas</st> <st c="12970">to_datetime()</st><st c="12946">datetime</st>。
-
让我们执行与步骤 3中相同的操作,但现在在一行代码中完成:
df[["h", "m", "s"]] = pd.DataFrame( [(x.hour, x.minute, x.second) for x in df["date"]] ) df.head()在以下输出中,我们看到新创建的变量:


无
您可以使用 <st c="13592">pandas</st><st c="13601">unique()</st><st c="13637">df['hour'].unique()</st>
-
最后,让我们 创建一个二进制变量,该变量 标记在早晨发生的事件,在 早上 6 点 和 中午 12 点 之间: df["is_morning"] = np.where( (df[«hour»] < 12) & (df[«hour»] > 6), 1, 0 ) df.head()我们在以下输出中看到了 <st c="13869">is_morning</st>变量:

<st c="14294">datetime</st>
它是如何工作的...
<st c="14519">datetime</st> <st c="14546">pandas</st> <st c="14552">date_range()</st><st c="14615">1h15min10s</st> <st c="14757">freq</st> <st c="14865">pandas</st><st c="14874">DataFrame()</st>
<st c="14932">pandas</st><st c="14941">dt</st> <st c="14958">hour</st><st c="14964">minute</st><st c="14976">second</st> <st c="14982">时间</st> <st c="15027">time</st><st c="15017">hour</st> <st c="15123">where()</st><st c="15140">where()</st> <st c="15160">hour</st> <st c="15253">1</st><st c="15290">0</st>
还有更多…
<st c="15514">pandas</st>
-
<st c="15532">pandas.Series.dt.microsecond</st> -
<st c="15561">pandas.Series.dt.nanosecond</st>
捕获 datetime 变量之间的经过时间
<st c="15932">datetime</st>
<st c="16181">pandas</st> <st c="16192">feature-engine</st><st c="16149">datetime</st>
如何实现...
<st c="16293">datatime</st>
-
让我们首先导入 <st c="16338">pandas</st>, <st c="16346">numpy</st>,和 <st c="16353">datetime</st>: import datetime import numpy as np import pandas as pd -
我们将首先创建两个 <st c="16451">datetime</st>变量,每个变量包含 20 个值;值从 <st c="16513">2024-05-17</st>开始,第一个变量的间隔为 <st c="16553">1</st>小时,第二个变量的间隔为 <st c="16588">1</st>月。 然后,我们将变量捕获到 DataFrame 中,添加列名,并显示 顶部行: date = "2024-05-17" rng_hr = pd.date_range(date, periods=20, freq="h") rng_month = pd.date_range(date, periods=20, freq="ME") df = pd.DataFrame( {"date1": rng_hr, "date2": rng_month}) df.head()在以下输出中,我们看到 来自 步骤 2 的 以下 DataFram e 的前五行:

-
让我们在新的特征中捕获两个变量之间的天数差异,然后显示 DataFrame 的 顶部行: df["elapsed_days"] = ( df["date2"] - df["date1"]).dt.days df.head()我们可以在以下输出中看到天数差异:

-
让我们捕获 两个 datetime 变量之间的 周数差异,然后显示 DataFrame 的 顶部行: df["weeks_passed"] = ( (df[«date2»] - df[«date1»]) / np.timedelta64(1, "W")) df.head()我们可以在以下屏幕截图中的变量之间看到周数差异:

-
现在,让我们计算变量之间的时间差,以分钟和秒为单位,然后显示 DataFrame 的 顶部行: df["diff_seconds"] = ( df[«date2»] - df[«date1»])/np.timedelta64(1, «s») df["diff_minutes"] = ( df[«date2»] - df[«date1»])/ np.timedelta64(1,»m») df.head()我们可以在以下输出中看到 新变量: (输出内容省略)

-
最后,让我们计算一个变量与当前日期之间的差异,以天数表示,然后显示 DataFrame 的前五行: df["to_today"] = ( datetime.datetime.today() - df["date1"]) df.head()我们可以在以下输出的 DataFrame 的最后一列中找到新变量:

<st c="20191">to_today</st>
<st c="20470">datetime</st>
它是如何工作的...
<st c="20584">datetime</st> <st c="20778">1</st> <st c="20838">1</st> <st c="20877">pandas</st><st c="20886">date_range()</st>
<st c="21089">datetime</st> <st c="21137">pandas</st> <st c="21198">pandas</st> <st c="21227">pandas</st> <st c="21242">pandas</st> <st c="21285">pandas</st><st c="21294">dt</st><st c="21310">days</st><st c="21368">timedelta()</st> <st c="21453">W</st> <st c="21557">s</st> <st c="21563">m</st> <st c="21576">timedelta()</st>
<st c="21634">timedelta</st> <st c="21658">– 1</st><st c="21719">datetime</st> <st c="21747">D</st><st c="21758">W</st><st c="21770">h</st><st c="21784">m</st><st c="21801">s</st>
最后,我们 <st c="21818">从</st> <st c="21846">一个</st> <st c="21851">日期</st> <st c="21855">时间</st> <st c="21860">变量到今天的日期</st> <st c="21887">捕获了差异。</st> <st c="21887">我们通过使用</st> <st c="21960">today()</st> <st c="21967">从内置的</st> <st c="21986">datetime</st> <st c="21994">Python 库</st> `
[还有更多...
[我们可以通过使用`
-
[让我们导入 `
pandas ` `和 ` `feature-engine ` `的转换器: import pandas as pd from feature_engine.datetime import ( DatetimeSubtraction ) -
[让我们重新创建我们在
步骤 2 <st c="22372">的</st> *<st c="22380">如何做</st>*它… 部分中描述的 样本数据集:date = "2024-05-17" rng_hr = pd.date_range(date, periods=20, freq="h") rng_month = pd.date_range(date, periods=20, freq="ME") df = pd.DataFrame( {"date1": rng_hr, "date2": rng_month}) -
[让我们设置
DatetimeSubstraction() 以返回第二个日期和第一个日期之间的时间差,以天为单位: ds = DatetimeSubtraction( variables="date2", reference="date1", output_unit="D", )
[注意
[我们可以通过在`
-
[让我们创建 `
然后显示 ` `新功能: dft = ds.fit_transform(df) dft.head()[在以下输出中,我们看到捕获了两个
日期时间 变量 之间时间差的变量,以天为单位:
![图 6.18 – 包含两个日期时间变量差值的新变量的 DataFrame
图 6.18 – 包含两个日期时间变量差值的新变量的 DataFrame
[更多详情,请查看 feature-engine.trainindata.com/en/latest/api_doc/datetime/DatetimeSubtraction.html
参见
[要了解更多关于 NumPy 的 `
[在不同时区处理时间
<st c="24184">datetime</st> <st c="24265">pandas</st>
如何操作...
-
让我们 导入 <st c="24413">pandas</st>: import pandas as pd -
让我们创建一个包含一个变量在不同 时区值的 DataFrame: df = pd.DataFrame() df['time1'] = pd.concat([ pd.Series( pd.date_range( start='2024-06-10 09:00', freq='h', periods=3, tz='Europe/Berlin')), pd.Series( pd.date_range( start='2024-09-10 09:00', freq='h', periods=3, tz='US/Central')) ], axis=0) -
让我们向 DataFrame 添加另一个 datetime 变量,它也包含不同 时区的值: df['time2'] = pd.concat([ pd.Series( pd.date_range( start='2024-07-01 09:00', freq='h', periods=3, tz='Europe/Berlin')), pd.Series( pd.date_range( start='2024-08-01 09:00', freq='h', periods=3, tz='US/Central')) ], axis=0)如果我们现在执行 <st c="25119">df</st>,我们将看到包含不同时区变量的 DataFrame,如下面的输出所示:

<st c="25668">+02</st> <st c="25676">-05</st>
-
为了处理不同的时区,我们通常将变量设置在同一个时区,在这种情况下,我们选择了 协调世界时(UTC): df['time1_utc'] = pd.to_datetime( df['time1'], utc=True) df['time2_utc'] = pd.to_datetime( df['time2'], utc=True)
<st c="26033">df</st><st c="26076">00</st>

-
让我们计算变量之间的天数差异,然后显示 DataFrame 的前五行: df['elapsed_days'] = ( df[‹time2_utc›] - df[‹time1_utc›]). dt.days df['elapsed_days'].head()我们在以下输出中看到变量之间的时间差: : <st c="27127">0 21</st> <st c="27132">1 21</st> <st c="27137">2 21</st> <st c="27142">0 -40</st> <st c="27148">1 -40</st> <st c="27231">datetime</st> variables to the <st c="27257">London</st> and <st c="27268">Berlin</st> time zones, and then display the resulting variables:df['time1_london'] = df[
‹time1_utc›].dt.tz_convert('Europe/London')df['time2_berlin'] = df[
‹time1_utc›].dt.tz_convert('Europe/Berlin')df[['time1_london', 'time2_berlin']]
<st c="27503">We see the variables in their</st> <st c="27534">respective time zones in the</st> <st c="27563">following output:</st>

<st c="28064">+01</st> <st c="28072">+02</st>
它是如何工作的...
<st c="28547">pandas</st><st c="28556">date_range()</st> <st c="28582">pandas</st><st c="28591">concat()</st><st c="28612">axis</st> <st c="28629">0</st> <st c="28733">pandas</st><st c="28742">date_range()</st>
<st c="28994">pandas</st><st c="29003">to_datetime()</st><st c="29026">utc=True</st><st c="29223">pandas</st><st c="29232">tz_convert</st><st c="29242">()</st>
另请参阅
<st c="29321">pandas</st><st c="29330">to_datetime()</st>
<st c="29461">’</st>
使用 Feature-engine 自动化日期时间特征提取
<st c="29640">feature-engine</st> <st c="29750">pandas</st> <st c="29773">DatetimeFeatures()</st> <st c="29861">pandas</st><st c="29870">dt</st> <st c="29889">DatetimeFeatures()</st>
-
月 -
季度 -
学期 -
年 -
周 -
周 的日 -
月份的 日 -
年份的 日 -
周末 -
月始 -
月末 -
季度始 -
季度尾 -
年始 -
年尾 -
闰年 -
一个月中的 天数 -
小时 -
分钟 -
秒
如何做...
<st c="30288">feature-engine</st><st c="30368">datetime</st>
-
让我们从导入 <st c="30404">pandas</st>和 <st c="30415">DatetimeFeatures()</st>: import pandas as pd from feature_engine.datetime import DatetimeFeatures -
让我们创建一个包含 20 个值 <st c="30524">datetime</st>变量,从 <st c="30573">2024-05-17</st>午夜开始,然后以 <st c="30626">1</st>天为增量。 然后,我们将此变量存储在一个 DataFrame 中: rng_ = pd.date_range( '2024-05-17', periods=20, freq='D') data = pd.DataFrame({'date': rng_}) -
我们将 首先设置 转换器以提取所有支持的 <st c="30839">datetime</st>特征: dtfs = DatetimeFeatures( variables=None, features_to_extract= "all", )
<st c="30933">DatetimeFeatures()</st> <st c="30994">datetime</st> <st c="31063">变量</st> <st c="31088">None</st><st c="31036">datetime</st> <st c="31188">日期</st> <st c="31197">特征</st>
-
让我们将 <st c="31226">日期</st>和 <st c="31235">时间</st>特征添加到 `数据中: dft = dtfs.fit_transform(data)
<st c="31310">DatetimeFeatures()</st> <st c="31371">datetime</st> <st c="31390">月份</st><st c="31397">年份</st><st c="31403">星期几</st><st c="31416">月份中的天数</st><st c="31430">小时</st><st c="31436">分钟</st><st c="31448">秒</st><st c="31495">features_to_extra</st><st c="31513">ct</st>
-
让我们 将新变量的名称捕获到一个列表中: vars_ = [v for v in dft.columns if "date" in v]
<st c="31656">DatetimeFeatures()</st> <st c="31747">date</st><st c="31831">date_day_of_week</st> <st c="31896">date</st>
<st c="31925">vars_</st>
<st c="31986">['date_month',</st>
<st c="32001">'date_quarter',</st>
<st c="32017">'date_semester',</st>
<st c="32034">'date_year',</st>
<st c="32047">'date_week',</st>
<st c="32060">'date_day_of_week',</st>
<st c="32080">'date_day_of_month',</st>
<st c="32101">'date_day_of_year',</st>
<st c="32121">'date_weekend',</st>
<st c="32137">'date_month_start',</st>
<st c="32157">'date_month_end',</st>
<st c="32175">'date_quarter_start',</st>
<st c="32197">'date_quarter_end',</st>
<st c="32217">'date_year_start',</st>
<st c="32236">'date_year_end',</st>
<st c="32253">'date_leap_year',</st>
<st c="32271">'date_days_in_month',</st>
<st c="32293">'date_hour',</st>
<st c="32306">'date_minute',</st>
<st c="32396">dft[vars_].head()</st>. We can’t show the resulting DataFrame in the book because it is too big.
<st c="32487">Note</st>
<st c="32492">We can create specific features by passing their names to the</st> `<st c="32555">features_to_extract</st>` <st c="32574">parameter.</st>
<st c="32585">For example, to extract</st> `<st c="32610">week</st>` <st c="32614">and</st> `<st c="32619">year</st>`<st c="32623">, we set the transformer like this:</st> `<st c="32659">dtfs = DatetimeFeatures(features_to_extract=["week", "year"])</st>`<st c="32720">. We can also extract all supported features by setting the</st> `<st c="32780">features_to_extract</st>` <st c="32799">parameter</st> <st c="32810">to</st> `<st c="32813">"all"</st>`<st c="32818">.</st>
`<st c="32819">DatetimeFe</st><st c="32830">atures()</st>` <st c="32839">can also</st> <st c="32849">create features from variables in different time zones.</st> <st c="32905">Let’s learn how to correctly set up the transformer in</st> <st c="32960">this situation.</st>
1. <st c="32975">Let’s create a sample DataFrame with a variable’s values in different</st> <st c="33046">time zones:</st>
```
df = pd.DataFrame()
df["time"] = pd.concat(
[
pd.Series(
pd.date_range(
start="2024-08-01 09:00",
freq="h",
periods=3,
tz="Europe/Berlin"
)
),
pd.Series(
pd.date_range(
start="2024-08-01 09:00",
freq="h",
periods=3, tz="US/Central"
)
),
],
axis=0,
)
```py
<st c="33308">If we</st> <st c="33314">execute</st> `<st c="33323">df</st>`<st c="33325">, we</st> <st c="33330">will se</st><st c="33337">e the DataFrame from</st> *<st c="33359">Step 6</st>*<st c="33365">, as shown in the</st> <st c="33383">following output:</st>

<st c="33562">Figure 6.22 – A DataFrame with a variable’s values in different time zones</st>
1. <st c="33636">We’ll</st> <st c="33643">set the transformer to</st> <st c="33666">extract three specific features from this variable after setting it to</st> <st c="33737">the UTC:</st>
```
dfts = DatetimeFeatures(
features_to_extract=
["day_of_week", "hour","minute"],
drop_original=False,
utc=True,
)
```py
2. <st c="33858">Let’s create the</st> <st c="33876">new features:</st>
```
dft = dfts.fit_transform(df)
```py
`<st c="33918">DatetimeFeatures()</st>` <st c="33937">will set all variables into UTC before deriving the</st> <st c="33989">features.</st> <st c="34000">With</st> `<st c="34005">dft.head()</st>`<st c="34015">, we can see the</st> <st c="34032">resulting DataFrame:</st>

<st c="34269">Fig</st><st c="34272">ure 6.23 – A DataFrame with t</st><st c="34302">he original and new variables</st>
<st c="34332">With that, we’ve</st> <st c="34350">created multiple date</st> <st c="34372">and time-related features in a few lines of code.</st> `<st c="34422">feature-engine</st>` <st c="34436">offers a great alternative to manually creating features per feature with</st> `<st c="34511">pandas</st>`<st c="34517">. In addition,</st> `<st c="34532">DatetimeFeatures()</st>` <st c="34550">can be integrated in</st><st c="34571">to scikit-learn’s</st> `<st c="34590">Pipeline</st>` <st c="34598">and</st> `<st c="34603">GridSearchCV</st>`<st c="34615">, among</st> <st c="34623">other classes.</st>
<st c="34637">How it works...</st>
`<st c="34653">DatetimeFeatures()</st>` <st c="34672">extracts several date and time features from</st> `<st c="34718">datetime</st>` <st c="34726">variables automatically by utilizing</st> `<st c="34764">pandas</st>`<st c="34770">’</st> `<st c="34773">dt</st>` <st c="34775">under the hood.</st> <st c="34792">It works with variables whose original data types are</st> `<st c="34846">datetime</st>`<st c="34854">, as well as with object-like and categorical variables, provided that they can be parsed into a</st> `<st c="34951">datetime</st>` <st c="34959">format.</st>
`<st c="34967">DatetimeFeatures()</st>` <st c="34986">extracts the following features by default:</st> `<st c="35031">month</st>`<st c="35036">,</st> `<st c="35038">year</st>`<st c="35042">,</st> `<st c="35044">day_of_week</st>`<st c="35055">,</st> `<st c="35057">day_of_month</st>`<st c="35069">,</st> `<st c="35071">hour</st>`<st c="35075">,</st> `<st c="35077">minute</st>` <st c="35083">and</st> `<st c="35088">second</st>`<st c="35094">. We can make the transformer return all the features it supports by setting the parameter</st> `<st c="35185">features_to_extract</st>` <st c="35204">to</st> `<st c="35208">all</st>`<st c="35211">. In addition, we can extract a specific subset of features by passing the feature names in a list, as we did in</st> *<st c="35324">Step 7</st>*<st c="35330">.</st>
`<st c="35331">DatetimeFeatures()</st>` <st c="35350">automatically finds</st> `<st c="35371">datetime</st>` <st c="35379">variables or variables that can be parsed as</st> `<st c="35425">datetime</st>` <st c="35433">in the DataFrame passed to the</st> `<st c="35465">fit()</st>` <st c="35470">method.</st> <st c="35479">To extract features from a selected variable or group of variables, we can pass their name in a list to the</st> `<st c="35587">variables</st>` <st c="35596">parameter when we set up</st> <st c="35622">the transformer.</st>
<st c="35638">With</st> `<st c="35644">fit()</st>`<st c="35649">,</st> `<st c="35651">Dat</st><st c="35654">etimeFeatures()</st>` <st c="35670">doe</st><st c="35674">s not learn any parameters; instead, it checks that the variables entered by the user are, or can be, parsed into a</st> `<st c="35791">datetime</st>` <st c="35799">format.</st> <st c="35808">If the user does not indicate variable names,</st> `<st c="35854">DatetimeFeatures()</st>` <st c="35872">finds the</st> `<st c="35883">datetime</st>` <st c="35891">variables</st> <st c="35902">automatically.</st> <st c="35917">With</st> `<st c="35922">transform()</st>`<st c="35933">, the</st> <st c="35939">tr</st><st c="35941">ansformer adds the date and time-derived variables to</st> <st c="35996">the DataFrame.</st>
第八章:7
执行特征缩放
-
标准化 特征 -
缩放到最大值和 最小值 -
使用中位数和分位数进行缩放 和 -
执行 均值归一化 -
实现最大 绝对缩放 -
缩放到向量 单位长度
技术要求
<st c="1375">sklearn</st><st c="1430">matplotlib</st>
标准化特征
<st c="1542">0</st> <st c="1578">1</st>

准备中

<st c="3010">1</st><st c="3109">-1</st>
<st c="3412">-1</st> <st c="3478">1</st> <st c="3755">-1</st> <st c="3798">1</st>
均值和标准差对异常值敏感;因此,在使用标准化时,特征可能在存在异常值的情况下以不同的方式缩放。
在实践中,我们经常在忽略分布形状的情况下应用标准化。然而,请记住,如果您使用的模型或测试假设数据分布,您可能从在标准化之前转换变量中受益,或者尝试不同的缩放方法。
如何做到这一点...
在这个菜谱中,我们将对加利福尼亚住房数据集的变量应用标准化:
-
让我们先导入所需的 Python 包、类和函数:
import pandas as pd from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler -
让我们将加利福尼亚住房数据集从 scikit-learn 加载到 DataFrame 中,并删除
<st c="4758">纬度</st>和<st c="4766">经度</st>变量:X, y = fetch_california_housing( return_X_y=True, as_frame=True) X.drop(labels=["Latitude", "Longitude"], axis=1, inplace=True) -
现在,让我们将数据分为训练集和测试集:
X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
接下来,我们将设置 scikit-learn 的
<st c="5086">StandardScaler()</st>函数,并将其拟合到训练集,以便它学习每个变量的均值和标准差:scaler = StandardScaler().set_output( transform="pandas") scaler.fit(X_train)
Scikit-learn 的缩放器,就像任何 scikit-learn 转换器一样,默认返回 NumPy 数组。要返回 <st c="5403">pandas</st> 或 <st c="5413">polars</st> 数据帧,我们需要使用 <st c="5481">set_output()</st> 方法指定输出容器,就像我们在
-
现在,让我们使用训练好的缩放器对训练集和测试集进行标准化: X_train_scaled = scaler.transform(X_train) X_test_scaled = scaler.transform(X_test)<st c="5678">StandardScaler()</st>在<st c="5772">fit()</st>过程中存储从训练集学习到的均值和标准差。让我们可视化学习到的参数。 -
首先,我们将打印
<st c="5875">scaler</st>学习到的均值:scaler.mean_我们可以在以下输出中看到每个变量的均值:
<st c="6133">scaler</st>:scaler.scale_
array([1.89109236e+00, 1.25962585e+01, 2.28754018e+00, 4.52736275e-01, 1.14954037e+03, 6.86792905e+00]) <st c="6331">Let’s compare the transformed data with the original data to understand</st> <st c="6404">the changes.</st> -
让我们打印测试集中原始变量的描述性统计信息:
X_test.describe()在以下输出中,我们可以看到变量的均值与零不同,并且方差有所变化:

-
现在让我们打印 转换变量的描述性统计值: X_test_scaled.describe()在下面的输出中,我们看到变量的均值现在集中在 <st c="7469">0</st>,方差大约为 1 `:

<st c="8206">AveRooms</st><st c="8216">AveBedrms</st><st c="8231">AveOccup</st> <st c="8438">1</st>
<st c="8704">X_test.hist()</st> <st c="8727">X_test_scaled.hist()</st>
它是如何工作的...
<st c="9304">StandardScaler()</st> <st c="9417">fit()</st><st c="9510">mean_</st> <st c="9520">scale_</st> <st c="9545">transform()</st><st c="9646">StandardScaler()</st> <st c="9697">set_output()</st> <st c="9761">pandas</st> <st c="9806">polars</st><st c="9825">transform="polars"</st>
<st c="9849">StandardScaler()</st> <st c="10046">with_std=False</st> <st c="10130">1</st><st c="10193">with_mean=False</st>
缩放到最大和最小值

准备就绪

<st c="12210">1</st> <st c="12228">0</st>
如何操作...
<st c="12420">1</st>
-
让我们首先导入 <st c="12451">pandas</st>和所需的类 和函数: import pandas as pd from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler -
让我们从 scikit-learn 中加载 加利福尼亚住房数据集到 <st c="12739">pandas</st>DataFrame 中,删除 <st c="12770">纬度</st>和 <st c="12783">经度</st>变量: X, y = fetch_california_housing( return_X_y=True, as_frame=True) X.drop(labels=["Latitude", "Longitude"], axis=1, inplace=True) -
让我们将数据分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
让我们设置缩放器并将其拟合到训练集,以便它学习每个变量的最小值、最大值和值范围: scaler = MinMaxScaler().set_output( transform="pandas"") scaler.fit(X_train) -
最后,让我们 使用训练好的缩放器缩放训练集和测试集中的变量: X_train_scaled = scaler.transform(X_train) X_test_scaled = scaler.transform(X_test)
<st c="13465">MinMaxScale</st><st c="13477">r()</st> <st c="13548">data_max_</st><st c="13559">min_</st><st c="13569">data_range_</st>
我们可以通过执行 <st c="13687">X_test_scaled.min()</st> 来验证变换变量的最小值,这将返回以下输出:
<st c="13747">MedInc 0.000000</st>
<st c="13763">HouseAge 0.000000</st>
<st c="13781">AveRooms 0.004705</st>
<st c="13799">AveBedrms 0.004941</st>
<st c="13818">Population 0.000140</st>
<st c="13838">AveOccup -0.000096</st>
<st c="13886">X_test_scaled.max()</st>, we see that the maximum values of the variables are around <st c="13966">1</st>:
<st c="14091">Note</st>
<st c="14096">If you check the maximum values of the variables in the train set after the transformation, you’ll see that they are exactly</st> `<st c="14222">1</st>`<st c="14223">. Yet, in the test set, we see values greater and smaller than</st> `<st c="14286">1</st>`<st c="14287">. This occurs because, in the test set, there are observations with larger or smaller magnitudes than those in the train set.</st> <st c="14413">In fact, we see the greatest differences in the variables that deviate the most from the normal distribution (the last four variables in the dataset).</st> <st c="14564">This behavior is expected because scaling to the minimum and maximum values is sensitive to outliers and very</st> <st c="14674">skewed distributions.</st>
<st c="14695">Scaling to the</st> <st c="14710">minimum and maximum value does not change the shape</st> <st c="14762">of the variable’s distribution.</st> <st c="14795">You can corroborate that by displaying the histograms before and after</st> <st c="14866">the tr</st><st c="14872">ansformation.</st>
<st c="14886">How it works...</st>
<st c="14902">In this rec</st><st c="14914">ipe, we scaled the variables of the California housing dataset to values between</st> `<st c="14996">0</st>` <st c="14997">and</st> `<st c="15001">1</st>`<st c="15002">.</st>
`<st c="15003">MinMaxScaler()</st>` <st c="15018">from scikit-learn learned the minimum and maximum values and the value range of each variable when we called the</st> `<st c="15132">fit()</st>` <st c="15137">method and stored these parameters in its</st> `<st c="15180">data_max_</st>`<st c="15189">,</st> `<st c="15191">min_</st>`<st c="15195">, and</st> `<st c="15201">data_range_</st>` <st c="15212">attributes.</st> <st c="15225">By using</st> `<st c="15234">transform()</st>`<st c="15245">, we made the scaler remove the minimum value from each variable in the train and test sets and divide the result by the</st> <st c="15366">value range.</st>
<st c="15378">Note</st>
`<st c="15383">MinMaxScaler()</st>` <st c="15398">will scale all variables by default.</st> <st c="15436">To scale only a subset of the variables in the dataset, you can use</st> `<st c="15504">ColumnTransformer()</st>` <st c="15523">from scikit-learn or</st> `<st c="15545">SklearnTransformerWrapper()</st>` <st c="15572">from</st> `<st c="15578">Feature-engine</st>`<st c="15592">.</st>
`<st c="15593">MinMaxScaler()</st>` <st c="15608">will scale the variables between</st> `<st c="15642">0</st>` <st c="15643">and</st> `<st c="15648">1</st>` <st c="15649">by default.</st> <st c="15662">However, we have the option to scale to a different range by adjusting the tuple passed to the</st> `<st c="15757">feature_range</st>` <st c="15770">parameter.</st>
<st c="15781">By default,</st> `<st c="15794">MinMaxScaler()</st>` <st c="15808">returns</st> <st c="15817">NumPy arrays, but we can modify this</st> <st c="15854">behavior to return</st> `<st c="15873">pandas</st>` <st c="15879">DataFrames with the</st> `<st c="15900">set_output()</st>` <st c="15912">method, as we</st> <st c="15926">did in</st> *<st c="15934">Step 4</st>*<st c="15940">.</st>
<st c="15941">Scaling with the median and quantiles</st>
<st c="15979">When</st> <st c="15985">scaling variab</st><st c="15999">les to</st> <st c="16007">the median and quantiles, the median value is removed from the observations, and the result is divided by</st> <st c="16112">the</st> **<st c="16117">Inter-Quarti</st><st c="16129">le Range</st>** <st c="16138">(</st>**<st c="16140">IQR</st>**<st c="16143">).</st> <st c="16147">The IQR is the difference between the 3rd quartile and the 1st quartile, or, in other words, the difference between the 75th percentile and the</st> <st c="16291">25th percent</st><st c="16303">ile:</st>

<st c="16310">This method is known</st> <st c="16330">as</st> **<st c="16334">robust</st> <st c="16340">scaling</st>** <st c="16348">because it produces more robust estimates for the center and value range of the variable.</st> <st c="16439">Robust scaling is a suitable alternative to standardization when models require the variables to be centered and the data contains outliers.</st> <st c="16580">It is worth noting that robust scaling will not change the overall shape of the</st> <st c="16660">vari</st><st c="16664">able distribution.</st>
<st c="16683">How to do it...</st>
<st c="16699">In this recipe, we will implement scaling with the median and IQR by</st> <st c="16769">utilizing scikit-learn:</st>
1. <st c="16792">Let’s start</st> <st c="16805">by importing</st> `<st c="16818">pandas</st>` <st c="16824">and the required scikit-learn classes</st> <st c="16863">and functions:</st>
```
导入 pandas as pd
从 `sklearn.datasets` 导入 `fetch_california_housing`
从 `sklearn.model_selection` 导入 `train_test_split`
从 `sklearn.preprocessing` 导入 `RobustScaler`
```py
2. <st c="17051">Let’s load the California housing dataset into a</st> `<st c="17101">pandas</st>` <st c="17107">DataFrame and drop the</st> `<st c="17131">Latitude</st>` <st c="17139">and</st> `<st c="17144">Longitude</st>` <st c="17153">variables:</st>
```
X, y = fetch_california_housing(
return_X_y=True, as_frame=True)
X.drop(labels=[ "Latitude", "Longitude"], axis=1,
inplace=True)
```py
3. <st c="17294">Let’s divide the data into train and</st> <st c="17332">test sets:</st>
```
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0)
```py
4. <st c="17432">Let’s set up scikit-learn’s</st> `<st c="17461">RobustScaler()</st>`<st c="17475">and fit it to the train set so that it learns and stores the median</st> <st c="17544">and IQR:</st>
```
scaler = RobustScaler().set_output(
transform="pandas")
scaler.fit(X_train)
```py
5. <st c="17628">Finally, let’s</st> <st c="17643">scale the</st> <st c="17654">variables in the train and test sets with the</st> <st c="17700">trained scaler:</st>
```
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
```py
6. <st c="17799">Let’s print the variable median values learned</st> <st c="17847">by</st> `<st c="17850">RobustScaler()</st>`<st c="17864">:</st>
```
scaler.center_
```py
<st c="17881">We see the parameters learned by</st> `<st c="17915">RobustScaler()</st>` <st c="17929">in the</st> <st c="17937">following output:</st>
```
<st c="18097">RobustScaler()</st>:
```py
scaler.scale_
<st c="18189">array([2.16550000e+00, 1.90000000e+01, 1.59537022e+00,</st> <st c="18244">9.41284380e-02, 9.40000000e</st><st c="18272">+02, 8.53176853e-01])</st>
```
<st c="18294">此缩放过程不会改变变量的分布。</st> <st c="18362">继续使用直方图比较变换前后变量的分布</st> <st c="18452">。</st>
```py
<st c="18473">How it works...</st>
<st c="18489">To scale the</st> <st c="18502">features using</st> <st c="18517">the median and IQR, we created an instance of</st> `<st c="18564">RobustScaler()</st>`<st c="18578">. With</st> `<st c="18585">fit()</st>`<st c="18590">, the scaler learned the median and IQR for each variable from the train set.</st> <st c="18668">With</st> `<st c="18673">transform()</st>`<st c="18684">, the scaler subtracted the median from each variable in the train and test sets and divided the result by</st> <st c="18791">the IQR.</st>
<st c="18799">After the transformation, the median values of the variables were centered at</st> `<st c="18878">0</st>`<st c="18879">, but the overall shape of the distributions did not change.</st> <st c="18940">You can corroborate the effect of the transformation by displaying the histograms of the variables before and after the transformation and by printing out the main statistical parameters through</st> `<st c="19135">X_test.describe()</st>` <st c="19152">and</st> `<st c="19157">X_test_scaled.b()</st>`<st c="19174">.</st>
<st c="19175">Performing mean normalization</st>
<st c="19205">I</st><st c="19207">n mean normalization, we</st> <st c="19232">center the variable at</st> `<st c="19255">0</st>` <st c="19256">and rescale the distribution to the value range, so that its values lie between</st> `<st c="19337">-1</st>` <st c="19339">and</st> `<st c="19344">1</st>`<st c="19345">. This procedure involves subtracting the mean from each observation and then dividing the result by the difference between the minimum and maximum values, as</st> <st c="19504">sh</st><st c="19506">own here:</st>

<st c="19558">Note</st>
<st c="19562">Mean normalization is an alternative to standardization.</st> <st c="19620">In both cases, the variables are centered at</st> `<st c="19665">0</st>`<st c="19666">. In mean normalization, the variance varies, while the values lie between</st> `<st c="19741">-1</st>` <st c="19743">and</st> `<st c="19748">1</st>`<st c="19749">. On the other hand, in standardization, the variance is set to</st> `<st c="19813">1</st>` <st c="19814">and the value</st> <st c="19829">range varies.</st>
<st c="19842">Mean</st> <st c="19848">normalization is a suitable alternative for models that need the variables to be centered at zero.</st> <st c="19947">However, it is sensitive to outliers and not a suitable option for sparse data, as it will d</st><st c="20039">estroy the</st> <st c="20051">sparse nature.</st>
<st c="20065">How to do it...</st>
<st c="20081">In this recipe, we will implement mean normalization</st> <st c="20135">with</st> `<st c="20140">pandas</st>`<st c="20146">:</st>
1. <st c="20148">Let’s import</st> `<st c="20161">pandas</st>` <st c="20167">and the required scikit-learn class</st> <st c="20204">and function:</st>
```
导入 pandas as pd
从 `sklearn.datasets` 导入 `fetch_california_housing`
从 `sklearn.model_selection` 导入 `train_test_split`
```py
2. <st c="20344">Let’s load the California housing dataset from scikit-learn into a</st> `<st c="20412">pandas</st>` <st c="20418">DataFrame, dropping the</st> `<st c="20443">Latitude</st>` <st c="20451">and</st> `<st c="20456">Longitude</st>` <st c="20465">variables:</st>
```
X, y = fetch_california_housing(
return_X_y=True, as_frame=True)
X.drop(labels=[
"Latitude", "Longitude"], axis=1, inplace=True)
```py
3. <st c="20606">Let’s divide the data into train and</st> <st c="20644">test sets:</st>
```
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0)
```py
4. <st c="20744">Let’s learn the mean values from the variables in the</st> <st c="20799">train set:</st>
```
means = X_train.mean(axis=0)
```py
<st c="20839">Note</st>
<st c="20844">We set</st> `<st c="20852">axis=0</st>` <st c="20858">to take the mean of the rows – that is, of the observations in each variable.</st> <st c="20937">If we set</st> `<st c="20947">axis=1</st>` <st c="20953">instead,</st> `<st c="20963">pandas</st>` <st c="20969">will calculate the mean value per observation across all</st> <st c="21027">the columns.</st>
<st c="21039">By</st> <st c="21043">executing</st> `<st c="21053">print(mean)</st>`<st c="21064">, we display the mean values</st> <st c="21093">per variable:</st>
1. <st c="21235">Now, let’s determine the difference between the maximum and minimum values</st> <st c="21311">per variable:</st>
```
ranges = X_train.max(axis=0)-X_train.min(axis=0)
```py
<st c="21373">By executing</st> `<st c="21387">print(ranges)</st>`<st c="21400">, we display the value ranges</st> <st c="21430">per variable:</st>
```
<st c="21443">MedInc</st> <st c="21450">14.500200</st>
<st c="21460">HouseAge 51.000000</st>
<st c="21479">AveRooms 131.687179</st>
<st c="21499">平均卧室数 33.733333</st>
<st c="21519">人口 35679.000000</st>
<st c="21543">平均占用 598.964286</st>
<st c="21563">dtype: float64</st>
```py
<st c="21578">Note</st>
<st c="21583">The</st> `<st c="21588">pandas</st>` `<st c="21594">mean()</st>`<st c="21601">,</st> `<st c="21603">max()</st>`<st c="21608">, and</st> `<st c="21614">min()</st>` <st c="21619">methods return a</st> `<st c="21637">pandas</st>` <st c="21643">series.</st>
1. <st c="21651">Now, we’ll</st> <st c="21662">apply mean normalization to the train and test se</st><st c="21712">ts by utilizing the</st> <st c="21733">learned parameters:</st>
```
X_train_scaled = (X_train - means) / ranges
X_test_scaled = (X_test - means) / ranges
```py
<st c="21838">Note</st>
<st c="21843">In order to transform future data, you will need to store these parameters, for example, in a</st> `<st c="21938">.txt</st>` <st c="21942">or</st> `<st c="21946">.</st>``<st c="21947">csv</st>` <st c="21951">file.</st>
*<st c="21957">Step 6</st>* <st c="21964">returns</st> `<st c="21973">pandas</st>` <st c="21979">DataFrames with the transformed train and test sets.</st> <st c="22033">Go ahead and compare the variables before and after the transformations.</st> <st c="22106">You’ll see that the distributions did not change, but the variables are centered at</st> `<st c="22190">0</st>`<st c="22191">, and their v</st><st c="22204">alues lie between</st> `<st c="22223">-1</st>` <st c="22225">and</st> `<st c="22230">1</st>`<st c="22231">.</st>
<st c="22232">How it works…</st>
<st c="22246">To implement mean normalization, we captured the mean values of the numerical variables in the train set using</st> `<st c="22358">mean()</st>` <st c="22364">from</st> `<st c="22370">pandas</st>`<st c="22376">. Next, we determined the difference between the maximum and minimum values of the numerical variables in the train set by utilizing</st> `<st c="22509">max()</st>` <st c="22514">and</st> `<st c="22519">min()</st>` <st c="22524">from</st> `<st c="22530">pandas</st>`<st c="22536">. Finally, we used the</st> `<st c="22559">pandas</st>` <st c="22565">series returned by these functions containing the mean values and the value ranges to normalize the train and test sets.</st> <st c="22687">We subtracted the mean from each observation in our train and test sets</st> <st c="22758">and divided the result by the value ranges.</st> <st c="22803">This returned the normalized vari</st><st c="22836">ables in a</st> `<st c="22848">pandas</st>` <st c="22854">DataFrame.</st>
<st c="22865">There’s more...</st>
<st c="22881">There is no dedicated scikit-learn transformer to implement mean normalization, but we can combine the use of two transformers to</st> <st c="23012">do so.</st>
<st c="23018">To do this, we need to import</st> `<st c="23049">pandas</st>` <st c="23055">and load the data, just like we did in</st> *<st c="23095">Steps 1</st>* <st c="23102">to</st> *<st c="23105">3</st>* <st c="23107">in the</st> *<st c="23115">How to do it...</st>* <st c="23130">section of this recipe.</st> <st c="23155">Then, follow</st> <st c="23168">these steps:</st>
1. <st c="23180">Import the</st> <st c="23192">scikit-learn transformers:</st>
```
from sklearn.preprocessing import (
StandardScaler, RobustScaler
)
```py
2. <st c="23285">Let’s set up</st> `<st c="23299">StandardScaler()</st>` <st c="23315">to learn and subtract</st> <st c="23338">the mean without dividing the result by the</st> <st c="23382">standard deviation:</st>
```
scaler_mean = StandardScaler(
with_mean=True, with_std=False,
).set_output(transform="pandas")
```py
3. <st c="23496">Now, let’s set up</st> `<st c="23515">RobustScaler()</st>` <st c="23529">so that it does not remove the median from the values but divides them by the value range – that is, the difference between the maximum and</st> <st c="23670">minimum values:</st>
```
scaler_minmax = RobustScaler(
with_centering=False,
with_scaling=True,
quantile_range=(0, 100)
).set_output(transform="pandas")
```py
<st c="23813">Note</st>
<st c="23818">To divide by the difference between the minimum and maximum values, we need to specify</st> `<st c="23906">(0, 100)</st>` <st c="23914">in the</st> `<st c="23922">quantile_range</st>` <st c="23936">argument</st> <st c="23946">of</st> `<st c="23949">RobustScaler()</st>`<st c="23963">.</st>
1. <st c="23964">Let’s fit the scalers to the train set so that they learn and store the mean, maximum, and</st> <st c="24056">minimum values:</st>
```
scaler_mean.fit(X_train)
scaler_minmax.fit(X_train)
```py
2. <st c="24123">Finally, let’s apply mean normalization to the train and</st> <st c="24181">test sets:</st>
```
X_train_scaled = scaler_minmax.transform(
scaler_mean.transform(X_train)
])
X_test_scaled = scaler_minmax.transform(
scaler_mean.transform(X_test)
)
```py
<st c="24339">We transformed the data with</st> `<st c="24369">StandardScaler()</st>` <st c="24385">to remove the mean and then transformed the resulting DataFrame with</st> `<st c="24455">RobustScaler()</st>` <st c="24469">to divide the result by the range between the minimum and maximum values.</st> <st c="24544">We described the functionality of</st> `<st c="24578">StandardScaler()</st>` <st c="24594">in this chapter’s</st> *<st c="24613">Standardizing the features</st>* <st c="24639">recipe and</st> `<st c="24651">RobustScaler()</st>` <st c="24665">in the</st> *<st c="24673">Scaling with the median and quant</st><st c="24706">iles</st>* <st c="24711">recipe of</st> <st c="24722">this chapter.</st>
<st c="24735">Implementing maximum absol</st><st c="24762">ute scaling</st>
<st c="24774">Maximum absolute scaling</st> <st c="24800">scales the data to its maximum value – that is, it divides every observation by the maximum value of</st> <st c="24901">the variable:</st>

<st c="24936">As a result, the maximum value of each feature will be</st> `<st c="24991">1.0</st>`<st c="24994">. Note that maximum absolute scaling does not center the data, and hence, it’s suitable for scaling sparse data.</st> <st c="25107">In this recipe, we will implement maximum absolute scaling</st> <st c="25166">with scikit-learn.</st>
<st c="25184">Note</st>
<st c="25189">Scikit-learn recommends using this transformer on data that is cen</st><st c="25256">tered at</st> `<st c="25266">0</st>` <st c="25267">or on</st> <st c="25274">sparse data.</st>
<st c="25286">Getting ready</st>
<st c="25300">Maximum absolute scaling</st> <st c="25325">was specifically designed to scale sparse data.</st> <st c="25374">Thus, we will use a bag-of-words dataset that contains sparse variables for the recipe.</st> <st c="25462">In this dataset, the variables are words, the observations are documents, and the values are the number of times each word appears in the document.</st> <st c="25610">Most entries</st> <st c="25623">in the data</st> <st c="25635">are</st> `<st c="25639">0</st>`<st c="25640">.</st>
<st c="25641">We will use a dataset consisting of a bag of words, which is available in the UCI Machine Learning Repository (https://archive.ics.uci.edu/ml/datasets/Bag+of+Words), which is licensed under CC BY</st> <st c="25838">4.0 (</st><st c="25843">https://creativecommons.org/licenses/by/4.0/legalcode</st><st c="25897">).</st>
<st c="25900">I downloaded and prepared a small bag of words representing a simplified version of one of those datasets.</st> <st c="26008">You will find this dataset in the accompanying GitHub</st> <st c="26062">repository:</st> [<st c="26074">https://github.com/PacktPublishing/Python-Feature-Engineering-Cookbook-Third-Edition/tree/main/ch07-scaling</st>](https://github.com/PacktPublishing/Python-Feature-Engineering-Cookbook-Third-Edition/tree/main/ch07-scaling)<st c="26181">.</st>
<st c="26182">How to do it...</st>
<st c="26198">Let’s begin by</st> <st c="26213">importing the required packages and loading</st> <st c="26258">the dataset:</st>
1. <st c="26270">Let’s import the required libraries and</st> <st c="26311">the scaler:</st>
```
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import MaxAbsScaler
```py
2. <st c="26421">Let’s load the</st> <st c="26437">bag-of-words dataset:</st>
```
data = pd.read_csv("bag_of_words.csv")
```py
<st c="26497">If we execute</st> `<st c="26512">data.head()</st>`<st c="26523">, we will see the DataFrame consisting of the words as columns, the documents as rows, and the number of times each word appeared in a document</st> <st c="26667">as values:</st>

<st c="26944">Figure 7.5 – DataFrame with the bag of words</st>
<st c="26988">Note</st>
<st c="26993">Although we omit this step in the recipe, remember that the maximum absolute values should be learned from a training dataset only.</st> <st c="27126">Split the dataset into train and test sets when carrying out</st> <st c="27187">your analysis.</st>
1. <st c="27201">Let’s set</st> <st c="27211">up</st> `<st c="27215">MaxAbsScaler()</st>` <st c="27229">and fit it to the data so that it learns the variables’</st> <st c="27286">maximum values:</st>
```
scaler = MaxAbsScaler().set_output(
transform="pandas")
scaler.fit(data)
```py
2. <st c="27374">Now, let’s scale the variables by utilizing the</st> <st c="27423">trained scaler:</st>
```
data_scaled = scaler.transform(data)
```py
<st c="27475">Note</st>
`<st c="27480">MaxAbsScaler ()</st>` <st c="27496">stores the maximum values in its</st> `<st c="27530">max_ab</st><st c="27536">s_</st>` <st c="27539">attribute.</st>
1. <st c="27550">Let’s display the maximum values stored by</st> <st c="27594">the scaler:</st>
```
scaler.max_abs_
<st c="27715">array([ 7., 6., 2., 2., 11., 4., 3., 6., 52., 2.])</st>
```py
<st c="27766">To follow up, let’s plot the distributions of the original and</st> <st c="27830">scaled variables.</st>
2. <st c="27847">Let’s make a histogram with the bag of words before</st> <st c="27900">the scaling:</st>
```
data.hist(bins=20, figsize=(20, 20))
plt.show()
```py
<st c="27960">In the</st> <st c="27968">following output, we see histograms with the number of times e</st><st c="28030">ach word appears in</st> <st c="28051">a document:</st>

<st c="28575">Figure 7.6 – Histograms with differen</st><st c="28612">t word counts</st>
1. <st c="28626">Now, let’s make a histogram with the</st> <st c="28664">scaled variables:</st>
```
data_scaled.hist(bins=20, figsize=(20, 20))
plt.show()
```py
<st c="28736">In the following output, we can corroborate the change of scale of the v</st><st c="28809">ariables, but their</st> <st c="28830">distr</st><st c="28835">ibution shape remains</st> <st c="28858">the same:</st>

<st c="29068">Figure 7.7 – Histograms of the word counts after the scaling</st>
<st c="29128">With scaling to the maximum absolute value, we linearly scale down</st> <st c="29196">the magnitude of</st> <st c="29213">the features.</st>
<st c="29226">How</st> <st c="29231">it works...</st>
<st c="29242">In this recipe, we</st> <st c="29261">scaled the sparse variables of a bag of words to their absolute maximum values by using</st> `<st c="29350">MaxAbsScaler()</st>`<st c="29364">. With</st> `<st c="29371">fit()</st>`<st c="29376">, the scaler learned the maximum absolute values for each variable and stored them in its</st> `<st c="29466">max_abs_</st>` <st c="29474">attribute.</st> <st c="29486">With</st> `<st c="29491">transform()</st>`<st c="29502">, the scaler divided the variables by their ab</st><st c="29548">solute maximum values, returning a</st> `<st c="29584">pandas</st>` <st c="29590">DataFrame.</st>
<st c="29601">Note</st>
<st c="29606">Remember that you can change the output container to a NumPy array or a</st> `<st c="29679">polars</st>` <st c="29685">DataFrame through the</st> `<st c="29708">set_output()</st>` <st c="29720">method of the scik</st><st c="29739">it-learn</st> <st c="29749">library’s transformers.</st>
<st c="29772">There’s m</st><st c="29782">ore...</st>
<st c="29789">If you want to center the variables’ distribution at</st> `<st c="29843">0</st>` <st c="29844">and then scale them to their absolute maximum, you can do so by combining the use of two scikit-learn transformers within</st> <st c="29967">a pipeline:</st>
1. <st c="29978">Let’s import the required libraries, transformers,</st> <st c="30030">and functions:</st>
```
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import (
MaxAbsScaler, StandardScaler)
from sklearn.pipeline import Pipeline
```py
2. <st c="30275">Let’s load the California housing dataset and split it into train and</st> <st c="30346">test sets:</st>
```
X, y = fetch_california_housing(
return_X_y=True, as_frame=True)
X.drop( labels=[ "纬度",
"经度"], axis=1, inplace=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0)
```py
3. <st c="30576">Let’s set up</st> `<st c="30590">StandardScaler()</st>` <st c="30606">from scikit-learn so that it learns and subtracts the mean but does not divide the result by the</st> <st c="30704">standard deviation:</st>
```
scaler_mean = StandardScaler(
with_mean=True, with_std=False)
```py
4. <st c="30785">Now, let’s set up</st> `<st c="30804">MaxAbsScaler()</st>` <st c="30818">with its</st> <st c="30828">default parameters:</st>
```
scaler_maxabs = MaxAbsScaler()
```py
5. <st c="30878">Let’s include both scalers within a pipeline that returns</st> <st c="30937">pandas DataFrames:</st>
```
scaler = Pipeline([
("scaler_mean", scaler_mean),
("scaler_max", scaler_maxabs),
]).set_output(transform="pandas")
```py
6. <st c="31070">Let’s fit the scalers to the train set so that they learn the</st> <st c="31133">required parameters:</st>
```
scaler.fit(X_train)
```py
7. <st c="31173">Finally, let’s transform the train and</st> <st c="31213">test sets:</st>
```
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
```py
<st c="31307">The pipeline applies</st> `<st c="31329">StandardScaler()</st>` <st c="31345">and</st> `<st c="31350">MaxAbsScaler()</st>` <st c="31364">in sequence to first remove the mean and then scale</st> <st c="31417">the resulting</st> <st c="31431">va</st><st c="31433">riables to their</st> <st c="31451">maximum values.</st>
<st c="31466">Scaling to vector unit length</st>
<st c="31496">Scaling to the vector unit length involves scaling individual observations (not features) to have a unit norm.</st> <st c="31608">Each sample (that is, each row of the data) is rescaled independently of other samples so that its norm equals one.</st> <st c="31724">Each</st> <st c="31729">row constitutes a</st> **<st c="31747">feature vector</st>** <st c="31761">containing the values of every variable for that row.</st> <st c="31816">Hence, with this scaling method, we rescale the</st> <st c="31864">feature vector.</st>
<st c="31879">The norm of a vector is a measure of its magnitude or length in a given space and it can be determined by using the Manhattan (</st>*<st c="32007">l1</st>*<st c="32010">) or the Euclidean (</st>*<st c="32031">l2</st>*<st c="32034">) distance.</st> <st c="32047">The Manhattan distance is given by the sum of the absolute components of</st> <st c="32120">the vector:</st>

<st c="32163">The Euclidean distance is given by the square root of the square sum of the component of</st> <st c="32252">the vector:</st>

<st c="32284">Here,</st> <st c="32290"><st c="32301">and</st> <st c="32305"><st c="32306">are the values of variables</st> *<st c="32334">1</st>*<st c="32335">,</st> *<st c="32337">2</st>*<st c="32338">, and</st> *<st c="32344">n</st>* <st c="32345">for each observation.</st> <st c="32368">Scaling to unit norm consists of dividing each feature vector’s value by either</st> *<st c="32448">l1</st>* <st c="32450">or</st> *<st c="32454">l2</st>*<st c="32456">, so that after the</st> <st c="32475">scaling, the norm of the feature vector is</st> *<st c="32519">1</st>*<st c="32520">. To be clear, we divide each of</st> <st c="32553"><st c="32564">and</st> <st c="32568"><st c="32569">by</st> *<st c="32572">l1</st>* <st c="32574">or</st> *<st c="32578">l2</st>*<st c="32580">.</st></st></st></st></st>
<st c="32581">This scaling procedure changes the variables’ distribution, as illustrated in the</st> <st c="32664">following figure:</st>

<st c="33144">Figure 7.8 – Distribution of a normal and skewed variable before and after scaling each observation’s feature vector to its norm</st>
<st c="33272">Note</st>
<st c="33277">This scaling technique scales each observation and not each variable.</st> <st c="33348">The scaling methods that we discussed so far in this chapter aimed at shifting and resetting the scale of the variables’ distribution.</st> <st c="33483">When we scale to the unit length, however, we normalize each observation individually, contemplating</st> <st c="33584">their values across</st> <st c="33603">all features.</st>
<st c="33617">Scaling</st> <st c="33626">to the unit norm can be used when utilizing kernels to quantify similarity for text classification and clustering.</st> <st c="33741">In this recipe, we will scale each observation’s feature vector to a un</st><st c="33812">it length of</st> `<st c="33826">1</st>` <st c="33827">using scikit-learn.</st>
<st c="33846">How to do it...</st>
<st c="33862">To begin, we’ll</st> <st c="33879">import the required packages, load the dataset, and prepare the train and</st> <st c="33953">test sets:</st>
1. <st c="33963">Let’s import the required Python packages, classes,</st> <st c="34016">and functions:</st>
```
import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import Normalizer
```py
2. <st c="34221">Let’s load the California housing dataset into a</st> `<st c="34271">pandas</st>` <st c="34277">DataFrame:</st>
```
X, y = fetch_california_housing(
return_X_y=True, as_frame=True)
X.drop(labels=[
"纬度", "经度"], axis=1, inplace=True)
```py
3. <st c="34418">Let’s divide the data into train and</st> <st c="34456">test sets:</st>
```
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0)
```py
4. <st c="34556">Let’s set up the scikit-learn library’s</st> `<st c="34597">Normalizer()</st>` <st c="34609">transformer to scale each observation to the Manhattan distance</st> <st c="34674">or</st> `<st c="34677">l1</st>`<st c="34679">:</st>
```
scaler = Normalizer(norm='l1')
```py
<st c="34712">Note</st>
<st c="34717">To normalize to the Euclidean distance, you need to set the norm to</st> `<st c="34786">l2</st>` <st c="34788">using</st> `<st c="34795">scaler =</st>` `<st c="34804">Normalizer(no</st><st c="34817">rm='l2')</st>`<st c="34826">.</st>
1. <st c="34827">Let’s</st> <st c="34834">transform the train and test sets – that is, we’ll divide each observation’s feature vector by</st> <st c="34929">its norm:</st>
```
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
```py
<st c="35026">We can calculate the length (that is, the Manhattan distance of each observation’s feature vector) using</st> `<st c="35132">linalg()</st>` <st c="35140">from NumPy.</st>
2. <st c="35152">Let’s calculate the norm (Manhattan distance) before scaling</st> <st c="35214">the variables:</st>
```
np.round(np.linalg.norm(X_train, ord=1, axis=1), 1)
```py
<st c="35280">As expected, the norm of each</st> <st c="35311">observation varies:</st>
```
<st c="35330">array([ 255.3, 889.1, 1421.7, ..., 744.6, 1099.5,</st>
<st c="35380">1048.9])</st>
```py
3. <st c="35389">Let’s now calculate the norm after</st> <st c="35425">the scaling:</st>
```
np.round(np.linalg.norm(
X_train_scaled, ord=1, axis=1), 1)
```py
<st c="35497">Note</st>
<st c="35502">You need to set</st> `<st c="35519">ord=1</st>` <st c="35524">for the Manhattan distance and</st> `<st c="35556">ord=2</st>` <st c="35561">for the Euclidean distance as arguments of NumPy’s</st> `<st c="35613">linalg()</st>`<st c="35621">function, depending on whether you scaled the features to the</st> `<st c="35684">l1</st>` <st c="35686">or</st> `<st c="35690">l2</st>` <st c="35692">norm.</st>
<st c="35698">We see that the Manhattan distance of each feature vector is</st> `<st c="35760">1</st>` <st c="35761">after scaling:</st>
array([1., 1., 1., ..., 1., 1., 1.])
<st c="35812">Based on the scikit-learn library’s documentation, this scaling method can be useful when using a quadratic form such as the dot-product or any other kernel to quantify</st> <st c="35982">the similarity of a pair</st> <st c="36007">of samples.</st>
<st c="36018">How it wo</st><st c="36028">rks...</st>
<st c="36035">In this recipe, we</st> <st c="36054">scaled the observations from the California housing dataset to their feature vector unit norm by utilizing the Manhattan or Euclidean distance.</st> <st c="36199">To scale the feature vectors, we created an instance of</st> `<st c="36255">Normalizer()</st>` <st c="36267">from scikit-learn and set the norm to</st> `<st c="36306">l1</st>` <st c="36308">for the Manhattan distance.</st> <st c="36337">For the Euclidean distance, we set the norm to</st> `<st c="36384">l2</st>`<st c="36386">. Then, we applied the</st> `<st c="36409">fit()</st>` <st c="36414">method, although there were no parameters to be learned, as this normalization procedure depends exclusively on the values of the features for each observation.</st> <st c="36576">Finally, with the</st> `<st c="36594">transform()</st>` <st c="36605">method, the scaler divided each observation’s feat</st><st c="36656">ure vector by its norm.</st> <st c="36681">This returned</st> <st c="36695">a NumPy array with the scaled dataset.</st> <st c="36734">After the scaling, we used NumPy’s</st> `<st c="36769">linalg.norm</st>` <st c="36780">function to calculate the norm (</st>`<st c="36813">l1</st>` <st c="36816">and</st> `<st c="36821">l2</st>`<st c="36823">) of each vector to confirm that after the transformation, it</st> <st c="36886">was</st> `<st c="36890">1</st>`<st c="36891">.</st>
第九章:8
创建新特征

-
将特征与 数学函数 结合 -
比较特征与 参考变量 -
执行 多项式展开 -
将特征与 决策树 结合 -
从
周期性变量 创建周期性特征 -
创建 样条特征
技术要求
<st c="2059">pandas</st><st c="2067">numpy</st><st c="2074">matplotlib</st><st c="2086">scikit-learn</st><st c="2104">feature-engine</st>
使用数学函数组合特征
<st c="3195">pandas</st> <st c="3206">feature-engine</st>
准备中
<st c="3547">scikit-learn</st>
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
print(data.DESCR)
如何操作...
在这个菜谱中,我们将通过使用多种数学运算来组合变量来创建新的特征:
-
让我们首先加载必要的库、类和 数据: 让我们首先加载必要的库、类和数据: import pandas as pd from feature_engine.creation import MathFeatures from sklearn.datasets import load_breast_cancer -
接下来,将乳腺癌数据集加载到一个 <st c="4725">pandas</st>DataFrame: 接下来,将乳腺癌数据集加载到一个 pandas DataFrame: data = load_breast_cancer() df = pd.DataFrame(data.data, columns=data.feature_names)在以下代码行中,我们将通过使用多种数学运算来组合变量来创建新的特征: 在以下代码行中,我们将通过使用多种数学运算来组合变量来创建新的特征: -
Let’s begin by creating a list with the subset of the features that we want to combine: 让我们首先创建一个列表,包含我们想要组合的特征子集: features = [ «mean smoothness", «mean compactness", «mean concavity", «mean concave points", «mean symmetry", ]步骤 3中的特征代表了图像中细胞核的平均特征。 可能需要获取所有 检查特征的平均值。 步骤 3中的特征代表了图像中细胞核的平均特征。 可能需要获取所有检查特征的平均值。 It might be useful to obtain the mean across all examined characteristics. -
让我们计算特征的均值,然后显示 结果特征: 然后显示计算得到的特征均值: df["mean_features"] = df[features].mean(axis=1) df["mean_features"].head()以下输出显示了从 步骤 3 中得到的特征的均值: 以下输出显示了从步骤 3中得到的特征的均值: <st c="5539">0 0.21702</st> <st c="5548">1 0.10033</st> <st c="5558">2 0.16034</st> <st c="5568">3 0.20654</st> <st c="5578">4 0.14326</st> <st c="5588">Name: mean_features, dtype: float64</st> -
同样,为了捕捉细胞核的一般 可变性,让我们确定平均特征的标准差,然后显示 结果特征: 同样,为了捕捉细胞核的一般可变性,让我们确定平均特征的标准差,然后显示结果特征: df["std_features"] = df[features].std(axis=1) df["std_features"].head()以下输出显示了从 步骤 3 中得到的特征的标准差:<st c="5949">0 0.080321</st> <st c="5960">1 0.045671</st> <st c="5971">2 0.042333</st> <st c="5982">3 0.078097</st> <st c="5993">4 0.044402</st> <st c="6004">Name: std_features, dtype: float64</st>
-
Le t’s make a list containing mathematical functions that we want to use to combine the features: 让我们创建一个包含我们想要用于组合特征的数学函数的列表: math_func = [ "sum", "prod", "mean", "std", "max", "min"] -
现在,让我们应用 步骤 6 中的函数来组合 步骤 3 中的特征,并将结果变量存储在一个新的 DataFrame 中: 现在,让我们应用步骤 6中的函数来组合步骤 3中的特征,并将结果变量存储在一个新的 DataFrame 中: df_t = df[features].agg(math_func, axis="columns")如果我们 执行 <st c="6725">df_t.head()</st>,我们将看到包含新</st> <st c="6779">创建的特征的 DataFrame:</st> <st c="6710">如果我们执行df_t.head()`,我们将看到包含新创建的特征的 DataFrame:

<st c="7135">pandas</st> <st c="7142">agg</st> <st c="7309">np.log</st>
<st c="7440">feature-engine</st><st c="7410">pandas</st>
-
让我们使用输出特性的名称创建一个列表: new_feature_names = [ "sum_f", "prod_f", "mean_f", „std_f", „max_f", „min_f"] -
让我们设置 <st c="7609">MathFeatures()</st>以将第 6 步 中的 特性 应用于第 8 *步 中的字符串,并将新特性命名为第8 步 中的字符串:create = MathFeatures( variables=features, func=math_func, new_variables_names=new_feature_names, ) -
让我们将 新特性 添加到原始 DataFrame 中,并将结果保存在一个 新变量 中:df_t = create.fit_transform(df)我们可以通过执行 <st c="8025">df_t[features +</st><st c="8041">new_feature_name</st><st c="8057">s].head()</st>来显示输入和输出特性 :

<st c="8706">pandas</st> <st c="8712">agg</st> <st c="8785">feature-engine</st>
它是如何工作的...
<st c="8899">pandas</st> <st c="9213">mean()</st>
<st c="9505">agg()</st> <st c="9774">agg()</st>
<st c="9953">特征工程</st><st c="10273">transform()</st> <st c="10313">pandas.agg</st>
另请参阅
<st c="10479">pandas</st>
[了解更多关于](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.agg<st c="10611">pandas</st> <st c="10617">聚合</st> 的信息。
比较特征与参考变量
<st c="11437">pandas</st> <st c="11448">feature-engine</st>
如何做 它…
-
加载必要的库、类和数据: import pandas as pd from feature_engine.creation import RelativeFeatures from sklearn.datasets import load_breast_cancer -
将乳腺癌数据集加载到一个 <st c="12033">pandas</st>DataFrame: data = load_breast_cancer() df = pd.DataFrame(data.data, columns=data.feature_names)在乳腺癌数据集中,一些特征捕捉了乳腺细胞细胞核的最坏和平均特征。 例如,对于每个图像(即每行),我们都有所有核中最差的紧密度和所有核的平均紧密度。 一个捕捉最坏值和平均值之间差异的特征 可以预测恶性。 -
让我们捕捉两个特征之间的差异,即 <st c="12566">最差紧密度</st>和 <st c="12588">平均紧密度</st>的细胞核,在一个新的变量中,并显示其值: 其值: df["difference"] = df["worst compactness"].sub( df["mean compactness"]) df["difference"].head()在以下输出中,我们可以看到这些特征值之间的差异: <st c="12841">0 0.38800</st> <st c="12851">1 0.10796</st> <st c="12861">2 0.26460</st> <st c="12871">3 0.58240</st> <st c="12881">4 0.07220</st> <st c="12891">Name: difference, dtype: float64</st>
<st c="12979">df["difference"] = df["worst compactness"] - (</st>```<st c="13025">df["mean compactness"])</st>
-
让我们创建一个新特征,该特征包含核最差半径与平均半径之间的比率,然后显示其值: 其值: df["quotient"] = df["worst radius"].div( df["mean radius"]) df["quotient"].head()在以下输出中,我们可以看到与特征之间的比率对应的值: 以下输出中,我们可以看到这些特征值之间的差异: <st c="13472">0 1.410784</st> <st c="13483">1 1.214876</st> <st c="13494">2 1.197054</st> <st c="13505">3 1.305604</st> <st c="13516">4 1.110892</st> <st c="13527">Name: quotient, dtype: float64</st>
<st c="13628">df["quotient"] = df["worst radius"] / (</st>```<st c="13667">df["me</st><st c="13674">an radius"])</st>
-
让我们列出分子中的特征: 分子: features = [ "mean smoothness", «mean compactness", "mean concavity", "mean symmetry" ] -
让我们列出分母中的特征: 分母: reference = ["mean radius", "mean area"]
<st c="14250">df[features].div(df["mean radius"])</st><st c="14317">df[features].sub(df["mean radius"])</st>
-
让我们设置 feature-engine库的 <st c="14396">RelativeFeatures()</st>,以便它从步骤 5 中的每个特征中减去或除以步骤 6 中的特征: : creator = RelativeFeatures( variables=features, reference=reference, func=["sub", "div"], )
<st c="14748">RelativeFeatures()</st>
-
让我们将新特征添加到 DataFrame 中,并将结果保存在一个 新变量 中:df_t = creator.fit_transform(df) -
让我们将新特征的名称保存在 一个列表 中:all_feat = creator.feature_names_in_ new_features = [ f for f in df_t.columns if f not in all_feat]
<st c="15058">feature_names_in_</st> <st c="15119">feature-engine</st> <st c="15302">transform()</st>
<st c="15546">print(new_features)</st><st c="15628">ReferenceFeatures()</st>
['mean smoothness_sub_mean radius',
'mean compactness_sub_mean radius',
'mean concavity_sub_mean radius',
'mean symmetry_sub_mean radius',
'mean smoothness_sub_mean area',
'mean compactness_sub_mean area',
'mean concavity_sub_mean area',
'mean symmetry_sub_mean area',
'mean smoothness_div_mean radius',
'mean compactness_div_mean radius',
'mean concavity_div_mean radius',
'mean symmetry_div_mean radius',
'mean smoothness_div_mean area',
'mean compactness_div_mean area',
'mean concavity_div_mean area',
'mean symmetry_div_mean area']
<st c="16449">df</st><st c="16451">_t[new_features].head()</st>

<st c="17039">feature-engine</st> <st c="17193">feature-engine</st>
它是如何工作的...
<st c="17288">pandas</st> <st c="17425">sub()</st> <st c="17435">div()</st>
为了从一个变量中减去另一个变量,我们对第一个变量应用了<st c="17608">sub()</st> <st c="17613">到<st c="17619">pandas</st> <st c="17625">系列,并将第二个变量的<st c="17670">pandas</st> <st c="17676">系列作为参数传递给<st c="17727">sub()</st> <st c="17732">。这个操作返回了一个第三个<st c="17766">pandas</st> <st c="17772">系列,其中包含第一个和第二个变量之间的差值。《st c="17840">要除以另一个变量,我们使用了 <st c="17885">div()</st> <st c="17890">,它的工作方式与<st c="17919">sub()</st> <st c="17924">相同——也就是说,它将左侧的变量除以作为<st c="18013">div()</st> <st c="18018">参数传递的变量。
然后,我们通过利用<st c="18141">ReferenceFeatures()</st> <st c="18160">from</st> <st c="18180">自动将几个变量与两个参考变量通过减法或除法组合起来。<st c="18186">ReferenceFeatures()</st> <st c="18205">transformer接受要组合的变量、参考变量以及用于组合它们的函数。<st c="18321">当使用</st> <st c="18337">时,转换器没有学习参数,而是检查了变量是否为数值型。《st c="18433">执行 <st c="18443">transform()</st> <st c="18454">将新特征添加到<st c="18481">DataFrame</st>中。
备注
<st c="18500">ReferenceFeatures()</st> <st c="18520">还可以对与第二组参考变量相关的一组变量进行加法、乘法、取模或求幂操作。《st c="18653">您可以在其 <st c="18682">文档</st> <st c="18697">[feature-engine.readthedocs.io/en/latest/api_doc/creation/RelativeFeatures.html](https://feature-engine.readthedocs.io/en/latest/api_doc/creation/RelativeFeatures.html) <st c="18783">中了解更多信息。
参见
要了解更多关于<st c="18849">pandas</st> <st c="18855">支持的二进制操作,请访问pandas.pydata.org/pandas-docs/stable/reference/frame.html#binary-operator-functions。
执行多项式展开


<st c="21684">scikit-learn</st>
准备就绪
<st c="22470">PolynomialFeatures()</st> <st c="22508">scikit-learn</st>
<st c="22720">PolynomialFeatures()</st>
<st c="23166">PolynomialFeatures()</st>
在返回的特征中,除了由二次多项式组合返回的特征外,我们现在还有特征与自身进行的三次组合(
<st c="24275">scikit-lea</st><st c="24285">rn</st>实现的多项式展开的输出,让我们直接进入
如何操作...
-
让我们导入所需的库、类 和数据: import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn import set_config from sklearn.preprocessing import PolynomialFeatures -
让我们设置 <st c="24809">scikit-learn</st>库的 <st c="24832">set_output</st>API 全局,以便所有转换器在 <st c="24919">transform()</st>方法的结果返回一个 DataFrame: set_config(transform_output="pandas") -
让我们 创建一个包含一个变量的 DataFrame,其值从 1 到 10: df = pd.DataFrame(np.linspace( 0, 10, 11), columns=["var"]) -
让我们设置 <st c="25126">PolynomialFeatures()</st>以创建单变量的所有可能的三次多项式组合,并从结果中排除偏差项 – 即,我们将排除 值 *1 。poly = PolynomialFeatures( degree=3, interaction_only=False, include_bias=False) -
现在,让我们创建 多项式组合: dft = poly.fit_transform(df)如果我们执行 <st c="25491">dft</st>`,我们将 看到一个包含原始特征、其值的平方以及其值的三次幂的 DataFrame:

<st c="25946">PolynomialFeatures()</st> <st c="26079">poly.get_feature_names_out()</st><st c="26123">array(['var', 'var²', '</st>``<st c="26147">var³'], dtype=object)</st>
-
现在,让我们将新的特征值与原始变量进行绘图: dft = pd.DataFrame( dft, columns=poly.get_feature_names_out()) plt.plot(df["var"], dft) plt.legend(dft.columns) plt.xlabel("original variable") plt.ylabel("new variables") plt.show()在下面的图中,我们可以看到多项式特征与原始变量之间的关系:

-
让我们向我们的玩具数据集添加 两个额外的变量,其值从 1 到 10: df["col"] = np.linspace(0, 5, 11) df["feat"] = np.linspace(0, 5, 11) -
接下来,让我们将数据集中的三个特征与多项式展开到二次度相结合,但这次,我们只返回由至少两个不同变量组合产生的特征——即,交互特征: poly = PolynomialFeatures( degree=2, interaction_only=True, include_bias=False) dft = poly.fit_transform(df)如果我们执行 <st c="27272">dft</st>,我们将看到多项式展开得到的所有特征,这些特征包含原始特征,以及三个变量的所有可能的组合,但没有二次项,因为我们设置了转换器只返回 特征的交互 作用:

如果您只想组合特征的一个子集,您可以通过使用 <st c="28508">ColumnTransformer()</st><st c="28615">SklearnTransformerWrapper()</st> <st c="28648">feature-engine</st>
它是如何工作的...
在这个方法中,我们通过使用特征与其自身或三个变量之间的多项式组合来创建特征。<st c="29044">PolynomialFeatures()</st> <st c="29070">scikit-learn</st><st c="29096">Polynomia</st><st c="29105">lFeatures()</st> <st c="29275">degree</st><st c="29294">degree</st> <st c="29304">3</st><st c="29447">interaction_only</st> <st c="29477">False</st><st c="29529">include_bias</st> <st c="29555">False</st>
<st c="29579">interaction_only</st> <st c="29609">True</st>
<st c="29727">fit()</st> <st c="29732">method</st>根据指定的参数确定了所有可能的特征组合。在这个阶段,转换器没有执行实际的数学计算。<st c="29917">transform()</st> <st c="29928">method</st>使用特征执行数学计算以创建新的变量。通过<st c="30032">get_feature_names()</st> <st c="30051">method</st>,我们可以识别展开的项 - 即每个新特征是如何计算的。
在步骤 2中,我们设置<st c="30171">scikit-learn</st>库的<st c="30183">library’s</st> <st c="30194">set_output</st> <st c="30204">API</st> <st c="30228">pandas</st> <st c="30234">DataFrames</st>作为<st c="30265">transform()</st> <st c="30276">method</st>的结果。<st c="30285">scikit-learn transformers</st>默认返回<st c="30318">NumPy</st> <st c="30323">arrays</st>。新的<st c="30351">set_output</st> <st c="30361">API</st>允许我们将结果的容器更改为<st c="30419">pandas</st> <st c="30425">或</st> <st c="30431">polars</st> <st c="30437">DataFrame</st>。每次设置转换器时,我们都可以单独设置输出 - 例如,通过使用<st c="30543">poly = PolynomialFeatures().set_output(transform="pandas")</st> <st c="30601">。或者,就像在这个食谱中做的那样,我们可以设置全局配置,然后每次设置新的转换器时,<st c="30729">它将返回一个</st> <st c="30747">pandas</st> <st c="30753">DataFrame</st>。
更多...
通过对乳腺癌数据集中变量的子集进行多项式展开来创建特征:
-
首先,导入必要的库、类和数据:
import pandas as pd from sklearn.datasets import load_breast_cancer from sklearn.compose import ColumnTransformer from sklearn.model_selection import train_test_split from sklearn.preprocessing import PolynomialFeatures -
然后,加载数据并将其分为训练集和测试集:
data = load_breast_cancer() df = pd.DataFrame(data.data, columns=data.feature_names) X_train, X_test, y_train, y_test = train_test_split( df, data.target, test_size=0.3, random_state=0 ) -
创建一个包含要组合的特征的列表:
features = [ "mean smoothness", "mean compactness", "mean concavity"] -
将
<st c="31539">PolynomialFeatures()</st><st c="31559">设置为创建所有可能的三次方组合:poly = PolynomialFeatures( degree=3, interaction_only=False, include_bias=False) -
设置列转换器以仅从步骤 3中指定的特征创建特征:
ct = ColumnTransformer([("poly", poly, features)]) -
创建多项式特征:
train_t = ct.fit_transform(X_train) test_t = ct.transform(X_test)
就这样。
注意
<st c="32039">ColumnTransformer()</st> <st c="32081">poly</st> <st c="32159">ColumnTransformer()</st> <st c="32366">feature-engine</st><st c="32383">SklearnTransformerWrapper()</st>
将特征与决策树结合
feature-engine自动化使用树创建特征,在这个菜谱中,我们将学习如何
如何做到...
-
让我们首先导入 <st c="34099">pandas</st>和所需的函数、类、 以及数据集: import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from <st c="34339">feature_engine.creation</st>, import DecisionTreeFeatures -
让我们将加利福尼亚住房数据集加载到一个 <st c="34441">pandas</st>DataFrame 中,并删除 <st c="34473">纬度</st>和 <st c="34486">经度</st>变量: X, y = fetch_california_housing( return_X_y=True, as_frame=True) X.drop(labels=[ "Latitude", "Longitude"], axis=1, inplace=True) -
将数据集分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=0) -
查看 皮尔逊相关系数 特征与目标之间的相关性,这是一个线性关系的度量: for var in X_train.columns: pearson = np.corrcoef(X_train[var], y_train)[0, 1] pearson = np.round(pearson, 2) print( f"corr {var} vs target: {pearson}")在以下输出中,我们可以看到,除了 <st c="35105">MedInc</st>之外,大多数变量与目标之间没有显示出强烈的线性关系;相关系数小于 0.5: <st c="35234">corr MedInc vs target: 0.69</st> <st c="35262">corr HouseAge vs target: 0.1</st> <st c="35291">corr AveRooms vs target: 0.16</st> <st c="35321">corr AveBedrms vs target: -0.05</st> <st c="35353">corr Population vs target: -0.03</st> <st c="35422">feature-engine</st> library’s <st c="35447">DecisionTreeFeatures()</st> selects the best tree by using cross-validation. -
创建一个超参数网格以优化每个 决策树: param_grid = {"max_depth": [2, 3, 4, None]}<st c="35632">feature-engine</st>库的 <st c="35657">DecisionTreeFeatures()</st>允许我们添加由一个或多个特征训练的决策树预测产生的特征。 我们可以以多种方式指导转换器组合特征。 我们将从创建两个变量之间所有可能的组合开始。 -
制作一个包含我们想要用作输入的 两个特征的列表: variables = ["AveRooms", "AveBedrms"] -
将 <st c="36059">DecisionTreeFeatures()</st>设置为创建来自 步骤 6 的所有可能的特征组合: dtf = DecisionTreeFeatures( variables=variables, features_to_combine=None, cv=5, param_grid=param_grid, scoring="neg_mean_squared_error", regression=True, )
<st c="36322">回归</st> <st c="36336">True</st> <st c="36463">False</st><st c="36512">scoring</st>
-
调整转换器,使其在输入特征上训练决策树: dtf.fit(X_train, y_train) -
如果你想知道 哪些特征被用于训练决策树,你可以这样检查它们 : dtf.input_features_在 以下输出中,我们可以看到 <st c="36824">DecisionTreeFeatures()</st>已训练了三个决策树 – 两个使用单个特征 <st c="36916">AveRooms</st>和 <st c="36929">AveBedrms</st>,另一个使用 这两个特征: <st c="36971">['AveRooms', 'AveBedrms', ['AveRooms', 'AveBedrms']]</st>
<st c="37029">DecisionTreeFeatures()</st> <st c="37121">dtf.estimators_</st>
-
现在,将特征添加到训练集和 测试集中: train_t = dtf.transform(X_train) test_t = dtf.transform(X_test) -
创建一个包含新特征名称的列表(转换器将单词 <st c="37338">tree</st>添加到 `特征名称 ):tree_features = [ var for var in test_t.columns if "tree" in var ] -
最后,显示添加到 测试集 中的特征:test_t[tree_features].head()在以下输出中,我们可以看到由第 8 步中训练的决策树产生的新的前五个特征, 步骤 8 :

-
为了检查 这种转换的 能力,计算 新特征与 目标之间的 皮尔逊相关系数: for var in tree_features: pearson = np.corrcoef(test_t[var], y_test)[0, 1] pearson = np.round(pearson, 2) print( f"corr {var} vs target: {pearson}")在以下输出中,我们可以看到新变量与目标之间的相关性大于原始特征显示的相关性(将这些值与 步骤 4 中的值进行比较): corr tree(AveRooms) vs target: 0.37 corr tree(AveBedrms) vs target: 0.12 corr tree(['AveRooms', 'AveBedrms']) vs target: 0.47如果您想结合特定的特征而不是获取变量之间所有可能的组合,您可以通过指定 输入特征 为元组来实现。 -
创建一个包含我们想要用作决策树输入的不同特征的元组元组: features = (('Population'), ('Population','AveOccup'), ('Population', 'AveOccup', 'HouseAge')) -
现在,我们需要 将这些元组传递给 <st c="38992">features_to_combine</st>参数 的 <st c="39025">DecisionTreeFeatures()</st>: dtf = DecisionTreeFeatures( variables=None, features_to_combine=features, cv=5, param_grid=param_grid, scoring="neg_mean_squared_error" ) dtf.fit(X_train, y_train) -
我们在上一步中拟合了转换器,因此我们可以继续将特征添加到训练集和 测试集中: train_t = dtf.transform(X_train) test_t = dtf.transform(X_test) -
显示 新特征: tree_features = [ var for var in test_t.columns if "tree" in var] test_t[tree_features].head()在以下输出中,我们可以看到测试集中决策树的预测结果产生的新特征:

-
导入 <st c="40177">Lasso</st>和 <st c="40191">cross_validate</st>函数 从 <st c="40220">scikit-learn</st>: from sklearn.linear_model import Lasso from sklearn.model_selection import cross_validate -
设置一个 Lasso 回归模型: lasso = Lasso(random_state=0, alpha=0.0001) -
使用原始数据通过交叉验证训练和评估模型,然后打印出 结果 r -squared: cv_results = cross_validate(lasso, X_train, y_train, cv=3) mean = cv_results['test_score'].mean() std = cv_results['test_score'].std() print(f"Results: {mean} +/- {std}")在以下输出中,我们可以看到使用原始特征训练的 Lasso 回归模型的 r -squared: <st c="40809">Results: 0.5480403481478856 +/- 0.004214649109293269</st> -
最后,使用从决策树派生的特征训练 Lasso 回归模型,并使用交叉验证进行评估: variables = ["AveRooms", "AveBedrms", "Population"] train_t = train_t.drop(variables, axis=1) cv_results = cross_validate(lasso, train_t, y_train, cv=3) mean = cv_results['test_score'].mean() std = cv_results['test_score'].std() print(f"Results: {mean} +/- {std}")在 以下输出中,我们可以看到基于树派生特征训练的 Lasso 回归模型的性能更好; r -square 大于 步骤 20 的结果: <st c="41447">Results: 0.5800993721099441 +/- 0.002845475651622909</st>
<st c="41609">feature-engine</st>
它是如何工作的...
<st c="41766">DecisionTreeFeatures()</st> <st c="41793">Feature-engine</st>
<st c="41918">DecisionTreeFeatures()</st> <st c="42251">scoring</st> <st c="42329">cv</st>
另请参阅
从周期性变量创建周期性特征
准备


我们应该使用正弦函数吗?还是应该使用余弦函数?实际上,我们需要两者都使用来无歧义地编码变量的所有值。因为正弦和余弦函数在 0 和 1 之间循环,它们会在多个 x 值上取值为 0。例如,0 的正弦值为 0,π的正弦值也是 0。因此,如果我们只使用正弦函数来编码变量,我们就无法再区分 0 和π的值了。然而,由于正弦和余弦函数的相位差,0 的余弦值为 1,而π的余弦值为-1。因此,通过使用两个函数来编码变量,我们现在能够区分 0 和 1,对于正弦函数,它们将取(0,1)和(0,-1)作为值,而对于余弦函数,则分别取(0,1)和(0,-1)。
如何实现...
在这个菜谱中,我们首先将玩具 DataFrame 中的小时变量通过正弦和余弦变换来转换,以了解新的变量表示形式。然后,我们将使用feature-engine来自动化从多个周期性变量中创建特征:
-
开始导入必要的库:
import numpy as np import pandas as pd import matplotlib.pyplot as plt -
创建一个包含一个变量 –
<小时>– 的玩具 DataFrame,其值在 0 和 23 之间:df = pd.DataFrame([i for i in range(24)], columns=["hour"]) -
接下来,创建两个特征,使用正弦和余弦变换,在将变量值归一化到 0 和 2π之间后:
df["hour_sin"] = np.sin( df["hour"] / df["hour"].max() * 2 * np.pi) df["hour_cos"] = np.cos( df["hour"] / df["hour"].max() * 2 * np.pi)如果我们执行
df.head(),我们将看到原始特征和新特征:

图 8.12 – 包含小时变量和通过正弦和余弦变换获得的新特征的 DataFrame
-
制作小时与其正弦变换值之间的散点图:
plt.scatter(df["hour"], df["hour_sin"]) plt.ylabel("Sine of hour") plt.xlabel("Hour") plt.title("Sine transformation")在下面的图中,我们可以看到在-1 和 1 之间的小时圆的值,就像变换后的正弦函数:

图 8.13 – 小时与其正弦变换值的散点图
-
现在,在 小时和其 余弦变换之间 制作一个散点图: plt.scatter(df["hour"], df["hour_cos"]) plt.ylabel("Cosine of hour") plt.xlabel("Hour") plt.title("Cosine transformation")在下面的图中,我们可以看到小时的值在-1 和 1 之间循环,就像变换后的余弦函数一样:

-
绘制正弦值与小时余弦值的 关系图,并使用 颜色图叠加小时的原值: fig, ax = plt.subplots(figsize=(7, 5)) sp = ax.scatter( df["hour_sin"], df["hour_cos"], c=df["hour"]) ax.set( xlabel="sin(hour)", ylabel="cos(hour)", ) _ = fig.colorbar(sp)在下面的图中,我们可以看到小时的两个三角变换如何反映 其循环性质,在一张让我们想起 钟表的图表中:

<st c="49846">feature-engine</st> <st c="49871">CyclicalFeatures()</st>
-
导入 <st c="49898">CyclicalFeatures()</st>: from feature_engine.creation import CyclicalFeatures -
让我们创建 一个包含 <st c="50019">小时</st>, <st c="50025">月份</st>, 和 <st c="50036">星期</st>变量的玩具数据集,这些变量的 值分别在 0 到 23,1 到 12,和 0 到 6 之间变化: df = pd.DataFrame() df["hour"] = pd.Series([i for i in range(24)]) df["month"] = pd.Series([i for i in range(1, 13)]*2) df["week"] = pd.Series([i for i in range(7)]*4)如果我们执行 e <st c="50308">df.head()</st>,我们将看到 玩具数据集 的前五行:

-
设置转换器以从 以下变量创建正弦和余弦特征: cyclic = CyclicalFeatures( variables=None, drop_original=False, )
<st c="50617">变量</st> <st c="50630">None</st><st c="50636">CyclicalFeatures()</st> <st c="50831">变量</st> <st c="50944">drop_original</st>
-
最后,将 特征添加到 DataFrame 中,并将结果捕获在一个 新变量中: dft = cyclic.fit_transform(df)如果我们执行 dft.head() ,我们将看到原始和 新特征:

工作原理...
<st c="52167">pandas.max()</st><st c="52267">numpy.pi</st><st c="52294">np.sin</st> <st c="52305">np.cos</st>
<st c="52435">Feature-engine</st> <st c="52460">CyclicalFeatures()</st><st c="52485">fit()</st><st c="52562">transform()</st><st c="52621">DataFrame</st>中。
<st c="52916">Feature-engine</st>
创建样条特征
准备工作


如何做…
在这个菜谱中,我们将使用样条曲线来模拟正弦函数。
-
让我们首先导入必要的库和类: import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.linear_model import Ridge from sklearn.preprocessing import SplineTransformer -
创建一个包含 20 个介于-1 和 11 之间的值的训练集 <st c="56637">X</st>,以及目标变量 <st c="56699">y</st>,它是 <st c="56720">X</st>的 <st c="56724">正弦</st>: X = np.linspace(-1, 11, 20) y = np.sin(X) -
绘制 <st c="56798">X</st>和 <st c="56803">y</st>之间的关系图: plt.plot(X, y) plt.ylabel("y") plt.xlabel("X")在下面的图中,我们可以看到 <st c="56909">X</st>的 <st c="56906">正弦</st>函数:

-
使用 Ridge 回归将线性模型拟合到预测 <st c="57106">y</st>来自 <st c="57113">X</st>,然后获得模型的预测结果: linmod = Ridge(random_state=10) linmod.fit(X.reshape(-1, 1), y) pred = linmod.predict(X.reshape(-1, 1)) -
现在,绘制 <st c="57333">X</st>和 <st c="57339">y</st>之间的关系图,并叠加 预测结果: plt.plot(X, y) plt.plot(X, pred) plt.ylabel("y") plt.xlabel("X") plt.legend( ["y", "linear"], bbox_to_anchor=(1, 1), loc="upper left")在下面的图中,我们可以看到线性模型 m 对 X 和 y 之间的非线性关系拟合得非常差:

-
现在,设置 <st c="57769">SplineTransformer()</st>以从 <st c="57824">X</st>中获取样条特征,通过使用三次多项式和五个等距节点: spl = SplineTransformer(degree=3, n_knots=5) -
获取样条特征并将 NumPy 数组转换为 <st c="58033">pandas</st>DataFrame,添加样条 <st c="58082">基函数</st>的名称: X_t = spl.fit_transform(X.reshape(-1, 1)) X_df = pd.DataFrame( X_t, columns=spl.get_feature_names_out(["var"]) )通过执行 <st c="58226">X_df.head()</st>,我们可以看到 <st c="58254">样条</st>特征:

<st c="58632">SplineTransformer()</st> <st c="58692">n_splines = n_knots + degree –</st> <st c="58723">1</st>
-
现在,将样条曲线与 的值 绘制出来 X : plt.plot(X, X_t) plt.legend( spl.get_feature_names_out(["var"]), bbox_to_anchor=(1, 1), loc="upper left") plt.xlabel("X") plt.ylabel("Splines values") plt.title("Splines") plt.show()在下面的图中,我们可以看到不同样条与预测变量 的值 之间 X 的关系:

-
现在,拟合一个线性模型来预测 y ,从 X 获得的样条特征,通过利用岭回归,然后获得模型的预测: linmod = Ridge(random_state=10) linmod.fit(X_t, y) pred = linmod.predict(X_t) -
现在,绘制 X 和 y 之间的关系图,并叠加预测: plt.plot(X, y) plt.plot(X, pred) plt.ylabel("y") plt.xlabel("X") plt.legend( ["y", "splines"], bbox_to_anchor=(1, 1), loc="upper left")在下面的图中,我们可以看到通过利用样条特征作为输入,岭回归可以更好地预测 y 的形状 :

-
从 <st c="60495">scikit-learn</st>导入一些额外的类和函数: from sklearn.datasets import fetch_california_housing from sklearn.compose import ColumnTransformer from sklearn.model_selection import cross_validate -
加载加利福尼亚住房数据集,并删除两个我们不会用于建模的变量: X, y = fetch_california_housing( return_X_y=True, as_frame=True) X.drop(["Latitude", "Longitude"], axis=1, inplace=True) -
首先,我们 将 拟合岭回归来预测房价,基于现有变量,通过利用交叉验证,然后获得模型的性能以设置 基准: linmod = Ridge(random_state=10) cv = cross_validate(linmod, X, y) mean_, std_ = np.mean( cv[«test_score"]), np.std(cv["test_score"]) print(f"Model score: {mean_} +- {std_}")在以下输出中,我们可以看到模型性能,其中值是 <st c="61334">R</st>-squared: <st c="61415">SplineTransformer()</st> to obtain spline features from four variables by utilizing third-degree polynomials and 50 knots, and then fit the pipeline to the data:spl = SplineTransformer(degree=3, n_knots=50)
ct = ColumnTransformer(
[("splines", spl, [ "平均房间数", "平均卧室数", "人口" "平均居住人数"] )], remainder="passthrough",)
ct.fit(X, y)
<st c="61791">ColumnTransformer()</st> <st c="61875">remainder=passthrough</st><st c="61985">MedInc</st> <st c="61996">HouseAge</st> <st c="62105">ct.get_feature_names_out()</st>
-
现在,使用交叉验证将岭回归拟合到基于 <st c="62202">MedInc</st>、 <st c="62210">HouseAge</st>和样条特征来预测房价,然后获得模型的性能: cv = cross_validate(linmod, ct.transform(X), y) mean_, std_ = np.mean( cv[«test_score"]), np.std(cv["test_score"]) print(f"Model score: {mean_} +- {std_}")在以下输出中,我们可以看到模型性能,其中值是 <st c="62551">R</st>-squared: <st c="62565">Model score: 0.5553526813919297 +- 0.02244513992785257</st>
它是如何工作的…
<st c="62993">SplineTransformer()</st> <st c="63018">scikit-learn</st><st c="63036">SplineTransformer()</st> <st c="63078">degree</st> <st c="63137">n_knots</st><st c="63253">X</st> <st c="63283">knots</st> <st c="63363">X</st> <st c="63426">X</st>
<st c="63740">fit()</st>,转换器计算样条的节点。</st> <st c="63798">使用</st> <st c="63814">,它返回 B 样条的数组。<st c="63875">n_splines=n_knots + degree –</st> <st c="63904">1</st>
<st c="63959">SplineTransformer()</st> <st c="64013">pandas</st> <st c="64115">set_output()</st>
<st c="64153">ColumnTransformer()</st> <st c="64233">remainder</st> <st c="64246">passthrough</st>,<st c="64280">将未用于获取样条的特征连接到结果样条矩阵中。</st> <st c="64379">通过这样做,我们使用样条拟合了 Ridge 回归,加上</st><st c="64456">和</st>
另请参阅
-
Perperoglou 等人。 R 中样条函数过程综述 ( https://bmcmedresmethodol.biomedcentral.com/articles/10.1186/s12874-019-0666-3 ). BMC Med Res Methodol 19, 46 (2019). -
Eilers 和 Marx. 使用 B 样条和 惩罚 ( https://projecteuclid.org/journals/statistical-science/volume-11/issue-2/Flexible-smoothing-with-B-splines-and-penalties/10.1214/ss/1038425655.full ). -
以下页面展示了如何使用 B 样条来建模时间序列数据的示例,请查看 <st c="65136">scikit-learn</st>库的 文档: https://scikit-learn.org/stable/auto_examples/applications/plot_cyclical_feature_engineering.html#periodic-spline-features 。
第十章:9
使用 Featuretools 从关系数据中提取特征
<st c="1887">featuretools</st> <st c="1899">Python 库</st>创建关系数据聚合视图的常见方法。
-
设置实体集并自动创建特征 自动创建特征 -
使用通用和累积操作创建特征 累积操作创建特征 -
结合 数值特征 -
从日期和时间中提取特征 和特征 -
从文本中提取特征 从文本中提取特征 -
使用聚合原语创建特征
技术要求
<st c="2479">pandas</st><st c="2487">matplotlib</st><st c="2503">featuretools</st> <st c="2562">featuretools</st> <st c="2580">pip</st>
pip install featuretools
conda install -c conda-forge featuretools
<st c="2727">featuretools</st> <st c="2786">dask</st> <st c="2888">pandas</st><st c="2935">featuretools</st> <st c="2967">graphviz</st>
设置实体集和自动创建特征
<st c="4265">featuretools</st><st c="4427">featuretools</st>
<st c="4981">featuretools</st>
<st c="5232">featuretoo</st><st c="5242">ls</st>
准备工作
<st c="5491">customer_id</st> <st c="5589">invoice</st> <st c="5805">stock code</st>

如何操作...
-
让我们导入所需的库: import pandas as pd import featuretools as ft from woodwork.logical_types import Categorical -
让我们加载在“准备” 部分中描述的零售数据集,并显示其前五行: df = pd.read_csv( «retail.csv», parse_dates=[«invoice_date»]) df.head()在下面的屏幕截图中,我们可以看到客户( <st c="6965">customer_id</st>)和发票( <st c="6994">invoice</st>)的唯一标识符,以及关于每张发票中购买的商品的附加信息,例如商品的代码( <st c="7099">stock_code</st>)、描述、数量、单价,以及发票的日期:

<st c="7699">pandas</st><st c="7708">unique()</st> <st c="7818">df["customer_id"].nunique()</st>
-
让我们用一个任意的名称初始化一个实体集,例如 如下 <st c="7910">data</st>: es = ft.EntitySet(id="data") -
让我们向实体集添加一个 DataFrame;我们给 DataFrame 起个名字( <st c="8017">data</st>)。 我们需要为每一行添加一个唯一标识符,我们称之为 <st c="8089">rows</st>,由于在这个数据集中我们没有唯一的行标识符,我们将通过设置 <st c="8214">make_index=True</st>创建它作为额外的列。最后,我们指出 <st c="8257">invoice_date</st>是 <st c="8280">datetime</st>类型,并且 <st c="8298">customer_id</st>应该被处理 为 <st c="8331">Categorical</st>类型: es = es.add_dataframe( dataframe=df, dataframe_name=»data», index="rows", make_index=True, time_index=»invoice_date», logical_types={ «customer_id»: Categorical}, ) -
接下来,我们添加原始 数据 DataFrame 和 <st c="8562">invoices</st>之间的关系。为此,我们指出原始或基础 DataFrame,我们在 步骤 4 中称之为 <st c="8664">data</st>,我们给新的 DataFrame 起个名字, <st c="8714">invoices</st>,我们添加发票的唯一标识符,并将包含 <st c="8800">customer_id</st>的列添加到这个 DataFrame 中: es.normalize_dataframe( base_dataframe_name=»data», new_dataframe_name=»invoices», index="invoice", copy_columns=[«customer_id»], )
<st c="8980">customer_id</st> <st c="9008">invoices</st>
-
现在,我们添加第二个关系,即客户和发票之间的关系。 为此,我们指出基础 DataFrame,我们在 步骤 5 中称之为 <st c="9241">invoices</st>,然后我们给新的 DataFrame 起个名字, <st c="9300">customers</st>,并添加唯一的 客户标识符: es.normalize_dataframe( base_dataframe_name=»invoices», new_dataframe_name=»customers», index=»customer_id», ) -
我们可以 添加一个第三种关系,在原始 数据 和 产品 之间: es.normalize_dataframe( base_dataframe_name=»data», new_dataframe_name=»items», index=»stock_code», ) -
让我们显示实体集中的信息: 实体集: es在以下输出中,我们看到实体集包含四个 DataFrame:原始数据, <st c="9793">invoices</st>DataFrame, <st c="9817">customers</st>DataFrame,以及产品或 <st c="9857">items</st>DataFrame。 实体还包含发票或项目与原始数据之间的关系,以及客户 和发票 之间的关系: <st c="10009">Entityset: data</st> <st c="10025">DataFrames:</st> <st c="10037">data [Rows: 741301, Columns: 8]</st> <st c="10069">invoices [Rows: 40505, Columns: 3]</st> <st c="10104">customers [Rows: 5410, Columns: 2]</st> <st c="10139">items [Rows: 4631, Columns: 2]</st> <st c="10170">Relationships:</st> <st c="10185">data.invoice -> invoices.invoice</st> <st c="10218">invoices.customer_id -> customers.customer_id</st> <st c="10319">invoices</st> DataFrame:es["invoices"].head()
<st c="10361">We see in</st> <st c="10372">the following output that</st> `<st c="10398">featuretools</st>` <st c="10410">au</st><st c="10413">tomatically created a DataFrame containing the invoice’s unique</st> <st c="10478">identifier, followed by the customer’s unique identifier and the first date registered for</st> <st c="10569">ea</st><st c="10571">ch invoice:</st>

-
现在让我们显示 <st c="10913">客户</st>DataFrame: es["customers"].head()在以下输出中,我们可以看到 <st c="10997">featuretools</st>自动创建了一个包含客户唯一标识符的 DataFrame,随后是此客户的首次发票日期:

<st c="11501">es["items"].head()</st> <st c="11586">pandas</st> <st c="11595">shape</st>
-
我们还可以以下方式显示这些数据表之间的 关系: es.plot()
<st c="11887">graphviz</st>

<st c="13035">featuretools</st>
-
让我们通过聚合客户级别的数据来创建特征。 为此,我们设置了 <st c="13185">featuretools</st>,将 <st c="13210">客户</st>指定为目标 DataFrame。 在创建特征时,我们希望忽略具有唯一标识符的两个列: feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»customers», ignore_columns={ «invoices»:[«invoice»], «invoices»:[«customer_id»], } )
<st c="13621">feature_matrix</st> <st c="13689">feature_defs</st> <st c="13769">feature_defs</st>
-
由于篇幅原因,我们无法在书中打印出所有特征 ,因此,让我们显示五个 创建特征的 名称: feature_defs[5:10]在以下输出中,我们看到由 <st c="14331">featuretools</st>` 创建的 114 个特征中的 5 个 :<st c="14345">[<Feature: MIN(data.price)>,</st> <st c="14373"><Feature: MIN(data.quantity)>,</st> <st c="14403"><Feature: MODE(data.description)>,</st> <st c="14437"><Feature: MODE(data.stock_code)>,</st> <st c="14470"><Feature: NUM_UNIQUE(data.description)>]</st>
<st c="14521">featuretools</st> <st c="14720">MIN(data.quantity)</st> <st c="14756">df.groupby(["customer_id"])["quantity"].min()</st><st c="14828">pandas</st>
-
让我们显示包含五个 创建特征的 DataFrame 的前五行: feature_matrix[feature_matrix.columns[5:10]].head()在以下输出中,我们可以看到包含五个 新特征值的 五个 新特征的前五行:

-
同样,我们可以通过在 发票级别聚合信息来自动创建特征: feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»invoices», ignore_columns = {«data»: [«customer_id»]}, max_depth = 1, ) -
上一步返回了 24 个特征 - 让我们显示它们的名称: feature_defs我们可以在以下输出中看到特征的名称: 如下: <st c="16024">[<Feature: customer_id>,</st> <st c="16049"><Feature: COUNT(data)>,</st> <st c="16072"><Feature: MAX(data.price)>,</st> <st c="16099"><Feature: MAX(data.quantity)>,</st> <st c="16129"><Feature: MEAN(data.price)>,</st> <st c="16157"><Feature: MEAN(data.quantity)>,</st> <st c="16188"><Feature: MIN(data.price)>,</st> <st c="16215"><Feature: MIN(data.quantity)>,</st> <st c="16245"><Feature: MODE(data.description)>,</st> <st c="16279"><Feature: MODE(data.stock_code)>,</st> <st c="16312"><Feature: NUM_UNIQUE(data.description)>,</st> <st c="16352"><Feature: NUM_UNIQUE(data.stock_code)>,</st> <st c="16391"><Feature: SKEW(data.price)>,</st> <st c="16419"><Feature: SKEW(data.quantity)>,</st> <st c="16450"><Feature: STD(data.price)>,</st> <st c="16477"><Feature: STD(data.quantity)>,</st> <st c="16507"><Feature: SUM(data.price)>,</st> <st c="16534"><Feature: SUM(data.quantity)>,</st> <st c="16564"><Feature: DAY(first_data_time)>,</st> <st c="16596"><Feature: MONTH(first_data_time)>,</st> <st c="16630"><Feature: WEEKDAY(first_data_time)>,</st> <st c="16666"><Feature: YEAR(first_data_time)>]</st>
<st c="16782">feature_matrix.head()</st>
<st c="16952">invoices</st> <st c="16964">items</st>
它是如何工作的...
<st c="17344">featuretools</st><st c="17358">EntitySet</st> <st c="17372">dfs</st>
<st c="17449">EntitySet</st> <st c="17645">featuretools</st><st c="17820">customer_id</st> <st c="17835">Categorical</st>
<st c="17890">featuretools</st><st c="17920">es["data"].ww</st><st c="17941">es</st> <st c="17966">data</st>
<st c="18000">The</st> <st c="18005">EntitySet</st> <st c="18014">类具有</st> <st c="18029">add_dataframe</st> <st c="18042">方法,我们在</st> *<st c="18068">步骤 4</st>* <st c="18099">在使用此方法时,我们需要指定唯一标识符,如果没有,则需要创建一个,就像我们在</st> *<st c="18228">步骤 4</st>* <st c="18247">make_index</st> <st c="18257">设置为</st> 在 <st c="18305">add_dataframe</st> <st c="18318">传递了</st> <st c="18392">rows</st> <st c="18396">列,其中包含每行的唯一标识符,这是一个从 0 开始的新整数序列。</st>
<st c="18518">注意</st>
<st c="18523">而不是使用</st> <st c="18545">add_dataframe</st> <st c="18558">方法将 DataFrame 添加到实体集中,我们可以通过执行</st> <st c="18630">es["df_name"]=df</st> <st c="18646">来添加,其中</st> <st c="18654">"df_name"</st> <st c="18663">是我们想要给 DataFrame 的名字,</st> <st c="18713">df</st> <st c="18715">是我们想要</st> <st c="18741">添加的 DataFrame。</st>
<st c="18748">EntitySet</st> <st c="18753">类具有</st> <st c="18777">normalize_dataframe</st> <st c="18796">方法,该方法用于从现有列的唯一值创建一个新的 DataFrame 和关系。</st> <st c="18907">该方法接受新 DataFrame 将关联的 DataFrame 的名称以及</st> <st c="18998">新 DataFrame 的名称。</st> <st c="19028">我们</st> <st c="19030">还需要在</st> <st c="19057">index</st> <st c="19062">参数中指示新 DataFrame 的唯一标识符。</st> <st c="19121">默认情况下,此方法创建一个包含唯一标识符的新 DataFrame,后面跟着一个</st> <st c="19217">datetime</st> <st c="19225">列,包含每个唯一标识符首次注册的日期。</st> <st c="19298">我们可以通过使用</st> <st c="19353">copy_columns</st> <st c="19365">参数来向此 DataFrame 添加更多列,就像我们在</st> <st c="19397">中做的那样。</st> <st c="19391">添加更多列到新 DataFrame 对于我们想要跟踪与此新 DataFrame 的关系是有用的,就像我们在</st> <st c="19535">中做的那样。</st>
<st c="19541">实体集</st> <st c="19570">plot()</st> <st c="19655">我们看到了我们的数据表之间的关系;</st> <st c="19719">和</st><st c="19729">(产品) 表与原始数据相关,而</st><st c="19804">表与</st>
<st c="19980">发票</st> <st c="19993">项目</st> <st c="20092">客户</st> <st c="20445">max_depth</st> <st c="20468">dfs</st>
<st c="20530">dfs</st> <st c="20539">featuretools</st> <st c="20614">dfs</st><st c="20727">dfs</st>
<st c="20977">dfs</st> <st c="20994">月份</st><st c="21001">年份</st><st c="21007">日</st><st c="21016">周</st>
考虑到转换和聚合原语的功能,让我们尝试理解在这个菜谱中创建的特性。<st c="21585">dfs</st>
<st c="21679">featuretools</st>
<st c="21837">featuretools</st> <st c="21903">客户</st> <st c="21936">发票</st> <st c="21995">特性是通过在两个级别上聚合数据创建的。</st> <st c="22052">首先,使用整个数据集对每个客户的数据进行了聚合。</st> <st c="22127">接下来,首先对每个发票的数据进行了聚合,然后对预聚合的数据再次进行聚合以生成</st>
<st c="22252">featuretools</st> <st c="22356">COUNT</st><st c="22363">MEAN</st><st c="22369">STD</st><st c="22378">SKEW</st><st c="22526">MEAN(data.quantity)</st> <st c="22676">df.groupby("customer_id"])["quantity"].mean()</st><st c="22748">pandas</st><st c="22779">MEAN(invoices.MEAN(data.quantity))</st> <st c="22889">df.groupby("invoice"])["quantity"].mean()</st>
<st c="23067">featuretools</st> <st c="23145">description</st> <st c="23181">NUM_UNIQUE(data.description)</st> <st c="23214">MODE(data.descripti</st><st c="23233">on)</st>
<st c="23453">NUM_UNIQUE(data.description)</st> <st c="23486">MODE(data.description)</st> <st c="23586">featuretools</st> <st c="23712">MAX(invoices.NUM_UNIQUE(data.description)</st>
<st c="23952">featuretools</st> <st c="24020">customers</st> <st c="24053">customer_id</st> <st c="24191">featuretools</st> <st c="24212">DAY(first_invoices_time)</st><st c="24238">MONTH(first_invoices_time)</st><st c="24266">WEEKDAY(first_invoices_time)</st><st c="24300">YEAR(first_invoices_time)</st>
<st c="24381">featuretools</st> <st c="24451">COUNT(invoices)</st><st c="24497">COUNT(data)</st>
另请参阅
<st c="24575">featuretools</st>
使用通用和累积操作创建特征
<st c="24829">f</st><st c="24830">eaturetools</st>
<st c="25066">featuretools</st>
<st c="25931">featuretools</st>
准备就绪
<st c="26422">featuretools</st>
<st c="26824">pandas</st>
-
让我们导入 <st c="26851">pandas</st>和 <st c="26862">numpy</st>: import numpy as np import pandas as pd -
让我们加载在 技术 要求 部分中描述的零售数据集: df = pd.read_csv( «retail.csv», parse_dates=[«invoice_date»]) -
让我们 将两个数值变量, <st c="27093">价格</st>和 <st c="27103">数量</st>,捕获到一个列表中: numeric_vars = ["quantity", "price"] -
让我们 将累积函数的名称捕获到一个列表中: func = ["cumsum", "cummax", "diff"] -
让我们创建一个包含我们将要创建的变量的新名称的列表: new_names = [f"{var}_{function}" for function in func for var in numeric_vars] -
让我们使用第 4 步中的累积函数创建新变量,并将其应用到第 3 步中的变量上,然后将它们添加到 DataFrame 中: df[new_names] = df.groupby( "invoice")[numeric_vars].agg(func)上一步返回了每个发票内的累积和、累积最大值以及行之间的差异。 一旦遇到新的发票号码,它 就重新开始。 -
让我们显示一个特定发票的原始和新特征: : df[df["invoice"] == "489434" ][ numeric_vars + new_names]在以下输出中,我们可以看到 <st c="27980">quantity_cumsum</st>是数量变量的累积和,而 <st c="28048">price_diff</st>是行 之后的行价格差异:

-
让我们创建一个包含新变量名称的列表: new_names = [ f"{var}_{function}" for function in ["sin", "cos"] for var in numeric_vars] -
让我们使用正弦 和余弦将价格和数量进行转换: df[new_names] = df[numeric_vars].agg( [np.sin, np.cos])第 9 步中的转换已应用于整个数据集,无论发票号码如何,这是可以的,因为它将一行映射到同一行,而不是像累积函数那样从一行映射到下一行。 您可以通过执行 <st c="29094">df[new_names].head()</st>来检查结果。
<st c="29213">featuretools</st>
如何做...
-
首先,我们将导入 <st c="29372">pandas</st>, <st c="29380">featuretools</st>, 以及 <st c="29402">Categorical</st>逻辑类型: import pandas as pd import featuretools as ft from woodwork.logical_types import Categorical -
让我们加载在技术 要求 部分 中描述的数据集: df = pd.read_csv( «retail.csv», parse_dates=[«invoice_date»]) -
让我们设置一个 实体集: es = ft.EntitySet(id="data") -
让我们将 DataFrame 添加到 实体集: es = es.add_dataframe( dataframe=df, dataframe_name=»data», index="rows", make_index=True, time_index=»invoice_date», logical_types={ "customer_id": Categorical, "invoice": Categorical, } )
<st c="29962">featuretools</st> <st c="30260">featuretools</st> <st c="30365">featuretools</st><st c="30387">es["data"].</st><st c="30406">ww</st>
-
让我们创建一个与步骤 4 的 DataFrame 相关的新 DataFrame: es.normalize_dataframe( base_dataframe_name=»data», new_dataframe_name=»invoices», index="invoice", copy_columns=[«customer_id»], )
-
让我们列出我们将用于创建特征的累积转换: cum_primitives = [ "cum_sum", "cum_max", "diff", "time_since_previous"]
<st c="30924">featuretools</st>
-
让我们列出要执行的一般转换: : general_primitives = ["sine", " cosine "]
<st c="31207">featuretools</st>
-
最后,让我们 创建特征。 我们使用 <st c="31409">dfs</st>类,将原始 DataFrame 设置为目标 DataFrame – 即,我们将使用其变量作为新特征的模板。 请注意,我们向 <st c="31595">agg_primitives</st>参数传递一个空列表;这是为了避免返回默认的聚合原语。 我们从 步骤 7 将通用原语传递到 <st c="31734">trans_primitives</st>参数,并将累积原语从 步骤 6 传递到 <st c="31810">groupby_trans_primitives</st>参数: feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»data», agg_primitives=[], trans_primitives=general_primitives, groupby_trans_primitives = cum_primitives, ignore_dataframes = [«invoices»], )
<st c="32319">features_only</st> <st c="32346">True</st><st c="32527">False</st>
-
现在让我们 显示 创建的特征 名称: feature_defs在以下输出中,我们可以看到我们创建的特征名称,包括价格和数量的正弦和余弦,以及将这些变量按发票号分组后的累积变换: : <st c="32828">[<Feature: customer_id>,</st> <st c="32853"><Feature: invoice>,</st> <st c="32872"><Feature: stock_code>,</st> <st c="32894"><Feature: description>,</st> <st c="32917"><Feature: quantity>,</st> <st c="32937"><Feature: price>,</st> <st c="32954"><Feature: COSINE(price)>,</st> <st c="32979"><Feature: COSINE(quantity)>,</st> <st c="33007"><Feature: SINE(price)>,</st> <st c="33030"><Feature: SINE(quantity)>,</st> <st c="33056"><Feature: CUM_MAX(price) by invoice>,</st> <st c="33093"><Feature: CUM_MAX(quantity) by invoice>,</st> <st c="33133"><Feature: CUM_SUM(price) by invoice>,</st> <st c="33170"><Feature: CUM_SUM(quantity) by invoice>,</st> <st c="33210"><Feature: DIFF(price) by invoice>,</st> <st c="33244"><Feature: DIFF(quantity) by invoice>,</st> <st c="33281"><Feature: TIME_SINCE_PREVIOUS(invoice_date) by invoice>]</st>
feature_matrix.head()

关于创建功能的更多详细信息,请查看
如何工作…
要使用 <st c="35087">featuretools</st>
为了应用累积和一般转换,我们使用了 <st c="35378">dfs</st> <st c="35393">featuretools</st><st c="35610">trans_primitives</st> <st c="35643">dfs</st>
我们在按 <st c="35703">invoice</st><st c="35807">groupby_trans_primitives</st> <st c="35847">dfs</st><st c="35969">normalize_dataframe</st> <st c="36001">EntitySet</st>
最后,我们不想从 <st c="36090">invoices</st> <st c="36123">dfs</st> <st c="36163">ignore_dataframes = ["</st>``<st c="36185">invoices"]</st>
<st c="36197">dfs</st> <st c="36405">SINE</st><st c="36411">COSINE</st><st c="36419">CUM_MAX</st><st c="36431">DIFF</st>
<st c="36578">featuretools</st> <st c="36692">正弦、余弦、累计总和、最大值和差值被应用于数值变量,而</st> <st c="36818">转换被应用于</st>
结合数值特征
<st c="37035">featuretools</st> <st c="37233">featuretools</st>
如何操作...
-
首先,我们将导入 <st c="37353">pandas</st>, <st c="37361">featuretools</st>,以及 <st c="37383">Categorical</st>逻辑类型: import pandas as pd import featuretools as ft from woodwork.logical_types import Categorical -
让我们加载在 *技术 * *要求 *部分中描述的数据集: df = pd.read_csv( «retail.csv», parse_dates=[«invoice_date»]) -
让我们设置一个 <st c="37657">实体集</st>:es = ft.EntitySet(id="data") -
让我们将 DataFrame 添加到 实体集: es = es.add_dataframe( dataframe=df, dataframe_name=»data», index="rows", make_index=True, time_index=»invoice_date», logical_types={«customer_id»: Categorical}, ) -
让我们创建一个新的 DataFrame,它与从 步骤 4 中的 DataFrame 相关: es.normalize_dataframe( base_dataframe_name=»data», new_dataframe_name=»invoices», index="invoice", copy_columns=[«customer_id»], )
-
我们将乘以 <st c="38260">数量</st>和 <st c="38273">价格</st>变量,分别反映购买的商品数量和单价,以获得总 支付金额: feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»data», agg_primitives=[], trans_primitives=[«multiply_numeric»], primitive_options={ («multiply_numeric»): { ‹include_columns›: { 'data': ["quantity", "price"] } } }, ignore_dataframes=[«invoices»], )
<st c="38687">agg_primitives</st> <st c="38744">默认原语</st>。
-
现在 让我们显示新特征的名称: feature_defs在以下输出中,我们看到特征名称,其中最后一个对应于 <st c="38936">价格</st>和 <st c="38946">数量</st>变量的组合: <st c="38965">[<Feature: customer_id>,</st> <st c="38990"><Feature: stock_code>,</st> <st c="39012"><Feature: description>,</st> <st c="39035"><Feature: quantity>,</st> <st c="39055"><Feature: price>,</st> <st c="39072"><Feature: price * quantity>]</st> -
最后,让我们检查在 步骤 6 中创建的新 DataFrame: feature_matrix.head()在以下输出中,我们可以看到新特征被附加到原始 DataFrame 的右侧:

<st c="39776">featuretools</st> <st c="39833">df["price"].mul(df["quantity"])</st>
接下来...
<st c="40201">featuretools</st><st c="40170">MultiplyNumeric</st> <st c="40242">dfs</st> <st c="40256">multiply_numeric</st> <st c="40316">trans_primitive</st> <st c="40360">primitive_options</st> <st c="40482">agg_primitives</st> <st c="40606">invoices</st>
<st c="40828">MultiplyNumeric</st> <st c="40848">DivideNumeric</st> <st c="41239">ft.get_valid_primitives(es, target_dataframe_name="data", max_dep</st><st c="41304">th=2)</st>
从日期和时间中提取特征
<st c="41713">featuretools</st>
featuretools
<st c="42656">featuretools</st>
如何做到这一点...
-
首先,我们将导入 <st c="42776">pandas</st>, <st c="42784">featuretools</st>,以及一些特殊的 datetime 原始设置: import pandas as pd import featuretools as ft from featuretools.primitives import ( IsFederalHoliday, DistanceToHoliday) from woodwork.logical_types import Categorical -
让我们加载 技术 要求 部分中描述的数据集: df = pd.read_csv( «retail.csv», parse_dates=[«invoice_date»]) -
让我们设置一个 实体集: es = ft.EntitySet(id="data") -
让我们将 DataFrame 添加到 实体集: es = es.add_dataframe( dataframe=df, dataframe_name=»data», index="rows", make_index=True, time_index=»invoice_date», logical_types={«customer_id»: Categorical}, ) -
让我们创建一个新的 DataFrame,它与 步骤 4 中的 DataFrame 有关系: es.normalize_dataframe( base_dataframe_name=»data», new_dataframe_name=»invoices», index="invoice", copy_columns=[«customer_id»], )
-
让我们创建一个 返回一个布尔向量,指示日期是否与英国的银行假日(即非工作日)相符的原始设置: : is_bank_hol = IsFederalHoliday(country="UK")
-
让我们检查这个原始设置中包含哪些银行假日: hols = is_bank_hol.holidayUtil.federal_holidays.values() available_hols = list(set(hols))如果我们 执行 <st c="44311">available_hols</st>`,我们将看到支持英国银行假日的列表: <st c="44382">['May Day',</st> <st c="44394">'Good Friday',</st> <st c="44409">'Wedding of William and Catherine',</st> <st c="44445">'Coronation of Charles III',</st> <st c="44474">'Christmas Day',</st> <st c="44491">'Wedding of Charles and Diana',</st> <st c="44523">'Christmas Day (observed)',</st> <st c="44551">'State Funeral of Queen Elizabeth II',</st> <st c="44590">'Silver Jubilee of Elizabeth II',</st> <st c="44624">'Spring Bank Holiday',</st> <st c="44647">'Diamond Jubilee of Elizabeth II',</st> <st c="44682">'Boxing Day (observed)',</st> <st c="44707">'Platinum Jubilee of Elizabeth II',</st> <st c="44743">"New Year's Day (observed)",</st> <st c="44772">'Boxing Day',</st> <st c="44786">'Golden Jubilee of Elizabeth II',</st> <st c="44820">'Millennium Celebrations',</st> <st c="44847">"New Year's Day"]</st> -
让我们创建另一个原始设置,用于确定到特定日期的天数——在这种情况下,到节礼日的距离: days_to_boxing = DistanceToHoliday( holiday="Boxing Day", country="UK") -
现在,让我们创建一个包含字符串的列表,这些字符串可以识别我们从 <st c="45149">datetime</st>和包括从 步骤 6 和 步骤 8 中获取的原始设置: date_primitives = [ "day", "year", "month", "weekday", "days_in_month", "part_of_day", "hour", "minute", is_bank_hol, days_to_boxing ] -
现在让我们 从 第 9 步 创建基于 <st c="45406">invoice_date</st>日期变量的 日期和时间特征:feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»invoices», agg_primitives=[], trans_primitives=date_primitives, )
<st c="45607">invoice_date</st> <st c="45655">featuretools</st>
-
让我们显示创建的 特征名称: feature_defs在以下输出中,我们看到原始特征和 时间特征的名称: <st c="45872">[<Feature: customer_id>,</st> <st c="45897"><Feature: DAY(first_data_time)>,</st> <st c="45929"><Feature: DAYS_IN_MONTH(first_data_time)>,</st> <st c="45971"><Feature: DISTANCE_TO_HOLIDAY(</st> <st c="46002">first_data_time, holiday=Boxing Day, country=UK)>,</st> <st c="46053"><Feature: HOUR(first_data_time)>,</st> <st c="46086"><Feature: IS_FEDERAL_HOLIDAY(</st> <st c="46116">first_data_time, , country=UK)>,</st> <st c="46149"><Feature: MINUTE(first_data_time)>,</st> <st c="46184"><Feature: MONTH(first_data_time)>,</st> <st c="46218"><Feature: PART_OF_DAY(first_data_time)>,</st> <st c="46258"><Feature: WEEKDAY(first_data_time)>,</st> <st c="46350">feature_matrix.head()</st> to take a look at the resulting DataFrame with the features created from the invoice date. The DataFrame is quite big, so for reasons of space, we’ll only display a few columns in the book. -
让我们显示包含三个新特征的 结果 DataFrame: columns = [ "DISTANCE_TO_HOLIDAY(first_data_time, holiday=Boxing Day, country=UK)", "HOUR(first_data_time)", "IS_FEDERAL_HOLIDAY(first_data_time, country=UK)", ] feature_matrix[columns].head()在以下输出中,我们看到包含新特征的 DataFrame:

<st c="47281">HOUR</st> <st c="47289">DAY</st><st c="47321">IS_FEDERAL_HOLIDAY</st><st c="47375">PART_OF_DAY</st><st c="47420">PART_OF_DAY</st><st c="47441">feature_matrix["PAR</st><st c="47460">T_OF_DAY(first_data_time)"].unique()</st>
它是如何工作的...
<st c="47602">featuretools</st> <st c="47749">dfs</st> <st c="47826">trans_primitive</st> <st c="47907">agg_primitives</st> <st c="48091">invoices</st>
我们将agg_primitives设置为一个空列表,并忽略了invoices DataFrame,以保持输出简单并能够专注于日期时间特征。然而,请注意,featuretools的真正威力在于从datetim中创建原始数据,然后在不同实体级别进一步聚合它们。
从文本中提取特征
在第十一章中,从文本变量中提取特征,我们将讨论我们可以从文本片段中利用pandas和scikit-learn提取的各种特征。我们还可以通过利用featuretools自动从文本中提取多个特征。
<st c="48704">featuretools</st>库支持创建几个基本特征作为其默认功能的一部分,例如文本中的字符数、单词数、每个单词的平均字符数以及文本中的中位词长等。
注意
要获取默认文本原始数据的完整列表,请访问featuretools.alteryx.com/en/stable/api_reference.html#naturallanguage-transform-primitives。
此外,还有一个配套的 Python 库nlp_primitives,它包含额外的原始数据,用于创建基于 NLP 的更高级特征。在这些函数中,我们发现了一些用于确定多样性得分、极性得分或停用词计数的原始数据。
注意
在编写本文时,没有关于nlp_primitives库支持的原始数据的文档,因此要了解更多信息,您需要检查源代码:github.com/alteryx/nlp_primitives/tree/6243ef2379501bfec2c3f19e35a30b5954605e57/nlp_primitives。
<st c="49788">featuretools</st><st c="49877">nlp_primitives</st> <st c="49857">p</st>原语。
准备就绪
<st c="49973">nlp_primitives</st> <st c="50014">pip</st>
pip install nlp_primitives
conda install -c conda-forge nlp-primitives
<st c="50160">nlp_primi</st><st c="50169">tives</st>
如何操作...
-
首先,我们将导入 <st c="50343">pandas</st>, <st c="50351">featuretools</st>,以及 逻辑类型: import pandas as pd import featuretools as ft from woodwork.logical_types import ( Categorical, NaturalLanguage) -
让我们加载在 <st c="50541">技术</st>*要求 *部分中描述的数据集: df = pd.read_csv( «retail.csv», parse_dates=[«invoice_date»]) -
让我们设置一个 实体集: es = ft.EntitySet(id="data") -
让我们将 DataFrame 添加到实体集中,突出显示 <st c="50757">描述</st>变量是一个 文本变量: es = es.add_dataframe( dataframe=df, dataframe_name=»data», index="rows", make_index=True, time_index=»invoice_date», logical_types={ «customer_id»: Categorical, "invoice": Categorical, «description»: NaturalLanguage, } )
<st c="51033">featuretools</st> <st c="51139">NaturalLanguage</st>
-
让我们创建 一个新的 DataFrame,它与 <st c="51240">步骤 4</st>中的 DataFrame 有关系: es.normalize_dataframe( base_dataframe_name=»data», new_dataframe_name=»invoices», index="invoice", copy_columns=[«customer_id»], )
-
让我们创建一个与我们要创建的文本特征对应的字符串列表: text_primitives = [ "num_words", "num_characters", "MeanCharactersPerWord" , "PunctuationCount"] -
现在让我们 从 <st c="51730">描述</st>变量中提取文本特征: feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»data», agg_primitives=[], trans_primitives=text_primitives, ignore_dataframes=[«invoices»], ) -
让我们显示已创建的特征的名称: feature_defs在以下输出中,我们看到原始特征的名称,然后是来自 <st c="52087">description</st>变量的特征: <st c="52108">[<Feature: customer_id>,</st> <st c="52133"><Feature: invoice>,</st> <st c="52152"><Feature: stock_code>,</st> <st c="52174"><Feature: quantity>,</st> <st c="52194"><Feature: price>,</st> <st c="52211"><Feature: MEAN_CHARACTERS_PER_WORD(description)>,</st> <st c="52260"><Feature: NUM_CHARACTERS(description)>,</st> <st c="52299"><Feature: NUM_WORDS(description)>,</st> <st c="52422">feature_matrix.head()</st>. -
让我们显示包含从文本派生特征的 DataFrame 的一个片段: <st c="52521">text_f = [</st> <st c="52532">"NUM_CHARACTERS(description)",</st> <st c="52563">"NUM_WORDS(description)",</st> <st c="52589">"PUNCTUATION_COUNT(description)",</st> <st c="52623">]</st> <st c="52625">feature_matrix[text_f].head()</st>在以下输出中,我们看到一个从 文本中创建的特征 DataFrame:

<st c="52943">featuretools</st> <st c="53000">description</st>
<st c="53109">nlp_primitives</st> <st c="53196">from nlp_primitives import DiversityScore</st>
它是如何工作的...
<st c="53504">featuretools</st><st c="53663">trans_primitives</st> <st c="53556">dfs</st> <st c="53663">trans_primitives</st>
<st c="53773">nlp_primitives</st> <st c="53857">dfs</st><st c="53825">trans_primitives</st> <st c="53873">dfs</st> <st c="53966">nlp_primitives</st> <st c="54002">nltk</st>
使用聚合原始函数创建特征
<st c="54540">featuretools</st> <st c="54773">价格</st>
<st c="55009">featuretools</st> <st c="55069">groupby</st> <st c="55080">pandas</st><st c="55100">pandas</st> <st c="55126">mean</st><st c="55132">sum</st><st c="55137">std</st><st c="55146">count</st>
<st c="55772">featuretools</st>
准备就绪
<st c="56147">featuretools</st>
如何做到...
-
首先,我们将导入 <st c="56442">pandas</st>, <st c="56450">featuretools</st>,以及逻辑类型: import pandas as pd import featuretools as ft from woodwork.logical_types import ( Categorical, NaturalLanguage) -
让我们加载 在 技术 要求 部分中描述的 数据集:df = pd.read_csv( «retail.csv», parse_dates=[«invoice_date»]) -
让我们设置一个 实体集: es = ft.EntitySet(id="data") -
让我们将 DataFrame 添加到实体集中,并强调以下内容: <st c="56856">描述</st>变量是一个文本变量, <st c="56897">customer_id</st>是分类变量, <st c="56929">invoice_date</st>是一个 日期时间特征: es = es.add_dataframe( dataframe=df, dataframe_name=»data», index="rows", make_index=True, time_index=»invoice_date», logical_types={ «customer_id»: Categorical, «description»: NaturalLanguage, } ) -
让我们创建一个新的 DataFrame,它与来自 步骤 4 的 DataFrame 有关系: es.normalize_dataframe( base_dataframe_name=»data», new_dataframe_name=»invoices», index="invoice", copy_columns=[«customer_id»], ) -
现在,我们添加第二个关系,即客户与发票之间的关系。 为此,我们指示基础 DataFrame,我们在 步骤 5 中将其称为 <st c="57514">invoices</st>,我们给新的 DataFrame 起名, <st c="57568">customers</st>,并添加一个唯一的 客户标识符: es.normalize_dataframe( base_dataframe_name=»invoices», new_dataframe_name=»customers», index=»customer_id», )
-
让我们创建一个包含字符串名称的列表,这些名称标识了我们想要使用的聚合原语: 要使用: agg_primitives = ["mean", "max", "min", "sum"] -
让我们通过在客户级别聚合数据来创建特征。 为此,我们设置了 <st c="58087">dfs</st>类从 <st c="58102">featuretools</st>,表示 <st c="58127">customers</st>作为目标 DataFrame,并将来自 步骤 7 的聚合原语传递给 <st c="58237">trans_primitives</st>参数,以防止 <st c="58275">dfs</st>返回默认转换: feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»customers», agg_primitives=agg_primitives, trans_primitives=[], ) -
让我们显示 创建的特征的 名称:feature_defs在以下输出中,我们看到在 客户级别 聚合的名称特征:<st c="58621">[<Feature: MAX(data.price)>,</st> <st c="58650"><Feature: MAX(data.quantity)>,</st> <st c="58680"><Feature: MEAN(data.price)>,</st> <st c="58708"><Feature: MEAN(data.quantity)>,</st> <st c="58739"><Feature: MIN(data.price)>,</st> <st c="58766"><Feature: MIN(data.quantity)>,</st> <st c="58796"><Feature: SUM(data.price)>,</st> <st c="58823"><Feature: SUM(data.quantity)>,</st> <st c="58853"><Feature: MAX(invoices.MEAN(data.price))>,</st> <st c="58895"><Feature: MAX(invoices.MEAN(data.quantity))>,</st> <st c="58940"><Feature: MAX(invoices.MIN(data.price))>,</st> <st c="58981"><Feature: MAX(invoices.MIN(data.quantity))>,</st> <st c="59025"><Feature: MAX(invoices.SUM(data.price))>,</st> <st c="59066"><Feature: MAX(invoices.SUM(data.quantity))>,</st> <st c="59110"><Feature: MEAN(invoices.MAX(data.price))>,</st> <st c="59152"><Feature: MEAN(invoices.MAX(data.quantity))>,</st> <st c="59197"><Feature: MEAN(invoices.MEAN(data.price))>,</st> <st c="59240"><Feature: MEAN(invoices.MEAN(data.quantity))>,</st> <st c="59286"><Feature: MEAN(invoices.MIN(data.price))>,</st> <st c="59328"><Feature: MEAN(invoices.MIN(data.quantity))>,</st> <st c="59373"><Feature: MEAN(invoices.SUM(data.price))>,</st> <st c="59415"><Feature: MEAN(invoices.SUM(data.quantity))>,</st> <st c="59460"><Feature: MIN(invoices.MAX(data.price))>,</st> <st c="59501"><Feature: MIN(invoices.MAX(data.quantity))>,</st> <st c="59545"><Feature: MIN(invoices.MEAN(data.price))>,</st> <st c="59587"><Feature: MIN(invoices.MEAN(data.quantity))>,</st> <st c="59632"><Feature: MIN(invoices.SUM(data.price))>,</st> <st c="59673"><Feature: MIN(invoices.SUM(data.quantity))>,</st> <st c="59717"><Feature: SUM(invoices.MAX(data.price))>,</st> <st c="59758"><Feature: SUM(invoices.MAX(data.quantity))>,</st> <st c="59802"><Feature: SUM(invoices.MEAN(data.price))>,</st> <st c="59844"><Feature: SUM(invoices.MEAN(data.quantity))>,</st> <st c="59889"><Feature: SUM(invoices.MIN(data.price))>,</st> <st c="59930"><Feature: SUM(invoices.MIN(data.quantity))>]</st>
<st c="59995">featuretools</st> <st c="60187">MAX(data.price)</st> <st c="60282">MEAN(invoices.MAX(data.price))</st>
-
现在让我们 显示包含原始数据和 新特征的 结果 DataFrame:feature_matrix.head()在以下输出中,我们看到 一些 由 <st c="60742">dfs</st>返回的 DataFrame 中的变量:

-
让我们使用日期和 文本原语来创建列表: trans_primitives = ["month", "weekday", "num_words"] -
让我们使用聚合原语创建一个列表: agg_primitives = ["mean"] -
现在让我们通过转换然后 自动创建特征,并聚合变量: feature_matrix, feature_defs = ft.dfs( entityset=es, target_dataframe_name=»customers», agg_primitives=agg_primitives, trans_primitives=trans_primitives, max_depth=3, )从 步骤 13 触发的代码会创建功能,并在 客户级别进行后续聚合。 -
让我们显示新功能的 名称: feature_defs在以下输出中,我们可以看到创建的变量的 名称: <st c="62965">[<Feature: MEAN(data.price)>,</st> <st c="62995"><Feature: MEAN(data.quantity)>,</st> <st c="63026"><Feature: MONTH(first_invoices_time)>,</st> <st c="63064"><Feature: WEEKDAY(first_invoices_time)>,</st> <st c="63104"><Feature: MEAN(invoices.MEAN(data.price))>,</st> <st c="63147"><Feature: MEAN(invoices.MEAN(data.quantity))>,</st> <st c="63193"><Feature: MEAN(data.NUM_WORDS(description))>,</st> <st c="63238"><Feature: MEAN(invoi</st><st c="63259">ces.MEAN(data.NUM_</st> <st c="63278">WORDS(description)))>] WORDS(description)))>]</st>
<st c="63544">featuretools</st>
它的工作原理...
<st c="63790">featuretools</st>
<st c="64080">dfs</st> <st c="64188">agg_primitives</st> <st c="64218">dfs</st><st c="64317">trans_primitives</st> <st c="64347">dfs</st>
<st c="64356">customers</st> <st c="64396">invoice</st> <st c="64396">invoice</st> <st c="64472">dfs</st> <st c="64576">MEAN(data.price)</st> <st c="64702">MEAN(invoices.MEAN(data.price))</st> <st c="64873">featuretools</st> <st c="65000">MEAN(data.price)</st> <st c="65021">MEAN(invoices.MEAN(data.price))</st>
<st c="65683">trans_primitives</st> <st c="65790">agg_primitives</st> <st c="65818">dfs</st>
<st c="65984">MONTH(first_invoices_time)</st> <st c="66015">WEEKDAY(first_invoices_time)</st><st c="66132">MEAN(data.NUM_WORDS(description))</st> <st c="66170">MEAN(invoices.MEAN(data.NUM_WORDS(description)))</st><st c="66299">MEAN(data.price)</st> <st c="66320">MEAN(invoices.MEAN(data.price))</st>
第十一章:10
使用 tsfresh 从时间序列创建特征
从时间序列创建特征可能非常耗时;我们需要应用各种信号处理和时间序列分析算法来识别和提取有意义的特征。 <st c="2339">tsfresh</st> <st c="2597">tsfresh</st> <st c="2714">通过自动化应用复杂的时间序列方法,</st>
在本章中,我们将学习如何通过利用
在本章中,我们将介绍以下食谱:
-
从
时间序列 自动提取数百个特征 -
从
时间序列数据 自动创建和选择预测特征 -
从不同的
时间序列 中提取不同的特征 -
通过
特征选择 创建特征子集 -
将特征创建嵌入到
scikit-learn 管道
技术要求
在本章中,我们将使用开源的
如果您使用的是旧版的 Microsoft 操作系统,您可能需要更新 Microsoft C++构建工具才能继续安装
我们将使用来自 UCI 机器学习仓库的
Candanedo, Luis.
我下载并修改了如本笔记本所示的数据:
若要获取修改后的数据集和目标变量,请查看以下链接中的文件<st c="4971">occupancy.csv</st> <st c="4989">occupancy_target.csv</st>github.com/PacktPublishing/Python-Feature-engineering-Cookbook-Third-Edition/blob/main/ch10-tsfresh
占用检测数据集包含在 135 小时内每分钟间隔采集的时间序列数据。
<st c="5529">1</st><st c="5612">0</st>
从时间序列自动提取数百个特征





<st c="8267">tsfresh</st> <st c="8474">tsfresh</st>
准备就绪
-
让我们 <st c="9138">pandas</st>和 <st c="9149">matplotlib</st>: import matplotlib.pyplot as plt import pandas as pd -
加载数据集并显示前五行: : X = pd.read_csv( "occupancy.csv", parse_dates=["date"]) X.head()在下面的图中,我们可以看到包含唯一标识符的数据集,随后是测量日期和时间以及五个时间序列的值,这些时间序列捕捉了温度、湿度、照明和
办公室的 级别:

-
让我们 创建 一个函数来 绘制从 步骤 2 在给定小时的时间序列(记录的 135 小时中的每一小时都有一个唯一的标识符 <st c="10066">id</st>列): def plot_timeseries(n_id): fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(20, 10)) X[X[«id»] == n_id]["temperature"].plot( ax=axes[0, 0], title="temperature") X[X[«id»] == n_id]["humidity"].plot( ax=axes[0, 1], title="humidity") X[X[«id»] == n_id]["light"].plot( ax=axes[0, 2], title="light") X[X[«id»] == n_id]["co2"].plot( ax=axes[1, 0], title="co2") X[X[«id»] == n_id]["humidity_ratio"].plot( ax=axes[1,1], title="humidity_ratio") plt.show() -
让我们 绘制办公室未被占用的小时对应的时间序列: plot_timeseries(2)在下面的图中,我们可以看到记录的第二个小时的时间序列值,当时办公室 是空的:

-
现在,让我们绘制出办公室被占用的小时对应的时间序列数据: plot_timeseries(15)在下面的图中,我们可以看到记录的第十五个小时的时间序列值,当时办公室 被占用:

注意这次灯光是亮着的(
<st c="12453">tsfresh</st>
如何做到...
<st c="12628">灯光</st>
-
让我们导入所需的 Python 库 和函数: import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split from tsfresh import extract_features from tsfresh.utilities.dataframe_functions import ( impute ) -
加载在 技术 要求 部分中描述的 数据集:X = pd.read_csv("occupancy.csv", parse_dates=["date"]) -
L 将目标变量加载到一个 <st c="13210">pandas</st>序列中: y = pd.read_csv("occupancy_target.csv", index_col="id")["occupancy"] -
让我们使用 <st c="13379">tsfresh</st>自动为每小时的灯光记录创建数百个特征。要从 <st c="13416">light</st>变量中创建特征,我们将包含此变量和每个时间序列的唯一标识符的 DataFrame 传递给 <st c="13533">extract_features</st>函数 来自 <st c="13564">tsfresh</st>: features = extract_features( X[[«id», «light»]], column_id="id")如果我们执行 <st c="13653">features.shape</st>,我们将获得 <st c="13682">(135, 789)</st>,这对应于结果 DataFrame 的大小 ,其中每一行代表一个小时的记录,每一列代表由 <st c="13839">tsfresh</st>创建的一个特征。有 789 个特征可以描述任何给定小时的光消耗。 继续执行 <st c="13947">features.head()</st>以查看结果 DataFrame。 由于篇幅原因,我们无法在书中显示整个 DataFrame。 因此,我们将探索一些 特征。 -
让我们将创建的五个特征名称存储在 一个数组中: feats = features.columns[10:15]如果我们执行 <st c="14241">feats</st>,我们将看到与平均数、长度、标准差、变异系数和光消耗的方差相对应的五个特征名称 每小时: <st c="14413">Index(['light__mean', 'light__length',</st> <st c="14453">'light__standard_deviation',</st> <st c="14482">'light__variation_coefficient',</st> <st c="14514">'light__variance'], dtype='object')</st> -
现在,让我们显示从 步骤 5 开始的第一个 五个小时 的特征值:features[feats].head()在以下 DataFrame 中,我们看到从 前五个小时的 光 合 消耗 的时间序列中提取的特征:

<st c="15383">tsfresh</st> <st c="15775">0</st> <st c="15863">NaN</st>
-
<st c="15975">tsfresh</st>包含一个用于填充包含 <st c="16048">NaN</st>值的特征的填充函数。 让我们继续填充 我们的特征: impute(features)The <st c="16121">impute</st>函数来自 <st c="16142">tsfresh</st>,它将 <st c="16159">NaN</st>、 <st c="16164">-Inf</st>和 <st c="16174">Inf</st>值分别替换为变量的中位数、最小值或最大值。 让我们使用这些特征来训练一个逻辑回归模型,并预测办公室 是否被占用。 -
让我们开始 将 数据集 分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( features, y, test_size=0.1, random_state=42, ) -
现在,让我们设置并训练一个逻辑回归模型,然后评估 其性能: cls = LogisticRegression(random_state=10, C=0.01) cls.fit(X_train, y_train) print(classification_report( y_test, cls.predict(X_test)))在以下输出中,我们看到用于分类分析的常用评估指标的值,这表明创建的特征对于预测 办公室占用情况是有用的: <st c="16959">precision recall</st> <st c="16976">f1-score support</st> <st c="16993">0 1.00 1.00 1.00 11</st> <st c="17013">1 1.00</st> <st c="17021">1.00 1.00 3</st> <st c="17032">accuracy 1.00 14</st> <st c="17049">macro avg 1.00 1.00 1.00</st> <st c="17074">14</st> <st c="17077">weighted avg 1.00 1.00 1.00 14</st>
-
最后,让我们 提取每个时间序列的特征,即 <st c="17358">光照</st>、 <st c="17365">温度</st>、 <st c="17378">湿度</st>和 <st c="17392">二氧化碳</st>,这次,我们将在提取后立即填充特征: features = extract_features( X, column_id="id", impute_function=impute, column_sort="date", )
<st c="17716">date</st> <st c="17737">column_sort</st> <st c="17882">None</st><st c="17888">tsfresh</st>
<st c="18051">features.shape</st>
它是如何工作的...
<st c="18397">tsfresh</st>
<st c="18612">tsfresh</st><st c="18700">id</st>
<st c="18817">extract_features</st> <st c="18848">tsfresh</st><st c="18051">features.shape</st>
<extract_features>有三个关键参数:column_id、column_sort和impute_function。column_id接收用于提取特征时每个序列的唯一标识符的列名。column_sort用于在提取特征之前重新排序时间序列。当column_sort被设置为None时,tsfresh假设数据是按时间顺序排列的,并且时间戳是等距的。在步骤 10中,我们传递了date变量作为排序变量,这通知tsfresh在提取特征之前如何排序数据。
注意
在我们的数据集中,将column_sort设置为None或传递date变量没有区别,因为我们的时间序列已经是按时间顺序排列的,并且时间戳是等距的。如果您的时序不是这种情况,请使用此参数正确创建特征。
最后,extract_features也接受通过impute_function参数的impute函数,以自动从创建的特征中移除无限和NaN值。将在后续菜谱中讨论extract_features的附加参数。
注意
<st c="20313">impute</st> <st c="20404">extract_features</st> <st c="20462">NAN</st>、<st c="20465">-Inf</st>和<st c="20477">Inf</st>值。<st c="20587">NaN</st>
<st c="20714">extract_features</st>
在步骤 5中,我们探索了一些生成的特征,这些特征捕捉了时间序列的均值、方差和变异系数,以及它们的长度。
<st c="21466">'light__has_duplicate_max'</st><st c="21494">'light__has_duplicate_min'</st><st c="21548">变量指示时间序列在时间间隔内是否有重复值或重复的最小值或最大值。</st><st c="21680"><st c="21708">,<st c="21734">,以及</st><st c="21740">'light__quantile__q_0.3'</st><st c="21877">,<st c="21910">,以及</st><st c="21916">'light__autocorrelation__lag_2'</st>
<st c="22281">'light__cwt_coefficients__coeff_0__w_2__widths_(2, 5, 10, 20)'</st><st c="22345">'light__cwt_coefficients__coeff_0__w_5__widths_(2, 5, 10, 20)'</st><st c="22409">'light__cwt_coefficients__coeff_0__w_10__widths_(2, 5, 10, 20)'</st>
<st c="22768">tsfresh</st>支持的转换及其公式的更多详细信息。
<st c="22934">tsfresh</st>
另请参阅
<st c="23690">tsfresh</st>
自动从时间序列数据中创建和选择预测特征
在上一个<st c="24166">tsfresh</st>
<st c="24838">tsfresh</st> 包含一个基于非参数统计假设检验的高度可并行化的特征选择算法,该算法可以在特征创建过程之后执行,以快速去除不相关的特征。特征选择过程利用不同的测试针对不同的特征。
<st c="25149">tsfresh</st> 使用以下测试来 <st c="25186">选择特征</st>:
-
如果特征和目标
<st c="25275">都是二进制</st>的<st c="25202">费舍尔精确检验独立性</st>, -
如果特征或目标
<st c="25347">是二进制</st>的<st c="25285">科尔莫哥洛夫-斯米尔诺夫检验</st>, -
如果特征或目标
<st c="25414">都不是二进制</st>的<st c="25356">肯德尔秩次检验</st>,
这些测试的优势在于它们是非参数的,因此不对被测试变量的潜在分布做出假设。
这些测试的结果是一个衡量每个特征与目标之间关联显著性的 p 值向量。然后根据 <st c="25756">本雅明-叶库蒂埃</st> 程序对这些 p 值进行评估,以决定保留哪些特征。
<st c="25824">注意</st>
关于 <st c="25854">tsfresh</st> 的特征选择过程的更多详细信息,请参阅 Christ, Kempa-Liehr, 和 Feindt 的文章,
在这个食谱中,我们将自动从各种时间序列中创建数百个特征,然后通过使用 <st c="26348">tsfresh</st> 来选择最相关的特征。
<st c="26356">如何做到...</st>
我们将首先自动从单个时间序列 <st c="26458">lights</st> 中创建和选择特征,然后我们将自动化针对多个 <st c="26519">时间序列</st> 的过程:
-
让我们导入所需的 Python 库和函数:
import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split from tsfresh import ( extract_features, extract_relevant_features, select_features, ) from tsfresh.utilities.dataframe_functions import impute -
加载在
技术 要求 部分中描述的<st c="26925">数据集</st>和目标变量:X = pd.read_csv("occupancy.csv", parse_dates=["date"]) y = pd.read_csv("occupancy_target.csv", index_col="id")["occupancy"] -
让我们为每个小时的使用记录自动创建数百个特征,并填补结果特征:
features = extract_features( X[[«id», «light»]], column_id="id", impute_function=impute, )上一步的输出是一个包含 135 行和 789 列的 DataFrame,对应于从每个小时的能耗中创建的特征。
注意
关于步骤 3或占用检测数据集的更多详细信息,请查看从时间序列中自动提取数百个特征的食谱。
-
现在,让我们根据我们在本食谱介绍中提到的非参数测试来选择特征:
features = select_features(features, y)如果我们执行
<st c="27818">len(features)</st>,我们将看到值 <st c="27853">135</st>,这意味着从步骤 3中创建的 789 个特征中,只有 135 个具有统计学意义。继续执行 <st c="27977">features.head()</st>以显示结果 DataFrame 的前五行。 -
由于空间原因,我们只将显示前五个特征:
feats = features.columns[0:5] features[feats].head()在以下 DataFrame 中,我们可以看到前五个小时的能耗的前五个特征的值:

图 10.6 – 从每个小时的能耗中创建的五个选定特征的 DataFrame
在如何工作…部分查看讨论,以获取关于步骤 4产生的 DataFrame 的更详细分析。
-
现在,我们将使用步骤 4中的特征来训练一个逻辑回归模型,并预测办公室是否被占用。首先,我们将数据集分为训练集和测试集:
X_train, X_test, y_train, y_test = train_test_split( features, y, test_size=0.1, random_state=42, ) -
让我们设置并训练一个逻辑回归模型,然后评估其性能:
cls = LogisticRegression( random_state=10, C=0.1, max_iter=1000) cls.fit(X_train, y_train) print(classification_report( y_test, cls.predict(X_test)))在以下输出中,我们可以看到用于分类分析的常用评估指标值。这些指标表明,所选特征对预测办公室占用情况很有用:
<st c="29491">precision recall f1-score support</st> <st c="29525">0</st> <st c="29527">1.00 0.91 0.95 11</st> <st c="29545">1 0.75 1.00 0.86 3</st> **<st c="29564">accuracy 0.93 14</st>** **<st c="29581">macro avg 0.88 0.95 0.90 14</st>** `<st c="29980">extract_relevant_features</st>`<st c="30005">, and, like this, combine</st> *<st c="30031">steps 3</st>* <st c="30038">and</st> *<st c="30043">4</st>*<st c="30044">. We’ll do that to create and select features automatically for the five time series in</st> <st c="30132">our dataset:</st>features = extract_relevant_features(
X, y, column_id="id", column_sort="date",)
注意
extract_relevant_features 的参数与 extract_features 的参数非常相似。extract_features 的参数。
它是如何工作的...
<st c="31899">tsfresh</st>
然而,评估每个特征单独的特征选择方法无法去除冗余特征。<st c="32524">tsfresh</st> <st c="32811">tsfresh</st>
<st c="33054">extract_relevant_features</st> <st c="33090">extract_relevant_features</st> <st c="33128">extract_features</st> <st c="33238">select_features</st>
另请参阅
<st c="33495">tsfresh</st> <st c="33711">tsfresh</st>
从不同的时间序列中提取不同的特征
<st c="34035">tsfresh</st>
如何做到这一点...
<st c="34846">tsfresh</st> <st c="35037">tsfresh</st> <st c="35177">settings</st>
-
让我们导入所需的 Python 库、函数和 <st c="35259">settings</st>模块: import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report from sklearn.model_selection import train_test_split from tsfresh.feature_extraction import ( extract_features ) from tsfresh.feature_extraction import settings -
加载文档中描述的数据集和目标变量: X = pd.read_csv("occupancy.csv", parse_dates=["date"]) y = pd.read_csv("occupancy_target.csv", index_col="id")["occupancy"]<st c="35773">tsfresh</st>包括 三个主要字典,这些字典控制特征创建输出: <st c="35857">settings.ComprehensiveFCParameters</st>, <st c="35893">settings.EfficientFCParameters</st>, 和 <st c="35929">settings.MinimalFCParameters</st>。在这里,我们将探索返回特征最少的字典。 您可以重复这些步骤来探索 其他字典。 -
显示在使用返回最少特征的字典时将应用的特征创建方法: 最少特征: minimal_feat = settings.MinimalFCParameters() minimal_feat.items()在输出中的 步骤 3 ,我们看到一个以特征提取方法名称为键,如果有的话,使用这些方法的参数,作为值的字典: 作为值: <st c="36433">ItemsView({'sum_values': None, 'median': None, 'mean': None, 'length': None, 'standard_deviation': None, 'variance': None, 'root_mean_square': None, 'maximum': None, 'absolute_maximum': None, 'minimum': None})</st>
<st c="36709">settings.ComprehensiveFCParameters</st> <st c="36748">settings.EfficientFCParameters</st>
-
现在,让我们使用来自 步骤 3 的字典来提取仅从 <st c="36897">灯光</st>时间 序列中提取的特征,然后显示结果 DataFrame 的形状: features = extract_features( X[[«id», «light»]], column_id="id", <st c="37035">default_fc_parameters=minimal_feat,</st> ) features.shape步骤 4 的输出是 <st c="37112">(135, 10)</st>,这意味着为 135 个小时的灯光消耗数据中的每一个都创建了 10 个特征。 -
让我们显示 结果 DataFrame: features.head()我们在以下 DataFrame 中看到前五个小时灯光消耗的结果特征值: 在以下 DataFrame 中:

-
让我们首先将数据集分为训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( features, y, test_size=0.1, random_state=42, ) -
现在,让我们设置并训练一个逻辑回归模型,然后评估 其性能: cls = LogisticRegression(random_state=10, C=0.01) cls.fit(X_train, y_train) print(classification_report( y_test, cls.predict(X_test)))在以下输出中,我们看到用于分类分析的常用评估指标。 这些指标表明所选特征对预测办公室占用是有用的: <st c="38730">precision recall f1-score support</st> **<st c="38764">0 1.00 0.91 0.95 11</st>** **<st c="38784">1 0.75 1.00 0.86 3</st>** **<st c="38803">accuracy 0.93 14</st>** **<st c="38820">macro avg 0.88 0.95 0.90 14</st>** **<st c="38848">weighted avg 0.95 0.93</st>** **<st c="38871">0.93 14</st>**
**
-
让我们创建一个字典,包含我们想要从 <st c="39236">光</st>时间序列 中创建特征的方法的名称。 我们将方法的名称作为键,如果方法需要参数,我们将其作为附加字典传递给相应的键;否则,我们传递 <st c="39413">None</st>作为 值: light_feat = { «sum_values": None, "median": None, «standard_deviation": None, "quantile": [{"q": 0.2}, {"q": 0.7}], } -
现在,让我们创建一个字典,包含我们想要从 <st c="39634">co2</st>时间序列 中创建的特征: co2_feat = { «root_mean_square": None, «number_peaks": [{"n": 1}, {"n": 2}], } -
让我们 将这些字典合并成一个 新的字典: kind_to_fc_parameters = { «light»: light_feat, "co2": co2_feat, } -
最后,让我们使用 步骤 10 中的字典来创建两个时间序列的特征: features = extract_features( X[[«id», «light», «co2»]], column_id="id", kind_to_fc_parameters=kind_to_fc_parameters, )步骤 11 的输出由一个包含 135 行和 8 个特征的 DataFrame 组成。 如果我们执行 <st c="40154">features.columns</st>,我们将看到创建的特征的名称: <st c="40218">Index(['light__sum_values', 'light__median',</st> <st c="40263">'light__standard_deviation',</st> <st c="40292">'light__quantile__q_0.2',</st> <st c="40318">'light__quantile__q_0.7',</st> <st c="40344">'co2__root_mean_square',</st> <st c="40369">'co2__number_peaks__n_1',</st> <st c="40395">'co2__numb</st><st c="40406">er_peaks__n_2'],</st> <st c="40423">dtype='object')</st>
<st c="40534">光</st> <st c="40544">co2</st>
它是如何工作的...
<st c="40727">tsfresh</st>
<st c="40850">tsfresh</st> <st c="40939">settings</st> <st c="40960">MinimalFCParameters</st> <st c="41369">None</st>
<st c="41387">tsfresh</st> <st c="41440">EfficientFCParameters</st> <st c="41521">ComprehensiveFCParameters</st> extract_</st><st c="41615">features</st>
<st c="41710">tsfresh</st>
<st c="41863">default_fc_parameters</st> <st c="42029">default_fc_parameters</st> <st c="42061">extract_features</st>
<st c="42277">kind_to_fc_parameters</st> <st c="42312">tsfresh</st><st c="42323">extract_features</st>
在第 8 步中,我们创建了一个字典来指定从<st c="42543">light</st>时间序列创建特定特征。请注意,<st c="42576">"sum_values"</st>和<st c="42593">"mean"</st>方法将<st c="42613">None</st>作为值,但<st c="42637">quantile</st>方法需要额外的参数,这些参数对应于应从时间序列返回的分位数。在第 9 步中,我们创建了一个字典来指定从<st c="42838">co2</st>时间序列创建特征。在第 10 步中,我们将这两个字典合并为一个,以时间序列的名称作为键,特征创建字典作为值。然后,我们将这个字典传递给<st c="43079">tsfresh</st>的<st c="43090">extract_features</st>函数的<st c="43044">kind_to_fc_parameters</st>参数。如果我们使用领域知识来创建特征,或者只创建少量特征,这种方式指定特征是合适的。
如果我们想要为各种时间序列创建多个特征,是否需要手动将每个方法键入字典中?实际上并不需要。在下面的配方中,我们将学习如何根据 Lasso 选择的特征指定要创建哪些特征。
通过特征选择创建特征子集
在《自动从时间序列数据中创建和选择预测特征》的配方中,我们学习了如何使用<st c="43720">tsfresh</st>来选择相关特征。我们还讨论了<st c="43766">tsfresh</st>选择程序的局限性,并建议采用替代特征选择方法来识别预测特征,同时避免冗余。
在这个配方中,我们将使用<st c="43985">tsfresh</st>创建和选择特征。之后,我们将通过利用 Lasso 正则化进一步减少特征空间。然后,我们将学习如何从选定的特征名称创建字典,以触发仅从未来的时间序列创建这些特征。
...如何做到这一点...
-
让我们导入 所需的库和函数: import pandas as pd from sklearn.feature_selection import SelectFromModel from sklearn.linear_model import LogisticRegression from tsfresh import ( extract_features, extract_relevant_features, ) from tsfresh.feature_extraction import settings -
加载在 技术 要求 部分中描述的 占用检测数据集: X = pd.read_csv("occupancy.csv", parse_dates=["date"]) y = pd.read_csv( "occupancy_target.csv", index_col="id")["occupancy"] -
从我们的五个时间序列创建并选择特征,然后显示结果的 DataFrame 的形状: features = extract_relevant_features( X, y, column_id="id", column_sort="date", ) features.shape步骤 3 *的输出是 <st c="45068">(135, 968)</st>,表示从五个原始时间序列中返回了 968 个特征,对于每小时的记录。
-
设置具有 Lasso 正则化的逻辑回归,这是 <st c="45478">"l1"</st>惩罚。 我还任意设置了 一些额外的参数: cls = LogisticRegression( penalty="l1", solver=»liblinear", random_state=10, C=0.05, max_iter=1000, ) -
让我们设置一个转换器来保留那些逻辑回归系数 与 0 不同的特征: selector = SelectFromModel(cls) -
训练逻辑回归模型并选择 特征: selector.fit(features, y) -
现在,将所选特征存储在 一个变量中: features = selector.get_feature_names_out()如果我们 执行 <st c="45985">特征</st>,我们将 看到所选特征的 名称: array([ 'light__sum_of_reoccurring_data_points', 'co2__fft_coefficient__attr_"abs"__coeff_0', 'co2__spkt_welch_density__coeff_2', 'co2__variance', 'temperature__c3__lag_1', 'temperature__abs_energy', 'temperature__c3__lag_2', 'temperature__c3__lag_3', 'co2__sum_of_reoccurring_data_points', 'light__spkt_welch_density__coeff_8', 'light__agg_linear_trend__attr_"intercept"__chunk_len_50__f_agg_"var"', 'light__agg_linear_trend__attr_"slope"__chunk_len_50__f_agg_"var"', 'light__agg_linear_trend__attr_"intercept"__chunk_len_10__f_agg_"var"'], dtype=object) -
要从时间序列中提取仅来自 步骤 6 的特征,我们需要在字典中捕获特征创建方法名称和相应的参数。 我们可以通过使用 <st c="46815">tsfresh</st>从特征名称 自动完成此操作: kind_to_fc_parameters = settings.from_columns( selector.get_feature_names_out(), )如果我们执行 <st c="46922">kind_to_fc_parameters</st>,我们将看到从 步骤 6 的特征名称 *创建的字典: {'light': {‹sum_of_reoccurring_data_points': None, ‹spkt_welch_density': [{'coeff': 8}], 'variance': None, ‹agg_linear_trend': [ {‹attr': 'slope','chunk_len': 50, 'f_agg': 'var'}, {‹attr': 'intercept', 'chunk_len': 10,'f_agg':'var'} ] }, 'co2': {‹spkt_welch_density': [{'coeff': 2}], 'variance': None, ‹sum_of_reoccurring_data_points': None }, 'temperature': { 'c3': [{'lag': 1}, {'lag': 2}, {'lag':3}], 'abs_energy': None} } -
现在,我们可以使用 来自 步骤 8 的字典以及 <st c="47519">extract_features</st>函数来仅从 我们的数据集中创建那些特征: features = extract_features( X, column_id="id", column_sort="date", kind_to_fc_parameters=kind_to_fc_parameters, )
<st c="47763">features.head()</st>
它是如何工作的...
<st c="48261">tsfresh</st><st c="48282">extract_relevant_features</st>
<st c="48698">SelectFromModel()</st> <st c="48787">SelectFromModel()</st> <st c="48906">fit()</st><st c="48913">SelectFromModel()</st> <st c="49099">get_feature_names_out()</st>
<st c="49323">from_columns()</st> <st c="49352">tsfresh</st><st c="49670">extract_features</st>
<st c="49749">extract_features</st>
将特征创建集成到 scikit-learn 管道中
<st c="50096">tsfresh</st>
<st c="50226">tsfresh</st> <st c="50244">wrapper</st> <st c="50287">extract_features</st> <st c="50308">extract_relevant_features</st><st c="50402">scikit-learn 管道</st>兼容。
<st c="50526">tsfresh</st>
如何操作...
-
让我们导入所需的库 和函数: import pandas as pd from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from tsfresh.transformers import ( RelevantFeatureAugmenter) -
加载在 技术 要求 部分中描述的占用检测数据集: X = pd.read_csv("occupancy.csv", parse_dates=["date"]) y = pd.read_csv( "occupancy_target.csv", index_col="id")["occupancy"] -
创建一个空的 DataFrame,其中包含目标变量的索引: tmp = pd.DataFrame(index=y.index) -
现在,让我们将 从 步骤 3 和目标从 步骤 2 分成训练集和 测试集: X_train, X_test, y_train, y_test = train_test_split( tmp, y, random_state=0)
<st c="51548">X_train</st> <st c="51561">X_test</st> <st c="51628">tsfresh</st> <st c="51678">RelevantFe</st><st c="51688">atureAugmenter()</st>
-
让我们创建一个字典,指定从每个时间序列中提取的特征(我任意定义了以下 特征 ):kind_to_fc_parameters = { "light": { "c3": [{"lag": 3}, {"lag": 2}, {"lag": 1}], «abs_energy": None, «sum_values": None, «fft_coefficient": [ {«attr": "real", "coeff": 0}, {«attr": "abs", "coeff": 0}], «spkt_welch_density": [ {«coeff": 2}, {"coeff":5}, {"coeff": 8} ], «agg_linear_trend": [ {«attr": "intercept", „chunk_len": 50, „f_agg": „var"}, {"attr": "slope", «chunk_len": 50, "f_agg":"var"}, ], «change_quantiles": [ {«f_agg": "var", "isabs": False, «qh": 1.0,"ql": 0.8}, {«f_agg": "var", "isabs": True, «qh": 1.0,"ql": 0.8}, ], }, "co2": { «fft_coefficient": [ {«attr": "real", "coeff": 0}, {«attr": "abs", "coeff": 0}], "c3": [{"lag": 3}, {"lag": 2}, {"lag": 1}], «sum_values": None, «abs_energy": None, «sum_of_reoccurring_data_points": None, «sum_of_reoccurring_values": None, }, "temperature": {"c3": [{"lag": 1}, {«lag»: 2},{«lag»: 3}], «abs_energy": None}, }我们在《从不同时间序列中提取不同特征的方法》中讨论了该字典的参数。
-
让我们设置
<st c="52885">RelevantFeatureAugmenter()</st>,它是对<st c="52943">extract_relevant_features</st>函数的包装,以创建在第 5 步中指定的特征:augmenter = RelevantFeatureAugmenter( column_id="id", column_sort="date", kind_to_fc_parameters=kind_to_fc_parameters, )
注意
要创建所有可能的特征,请在第 6 步中使用<st c="53191">FeatureAugmenter()</st>类。
-
让我们将第 6 步中的特征创建实例与一个逻辑回归模型结合到一个 scikit-learn 流水线中:
pipe = Pipeline( [ ("augmenter", augmenter), («classifier», LogisticRegression( random_state=10, C=0.01)), ] ) -
现在,让我们告诉
<st c="53479">RelevantFeatureAugmenter()</st>它需要使用哪个数据集来创建特征:pipe.set_params(augmenter__timeseries_container=X) -
让我们拟合流水线,这将触发特征创建过程,然后是逻辑回归模型的训练:
pipe.fit(X_train, y_train) -
现在让我们使用测试集中的时间序列来获取预测,并通过以下分类报告来评估模型的性能:
print(classification_report( y_test, pipe.predict(X_test)))我们可以看到第 10 步的输出如下:
<st c="54007">precision recall f1-score support</st> <st c="54041">0 1.00 0.96 0.98</st> <st c="54058">28</st> <st c="54061">1 0.86 1.00 0.92 6</st> <st c="54080">accuracy 0.97 34</st> **<st c="54097">macro avg 0.93 0.98 0.95 34</st>** **<st c="54125">weighted avg 0.97 0.97 0.97 34</st>**
分类报告的值表明,提取的特征适合预测在任何给定小时办公室是否被占用。
它是如何工作的...
在这个方法中,我们将从时间序列中创建特征与<st c="54398">tsfresh</st>结合,并在一条流水线上训练来自 scikit-learn 库的机器学习算法。
<st c="54500">tsfresh</st>库包括两个包装类,围绕其主函数,以使特征创建过程与 scikit-learn 流水线兼容。在这个方法中,我们使用了<st c="54679">RelevantFeatureAugmenter()</st>类,它包装了<st c="54729">extract_relevant_features</st>函数,用于从时间序列中创建和选择特征。
<st c="54818">RelevantFeatureAugmenter()</st> <st c="54869">fit()</st><st c="54917">extract_relevant_features</st><st c="55031">transform()</st><st c="55044">RelevantFeatureAugmenter()</st>
<st c="55122">我们通过传递一个包含我们想要创建的特征的字典到其</st> <st c="55190">的</st><st c="55281">参数中,覆盖了</st><st c="55190">的默认</st>。</st> <st c="55293">因此,使用</st> <st c="55320">,</st>
<st c="55401">为了从时间序列创建所有特征,</st> <st c="55454">包含了</st><st c="55486">类,它具有与</st><st c="55556">相同的函数,但没有特征</st>
<st c="55597">RelevantFeatureAugmenter()</st> <st c="55629">FeatureAugmenter()</st> <st c="56011">transform()</st><st c="56135">其预测</st>。
<st c="56151">注意</st>
空 DataFrame 的索引被 <st c="56201">RelevantFeatureAugmenter()</st> 和 <st c="56232">FeatureAugmenter()</st> 用于识别从中提取特征的时间序列。因此,当传递 <st c="56356">X_train</st><st c="56336">fit()</st> 时,从其 <st c="56412">id</st> <st c="56596">id</st> <st c="56612">X_test</st>
当我们在管道上使用 <st c="56633">fit()时,我们从原始时间序列中创建了特征,并使用这些特征训练了一个逻辑回归模型。使用</st> 方法,我们从测试集中创建了特征,并基于这些特征获得了逻辑回归的预测结果。
参见:
关于本食谱中使用的课程和程序的更多详细信息,请访问以下链接:
第十二章:11
从文本变量中提取特征
<st c="1537">pandas</st>
-
统计字符、单词和词汇 -
通过统计句子来估计文本的复杂性 -
使用词袋模型和 n-gram 创建特征 -
实现词频-逆文档频率 -
清理和词干提取文本变量
技术要求
在本章中,我们将使用
如果您使用的是 Python Anaconda 发行版,请按照说明在
在您安装了
import nltk
nltk.download('punkt')
nltk.download('stopwords')
这些命令将为您下载运行本章中菜谱所需的数据。
注意
如果您尚未下载这些或其他对
计算字符、单词和词汇
文本的一个显著特征是其复杂性。长描述比短描述更有可能包含更多信息。包含不同、独特词汇的文本比反复重复相同词汇的文本更可能包含更丰富的细节。同样,当我们说话时,我们使用许多短词,如冠词和介词来构建句子结构,而主要概念通常来自我们使用的名词和形容词,这些往往是较长的词。所以,正如您所看到的,即使不阅读文本,我们也可以通过确定单词数量、唯一单词数量(单词的非重复出现)、词汇多样性和这些单词的长度来开始推断文本提供的信息量。在本菜谱中,我们将学习如何使用
准备工作
-
scikit -learn 数据集 网站: https://scikit-learn.org/stable/datasets/real_world.html#newsgroups-dataset -
20 个新闻组数据集的页面: http://qwone.com/~jason/20Newsgroups/
-
字符总数 -
单词总数 -
唯一单词的总数 -
词汇多样性(总单词数除以唯一单词数) -
单词平均长度(字符数除以单词数)
<st c="4932">pandas</st><st c="5023">str</st>
如何做到这一点...
<st c="5106">pandas</st>
-
Load <st c="5149">pandas</st>and the dataset from <st c="5177">scikit-learn</st>: import pandas as pd from sklearn.datasets import fetch_20newsgroups -
L et’s load the train set part of the 20 Newsgrou p dataset into a <st c="5326">pandas</st>DataFrame: data = fetch_20newsgroups(subset='train') df = pd.DataFrame(data.data, columns=['text'])
您可以通过执行print(df['text'][1])来打印 DataFrame 中的文本示例。[ <st c="5553">]<st c="5559">之间的数字以显示不同的文本。</st><st c="5694">您可以通过执行type(df["text"][1])`来检查数据类型。
现在我们已经将文本变量放入了一个pandas DataFrame 中,我们就可以准备提取特征了。
-
让我们在新列中捕获每个文本片段中的字符数:
df['num_char'] = df['text'].str.len()
提示
您可以在计数字符数之前,通过在len()方法之前添加strip()方法来移除字符串末尾的空白字符,包括换行符,如下所示:df['num_char'] = df['text'].str.strip().str.len()。
-
让我们在新列中捕获每个文本中的单词数:
df['num_words'] = df['text'].str.split().str.len()要计算单词数,我们使用
pandas库的split()方法,该方法在空白处分割文本。通过执行,例如, split()来查看split()的输出,df["text"].loc[1].split()以分离 DataFrame 中第二个文本的单词。 -
让我们在新列中捕获每个文本中的唯一单词数:
df['num_vocab']df[ 'text'].str.lower().str.split().apply( set).str.len()
注意
如果一个单词有一个大写字母,Python 会将其解释为两个不同的单词。为了避免这种行为,我们可以在split()方法之前应用lower()方法。
-
让我们创建一个捕获词汇多样性的特征——也就是说,总单词数(
步骤 4 )与唯一单词数(步骤 5 )的比较:df['lexical_div'] = df['num_words'] / df['num_vocab'] -
让我们通过将字符数(
步骤 3 )除以单词数(步骤 4 )来计算平均单词长度:df['ave_word_length'] = df[ 'num_char'] / df['num_words']如果我们执行 <st c="7292">df.head()</st>,我们将看到包含文本和刚刚 创 建的特征的 前五行数据:

它是如何工作的...
<st c="8578">str</st> <st c="8605">pandas</st> <st c="8686">train</st> <st c="8743">scikit-learn</st>
<st c="8833">str</st><st c="8850">len()</st><st c="8993">str.len()</st> <st c="9008">str.strip()</st>
<st c="9197">str</st><st c="9214">split()</st><st c="9270">split()</st> <st c="9401">str.len()</st>
<st c="9489">str.split()</st> <st c="9594">df['text'].str.split(';')</st> <st c="9659">;</st>
<st c="9719">str.split()</st> <st c="9848">apply()</st> <st c="9820">set()</st> <st c="10023">str.len()</st> <st c="10253">lower()</st>
<st c="10495">pandas</st>
还有更多...
-
导入 <st c="10936">matplotlib</st>: import matplotlib.pyplot as plt -
将带有新闻主题的目标添加到 20 Newsgroup DataFrame: df['target'] = data.target -
创建一个函数,用于显示每个新闻主题所选特征的直方图: def plot_features(df, text_var): nb_rows = 5 nb_cols = 4 fig, axs = plt.subplots( nb_rows, nb_cols,figsize=(12, 12)) plt.subplots_adjust(wspace=None, hspace=0.4) n = 0 for i in range(0, nb_rows): for j in range(0, nb_cols): axs[i, j].hist( df[df.target==n][text_var], bins=30) axs[i, j].set_title( text_var + ' | ' + str(n)) n += 1 plt.show() -
为单词特征运行该函数 的数量 次: plot_features(df, 'num_words')前面的命令返回以下图表,其中你可以看到每个 20 个新闻主题中单词数量的分布,编号从 0 到 19,在 图表标题中:

另请参阅
通过计算句子数量来估计文本复杂度
<st c="14027">NLTK</st>
准备工作
<st c="14130">NLTK</st> <st c="14184">NLTK</st>
如何操作...
-
让我们加载 <st c="14341">pandas</st>,从 <st c="14377">NLTK</st>中获取句子分词器,以及来自 <st c="14399">scikit-learn</st>的数据集 : import pandas as pd from nltk.tokenize import sent_tokenize from sklearn.datasets import fetch_20newsgroups -
为了理解 <st c="14590">NLTK</st>中的句子分词器的功能,让我们创建一个包含多个句子的字符串变量: text = """ The alarm rang at 7 in the morning as it usually did on Tuesdays. She rolled over, stretched her arm, and stumbled to the button till she finally managed to switch it off. Reluctantly, she got up and went for a shower. The water was cold as the day before the engineers did not manage to get the boiler working. Good thing it was still summer. Upstairs, her cat waited eagerly for his morning snack. Miaow! He voiced with excitement as he saw her climb the stairs. """ -
现在,让我们 使用 <st c="15185">步骤 2</st>中的字符串,通过 <st c="15213">NLTK</st>库的 句子分词器 将其分割成句子: sent_tokenize(text)
如果你在 <st c="15301">步骤 3</st><st c="15383">NLTK</st>
句子分词器返回以下输出中显示的句子列表:
<st c="15571">['\nThe alarm rang at 7 in the morning as it usually did on Tuesdays.',</st>
<st c="15643">'She rolled over,\nstretched her arm, and stumbled to the button till she finally managed to switch it off.',</st>
<st c="15753">'Reluctantly, she got up and went for a shower.',</st>
<st c="15803">'The water was cold as the day before the engineers\ndid not manage to get the boiler working.',</st>
<st c="15900">'Good thing it was still summer.',</st>
<st c="15935">'Upstairs, her cat waited eagerly for his morning snack.',</st>
<st c="15994">'Miaow!',</st>
<st c="16004">'He voiced with excitement\nas he saw her climb the stairs.']</st>
<st c="16066">注意</st>
跟随字母的转义字符,<st c="16117">\n</st>
-
<st c="16143">让我们计算文本变量中的句子数量:</st>len(sent_tokenize(text))之前的命令返回了
<st c="16257">8</st>,这是我们文本变量中的句子数量。 现在,让我们确定整个 DataFrame 中的句子数量。 -
<st c="16383">让我们将 20 个新闻组数据集的训练 <st c="16404">子集加载到 pandas DataFrame 中:</st>data = fetch_20newsgroups(subset='train') df = pd.DataFrame(data.data, columns=['text']) -
为了加快以下步骤,我们只需处理 DataFrame 的前
<st c="16620">10</st>`行: df = df.loc[1:10] -
让我们也删除文本的第一部分,这部分包含有关电子邮件发送者、主题和其他我们不感兴趣的信息。
大部分信息都在单词 <st c="16866">Lines</st>之后,后面跟着 <st c="16884">:</st>,所以让我们在 <st c="16916">Lines:</st>处拆分字符串,并捕获字符串的第二部分: df['text'] = df['text'].str.split('Lines:').apply( lambda x: x[1]) -
`
最终,让我们创建一个包含每段文本中句子数量的变量: df['num_sent'] = df['text'].apply( sent_tokenize).apply(len)<st c="17174">使用</st><st c="17184">df</st><st c="17186">命令,您可以显示包含text 变量和包含每段文本中句子数量的新特征的整个 DataFrame: `


<st c="17926">现在,我们可以将这个新功能作为输入提供给</st>
<st c="18000">它的工作原理...</st>
在这个示例中,我们使用 <st c="18086">sent_tokenizer</st> 从 <st c="18110">NLTK</st> <st c="18114">库</st> 中将包含文本的字符串分割成句子。<st c="18124">sent_tokenizer</st> 已经预先训练以识别大写字母和不同类型的标点符号,这些符号标志着句子的开始和结束。
首先,我们将 <st c="18294">sent_tokenizer</st> 应用到一个手动创建的字符串上,以便熟悉其功能。分词器将文本分割成八个句子的列表。我们结合了分词器和内置的 Python <st c="18495">len()</st> 方法来计算字符串中的句子数量。
接下来,我们加载了一个包含文本的数据集,为了加快计算速度,我们只保留了 DataFrame 的前 10 行,使用 pandas 的 <st c="18692">loc[]</st> 函数。接下来,我们移除了文本的第一部分,这部分包含了关于电子邮件发送者和主题的信息。为此,我们使用 pandas 的 <st c="18873">str.split("Lines:")</st> 函数在 <st c="18852">Lines:</st> 处分割字符串,该函数返回一个包含两个元素的列表:<st c="18973">Lines:</st> 前后的字符串。通过在 <st c="19016">apply()</st> 中使用 lambda 函数,我们保留了文本的第二部分——即列表中由 <st c="19118">split()</st> 返回的第二个字符串。
最后,我们使用 pandas 的 <st c="19207">apply()</st> 方法将 <st c="19126">sent_tokenizer</st> 应用到 DataFrame 的每一行上,将字符串分割成句子,然后使用内置的 Python <st c="19300">len()</st> 方法对句子列表进行操作,以返回每个字符串的句子数量。这样,我们创建了一个包含每个文本句子数量的新特征。
There’s mo re...
<st c="19485">NLTK</st> <st c="19490">具有单词分词等有用功能,我们可以使用这些功能而不是 来计数和返回单词数量。</st><st c="19666">NLTK</st> <st c="19670">功能的信息:</st>
-
《Python 3 Text Processing with NLTK 3 Cookbook》,作者 Jacob Perkkins,Packt Publishing
-
The <st c="19781">NLTK</st>文档 在 http://www.nltk.org/ 。
使用词袋和 n-gram 创建特征

-
Dogs like -
like cats -
cats but -
但是做 -
do not -
like dogs

<st c="21419">scikit-learn</st>
准备就绪
<st c="23042">scikit-learn</st>
如何做...
-
加载 <st c="23175">pandas</st>, <st c="23183">CountVectorizer</st>,以及来自 <st c="23221">scikit-learn</st>` 的数据集 :import pandas as pd from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import ( CountVectorizer ) -
让我们将 20 个新闻组数据集的 训练集部分 加载到一个 pandas DataFrame 中:data = fetch_20newsgroups(subset='train') df = pd.DataFrame(data.data, columns=['text']) -
为了使结果更容易解释,让我们从 文本变量 中删除标点符号和数字:df['text'] = df['text'].str.replace( ‹[^\w\s]›,››, regex=True).str.replace( ‹\d+›,››, regex=True)
-
现在,让我们 设置 <st c="23864">CountVectorizer()</st>,以便在创建 BoW 之前,它将文本转换为小写,删除停用词,并保留至少出现在 5%的 文本片段中的单词: vectorizer = CountVectorizer( lowercase=True, stop_words='english', ngram_range=(1, 1), min_df=0.05)
<st c="24221">ngrams_range</st> <st c="24251">(1,2)</st><st c="24371">(1,2)</st><st c="24378">CountVectorizer()</st>
-
让我们拟合 <st c="24468">CountVectorizer()</st>,以便它学习在 BoW 中应该使用哪些单词: vectorizer.fit(df['text']) -
现在,让我们创建 BoW: X = vectorizer.transform(df['text']) -
最后,让我们将 BoW 捕获到一个具有相应 特征名称 的 DataFrame 中: bagofwords = pd.DataFrame( X.toarray(), columns = vectorizer.get_feature_names_out() )有了 这 , 我们 已经 创建 了一个包含单词作为列和每个文本中它们出现的次数作为值的 <st c="24835">pandas</st>DataFrame。 您可以通过执行 <st c="24986">bagofwords.head()</st>来查看结果:

它是如何工作的...
<st c="25536">CountVectorizer()</st>
<st c="25843">scikit-l</st><st c="25851">earn</st> <st c="25926">replace()</st> <st c="25984">str</st> <st c="26015">'\d+'</st><st c="26034">'[^\w\s]'</st><st c="26065">''</st><st c="26083">CountVectorizer()</st> <st c="26131">lowercase</st> <st c="26154">True</st> <st c="26227">stop_words</st> <st c="26250">english</st> <st c="26329">ngram_range</st> <st c="26348">(1,1)</st> <st c="26416">min_df</st> <st c="26426">0.05</st>
<st c="26592">fit()</st> <st c="26704">transform()</st> <st c="26826">pandas</st>
另请参阅
<st c="26877">CountVectorizer()</st> <st c="26906">scikit-learn</st>
实现词频-逆文档频率

准备就绪
<st c="29031">scikit-learn</st>

<st c="29288">scikit-learn</st> <st c="29430">scikit-learn</st>
<st c="30217">TfidfVectorizer()</st>
如何做到这一点...
-
加载 <st c="30335">pandas</st>, <st c="30343">TfidfVectorizer()</st>和来自 <st c="30378">scikit-learn</st>的数据集: import pandas as pd from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import ( TfidfVectorizer ) -
让我们将 20 个新闻组数据集的训练集部分加载到一个 pandas DataFrame 中: data = fetch_20newsgroups(subset='train') df = pd.DataFrame(data.data, columns=['text']) -
为了使结果更容易解释,让我们从 文本变量中删除标点符号和数字: df['text'] = df['text'].str.replace( ‹[^\w\s]›,››, regex=True).str.replace( '\d+','', regex=True) -
现在,让我们设置 <st c="30921">TfidfVectorize</st><st c="30935">r()</st>从 <st c="30945">scikit-learn</st>中,以便在创建 TF-IDF 度量之前,将所有文本转换为小写,删除停用词,并且 保留至少在 5%的 文本片段中出现的单词: vectorizer = TfidfVectorizer( lowercase=True, stop_words='english', ngram_range=(1, 1), min_df=0.05)
<st c="31306">ngrams_range</st> <st c="31336">(1,2)</st><st c="31456">(1,2)</st><st c="31463">TfidfVectorizer()</st>
-
让我们拟合 <st c="31564">TfidfVectorizer()</st>,以便它学习哪些单词应该作为 TF-IDF 矩阵的列引入,并确定 单词的 <st c="31691">idf</st>: vectorizer.fit(df['text']) -
现在,让我们创建 TF-IDF 矩阵: X = vectorizer.transform(df['text']) -
最后,让我们将 TF-IDF 矩阵捕获到具有相应 特征名称 的 DataFrame 中:tfidf = pd.DataFrame( X.toarray(), columns = vectorizer.get_feature_names_out() )有了这个,我们就创建了一个 <st c="32003">pandas</st>DataFrame,其中包含单词作为列,TF-IDF 作为值。 您可以通过执行 tfidf.head() 来检查结果:

它是如何工作的...
<st c="33187">TfidfVectorizer()</st>
<st c="33269">scikit-learn</st> <st c="33356">replace()</st><st c="33405">str</st><st c="33429">'\d+'</st><st c="33448">'[^\w\s]'</st><st c="33479">''</st><st c="33497">TfidfVectorizer()</st> <st c="33565">lowercase</st> <st c="33588">True</st> <st c="33664">stop_words</st> <st c="33687">english</st> <st c="33746">ngram_range</st> <st c="33765">(1,1)</st> <st c="33833">min_df</st> <st c="33852">0.05</st>
<st c="34001">fi</st><st c="34003">t()</st> <st c="34098">transform()</st>
另请参阅 。
*<st c="34365">TfidfVectorizer()</st>
清理和词干提取文本变量
我们的数据集中的一些变量来自自由文本字段,这些字段是由用户手动
为了让计算机正确识别单词,也需要将所有单词设置为相同的格式,因为单词
最后,为了专注于文本的
在这个菜谱中,我们将学习如何使用 pandas 和 <st c="36704">NLTK</st>
准备工作
我们将使用 <st c="36748">NLTK</st>
关于 <st c="37135">NLTK</st>
如何做到这一点...
让我们首先加载必要的库并准备好数据集:
-
加载
<st c="37331">pandas</st>, <st c="37339">stopwords</st>, 和 <st c="37354">SnowballStemmer</st>从 <st c="37375">NLTK</st>以及数据集从 <st c="37396">scikit-learn</st>: import pandas as pd from nltk.corpus import stopwords from nltk.stem.snowball import SnowballStemmer from sklearn.datasets import fetch_20newsgroups -
让我们将 20 个新闻组数据集的训练集部分加载到一个 pandas DataFrame 中:
data = fetch_20newsgroups(subset='train') df = pd.DataFrame(data.data, columns=['text'])现在,让我们开始进行文本清理。
注意
在执行这个菜谱中的每个命令后,通过执行例如 <st c="37888">print(df['text'][10])</st>
-
首先,让我们移除标点符号:
df["text"] = df['text'].str.replace('[^\w\s]','')
小贴士
<st c="38191">string</st> <st c="38256">import string</st> <st c="38287">df['text'] =</st> <st c="38300">df['text'].str.replace('[{}]</st><st c="38328">'.format(string.punctuation), '')</st>
-
我们还可以 删除 数字字符,只留下字母, 如下所示: df['text'] = df['text'].str.replace( '\d+', '', regex=True) -
现在,让我们将所有单词 转换为小写: df['text'] = df['text'].str.lower()现在,让我们开始 移除 停用词 的过程。
<st c="38687">停用词</st>
-
让我们创建一个函数,该函数将字符串拆分为单词列表,移除停用词,并将剩余的单词重新连接成一个字符串: : def remove_stopwords(text): stop = set(stopwords.words('english')) text = [word for word in text.split() if word not in stop] text = ‹ ‹.join(x for x in text) return text
<st c="39160">scikit-learn</st> <st c="39179">CountVectorizer()</st> <st c="39205">TfidfVectorizer()</st>
-
现在,让我们使用 步骤 6 中的函数来从 <st c="39572">text</st>变量中移除停用词: df['text'] = df['text'].apply(remove_stopwords)如果您想知道哪些单词是 停用词 s, 执行 <st c="39692">stopwords.words('english')</st>。 最后,让我们对我们的数据进行词干提取。 我们将使用 <st c="39775">SnowballStemmer</st>从 <st c="39796">NLTK</st>中 进行此操作。 -
让我们为 <st c="39839">SnowballStemer</st>创建一个针对 英语语言 的实例:stemmer = SnowballStemmer("english")
<st c="39992">stemmer.stem('running')</st><st c="40032">run</st>
-
让我们创建一个函数,该函数将字符串分割成单词列表,将 <st c="40163">stemmer</st>应用于每个单词,并将还原的单词列表重新连接成字符串: 一个字符串: def stemm_words(text): text = [ stemmer.stem(word) for word in text.split() ] text = ‹ ‹.join(x for x in text) return text -
让我们使用 从 步骤 9 中的函数 来还原我们数据中的单词: 我们的数据: df['text'] = df['text'].apply(stemm_words)现在,我们的文本已经准备好根据字符和单词计数创建特征,以及创建 BoWs 或 TF-IDF 矩阵,正如本章前面的食谱中所述。 这一章。 如果我们执行 <st c="40673">print(df['text'][10])</st>,我们将看到清理后的文本示例: 清理后: <st c="40738">irwincmptrclonestarorg irwin arnstein subject recommend duc summari what worth distribut usa expir sat may gmt organ computrac inc richardson tx keyword ducati gts much line line ducati gts model k clock run well paint bronzebrownorang fade leak bit oil pop st hard accel shop fix tran oil leak sold bike owner want think like k opinion pleas email thank would nice stabl mate beemer ill get jap bike call axi motor tuba irwin honk therefor computracrichardsontx irwincmptrclonestarorg dod r</st>
它是如何工作的...
<st c="41653">replace()</st><st c="41702">str</st><st c="41726">'\d+'</st><st c="41745">'[^\w\s]'</st><st c="41776">''</st><st c="41810">punctuation</st> <st c="41847">string</st>
<st c="41871">string.punctuation</st> <st c="41929">string</st>
<st c="42067">str</st><st c="42118">lower()</st> <st c="42182">stopwords</st> <st c="42204">NLTK</st><st c="42388">str.split()</st><st c="42524">join()</st> <st c="42622">set()</st> <st c="42644">NLTK</st> <st c="42778">apply()</st>
<st c="42849">stopwords.words('english')</st> <st c="42915">stopwords</st> <st c="42930">NLTK</st>
<st c="43035">SnowballStemmer</st> <st c="43056">NLTK</st><st c="43062">SnowballStemmer</st> <st c="43206">str.split()</st><st c="43255">SnowballStemmer</st> <st c="43362">join()</st> <st c="43390">apply()</st>




浙公网安备 33010602011771号