在数据科学领域,数据集往往包含需要不同特征提取和处理流程的组件。这种情况可能发生在数据集包含异构数据类型(例如,栅格图像和文本标题)时,或者数据集存储在pandas.DataFrame中,不同的列需要不同的处理流程。本文将展示如何使用列变换器处理包含不同类型特征的数据集。虽然选择的特征不是特别有用,但它们可以很好地说明这种技术。
将使用20个新闻组数据集,该数据集包含来自20个主题的新闻组帖子。这个数据集根据特定日期前后发布的帖子被划分为训练和测试子集。为了加快运行时间,只使用2个类别的帖子。每个特征包括有关该帖子的元信息,例如主题和新闻帖子的正文。
from sklearn.datasets import fetch_20newsgroups
categories = ["sci.med", "sci.space"]
X_train, y_train = fetch_20newsgroups(random_state=1, subset="train", categories=categories, remove=("footers", "quotes"), return_X_y=True)
X_test, y_test = fetch_20newsgroups(random_state=1, subset="test", categories=categories, remove=("footers", "quotes"), return_X_y=True)
例如,打印出训练数据集中的第一个帖子,可以看到它包含了发件人、主题、文章ID、组织和行数等信息。
首先,希望创建一个变换器,它能够提取每个帖子的主题和正文。由于这是一个无状态转换(不需要从训练数据中获取状态信息),可以定义一个执行数据转换的函数,然后使用FunctionTransformer创建一个scikit-learn变换器。
def subject_body_extractor(posts):
features = np.empty(shape=(len(posts), 2), dtype=object)
for i, text in enumerate(posts):
headers, _, body = text.partition("\n\n")
features[i, 1] = body
prefix = "Subject:"
sub = ""
for line in headers.split("\n"):
if line.startswith(prefix):
sub = line[len(prefix):]
break
features[i, 0] = sub
return features
subject_body_transformer = FunctionTransformer(subject_body_extractor)
还将创建一个变换器,它能够提取文本的长度和句子数量。
def text_stats(posts):
return [{"length": len(text), "num_sentences": text.count(".")} for text in posts]
text_stats_transformer = FunctionTransformer(text_stats)
下面的管道使用SubjectBodyExtractor从每个帖子中提取主题和正文,生成一个(n_samples, 2)数组。然后使用ColumnTransformer计算主题和正文的标准词袋特征,以及正文的文本长度和句子数量。将它们结合起来,加权后训练一个分类器。
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import PCA
from sklearn.svm import LinearSVC
pipeline = Pipeline([
("subjectbody", subject_body_transformer),
("union", ColumnTransformer([
("subject", TfidfVectorizer(min_df=50), 0),
("body_bow", Pipeline([
("tfidf", TfidfVectorizer()),
("best", PCA(n_components=50, svd_solver="arpack"))
]), 1),
("body_stats", Pipeline([
("stats", text_stats_transformer),
("vect", DictVectorizer())
]), 1),
], transformer_weights={"subject": 0.8, "body_bow": 0.5, "body_stats": 1.0})),
("svc", LinearSVC(dual=False))
], verbose=True)
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print("Classification report:\n\n{}".format(classification_report(y_test, y_pred)))
最后,在训练数据上拟合管道,并使用它来预测X_test中的主题。然后打印出管道的性能指标。