特征工程练手(三):特征构建

本文为和鲸python 特征工程入门与实践·闯关训练营资料整理而来,加入了自己的理解(by GPT4o)

原活动链接

原作者:云中君,大厂后端研发工程师

0、 总结

在本节中,讨论了针对数值数据和文本数据进行特征构建的方法。以下是本节的主要内容和关键学习点的总结:

数值数据的特征构建:
填充分类特征:对于分类特征,可能需要处理缺失值。学习如何进行填充以确保数据的完整性和准确性。
编码分类变量:将分类变量转化为机器学习模型可以理解的形式。
扩展数值特征:通过使用现有数值特征创建新的特征,以提供更多信息给模型。

文本数据的特征构建:
词袋法:将文本表示为单词的出现频率,转化为向量形式,可用于机器学习算法。
CountVectorizer:将文本数据转换为其向量表示的最常用办法,和虚拟变量类似。
TF-IDF:考虑单词在文档中的重要性,通过计算权重进行特征构建,用于文本分类等任务。

下一步展望:
学习如何选择合适的特征用于机器学习。
特征选择是优化模型性能的关键一步,可以通过过滤法、包装法和嵌入法等方法进行选择。

通过本节的学习,你已经掌握了数值数据和文本数据特征构建的基本方法,为下一步选择合适的特征奠定了基础。在进入下一关之前,确保对特征构建的概念有清晰的理解,并准备好学习如何进行特征选择。祝你在学习的过程中取得更大的进步!

1、前言

本节,我们使用现有特征构建全新的特征,主要从以下几个方面进行讲解

  • 检查数据集
  • 填充分类特征
  • 编码分类变量
  • 扩展数值特征
  • 针对文本的特征构建

2、基础知识

2.1数据集

自己创建数据集,展示不同的数据等级和类型

import pandas as pd

X = pd.DataFrame({'city':['tokyo', None, 'london', 'seattle', 'san francisco', 'tokyo'], 
                  'boolean':['yes', 'no', None, 'no', 'no', 'yes'], 
                  'ordinal_column':['somewhat like', 'like', 'somewhat like', 'like', 'somewhat like', 'dislike'], 
                  'quantitative_column':[1, 11, -.5, 10, None, 20]})

print(X)
            city boolean ordinal_column  quantitative_column
0          tokyo     yes  somewhat like                  1.0
1           None      no           like                 11.0
2         london    None  somewhat like                 -0.5
3        seattle      no           like                 10.0
4  san francisco      no  somewhat like                  NaN
5          tokyo     yes        dislike                 20.0
  • boolean(布尔值):二元分类数据(是/否),定类等级
  • city(城市):分类数据,也是定类等级
  • ordinal_column(顺序列):顺序数据,定序等级
  • quantitative_column(定量列):是整数,定比等级。

2.2填充分类特征

用isnull和sum方法查看缺失值

X.isnull().sum()
city                   1
boolean                1
ordinal_column         0
quantitative_column    1
dtype: int64

scikit-learn的Imputer类有一个most_frequent方法可以用在定性数据上,但是只能处理整数型的分类数据。
对于数值数据,可以通过计算均值的方法填充缺失值;而对于分类数据,可以计算出最常见的类别用于填充。

# 寻找city列中最常见的元素
most_frequent = X['city'].value_counts().index[0]
most_frequent
'tokyo'
# 用最常见值填充city列
X['city'].fillna(most_frequent)
0            tokyo
1            tokyo
2           london
3          seattle
4    san francisco
5            tokyo
Name: city, dtype: object

自定义分类填充器

机器学习流水线:

  • 我们可以用流水线按顺序应用转换和最终的预测器
  • 流水线的中间步骤只能是转换,这意味着它们必须实现fit和transform方法
  • 最终的预测器只需要实现fit方法

流水线的目的是将几个可以交叉验证的步骤组装在一起,并设置不同的参数。在为每个需要填充的列构建好自定义转换器后,就可以把它们传入流水线,一口气转换好数据。

# 创建自定义分类填充器
from sklearn.base import TransformerMixin

class CustomCategoryImputer(TransformerMixin):
    def __init__(self, cols=None):
        self.cols = cols
        
    def transform(self, df):
        X = df.copy()
        for col in self.cols:
            X[col].fillna(X[col].value_counts().index[0], inplace=True)
        return X
    
    def fit(self, *_):
        return self
# 在列上应用自定义分类填充器
cci = CustomCategoryImputer(cols=['city', 'boolean'])
cci.fit_transform(X)
citybooleanordinal_columnquantitative_column
0tokyoyessomewhat like1.0
1tokyonolike11.0
2londonnosomewhat like-0.5
3seattlenolike10.0
4san francisconosomewhat likeNaN
5tokyoyesdislike20.0

自定义定量填充器

# 自定义定量填充器
from sklearn.impute import SimpleImputer
from sklearn.base import TransformerMixin

class CustomQuantitativeImputer(TransformerMixin):
    def __init__(self, cols=None, strategy='mean'):
        self.cols = cols
        self.strategy = strategy
        
    def transform(self, df):
        X = df.copy()
        impute = SimpleImputer(strategy=self.strategy)
        for col in self.cols:
            X[col] = impute.fit_transform(X[[col]])
        return X
    
    def fit(self, *_):
        return self
