使用Scikit-Learn创建一个自定义数据转换器
在机器学习中,数据转换器被用来使数据集适合训练过程。Scikit-Learn能够快速进行实验,以最小的时间实现涉及预处理、机器学习算法、评估和推理的数据管道,获得高质量的结果。
简介
Scikit-Learn提供了内置的数据准备方法,在数据被送入训练模型之前。然而,作为一个数据科学家,你可能需要执行更多的自定义清理过程,或添加更多的属性,以提高模型的性能。要做到这一点,你将需要为你的数据创建一个自定义的转化器。
在这篇文章中,我们将探讨如何做到这一点。
前提条件
要跟上本教程,你应该具备以下条件。
- 对Python编程语言有良好的理解。
- 熟悉Numpy和Pandas库。
- 有使用Jupyter笔记本或任何其他基于笔记本的技术的基本知识,例如Google Colab。
- 安装了Python和上述的库。
让我们开始吧。
这些代码片段是为笔记本定制的,但你也可以使用普通的python文件。
开始使用
加载数据
我们将使用下面的脚本从这个资源库获得我们的数据集。
import os
import tarfile
from six.moves import urllib
OUR_ROOT_URL = "https://raw.githubusercontent.com/ageron/handson-ml/master/"
OUR_PATH = "datasets/housing"
OUR_DATA_URL = OUR_ROOT_URL + OUR_PATH + "/housing.tgz"
def get_data(our_data_url=OUR_DATA_URL, our_path=OUR_PATH):
if not os.path.isdir(our_path):
os.makedirs(our_path)
#setting the zip file path
zipfile_path = os.path.join(our_path, "housing.tgz")
#getting the file from the url and extracting it
urllib.request.urlretrieve(our_data_url, zipfile_path)
our_zip_file = tarfile.open(zipfile_path)
our_zip_file.extractall(path=our_path)
our_zip_file.close()
get_data()
这段代码是为了从URL中下载数据,以不纠缠于此。首先,我们导入os 模块,与操作系统进行交互。之后,我们导入了tarfile 模块,用于访问和操作tar文件。最后,我们导入了urllib ,用于使用URL操作功能。
然后,我们适当地设置了我们的路径。在get_data() 函数中,我们为我们的数据建立了一个目录,从URL中检索它,然后提取并存储它。
因此,在你的工作目录中,你会注意到一个名为datasets的目录被创建。打开它,你会得到另一个名为housing的目录,里面有一个名为housing.csv的文件。我们将使用这个文件。
我们调用这个函数。然后,我们将加载CSV文件。
import pandas as pd
def load_our_data(our_path=OUR_PATH):
#setting the csv file path
our_file_path = os.path.join(our_path, "housing.csv")
#reading it using Pandas
return pd.read_csv(our_file_path)
our_dataset = load_our_data()
首先,我们导入pandas 库,从指定的路径加载CSV数据,our_file_path 。
你可以通过以下方式查看数据。
our_dataset.head()
our_dataset.info()
清理数据
我们在这里要做的清理操作是用中值来填充空的数字属性。我们将使用SimpleImputer ,一个估计器来做这件事。但是,首先,我们把strategy 设为median ,以计算每一列空数据的中值。
from sklearn.impute import SimpleImputer
'''setting the `strategy` to `median` so that it calculates the median value for each column's empty data'''
imputer = SimpleImputer(strategy="median")
#removing the ocean_proximity attribute for it is textual
our_dataset_num = our_dataset.drop("ocean_proximity", axis=1)
#estimation using the fit method
imputer.fit(our_dataset_num)
#transforming using the learnedparameters
X = imputer.transform(our_dataset_num)
#setting the transformed dataset to a DataFrame
our_dataset_numeric = pd.DataFrame(X, columns=our_dataset_num.columns)
我们放弃了ocean_proximity属性,因为它是一个文本属性,将在下一节处理。
产生的结果是一个数组,所以我们把它转换为一个DataFrame。
处理文本和分类属性
我们不能类似地处理文本和数字属性。因此,例如,我们不能计算文本的中位数。
我们将使用一个叫做OrdinalEncoder. 的转化器来处理这个问题。选择它是因为它对管道更友好。此外,它为相应的文本属性分配了数字,例如,1代表NEAR,2代表FAR。
from sklearn.preprocessing import OrdinalEncoder
#selecting the textual attribute
our_text_cats = our_dataset[['ocean_proximity']]
our_encoder = OrdinalEncoder()
#transforming it
our_encoded_dataset = our_encoder.fit_transform(our_text_cats)
我们的数据转换器
这就是我们要创建的自定义转换器。我们将添加这三个属性。
- 每户的房间。
- 每户人口。
- 每户的卧室。
为了使我们的转化器能够顺利地与Scikit-Learn一起工作,我们应该有三个方法。
fit()transform()fit_transform
我们包括这三个方法是因为Scikit-Learn是基于鸭子类型的。还使用了一个类,因为这使我们更容易包括所有的方法。
最后一个是通过使用TransformerMixin 作为基类自动得到的。BaseEstimator 让我们得到set_params() 和get_params() 方法,这些方法对超参数调整有帮助。
我们通过划分适当的属性来获得transform() 方法中的三个额外属性。一个例子是这样的。为了得到每户的房间数,我们用房间数除以户数。
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
#initialising column numbers
rooms, bedrooms, population, household = 3,4,5,6
class CustomTransformer(BaseEstimator, TransformerMixin):
#the constructor
'''setting the add_bedrooms_per_room to True helps us check if the hyperparameter is useful'''
def __init__(self, add_bedrooms_per_room = True):
self.add_bedrooms_per_room = add_bedrooms_per_room
#estimator method
def fit(self, X, y = None):
return self
#transfprmation
def transform(self, X, y = None):
#getting the three extra attributes by dividing appropriate attributes
rooms_per_household = X[:, rooms] / X[:, household]
population_per_household = X[:, population] / X[:, household]
if self.add_bedrooms_per_room:
bedrooms_per_room = X[:, bedrooms] / X[:, rooms]
return np.c_[X, rooms_per_household, population_per_household, bedrooms_per_room]
else:
return np.c_[X, rooms_per_household, population_per_household]
attrib_adder = CustomTransformer()
our_extra_attributes = attrib_adder.transform(our_dataset.values)
我们的流水线
我们在一个管道中实现它们,使数据转换步骤以正确的顺序执行。
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
#the numeric attributes transformation pipeline
numeric_pipeline = Pipeline([
('imputer', SimpleImputer(strategy="median")),
('attribs_adder', CustomTransformer()),
])
numeric_attribs = list(our_dataset_numeric)
#the textual transformation pipeline
text_attribs = ["ocean_proximity"]
#setting the order of the two pipelines
our_full_pipeline = ColumnTransformer([
("numeric", numeric_pipeline, numeric_attribs),
("text", OrdinalEncoder(), text_attribs),
])
'''Finally, scaling the data and learning the scaled parameters from the pipeline
'''
our_dataset_prepared = full_pipeline.fit_transform(our_dataset)
ColumnTransformer ,用于分别转化列,并将每个转化器产生的特征结合起来,形成一个单一的特征空间。
总结
我们已经看到了获取数据、转换数据,然后在一个管道中实现所有步骤的各种步骤。所以我希望你能得到一些启发。