文章目录
- Scikit-learn与特征工程
- 数据的特征工程
- 特征工程的意义
- 特征工程之特征处理
- 特征工程之特征抽取与特征选择
- 数据的来源与类型
- 数据的类型
- 数据的特征抽取
- TF-IDF
Scikit-learn与特征工程
“数据决定了机器学习的上限,而算法只是尽可能逼近这个上限”,这句话很好的阐述了数据在机器学习中的重要性。大部分直接拿过来的数据都是特征不明显的、没有经过处理的或者说是存在很多无用的数据,那么需要进行一些特征处理,特征的缩放等等,满足训练数据的要求。
数据的特征工程
从数据中抽取出来的对预测结果有用的信息,通过专业的技巧进行数据处理,使得特征能在机器学习算法中发挥更好的作用。优质的特征往往描述了数据的固有结构。 最初的原始特征数据集可能太大,或者信息冗余,因此在机器学习的应用中,一个初始步骤就是选择特征的子集,或构建一套新的特征集,减少功能来促进算法的学习,提高泛化能力和可解释性。
特征工程的意义
- 更好的特征意味着更强的鲁棒性
- 更好的特征意味着只需用简单模型
- 更好的特征意味着更好的结果
特征工程之特征处理
特征工程中最重要的一个环节就是特征处理,特征处理包含了很多具体的专业技巧
- 特征预处理
- 单个特征
- 归一化
- 标准化
- 缺失值
- 多个特征
- 降维
- PCA
特征工程之特征抽取与特征选择
如果说特征处理其实就是在对已有的数据进行运算达到我们目标的数据标准。特征抽取则是将任意数据格式(例如文本和图像)转换为机器学习的数字特征。而特征选择是在已有的特征中选择更好的特征。后面会详细介绍特征选择主要区别于降维。
数据的来源与类型
大部分的数据都来自已有的数据库,如果没有的话也可以交给很多爬虫工程师去采集,来提供。也可以来自平时的记录,反正数据无处不在,大都是可用的。
数据的类型
按照机器学习的数据分类我们可以将数据分成:
- 标称型:标称型目标变量的结果只在有限目标集中取值,如真与假(标称型目标变量主要用于分类)
- 数值型:数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 (数值型目标变量主要用于回归分析)
按照数据的本身分布特性
- 离散型
- 连续型
那么什么是离散型和连续型数据呢?首先连续型数据是有规律的,离散型数据是没有规律的
- 离散变量是指其数值只能用自然数或整数单位计算的则为离散变量.例如,班级人数、进球个数、是否是某个类别等等
- 连续型数据是指在指定区间内可以是任意一个数值,例如,票房数据、花瓣大小分布数据
数据的特征抽取
现实世界中多数特征都不是连续变量,比如分类、文字、图像等,为了对非连续变量做特征表述,需要对这些特征做数学化表述,因此就用到了特征提取
. sklearn.feature_extraction提供了特征提取的很多方法
DictVectorizer语法
- DictVectorizer(sparse=True,…)
- DictVectorizer.fit_transform(X)
X:字典或者包含字典的迭代器 返回值:返回sparse矩阵
- DictVectorizer.inverse_transform(X)
X:array数组或者sparse矩阵 返回值:转换之前数据格式
- DictVectorizer.get_feature_names()
返回类别名称
- DictVectorizer.transform(X)
按照原先的标准转换
分类特征变量提取
我们将城市和环境作为字典数据,来进行特征的提取。
sklearn.feature_extraction.DictVectorizer(sparse = True)
将映射列表转换为Numpy数组或scipy.sparse矩阵
- sparse 是否转换为scipy.sparse矩阵表示,默认开启
方法
fit_transform(X,y)
应用并转化映射列表X,y为目标类型
inverse_transform(X[, dict_type])
将Numpy数组或scipy.sparse矩阵转换为映射列表
from sklearn.feature_extraction import DictVectorizer
# 字典数据抽取
def dictvec():
# 实例化
dict = DictVectorizer(sparse=False)
# 调用fit_transform
data = dict.fit_transform([{'city': '北京','temperature':100},{'city': '上海','temperature':60}, {'city': '深圳','temperature':30}])
print(data)
return None
if __name__=="__main__":
dictvec()
运行结果:
文本特征抽取
- 作用:对文本数据进行特征值化
- 类:sklearn.feature_extraction.text.CountVectorizer
CountVectorizer语法
- CountVectorizer(max_df=1.0,min_df=1,…)
返回词频矩阵 - CountVectorizer.fit_transform(X,y)
X:文本或者包含文本字符串的可迭代对象
返回值:返回sparse矩阵 - CountVectorizer.inverse_transform(X)
X:array数组或者sparse矩阵
返回值:转换之前数据格式 - CountVectorizer.get_feature_names()
返回值:单词列表 - 注意返回格式,利用toarray()进行sparse矩阵转换array数组
from sklearn.feature_extraction.text import CountVectorizer
import jieba
def countvec():
# 创建实例
cv = CountVectorizer()
# 提取特征数据
data = cv.fit_transform(["life is short,i like python,like python","life is too long,i dislike python"])
# print(data)
print(cv.get_feature_names())
print(data.toarray())
return None
def cutword():
# 调用jieba库分词
con1 = jieba.cut("今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。")
con2 = jieba.cut("我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。")
con3 = jieba.cut("如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。")
# 转换成列表
content1 = list(con1)
content2 = list(con2)
content3 = list(con3)
# 把列表转换成字符串
c1 = ' '.join(content1)
c2 = ' '.join(content2)
c3 = ' '.join(content3)
return c1,c2,c3
def hanzivec():
# 获取分词结果
c1,c2,c3 = cutword()
print(c1,c2,c3)
# 创建实例
cv = CountVectorizer()
# 提取特征数据
data = cv.fit_transform([c1,c2,c3])
# 输出特征值对应的名称列表
print(cv.get_feature_names())
# 装换成数组
print(data.toarray())
if __name__=="__main__":
countvec()
hanzivec()
运行结果
TF-IDF
- TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的概率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
- TF-IDF作用:用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。
- 类:sklearn.feature_extraction.text.TfidfVectorizer
TfidfVectorizer语法
- TfidfVectorizer(stop_words=None,…)
返回词的权重矩阵 - TfidfVectorizer.fit_transform(X,y)
X:文本或者包含文本字符串的可迭代对象
返回值:返回sparse矩阵 - TfidfVectorizer.inverse_transform(X)
X:array数组或者sparse矩阵
返回值:转换之前数据格式 - TfidfVectorizer.get_feature_names()
返回值:单词列表
from sklearn.feature_extraction.text import TfidfVectorizer
import jieba
def cutword():
# 调用jieba库分词
con1 = jieba.cut("今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。")
con2 = jieba.cut("我们看到的从很远星系来的光是在几百万年之前发出的,这样当我们看到宇宙时,我们是在看它的过去。")
con3 = jieba.cut("如果只用一种方式了解某样事物,你就不会真正了解它。了解事物真正含义的秘密取决于如何将其与我们所了解的事物相联系。")
# 转换成列表
content1 = list(con1)
content2 = list(con2)
content3 = list(con3)
# 把列表转换成字符串
c1 = ' '.join(content1)
c2 = ' '.join(content2)
c3 = ' '.join(content3)
return c1,c2,c3
def tfidfvec():
# 获取分词结果
c1,c2,c3 = cutword()
print(c1,c2,c3)
# 创建实例
tf = TfidfVectorizer()
# 提取特征值
data = tf.fit_transform([c1,c2,c3])
# 输出特征值对应的名称列表
print(tf.get_feature_names())
# 转换成数组
print(data.toarray())
if __name__=="__main__":
tfidfvec()
运行结果
数据的特征预处理
单个特征
(1)归一化
归一化首先在特征(维度)非常多的时候,可以防止某一维或某几维对数据影响过大,也是为了把不同来源的数据统一到一个参考区间下,这样比较起来才有意义,其次可以程序可以运行更快。 例如:一个人的身高和体重两个特征,假如体重50kg,身高175cm,由于两个单位不一样,数值大小不一样。如果比较两个人的体型差距时,那么身高的影响结果会比较大,k-临近算法会有这个距离公式。
min-max方法
常用的方法是通过对原始数据进行线性变换把数据映射到[0,1]之间,变换的函数为:
mx,mi分别为指定区间值默认mx为1,mi为0
其中min是样本中最小值,max是样本中最大值,注意在数据流场景下最大值最小值是变化的,另外,最大值与最小值非常容易受异常点影响,所以这种方法鲁棒性较差,只适合传统精确小数据场景。
- min-max自定义处理
这里我们使用相亲约会对象数据在MatchData.txt,这个样本时男士的数据,三个特征,玩游戏所消耗时间的百分比、每年获得的飞行常客里程数、每周消费的冰淇淋公升数。然后有一个 所属类别,被女士评价的三个类别,不喜欢、魅力一般、极具魅力。 首先导入数据进行矩阵转换处理
import numpy as np
def data_matrix(file_name):
"""
将文本转化为matrix
:param file_name: 文件名
:return: 数据矩阵
"""
fr = open(file_name)
array_lines = fr.readlines()
number_lines = len(array_lines)
return_mat = zeros((number_lines, 3))
# classLabelVector = []
index = 0
for line in array_lines:
line = line.strip()
list_line = line.split('\t')
return_mat[index,:] = list_line[0:3]
# if(listFromLine[-1].isdigit()):
# classLabelVector.append(int(listFromLine[-1]))
# else:
# classLabelVector.append(love_dictionary.get(listFromLine[-1]))
# index += 1
return return_mat
输出结果为
[[ 4.09200000e+04 8.32697600e+00 9.53952000e-01]
[ 1.44880000e+04 7.15346900e+00 1.67390400e+00]
[ 2.60520000e+04 1.44187100e+00 8.05124000e-01]
...,
[ 2.65750000e+04 1.06501020e+01 8.66627000e-01]
[ 4.81110000e+04 9.13452800e+00 7.28045000e-01]
[ 4.37570000e+04 7.88260100e+00 1.33244600e+00]]
我们查看数据集会发现,有的数值大到几万,有的才个位数,同样如果计算两个样本之间的距离时,其中一个影响会特别大。也就是说飞行里程数对于结算结果或者说相亲结果影响较大,但是统计的人觉得这三个特征同等重要,所以需要将数据进行这样的处理。
这样每个特征任意的范围将变成[0,1]的区间内的值,或者也可以根据需求处理到[-1,1]之间,我们再定义一个函数,进行这样的转换。
def feature_normal(data_set):
"""
特征归一化
:param data_set:
:return:
"""
# 每列最小值
min_vals = data_set.min(0)
# 每列最大值
max_vals = data_set.max(0)
ranges = max_vals - min_vals
norm_data = np.zeros(np.shape(data_set))
# 得出行数
m = data_set.shape[0]
# 矩阵相减
norm_data = data_set - np.tile(min_vals, (m,1))
# 矩阵相除
norm_data = norm_data/np.tile(ranges, (m, 1)))
return norm_data
输出结果为
[[ 0.44832535 0.39805139 0.56233353]
[ 0.15873259 0.34195467 0.98724416]
[ 0.28542943 0.06892523 0.47449629]
...,
[ 0.29115949 0.50910294 0.51079493]
[ 0.52711097 0.43665451 0.4290048 ]
[ 0.47940793 0.3768091 0.78571804]]
这样得出的结果都非常相近,这样的数据可以直接提供测试验证了
- min-max的scikit-learn处理
scikit-learn.preprocessing中的类MinMaxScaler,将数据矩阵缩放到[0,1]之间
>>> X_train = np.array([[ 1., -1., 2.],
... [ 2., 0., 0.],
... [ 0., 1., -1.]])
...
>>> min_max_scaler = preprocessing.MinMaxScaler()
>>> X_train_minmax = min_max_scaler.fit_transform(X_train)
>>> X_train_minmax
array([[ 0.5 , 0. , 1. ],
[ 1. , 0.5 , 0.33333333],
[ 0. , 1. , 0. ]])
(3)标准化
常用的方法是z-score标准化,经过处理后的数据均值为0,标准差为1,处理方法是:
std为方差
其中μ是样本的均值,σ是样本的标准差,它们可以通过现有的样本进行估计,在已有的样本足够多的情况下比较稳定,适合嘈杂的数据场景
sklearn中提供了StandardScalar类实现列标准化:
In [2]: import numpy as np
In [3]: X_train = np.array([[ 1., -1., 2.],[ 2., 0., 0.],[ 0., 1., -1.]])
In [4]: from sklearn.preprocessing import StandardScaler
In [5]: std = StandardScaler()
In [6]: X_train_std = std.fit_transform(X_train)
In [7]: X_train_std
Out[7]:
array([[ 0. , -1.22474487, 1.33630621],
[ 1.22474487, 0. , -0.26726124],
[-1.22474487, 1.22474487, -1.06904497]])
(3)缺失值
由于各种原因,许多现实世界的数据集包含缺少的值,通常编码为空白,NaN或其他占位符。然而,这样的数据集与scikit的分类器不兼容,它们假设数组中的所有值都是数字,并且都具有和保持含义。使用不完整数据集的基本策略是丢弃包含缺失值的整个行和/或列。然而,这是以丢失可能是有价值的数据(即使不完整)的代价。更好的策略是估算缺失值,即从已知部分的数据中推断它们。
(1)填充缺失值 使用sklearn.impute 中的 SimpleImputer 类进行数据的填充
>>> import numpy as np
>>> from sklearn.impute import SimpleImputer
>>> imp = SimpleImputer(missing_values=np.nan, strategy='mean')
>>> imp.fit([[1, 2], [np.nan, 3], [7, 6]])
SimpleImputer()
>>> X = [[np.nan, 2], [6, np.nan], [7, 6]]
>>> print(imp.transform(X))
[[ 4. 2. ]
[ 6. 3.666...]
[ 7. 6. ]]
from sklearn.preprocessing import MinMaxScaler,StandardScaler
from sklearn.impute import SimpleImputer
import numpy as np
# 归一化
def mm():
# 创建实例(指定归一化范围)
mm = MinMaxScaler(feature_range=(2,3))
# 归一化处理
data = mm.fit_transform([[90,2,10,40],[60,4,15,45],[75,3,13,46]])
print(data)
return None
# 标准化
def stand():
# 创建实例
std = StandardScaler()
# 标准化处理
data = std.fit_transform([[ 1., -1., 3.],[ 2., 4., 2.],[ 4., 6., -1.]])
print(data)
return None
# 缺失数据处理
def im():
# 实例化 (np.nan用相应列的均值填充)
im = SimpleImputer(missing_values=np.nan,strategy='mean')
# 填充
# data2 = im.transform([[1, 2], [np.nan, 3], [7, 6]])
data2 = im.fit_transform([[1, 2], [np.nan, 3], [7, 6]])
print(data2)
return None
if __name__=="__main__":
mm()
stand()
im()
运行结果:
多个特征
降维
PCA(Principal component analysis),主成分分析。特点是保存数据集中对方差影响最大的那些特征,PCA极其容易受到数据中特征范围影响,所以在运用PCA前一定要做特征标准化,这样才能保证每维度特征的重要性等同。
sklearn.decomposition.PCA
class PCA(sklearn.decomposition.base)
"""
主成成分分析
:param n_components: int, float, None or string
这个参数可以帮我们指定希望PCA降维后的特征维度数目。最常用的做法是直接指定降维到的维度数目,此时n_components是一个大于1的整数。
我们也可以用默认值,即不输入n_components,此时n_components=min(样本数,特征数)
:param whiten: bool, optional (default False)
判断是否进行白化。所谓白化,就是对降维后的数据的每个特征进行归一化。对于PCA降维本身来说一般不需要白化,如果你PCA降维后有后续的数据处理动作,可以考虑白化,默认值是False,即不进行白化
:param svd_solver:
选择一个合适的SVD算法来降维,一般来说,使用默认值就够了。
"""
In [1]: import numpy as np
In [2]: from sklearn.decomposition import PCA
In [3]: x = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
# n_components为小数时,表示主成分析后保留多少数据
# 整数时,表示减少到的特征数量
In [4]: pca = PCA(n_components=0.95)
In [5]: pca.fit_transform(x)
Out[5]:
array([[ 1.38340578],
[ 2.22189802],
[ 3.6053038 ],
[-1.38340578],
[-2.22189802],
[-3.6053038 ]])
数据的特征选择
降维本质上是从一个维度空间映射到另一个维度空间,特征的多少别没有减少,当然在映射的过程中特征值也会相应的变化。举个例子,现在的特征是1000维,我们想要把它降到500维。降维的过程就是找个一个从1000维映射到500维的映射关系。原始数据中的1000个特征,每一个都对应着降维后的500维空间中的一个值。假设原始特征中有个特征的值是9,那么降维后对应的值可能是3。而对于特征选择来说,有很多方法:
- Filter(过滤式):VarianceThreshold
- Embedded(嵌入式):正则化、决策树
- Wrapper(包裹式)
其中过滤式的特征选择后,数据本身不变,而数据的维度减少。而嵌入式的特征选择方法也会改变数据的值,维度也改变。Embedded方式是一种自动学习的特征选择方法,后面讲到具体的方法的时候就能理解了。
特征选择主要有两个功能:
(1)减少特征数量,降维,使模型泛化能力更强,减少过拟合
(2)增强特征和特征值之间的理解
sklearn.feature_selection
去掉取值变化小的特征(删除低方差特征)
VarianceThreshold 是特征选择中的一项基本方法。它会移除所有方差不满足阈值的特征。默认设置下,它将移除所有方差为0的特征,即那些在所有样本中数值完全相同的特征。
假设我们要移除那些超过80%的数据都为1或0的特征
from sklearn.feature_selection import VarianceThreshold
# 实例化(threshold指定方差的大小,比指定方差小的特征值将被删除)
vac = VarianceThreshold(threshold=0)
data = vac.fit_transform([[0, 2, 0, 3], [0, 1, 4, 3], [0, 1, 1, 3]])
print(data)
[[2 0]
[1 4]
[1 1]]
交叉集(特殊的分组工具):
In [16]: x
Out[16]:
X Y Z W
A 1 1 2 3
B 1 5 6 7
C 8 9 10 11
In [17]: pd.crosstab(x["X"],x["Y"])
Out[17]:
Y 1 5 9
X
1 1 1 0
8 0 0 1