cqi = CustomQuantitativeImputer(cols=['quantitative_column'], strategy='mean')

cqi.fit_transform(X)
citybooleanordinal_columnquantitative_column
0tokyoyessomewhat like1.0
1Nonenolike11.0
2londonNonesomewhat like-0.5
3seattlenolike10.0
4san francisconosomewhat like8.3
5tokyoyesdislike20.0

流水线调用

# 从sklearn导入Pipeline
from sklearn.pipeline import Pipeline

imputer = Pipeline([('quant', cqi), ('category', cci)]) 
imputer.fit_transform(X)
citybooleanordinal_columnquantitative_column
0tokyoyessomewhat like1.0
1tokyonolike11.0
2londonnosomewhat like-0.5
3seattlenolike10.0
4san francisconosomewhat like8.3
5tokyoyesdislike20.0

2.3编码分类变量

将分类数据转换为数值数据,以供机器学习模型使用

定类等级的编码

将分类数据转换为虚拟变量(dummy variable)

  • 用Pandas自动找到分类变量并进行编码
  • 创建自定义虚拟变量编码器,在流水线中工作
pd.get_dummies(X, 
               columns = ['city', 'boolean'],  # 要虚拟化的列
               prefix_sep='__') # 前缀(列名)和单元格值之间的分隔符
ordinal_columnquantitative_columncity__londoncity__san franciscocity__seattlecity__tokyoboolean__noboolean__yes
0somewhat like1.0000101
1like11.0000010
2somewhat like-0.5100000
3like10.0001010
4somewhat likeNaN010010
5dislike20.0000101
# 自定义虚拟变量编码器
class CustomDummifier(TransformerMixin):
    def __init__(self, cols=None):
        self.cols = cols
        
    def transform(self, X):
        return pd.get_dummies(X, columns=self.cols)
    
    def fit(self, *_):
        return self
cd = CustomDummifier(cols=['boolean', 'city'])

cd.fit_transform(X)
ordinal_columnquantitative_columnboolean_noboolean_yescity_londoncity_san franciscocity_seattlecity_tokyo
0somewhat like1.0010001
1like11.0100000
2somewhat like-0.5001000
3like10.0100010
4somewhat likeNaN100100
5dislike20.0010001

定序等级的编码

为了保持顺序,我们使用标签编码器。标签编码器是指,顺序数据的每个标签都会有一个相关数值。在我们的例子中,这意味着顺序列的值(dislike、somewhat like和like)会用0、1、2来表示。

# 创建一个列表,顺序数据对应于列表索引
ordering = ['dislike', 'somewhat like', 'like']  # 0是dislike,1是somewhat like,2是like
# 在将ordering排序映射到顺序列之前,先看一下列

print(X['ordinal_column'])
0    somewhat like
1             like
2    somewhat like
3             like
4    somewhat like
5          dislike
Name: ordinal_column, dtype: object
# 将ordering映射到顺序列
print(X['ordinal_column'].map(lambda x: ordering.index(x)))
0    1
1    2
2    1
3    2
4    1
5    0
Name: ordinal_column, dtype: int64
# 将自定义标签编码器放进流水线中
class CustomEncoder(TransformerMixin):
    def __init__(self, col, ordering=None):
        self.ordering = ordering
        self.col = col
        
    def transform(self, df):
        X = df.copy()
        X[self.col] = X[self.col].map(lambda x: self.ordering.index(x))
        return X
    
    def fit(self, *_):
        return self
ce = CustomEncoder(col='ordinal_column', ordering = ['dislike', 'somewhat like', 'like'])

ce.fit_transform(X)
citybooleanordinal_columnquantitative_column
0tokyoyes11.0
1Noneno211.0
2londonNone1-0.5
3seattleno210.0
4san franciscono1NaN
5tokyoyes020.0

将连续特征分箱

用cut函数将数据分箱(binning),亦称为分桶(bucketing)。意思就是,它会创建数据的范围。

# 默认的类别名是分箱
pd.cut(X['quantitative_column'], bins=3)
0     (-0.52, 6.333]
1    (6.333, 13.167]
2     (-0.52, 6.333]
3    (6.333, 13.167]
4                NaN
5     (13.167, 20.0]
Name: quantitative_column, dtype: category
Categories (3, interval[float64, right]): [(-0.52, 6.333] < (6.333, 13.167] < (13.167, 20.0]]
# 不使用标签
pd.cut(X['quantitative_column'], bins=3, labels=False)
0    0.0
1    1.0
2    0.0
3    1.0
4    NaN
5    2.0
Name: quantitative_column, dtype: float64
class CustomCutter(TransformerMixin):
    def __init__(self, col, bins, labels=False):
        self.labels = labels
        self.bins = bins
        self.col = col
        
    def transform(self, df):
        X = df.copy()
        X[self.col] = pd.cut(X[self.col], bins=self.bins, labels=self.labels)
        return X
    
    def fit(self, *_):
        return self
cc = CustomCutter(col='quantitative_column', bins=3)

cc.fit_transform(X)
citybooleanordinal_columnquantitative_column
0tokyoyessomewhat like0.0
1Nonenolike1.0
2londonNonesomewhat like0.0
3seattlenolike1.0
4san francisconosomewhat likeNaN
5tokyoyesdislike2.0

创建流水线

流水线的顺序是:

  1. 用imputer填充缺失值
  2. 用虚拟变量填充分类列(one-hot编码)
  3. 对ordinal_column进行编码
  4. 将quantitative_column分箱
from sklearn.pipeline import Pipeline

pipe = Pipeline([("imputer", imputer), ('dummify', cd), ('encode', ce), ('cut', cc)])
# 进入流水线前的数据
print(X)
            city boolean ordinal_column  quantitative_column
0          tokyo     yes  somewhat like                  1.0
1           None      no           like                 11.0
2         london    None  somewhat like                 -0.5
3        seattle      no           like                 10.0
4  san francisco      no  somewhat like                  NaN
5          tokyo     yes        dislike                 20.0
# 拟合流水线
pipe.fit(X)
Pipeline(steps=[('imputer',
             Pipeline(steps=[(&#x27;quant&#x27;,
                              &lt;__main__.CustomQuantitativeImputer object at 0x00000211553EBA00&gt;),
                             (&#x27;category&#x27;,
                              &lt;__main__.CustomCategoryImputer object at 0x000002113D3443A0&gt;)])),
            (&#x27;dummify&#x27;,
             &lt;__main__.CustomDummifier object at 0x00000211559CEA60&gt;),
            (&#x27;encode&#x27;,
             &lt;__main__.CustomEncoder object at 0x00000211559D1BB0&gt;),
            (&#x27;cut&#x27;, &lt;__main__.CustomCutter object at 0x00000211559EB520&gt;)])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class="sk-container" hidden><div class="sk-item sk-dashed-wrapped"><div class="sk-label-container"><div class="sk-label sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-1" type="checkbox" ><label for="sk-estimator-id-1" class="sk-toggleable__label sk-toggleable__label-arrow">Pipeline</label><div class="sk-toggleable__content"><pre>Pipeline(steps=[(&#x27;imputer&#x27;,
             Pipeline(steps=[(&#x27;quant&#x27;,
                              &lt;__main__.CustomQuantitativeImputer object at 0x00000211553EBA00&gt;),
                             (&#x27;category&#x27;,
                              &lt;__main__.CustomCategoryImputer object at 0x000002113D3443A0&gt;)])),
            (&#x27;dummify&#x27;,
             &lt;__main__.CustomDummifier object at 0x00000211559CEA60&gt;),
            (&#x27;encode&#x27;,
             &lt;__main__.CustomEncoder object at 0x00000211559D1BB0&gt;),
            (&#x27;cut&#x27;, &lt;__main__.CustomCutter object at 0x00000211559EB520&gt;)])</pre></div></div></div><div class="sk-serial"><div class="sk-item"><div class="sk-label-container"><div class="sk-label sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-2" type="checkbox" ><label for="sk-estimator-id-2" class="sk-toggleable__label sk-toggleable__label-arrow">imputer: Pipeline</label><div class="sk-toggleable__content"><pre>Pipeline(steps=[(&#x27;quant&#x27;,
             &lt;__main__.CustomQuantitativeImputer object at 0x00000211553EBA00&gt;),
            (&#x27;category&#x27;,
             &lt;__main__.CustomCategoryImputer object at 0x000002113D3443A0&gt;)])</pre></div></div></div><div class="sk-serial"><div class="sk-item"><div class="sk-estimator sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-3" type="checkbox" ><label for="sk-estimator-id-3" class="sk-toggleable__label sk-toggleable__label-arrow">CustomQuantitativeImputer</label><div class="sk-toggleable__content"><pre>&lt;__main__.CustomQuantitativeImputer object at 0x00000211553EBA00&gt;</pre></div></div></div><div class="sk-item"><div class="sk-estimator sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-4" type="checkbox" ><label for="sk-estimator-id-4" class="sk-toggleable__label sk-toggleable__label-arrow">CustomCategoryImputer</label><div class="sk-toggleable__content"><pre>&lt;__main__.CustomCategoryImputer object at 0x000002113D3443A0&gt;</pre></div></div></div></div></div><div class="sk-item"><div class="sk-estimator sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-5" type="checkbox" ><label for="sk-estimator-id-5" class="sk-toggleable__label sk-toggleable__label-arrow">CustomDummifier</label><div class="sk-toggleable__content"><pre>&lt;__main__.CustomDummifier object at 0x00000211559CEA60&gt;</pre></div></div></div><div class="sk-item"><div class="sk-estimator sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-6" type="checkbox" ><label for="sk-estimator-id-6" class="sk-toggleable__label sk-toggleable__label-arrow">CustomEncoder</label><div class="sk-toggleable__content"><pre>&lt;__main__.CustomEncoder object at 0x00000211559D1BB0&gt;</pre></div></div></div><div class="sk-item"><div class="sk-estimator sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-7" type="checkbox" ><label for="sk-estimator-id-7" class="sk-toggleable__label sk-toggleable__label-arrow">CustomCutter</label><div class="sk-toggleable__content"><pre>&lt;__main__.CustomCutter object at 0x00000211559EB520&gt;</pre></div></div></div></div></div></div></div>
pipe.transform(X)
ordinal_columnquantitative_columnboolean_noboolean_yescity_londoncity_san franciscocity_seattlecity_tokyo
010010001
121100001
210101000
321100010
411100100
502010001

2.4扩展数值特征

根据胸部加速度计识别动作的数据集

数据集按参与者划分,包含以下内容:

  • 序号
  • x轴加速度
  • y轴加速度
  • z轴加速度
  • 标签。标签是数字,每个数字代表一种动作(activity):1在电脑前工作;2站立、走路和上下楼梯;3站立;4走路;5上下楼梯;6与人边走边聊;7站立着讲话。
path = './data/activity_recognizer.csv'
df = pd.read_csv(path, header=None)
df.columns = ['index', 'x', 'y', 'z', 'activity']
df.head()
indexxyzactivity
00.01502221521531
11.01667207220471
22.01611195719061
33.01601193918311
44.01643196518791
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 162501 entries, 0 to 162500
Data columns (total 5 columns):
 #   Column    Non-Null Count   Dtype  
---  ------    --------------   -----  
 0   index     162501 non-null  float64
 1   x         162501 non-null  int64  
 2   y         162501 non-null  int64  
 3   z         162501 non-null  int64  
 4   activity  162501 non-null  int64  
dtypes: float64(1), int64(4)
memory usage: 6.2 MB

查看空准确率

df['activity'].value_counts(normalize=True)
7    0.515369
1    0.207242
4    0.165291
3    0.068793
5    0.019637
6    0.017951
2    0.005711
0    0.000006
Name: activity, dtype: float64

空准确率是51.54%,意味着如果我们猜7(站立着讲话),正确率就超过一半了

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
import warnings
warnings.filterwarnings('ignore')

X = df[['x', 'y', 'z']]
# 删除响应变量,建立特征矩阵
y = df['activity']

# 网格搜索所需的变量和实例

# 需要试验的KNN模型参数
knn_params = {'n_neighbors':[3, 4, 5, 6]}

knn = KNeighborsClassifier()
grid = GridSearchCV(knn, knn_params)
grid.fit(X, y)

print(grid.best_score_, grid.best_params_)
0.7357680039194061 {'n_neighbors': 5}

使用5个邻居作为参数时,KNN模型准确率达到了72.08%,比51.54%的空准确率高得多

多项式特征

使用Polynomial-Features创建新的列,它们是原有列的乘积,用于捕获特征交互。

  • degree是多项式特征的阶数,默认值是2。
  • interaction_only是布尔值:如果为True(默认False),表示只生成互相影响/交互的特征,也就是不同阶数特征的乘积。
  • include_bias也是布尔值:如果为True(默认),会生成一列阶数为0的偏差列,也就是说列中全是数字1。
from sklearn.preprocessing import PolynomialFeatures

poly = PolynomialFeatures(degree=2, include_bias=False, interaction_only=False)

X_poly = poly.fit_transform(X)
X_poly.shape
(162501, 9)
pd.DataFrame(X_poly, columns=poly.get_feature_names_out()).head()
xyzx^2x yx zy^2y zz^2
01502.02215.02153.02256004.03326930.03233806.04906225.04768895.04635409.0
11667.02072.02047.02778889.03454024.03412349.04293184.04241384.04190209.0
21611.01957.01906.02595321.03152727.03070566.03829849.03730042.03632836.0
31601.01939.01831.02563201.03104339.02931431.03759721.03550309.03352561.0
41643.01965.01879.02699449.03228495.03087197.03861225.03692235.03530641.0
探索性数据分析
%matplotlib inline
import seaborn as sns

sns.heatmap(pd.DataFrame(X_poly, columns=poly.get_feature_names_out()).corr())
<AxesSubplot:>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# 将interaction_only被设置成了True
poly = PolynomialFeatures(degree=2, include_bias=False, interaction_only=True) 
X_poly = poly.fit_transform(X) 
print(X_poly.shape)
(162501, 6)

pd.DataFrame(X_poly, columns=poly.get_feature_names_out()).head()
xyzx yx zy z
01502.02215.02153.03326930.03233806.04768895.0
11667.02072.02047.03454024.03412349.04241384.0
21611.01957.01906.03152727.03070566.03730042.0
31601.01939.01831.03104339.02931431.03550309.0
41643.01965.01879.03228495.03087197.03692235.0
sns.heatmap(pd.DataFrame(X_poly, columns=poly.get_feature_names_out()).corr())
<AxesSubplot:>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# 流水线
from sklearn.pipeline import Pipeline

pipe_params = {'poly_features__degree':[1, 2, 3], 'poly_features__interaction_only':[True, False], 'classify__n_neighbors':[3, 4, 5, 6]}

pipe = Pipeline([('poly_features', poly), ('classify', knn)])

grid = GridSearchCV(pipe, pipe_params)
grid.fit(X, y)

print(grid.best_score_, grid.best_params_)
0.7395217705490719 {'classify__n_neighbors': 5, 'poly_features__degree': 2, 'poly_features__interaction_only': True}

2.5文本专用特征构建

词袋法

通过单词的出现来描述文档,完全忽略单词在文档中的位置。词袋的3个步骤是:

  • 分词(tokenizing)
  • 计数(counting)
  • 归一化(normalizing)
twitter_path = './data/twitter_sentiment.csv'
tweets = pd.read_csv(twitter_path, encoding='latin1')

tweets.head()
ItemIDSentimentSentimentText
010is so sad for my APL frie...
120I missed the New Moon trail...
231omg its already 7:30 :O
340.. Omgaga. Im sooo im gunna CRy. I'...
450i think mi bf is cheating on me!!! ...
del tweets['ItemID']

tweets.head()
SentimentSentimentText
00is so sad for my APL frie...
10I missed the New Moon trail...
21omg its already 7:30 :O
30.. Omgaga. Im sooo im gunna CRy. I'...
40i think mi bf is cheating on me!!! ...
from sklearn.feature_extraction.text import CountVectorizer

X = tweets['SentimentText']
y = tweets['Sentiment']

vect = CountVectorizer()
_ = vect.fit_transform(X)
print(_.shape)
(99989, 105849)

CountVectorizer的参数

CountVectorizer将文本列转换为矩阵,其中列是词项,单元值是每个文档中每个词项的出现次数

  • stop_words:停用词
  • min_df:忽略在文档中出现频率低于阈值的词,减少特征的数量
  • max_df:忽略在文档中出现频率高于阈值的词,减少特征的数量
  • ngram_range:接收一个元组,表示n值的范围(代表要提取的不同n-gram的数量)上下界
  • analyzer:设置分析器作为参数,以判断特征是单词还是短语。默认是单词
vect = CountVectorizer(stop_words='english')  # 删除英语停用词(if、a、the, 等等)
_ = vect.fit_transform(X)
print(_.shape)
(99989, 105545)

vect = CountVectorizer(min_df=.05)  # 只保留至少在5%文档中出现的单词
# 减少特征数
_ = vect.fit_transform(X)
print(_.shape)
(99989, 31)

vect = CountVectorizer(max_df=.8)  # 只保留至多在80%文档中出现的单词
# “推断”停用词
_ = vect.fit_transform(X)
print(_.shape)
(99989, 105849)

vect = CountVectorizer(ngram_range=(1, 5))  # 包括最多5个单词的短语
_ = vect.fit_transform(X)
print(_.shape)  # 特征数爆炸
(99989, 3219557)

vect = CountVectorizer(analyzer='word')  # 默认分析器,划分为单词
_ = vect.fit_transform(X)
print(_.shape)
(99989, 105849)

词干提取(stemming)是一种常见的自然语言处理方法,可以将词汇中的词干提取出来,也就是把单词转换为其词根,从而缩小词汇量

from nltk.stem.snowball import SnowballStemmer

stemmer = SnowballStemmer('english')
stemmer.stem('interesting')
'interest'
# 将文本变成词根的函数
def word_tokenize(text, how='lemma'):
    words = text.split(' ')  # 按词分词
    return [stemmer.stem(word) for word in words]

word_tokenize("hello you are very interesting")
['hello', 'you', 'are', 'veri', 'interest']
vect = CountVectorizer(analyzer=word_tokenize)
_ = vect.fit_transform(X)
print(_.shape)   # 单词变小,特征少了
(99989, 154397)

TF-IDF矢量化

  • TF(term frequency,词频):衡量词在文档中出现的频率。由于文档的长度不同,词在长文中的出现次数有可能比在短文中出现的次数多得多。因此,一般会对词频进行归一化,用其除以文档长度或文档的总词数。

  • IDF(inverse document frequency,逆文档频率):衡量词的重要性。在计算词频时,我们认为所有的词都同等重要。但是某些词(如is、of和that)有可能出现很多次,但这些词并不重要。因此,我们需要减少常见词的权重,加大稀有词的权重。

再次强调,TfidfVectorizer和CountVectorizer相同,都从词项构造了特征,但是TfidfVectorizer进一步将词项计数按照在语料库中出现的频率进行了归一化

from sklearn.feature_extraction.text import TfidfVectorizer

# 用CountVectorizer生成文档-词矩阵
vect = CountVectorizer()
_ = vect.fit_transform(X)
print(_.shape, _[0,:].mean())
(99989, 105849) 6.613194267305311e-05

# TfidfVectorizer
vect = TfidfVectorizer()
_ = vect.fit_transform(X)
print(_.shape, _[0,:].mean())  # 行列数相同,内容不同
(99989, 105849) 2.1863060975751192e-05

在机器学习流水线中使用文本

from sklearn.naive_bayes import MultinomialNB

# 取空准确率
y.value_counts(normalize=True)
1    0.564632
0    0.435368
Name: Sentiment, dtype: float64

要让准确率超过56.5%。我们分两步创建流水线:

  • 用CountVectorizer将推文变成特征
  • 用朴素贝叶斯模型MultiNomialNB进行正负面情绪的分类
# 设置流水线参数
pipe_params = {'vect__ngram_range':[(1, 1), (1, 2)], 'vect__max_features':[1000, 10000], 'vect__stop_words':[None, 'english']}
 
# 实例化流水线
pipe = Pipeline([('vect', CountVectorizer()), ('classify', MultinomialNB())])
 
# 实例化网格搜索
grid = GridSearchCV(pipe, pipe_params)
# 拟合网格搜索对象
grid.fit(X, y)
 
# 取结果
print(grid.best_score_, grid.best_params_)
0.7558931564507154 {'vect__max_features': 10000, 'vect__ngram_range': (1, 2), 'vect__stop_words': None}

scikit-learn有一个FeatureUnion模块,可以水平(并排)排列特征。这样,在一个流水线中可以使用多种类型的文本特征构建器。

from sklearn.pipeline import FeatureUnion

# 单独的特征构建器对象
featurizer = FeatureUnion([('tfidf_vect', TfidfVectorizer()), 
                           ('count_vect', CountVectorizer())])

_ = featurizer.fit_transform(X)
print(_.shape) # 行数相同,但列数为2倍
(99989, 211698)

# 改变featurizer对象的参数,看看效果
featurizer.set_params(tfidf_vect__max_features=100, count_vect__ngram_range=(1, 2), count_vect__max_features=300)
# TfidfVectorizer只保留100个单词,而CountVectorizer保留300个1~2个单词的短语
_ = featurizer.fit_transform(X)
print(_.shape) # 行数相同,但列数为2倍
(99989, 400)

# 完整的流水线
pipe_params = {'featurizer__count_vect__ngram_range':[(1, 1), (1, 2)], 
               'featurizer__count_vect__max_features':[1000, 10000], 
               'featurizer__count_vect__stop_words':[None, 'english'],
               'featurizer__tfidf_vect__ngram_range':[(1, 1), (1, 2)],
               'featurizer__tfidf_vect__max_features':[1000, 10000], 
               'featurizer__tfidf_vect__stop_words':[None, 'english']} 

pipe = Pipeline([('featurizer', featurizer), ('classify', MultinomialNB())])
grid = GridSearchCV(pipe, pipe_params)

grid.fit(X, y)
print(grid.best_score_, grid.best_params_)
0.7580933924767184 {'featurizer__count_vect__max_features': 10000, 'featurizer__count_vect__ngram_range': (1, 2), 'featurizer__count_vect__stop_words': None, 'featurizer__tfidf_vect__max_features': 10000, 'featurizer__tfidf_vect__ngram_range': (1, 1), 'featurizer__tfidf_vect__stop_words': 'english'}

3、代码解析

3.1 自定义分类填充器代码解析

这段代码实现了一个自定义的分类填充器,用于填充数据集中某些列中的缺失值。下面是对代码的详细解析:

1. 导入必要的库

from sklearn.base import TransformerMixin  
  • TransformerMixin 是 Scikit-learn 中的一个混入类(Mixin),它允许你方便地创建自定义的转换器(Transformer)。通过继承 TransformerMixin,你可以轻松实现 fittransform 方法,并且让你的自定义类兼容 Scikit-learn 的流水线(Pipeline)。

2. 定义 CustomCategoryImputer

class CustomCategoryImputer(TransformerMixin):  
    def __init__(self, cols=None):  
        self.cols = cols  
  • 这是一个自定义的类,用于填充指定列的缺失值。
  • __init__ 方法是类的构造函数,用来初始化对象。当你创建 CustomCategoryImputer 的实例时,cols 参数允许你指定需要填充的列。如果没有指定列,cols 默认值为 None

3. 定义 transform 方法

def transform(self, df):  
    X = df.copy()  
    for col in self.cols:  
        X[col].fillna(X[col].value_counts().index[0], inplace=True)  
    return X  
  • transform 方法用于执行实际的转换操作。
  • 首先,它创建了数据框 df 的副本 X,以确保不修改原始数据。
  • 接下来,它对指定的列(即 self.cols 中的每一列)进行遍历,并使用 fillna 方法填充缺失值。填充值是该列中最常见的值(通过 value_counts().index[0] 获取)。
  • 最后,transform 方法返回经过填充处理后的数据框。

4. 定义 fit 方法

def fit(self, *_):  
    return self  
  • fit 方法通常用于在转换器中进行一些拟合操作,比如计算平均值、标准差等,但在这个类中,fit 方法什么都不做,直接返回 self,表示这个转换器不需要进行拟合操作。
  • 在流水线中,fit 方法必须存在,因此尽管它在这个例子中什么也不做,依然需要定义。

5. 使用自定义填充器

cci = CustomCategoryImputer(cols=['city', 'boolean'])  
cci.fit_transform(X)  
  • cciCustomCategoryImputer 类的一个实例,指定了要填充的列为 ['city', 'boolean']
  • fit_transform(X) 方法首先调用 fit 方法(在这个例子中并未做任何操作),然后调用 transform 方法对 X 数据框中的 cityboolean 列进行缺失值填充。

总结:
这段代码定义了一个简单的自定义填充器,用于填充数据框中指定列的缺失值,填充值为该列中出现频率最高的值。这种方法常用于分类特征的数据预处理,特别是在数据清理的阶段。

3.2 自定义定量填充器代码解析

这段代码扩展了之前的自定义填充器概念,创建了一个针对定量(数值)数据的自定义填充器,并将其与之前的分类填充器结合起来,通过 Pipeline 进行流水线处理。以下是详细解析:

1. 导入必要的库

from sklearn.impute import SimpleImputer  
from sklearn.base import TransformerMixin  
  • SimpleImputer 是 Scikit-learn 中的一个类,用于处理缺失值填充。它提供了多种策略(如均值、中位数、众数等)来填充缺失值。
  • TransformerMixin 允许我们方便地创建自定义的转换器,继承这个类可以让自定义类与 Scikit-learn 的其他组件兼容。

2. 定义 CustomQuantitativeImputer

class CustomQuantitativeImputer(TransformerMixin):  
    def __init__(self, cols=None, strategy='mean'):  
        self.cols = cols  
        self.strategy = strategy  
  • CustomQuantitativeImputer 是一个自定义的类,用于填充数值列的缺失值。
  • cols 参数指定要填充的列,如果没有指定,默认值为 None
  • strategy 参数指定填充策略,默认是 'mean',即用均值填充。这个参数可以设置为 'mean''median'(中位数)、'most_frequent'(众数)等。

3. 定义 transform 方法

def transform(self, df):  
    X = df.copy()  
    impute = SimpleImputer(strategy=self.strategy)  
    for col in self.cols:  
        X[col] = impute.fit_transform(X[[col]])  
    return X  
  • transform 方法用于执行实际的填充操作。
  • 首先,创建数据框 df 的副本 X,以确保不修改原始数据。
  • 然后,初始化 SimpleImputer 对象 impute,并使用指定的 strategy 进行填充。
  • 接下来,遍历指定的列(self.cols),并对每一列应用 SimpleImputerfit_transform 方法进行缺失值填充。
  • 最后,返回经过填充处理后的数据框。

4. 定义 fit 方法

def fit(self, *_):  
    return self  
  • fit 方法通常用于在转换器中进行拟合操作,但在这个类中,fit 方法什么都不做,直接返回 self。这是为了与 Scikit-learn 的流水线兼容。

5. 使用自定义定量填充器

cqi = CustomQuantitativeImputer(cols=['quantitative_column'], strategy='mean')  
cqi.fit_transform(X)  
  • cqiCustomQuantitativeImputer 类的一个实例,指定要填充的列为 ['quantitative_column'],填充策略为 'mean'(均值)。
  • fit_transform(X) 方法首先调用 fit 方法(未做任何操作),然后调用 transform 方法对 X 数据框中的 quantitative_column 列进行缺失值填充。

6. 创建和使用 Pipeline

from sklearn.pipeline import Pipeline  

imputer = Pipeline([('quant', cqi), ('category', cci)])  
imputer.fit_transform(X)  
  • Pipeline 是 Scikit-learn 提供的一种工具,用于将多个数据预处理步骤按顺序组合在一起。
  • Pipeline 的参数是一个包含步骤的列表,每个步骤都是一个二元组(名称, 转换器)。在这个例子中,Pipeline 包含两个步骤:第一个是 cqi(自定义定量填充器),第二个是 cci(自定义分类填充器)。
  • fit_transform(X) 方法会按顺序调用 Pipeline 中每个步骤的 fit_transform 方法。首先,使用 cqi 对数值列进行填充,然后使用 cci 对分类列进行填充。

总结
这段代码通过定义自定义的定量填充器,并将其与之前定义的分类填充器结合,创建了一个完整的预处理流水线。这个流水线可以处理数据框中的数值列和分类列,填充它们的缺失值,使得数据可以进一步用于建模或分析。使用 Pipeline 使得预处理步骤的组织更加清晰,并且能够很方便地扩展和调整。

3.3 自定义虚拟变量编码器

这段代码实现了一个自定义的虚拟变量编码器,用于将指定的分类变量转换为虚拟变量(即one-hot编码)。下面是详细解析:

1. 使用 pd.get_dummies 进行虚拟变量编码

pd.get_dummies(X,  
               columns=['city', 'boolean'],  # 要虚拟化的列  
               prefix_sep='__')  # 前缀(列名)和单元格值之间的分隔符  
  • pd.get_dummies 是 Pandas 中用于进行虚拟变量编码的函数。它将指定的列(在 columns 参数中指定)转换为多个二进制列。
  • columns=['city', 'boolean'] 指定了要进行编码的列,这两列中的每个独立值都会变成一个新的二进制列。
  • prefix_sep='__' 设置了新列名的前缀和实际值之间的分隔符。例如,如果 city 列中有一个值 NewYork,则对应的虚拟变量列名可能为 city__NewYork

2. 定义 CustomDummifier

class CustomDummifier(TransformerMixin):  
    def __init__(self, cols=None):  
        self.cols = cols  
  • CustomDummifier 是一个自定义类,用于实现虚拟变量编码。
  • cols 参数用于指定要进行虚拟变量编码的列,如果不指定,默认值为 None

3. 定义 transform 方法

def transform(self, X):  
    return pd.get_dummies(X, columns=self.cols)  
  • transform 方法用于执行虚拟变量编码操作。
  • 直接调用 pd.get_dummies 函数,将指定的列(self.cols)进行虚拟变量编码,并返回转换后的数据框。

4. 定义 fit 方法

def fit(self, *_):  
    return self  
  • fit 方法在这个类中没有实际操作,仅返回 self。这是为了使这个类与 Scikit-learn 的流水线兼容。

5. 使用自定义虚拟变量编码器

cd = CustomDummifier(cols=['boolean', 'city'])  
cd.fit_transform(X)  
  • cdCustomDummifier 类的一个实例,指定要进行编码的列为 ['boolean', 'city']
  • 调用 fit_transform(X) 方法会对 X 数据框中的 booleancity 列进行虚拟变量编码,返回转换后的数据框。

总结

  • 这个自定义虚拟变量编码器 (CustomDummifier) 使用了 Pandas 的 pd.get_dummies 函数来实现虚拟变量编码。它通过 Scikit-learn 的 TransformerMixin 使得该类能够与 Scikit-learn 的流水线兼容。这样可以将数据预处理的各个步骤(如缺失值填充、虚拟变量编码等)组合在一起,形成一个完整的流水线,以便于数据的预处理和建模。

3.4 自定义定序等级编码器

这段代码实现了一个自定义的定序等级编码器,用于将顺序变量(ordinal variable)按照指定的顺序映射为整数值。以下是详细解析:

1. 创建顺序映射

ordering = ['dislike', 'somewhat like', 'like']  # 0是dislike,1是somewhat like,2是like  
  • 这里定义了一个顺序列表 ordering,表示某个顺序变量(如用户喜好)的等级顺序。
  • 在这个列表中,'dislike' 被映射为 0'somewhat like' 被映射为 1'like' 被映射为 2。这个列表的顺序决定了最终映射的编码。

2. 将顺序映射应用到数据

print(X['ordinal_column'].map(lambda x: ordering.index(x)))  
  • 这行代码使用 map 函数将 ordering 列表中的顺序映射到 X 数据框的 ordinal_column 列。
  • lambda x: ordering.index(x) 表示对每个元素 x,找到它在 ordering 列表中的索引,从而实现顺序编码。
  • 这个过程将原始的顺序变量转换为对应的整数值,并打印结果。

3. 定义 CustomEncoder

class CustomEncoder(TransformerMixin):  
    def __init__(self, col, ordering=None):  
        self.ordering = ordering  
        self.col = col  
  • CustomEncoder 是一个自定义类,用于将顺序变量按照指定的顺序进行编码。
  • col 参数指定要进行编码的列。
  • ordering 参数用于指定顺序映射列表。如果没有指定,默认值为 None

4. 定义 transform 方法

def transform(self, df):  
    X = df.copy()  
    X[self.col] = X[self.col].map(lambda x: self.ordering.index(x))  
    return X  
  • transform 方法用于执行顺序编码操作。
  • 首先,创建数据框 df 的副本 X,以确保不修改原始数据。
  • 然后,使用 map 函数和 lambda 函数将 self.col 列中的值映射为对应的整数值。
  • 最后,返回经过编码处理后的数据框。

5. 定义 fit 方法

def fit(self, *_):  
    return self  
  • fit 方法在这个类中没有实际操作,仅返回 self。这是为了使这个类与 Scikit-learn 的流水线兼容。

3.5 使用自定义定序等级编码器

ce = CustomEncoder(col='ordinal_column', ordering=['dislike', 'somewhat like', 'like'])  

ce.fit_transform(X)  
  • ceCustomEncoder 类的一个实例,指定要进行编码的列为 'ordinal_column',并且使用 ordering=['dislike', 'somewhat like', 'like'] 作为顺序映射。
  • 调用 fit_transform(X) 方法会对 X 数据框中的 ordinal_column 列进行顺序编码,返回转换后的数据框。

总结

  • 这个自定义定序等级编码器 (CustomEncoder) 是一个简单而有效的方法,可以将顺序变量(ordinal variables)映射为整数值。通过定义顺序映射列表 ordering,你可以灵活地控制编码的顺序。
  • 该编码器通过继承 TransformerMixin,可以与 Scikit-learn 的流水线兼容,因此可以方便地与其他数据预处理步骤组合在一起使用。这种编码方法适用于顺序变量的处理,使得这些变量可以直接用于模型训练。

4、闯关题

Q1. (判断题)对分类数据进行编码,可以转化为数值数据?

Q2. (判断题)使用词袋法可以将文本特征转化为数值特征?

Q3. (判断题)处理数值数据时,使用多项式方法不能创造新的特征?

#填入你的答案并运行,注意大小写
a1 = 'T'  # 如 a1= 'T/F'
a2 = 'T'  # 如 a2= 'T/F'
a3 = 'F'  # 如 a3= 'T/F'

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值