前提
数据预处理 针对 结构化数据(数据库存储模式)和非结构化数据(文字、视频、图像等)有各种不同独特方式处理。如图像的旋转、裁剪等,文字的提取分词、向量化等。
这里仅讨论 结构化数据 的 通用预处理 ,部分也可适用于非结构化数据,具体使用根据实际情况而定。
—— 数据预处理(Python版) ——
文章目录
0. 导入需要的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
1. 加载数据
1.1 根据文件类型(常用 csv、txt、xlsx), 使用对应读取方式
data_csv = pd.read_csv(r'../../data/practice/insurance.csv') #读取csv文件
data = pd.read_table(r'../../data/practice/TestFile.txt') #读取txt文件
data = pd.read_excel(r'../../data/practice/excelTest.xlsx') #读取excel文件
house_data = pd.read_csv(r'../../data/practice/housing.csv')
1.2 直接读取数据库
# 导入数据库模块
import pymysql
#创建游标
cur = conn.cursor()
#连接数据库,注意修改成要连的数据库信息
conn = pymysql.connect(host='localhost',user='root',passwd='12345',db='mydb')
#train_data是要读取的数据名
cur.execute("select * from train_data limit 100")
#获取数据
data = cur.fetchall()
#获取列名
cols = cur.description
#执行
conn.commit()
#关闭游标
cur.close()
#关闭数据库连接
conn.close()
col = []
for i in cols:
col.append(i[0])
data = list(map(list,data))
data = pd.DataFrame(data,columns=col)
1.3 CSV文件合并
当数据可能分布在一个个csv或者txt文档中时,可以将一个个文档合并到一个文件
#合并多个csv文件成一个文件
import glob
#合并
def file_merge():
csv_list = glob.glob('*.csv') #查看同文件夹下的csv文件数
print(u'共发现%s个CSV文件'% len(csv_list))
print(u'正在处理............')
for i in csv_list: #循环读取同文件夹下的csv文件
fr = open(i,'rb').read()
with open('result.csv','ab') as f: #将结果保存为result.csv
f.write(fr)
print(u'合并完毕!')
#去重
def deduplicate(file):
df = pd.read_csv(file,header=0)
datalist = df.drop_duplicates()
datalist.to_csv(file)
if __name__ == '__main__':
file_merge()
deduplicate("result.csv.csv")
1.4 CSV文件拆分
数据量比较大的文件,直接读取或者打开比较困难,可以拆分数据,查看数据样式以及读取部分数据
##csv比较大,打不开,将其切分成一个个小文件,看数据形式
f = open('NEW_Data.csv','r') #打开大文件
i = 0 #设置计数器
#这里1234567表示文件行数,如果不知道行数可用每行长度等其他条件来判断
while i<1234567 :
with open('newfile'+str(i),'w') as f1:
for j in range(0,10000) : #这里设置每个子文件的大小
if i < 1234567: #这里判断是否已结束,否则最后可能报错
f1.writelines(f.readline())
i = i+1
else:
break
1.5 数据查看
显示前五行数据
data_csv.head()
age | sex | bmi | children | smoker | region | charges | |
---|---|---|---|---|---|---|---|
0 | 19 | female | 27.900 | 0.0 | yes | southwest | 16884.92400 |
1 | 18 | male | 33.770 | 1.0 | no | southeast | 1725.55230 |
2 | 28 | male | 33.000 | 3.0 | no | southeast | 4449.46200 |
3 | 33 | male | 22.705 | 0.0 | no | northwest | 21984.47061 |
4 | 32 | male | 28.880 | 0.0 | no | northwest | 3866.85520 |
显示末尾五行数据
data_csv.tail()
age | sex | bmi | children | smoker | region | charges | |
---|---|---|---|---|---|---|---|
1333 | 50 | male | 30.97 | 3.0 | no | northwest | 10600.5483 |
1334 | 18 | female | 31.92 | 0.0 | no | northeast | 2205.9808 |
1335 | 18 | female | 36.85 | 0.0 | no | southeast | 1629.8335 |
1336 | 21 | female | 25.80 | 0.0 | no | southwest | 2007.9450 |
1337 | 61 | female | 29.07 | 0.0 | yes | northwest | 29141.3603 |
查看各字段的信息
data_csv.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1338 entries, 0 to 1337
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 age 1338 non-null int64
1 sex 1338 non-null object
2 bmi 1336 non-null float64
3 children 1337 non-null float64
4 smoker 1338 non-null object
5 region 1338 non-null object
6 charges 1337 non-null float64
dtypes: float64(3), int64(1), object(3)
memory usage: 73.3+ KB
查看数据集有几行几列,data.shape[0]是行数,data.shape[1]是列数
data_csv.shape
(1338, 7)
查看数据的大体情况,均值,最值,分位数值…
data_csv.describe()
age | bmi | children | charges | |
---|---|---|---|---|
count | 1338.000000 | 1336.000000 | 1337.000000 | 1337.000000 |
mean | 39.207025 | 30.667362 | 1.095737 | 13274.901634 |
std | 14.049960 | 6.101880 | 1.205571 | 12113.433665 |
min | 18.000000 | 15.960000 | 0.000000 | 1121.873900 |
25% | 27.000000 | 26.272500 | 0.000000 | 4738.268200 |
50% | 39.000000 | 30.400000 | 1.000000 | 9386.161300 |
75% | 51.000000 | 34.700000 | 2.000000 | 16657.717450 |
max | 64.000000 | 53.130000 | 5.000000 | 63770.428010 |
得到列名的list
data_csv.columns.tolist()
['age', 'sex', 'bmi', 'children', 'smoker', 'region', 'charges']
寻找相关性
相关系数的范围从-1变化到1。
越接近1,表示有越强的正相关,例如,收入中位数。
越接近-1,表示有较强的负相关,例如,经纬度。
越接近0,表示说明二者之间没有线性相关性。
# 计算每对属性之间的 皮尔逊 相关系数
corr_matrix = house_data.corr()
# 查看 房价与各个数值属性的 相关性 (下降)
corr_matrix['median_house_value'].sort_values(ascending=False)
median_house_value 1.000000
median_income 0.688075
total_rooms 0.134153
housing_median_age 0.105623
households 0.065843
total_bedrooms 0.049686
population -0.024650
longitude -0.045967
latitude -0.144160
Name: median_house_value, dtype: float64
1.6 去除冗余
# 冗余处理
def re_check(df):
flag = []
for f in df.duplicated().values:
flag.append(~f)
df = df[flag]
return df
if __name__=='__main__':
df = pd.DataFrame({'a':['dog']*3+['fish']*3+['dog'],'b':[10,11,12,12,14,14,10]})
display(df)
df = re_check(df)
display(df)
a | b | |
---|---|---|
0 | dog | 10 |
1 | dog | 11 |
2 | dog | 12 |
3 | fish | 12 |
4 | fish | 14 |
5 | fish | 14 |
6 | dog | 10 |
a | b | |
---|---|---|
0 | dog | 10 |
1 | dog | 11 |
2 | dog | 12 |
3 | fish | 12 |
4 | fish | 14 |
2. 缺失值
现实获取的数据经常存在缺失和不完整的情况,一般会对这些缺失数据进行识别和处理
2.1 缺失值查看
#统计每列有几个缺失值
data_csv.isnull().sum()
age 0
sex 0
bmi 2
children 1
smoker 0
region 0
charges 1
dtype: int64
#找出存在缺失值的列
missing_col = data_csv.columns[data_csv.isnull().any()].tolist()
missing_col
['bmi', 'children', 'charges']
#统计每个变量的缺失值占比
def CountNA(data):
cols = data.columns.tolist() #cols为data的所有列名
n_df = data.shape[0] #n_df为数据的行数
for col in cols:
missing = np.count_nonzero(data[col].isnull().values) #col列中存在的缺失值个数
mis_perc = float(missing) / n_df * 100
print("{col}的缺失比例是{miss}%".format(col=col,miss=mis_perc))
CountNA(data_csv)
age的缺失比例是0.0%
sex的缺失比例是0.0%
bmi的缺失比例是0.14947683109118087%
children的缺失比例是0.07473841554559044%
smoker的缺失比例是0.0%
region的缺失比例是0.0%
charges的缺失比例是0.07473841554559044%
2.2 缺失值处理
面对缺失值,三种处理方法:不处理、删除以及填充
2.2.1 不处理
- 不涉及距离的算法(贝叶斯、xgboost、神经网络等)对缺失值不敏感;
- 有些字段对结果分析作用不大。
2.2.2 删除
在数据量比较大时候或者一条记录中多个字段缺失,不方便填补的时候可以选择删除缺失值
#axis= 0 代表'行','any'代表任何空值行,若是'all'则代表所有值都为空时,才删除该行
data.dropna(axis=0,how="any",inplace=True)
#删除带有空值的行
data.dropna(axis=0,inplace=True)
#删除带有空值的列
data.dropna(axis=1,inplace=True)
2.2.3 填充
数据量较少时候,以最可能的值来插补缺失值
- 单变量插补
- 多变量插补
可以多种方法结合使用,如众数填充文本缺失,然后用适合的数字型插补填充其他
2.2.3.1 单变量插补
from sklearn.impute import SimpleImputer
详细参数介绍参考
sklearn 单变量API介绍
2.2.3.1.1 固定值填充
简单不实用,看看就行
#缺失值全部用0插补
data = data.fillna(0)
#某列缺失值用固定值插补
data['col_name'] = data['col_name'].fillna('UNKNOWN')
2.2.3.1.2 众数填充
离散/连续数据都行,特别适用于非数值变量,如性别
# mode返回出现最多的数据,col_name为列名
freq_port = data.col_name.dropna().mode()[0]
#采用出现最频繁的值插补
data['col_name'] = data['col_name'].fillna(freq_port)
# 功能函数 寻找 描述变量
def seek_object_column(df):
"""
寻找描述变量列
将其存储到cat_vars这个list中
"""
cat_vars = []
cols = df.columns.tolist()
for col in cols:
if df[col].dtype == 'object':
cat_vars.append(col)
return cat_vars
# sklearn 简易方式
imp = SimpleImputer(strategy='most_frequent')
cat_vars = seek_object_column(data_csv)
data_csv[cat_vars] = imp.fit_transform(data_csv[cat_vars])
data_csv.head()
age | sex | bmi | children | smoker | region | charges | |
---|---|---|---|---|---|---|---|
0 | 19 | female | 27.900 | 0.0 | yes | southwest | 16884.92400 |
1 | 18 | male | 33.770 | 1.0 | no | southeast | 1725.55230 |
2 | 28 | male | 33.000 | 3.0 | no | southeast | 4449.46200 |
3 | 33 | male | 22.705 | 0.0 | no | northwest | 21984.47061 |
4 | 32 | male | 28.880 | 0.0 | no | northwest | 3866.85520 |
2.2.3.1.3 中位数/均值填充
数值型数据
中位数:适用于偏态分布或者有离群点的分布
均值:适用于正态分布
#中位数插补
data['col_name'].fillna(data['col_name'].dropna().median(),inplace=True)
# 均值插补
data['col_name'].fillna(data['col_name'].dropna().mean(),inplace=True)
# sklearn 简易方式
imp = SimpleImputer(strategy='mean')
data_num = imp.fit_transform(data_num)
# sklearn 简易方式
imp = SimpleImputer(strategy='median')
data_num = imp.fit_transform(data_num)
2.2.3.1.4 用前后数据填充
数值或字符串均可
#用前一个数据填充
data['col_name'] = data['col_name'].fillna(method='pad')
#用后一个数据填充
data['col_name'] = data['col_name'].fillna(method='bfill')
2.2.3.1.5 拉格朗日插值法
一般针对有序的数据,如带有时间列的数据集,且缺失值为连续型数值小批量数据
(连续超过5个数据误差会很大,不建议用)
from scipy.interpolate import lagrange
def lagrange_fill(data):
#自定义列向量插值函数,s为列向量,n为被插值的位置,k为取前后的数据个数,默认5
def ployinterp_columns(s, n, k=5):
y = s[list(range(n-k,n)) + list(range(n+1,n+1+k))] #取数
y = y[y.notnull()] #剔除空值
return lagrange(y.index, list(y))(n) #插值并返回插值结果
#逐个元素判断是否需要插值
for i in data.columns:
for j in range(len(data)):
if (data[i].isnull())[j]: #如果为空即插值
data[i][j] = ployinterp_columns(data[i],j)
return data
data = lagrange_fill(data_csv)
data.head()
C:\Users\Not a Literary Gaint\AppData\Local\Temp\ipykernel_19952\3195800559.py:14: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
data[i][j] = ployinterp_columns(data[i],j)
age | sex | bmi | children | smoker | region | charges | |
---|---|---|---|---|---|---|---|
0 | 19 | female | 27.900 | 0.0 | yes | southwest | 16884.92400 |
1 | 18 | male | 33.770 | 1.0 | no | southeast | 1725.55230 |
2 | 28 | male | 33.000 | 3.0 | no | southeast | 4449.46200 |
3 | 33 | male | 22.705 | 0.0 | no | northwest | 21984.47061 |
4 | 32 | male | 28.880 | 0.0 | no | northwest | 3866.85520 |
2.2.3.1.6 热卡插补
在完整数据中找到一个与它最相似的对象,用最相似的值填充当前值。
热卡填充其实就是使用KNN去预测的一种特殊形式,KNN是参考K个,而热卡填充是参考最近的1个。
from sklearn.impute import KNNImputer
详细参数介绍参考
sklearn 热卡插补API 介绍
imp = KNNImputer(n_neighbors=2, weights='uniform')
data_num = imp.fit_transform(data_num)
2.2.3.1.7 拟合插补
拟合插补法则是利用有监督的机器学习方法,
比如回归、最邻近、随机森林、支持向量机等模型,对缺失值作预测。
其优势在于预测的准确性高,缺点是需要大量的计算,导致缺失值的处理速度大打折扣。
回归插补
前提是 缺失值是连续的, 可以回归预测。
基于完整的数据集,建立回归方程。
对于包含空值的对象,将已知属性值代入方程来估计未知属性值,以此估计值来进行填充。当变量不是线性相关时会导致有偏差的估计。缺失值是连续的,即定量的类型,才可以使用回归来预测。
# 这里采用的是 Ridge 回归,也可以用其他回归
from sklearn.linear_model import Ridge
def lin_filled_func(x_train, y_train, test):
# params: x_train 为不含缺失值的属性数据
# params: y_train 为不含缺失值的标签数据
# params: test 为已知的属性值,用来预测
ridge = Ridge(alpha=0.01, max_iter=10000)
ridge.fit(x_train, y_train)
return ridge.predict(test)
最邻近填充
利用knn算法填充,其实是把目标列当做目标标量,利用非缺失的数据进行knn算法拟合,最后对目标列缺失进行预测。(对于连续特征一般是加权平均,对于离散特征一般是加权投票)。
KNN算法的详细介绍
sklearn KNN算法
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
def knn_filled_func(x_train, y_train, test, k = 3, dispersed = True):
# params: x_train 为目标列不含缺失值的数据(不包括目标列)
# params: y_train 为不含缺失值的目标列
# params: test 为目标列为缺失值的数据(不包括目标列)
if dispersed:
knn= KNeighborsClassifier(n_neighbors = k, weights = "distance")
else:
knn= KNeighborsRegressor(n_neighbors = k, weights = "distance")
knn.fit(x_train, y_train)
return test.index, knn.predict(test)
随机森林插补
随机森林算法填充的思想和knn填充是类似的,即利用已有数据拟合模型,对缺失变量进行预测。
随机森林详细介绍可参考
集成方法 的 树类API
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
def forest_filled_func(x_train, y_train, test, k = 3, dispersed = True):
# params: x_train 为目标列不含缺失值的数据(不包括目标列)
# params: y_train 为不含缺失值的目标列
# params: test 为目标列为缺失值的数据(不包括目标列)
if dispersed:
rf= RandomForestRegressor()
else:
rf= RandomForestClassifier()
rf.fit(x_train, y_train)
return test.index, rf.predict(test)
其他拟合方法类似,这里不再赘述。
2.2.3.2 多变量插补
多重插补(Mutiple imputation,MI)的思想来源于贝叶斯估计,认为待插补的值是随机的,它的值来自于已观测到的值。具体实践上通常是估计出待插补的值,然后再加上不同的噪声,形成多组可选插补值。根据某种选择依据,选取最合适的插补值。
详细参数介绍参考
sklearn多重插补 API介绍
sklearn多重插补 不同估算器比较
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imp = IterativeImputer(max_iter=10, random_state=0)
data_num = imp.fit_transform(data_num)
3. 异常值
异常值是指样本中的个别值,其数值明显偏离它所属样本的其余观测值。
异常值有时是记录错误或者其它情况导致的错误数据,有时是代表少数情况的正常值。
3.1 异常值识别
3.1.1 描述性统计法
与业务或者基本认知不符的数据,如年龄为负;
即根据属性值自身的范围进行筛选。
neg_list = ['col_name_1','col_name_2','col_name_3']
# 这里以 大于0 为 筛选条件(其他筛选条件可以根据实际情况照例编写)
# 可以先打印,在筛选
# 小于零的打印结果
for item in neg_list:
neg_item = data[item] < 0
print(item + '小于0的有' + str(neg_item.sum())+'个')
#删除小于0的记录
for item in neg_list:
data = data[(data[item]>=0)]
# 或者根据条件直接筛选
# 列名1 指标 <800 且 列名2 指标 >600 的数据
data = data[(data['col_name_1'] < 800) & (data['col_name_2'] > 600)]
# 多个条件也类似
这个套路可以根据确定的条件无限筛选出想要的数据,注意每个独立的小条件都要有括号 ‘( )’
3.1.2 三σ法
# 检测正态分布的 API
from scipy.stats import kstest
当数据服从正态分布时,
99.7%的数值应该位于距离均值3个标准差之内的距离:P(|x−μ|>3σ)≤0.003
超出这个范围即可认为它为异常值
def KsNormDetect(df, col_name):
"""
检测是否符合正太分布
"""
# 计算均值
u = df[col_name].mean()
# 计算标准差
std = df[col_name].std()
# 计算P值
res=kstest(df[col_name], 'norm', (u, std))[1]
# 判断p值是否服从正态分布,p<=0.05 则服从正态分布,否则不服从。
if res<=0.05:
print('该列数据服从正态分布------------')
print('均值为:%.3f,标准差为:%.3f' % (u, std))
print('------------------------------')
return 1
else:
return 0
def OutlierDetection(df, col_name, ks_res):
"""
异常值检测与剔除
"""
# 计算均值
u = df[col_name].mean()
# 计算标准差
std = df[col_name].std()
if ks_res==1:
# 定义3σ法则识别异常值
# 识别异常值
error = df[np.abs(df['value'] - u) > 3 * std]
# 剔除异常值,保留正常的数据
data_c = df[np.abs(df['value'] - u) <= 3 * std]
# 输出异常数据
print('检测到的异常值如下---------------------')
display(error)
return data_c
else:
print('请先检测数据是否服从正态分布-----------')
return None
if __name__ == '__main__':
# 创建数据
data = [1222, 87, 77, 92, 68, 80, 78,
84, 77, 81, 80, 80, 77, 92, 86,
76, 80, 81, 75, 77, 72, 81, 72,
84, 86, 80, 68, 77, 87, 76, 77,
78, 92, 75, 80, 78, 123, 3, 1223, 1232]
df = pd.DataFrame(data, columns=['value'])
ks_res=KsNormDetect(df, 'value')
df = OutlierDetection(df, 'value',ks_res)
该列数据服从正态分布------------
均值为:164.850,标准差为:306.289
------------------------------
检测到的异常值如下---------------------
value | |
---|---|
0 | 1222 |
38 | 1223 |
39 | 1232 |
3.1.3 Z-score 异常值检测
zscore和3∂原则的计算思路相同且需要数据符合正态分布,计算公式是:
Z
i
=
x
i
−
μ
σ
Z_i\,\,=\,\,\frac{x_{i\,\,}\,\,-\,\,\mu}{\sigma}
Zi=σxi−μ
其中 xi 是一个数据点,μ 是所有点 xi 的平均值,δ 是所有点 xi 的标准偏差。
threshold 一般为 2.5 ,3.0 ,3.5
def KsNormDetect(df, col_name):
"""
检测是否符合正太分布
"""
# 计算均值
u = df[col_name].mean()
# 计算标准差
std = df[col_name].std()
# 计算P值
res=kstest(df[col_name], 'norm', (u, std))[1]
# 判断p值是否服从正态分布,p<=0.05 则服从正态分布,否则不服从。
if res<=0.05:
print('该列数据服从正态分布------------')
print('均值为:%.3f,标准差为:%.3f' % (u, std))
print('------------------------------')
return 1
else:
return 0
# threshold 一般为 2.5 ,3.0 ,3.5
def zscore_check(df,colname,threshold=3):
if ks_res==1:
# 取出数据
data = df[colname]
# 计算均值
u = data.mean()
# 计算标准差
std = data.std()
# 计算得分
zscore = (data - u) / (std)
# 打印异常值
error = df[zscore.abs() > threshold]
print('检测到的异常值如下---------------------')
display(error)
return df[zscore.abs() <= threshold]
else:
print('请先检测数据是否服从正态分布-----------')
return None
if __name__ == '__main__':
# 创建数据
data = [1222, 87, 77, 92, 68, 80, 78,
84, 77, 81, 80, 80, 77, 92, 86,
76, 80, 81, 75, 77, 72, 81, 72,
84, 86, 80, 68, 77, 87, 76, 77,
78, 92, 75, 80, 78, 123, 3, 1223, 1232]
df = pd.DataFrame(data, columns=['value'])
ks_res=KsNormDetect(df, 'value')
df = zscore_check(df, 'value',ks_res)
该列数据服从正态分布------------
均值为:164.850,标准差为:306.289
------------------------------
检测到的异常值如下---------------------
value | |
---|---|
0 | 1222 |
38 | 1223 |
39 | 1232 |
3.1.4 基于MAD的Z-score 异常值检测
MAD为(Mean Absolute Deviation,中位数绝对偏差),是单变量数据集中样本差异性的统计量,比标准差更有弹性,它的计算公式是:
M
A
D
=
∑
i
=
1
n
∣
x
i
−
x
ˉ
∣
n
x
i
:
i
期的实际值
,
x
ˉ
:
平均值
,
n
:
数据值
MAD\,\,=\,\,\frac{\sum_{i=1}^n{\,\,|x_i\,\,-\,\,\bar{x}|}}{n}\,\, \\ x_i\,\,: i\text{期的实际值}, \bar{x}\,\,: \text{平均值}, n\,\,: \text{数据值}
MAD=n∑i=1n∣xi−xˉ∣xi:i期的实际值,xˉ:平均值,n:数据值
推理过程,根据推理,我们得到一个结果:MAD 约等于 0.6745*δ , 比较利于编程。
同样的, threshold 一般设置为 2.5 3.0 3.5
def KsNormDetect(df, col_name):
"""
检测是否符合正太分布
"""
# 计算均值
u = df[col_name].mean()
# 计算标准差
std = df[col_name].std()
# 计算P值
res=kstest(df[col_name], 'norm', (u, std))[1]
# 判断p值是否服从正态分布,p<=0.05 则服从正态分布,否则不服从。
if res<=0.05:
print('该列数据服从正态分布------------')
print('均值为:%.3f,标准差为:%.3f' % (u, std))
print('------------------------------')
return 1
else:
return 0
# threshold 一般为 2.5 ,3.0 ,3.5
def zscore_check(df,colname,threshold=3.5):
if ks_res==1:
# 取出数据
data = df[colname]
# 计算 MAD
MAD = (data - data.median()).abs().median()
# 计算得分
zscore = ((data - data.median())*0.6475 / MAD)
# 打印异常值
error = df[zscore.abs() > threshold]
print('检测到的异常值如下---------------------')
display(error)
return df[zscore.abs() <= threshold]
else:
print('请先检测数据是否服从正态分布-----------')
return None
if __name__ == '__main__':
# 创建数据
data = [1222, 87, 77, 92, 68, 80, 78,
84, 77, 81, 80, 80, 77, 92, 86,
76, 80, 81, 75, 77, 72, 81, 72,
84, 86, 80, 68, 77, 87, 76, 77,
78, 92, 75, 80, 78, 123, 3, 1223, 1232]
df = pd.DataFrame(data, columns=['value'])
ks_res=KsNormDetect(df, 'value')
df = zscore_check(df, 'value',ks_res)
该列数据服从正态分布------------
均值为:164.850,标准差为:306.289
------------------------------
检测到的异常值如下---------------------
value | |
---|---|
0 | 1222 |
1 | 87 |
3 | 92 |
4 | 68 |
13 | 92 |
20 | 72 |
22 | 72 |
26 | 68 |
28 | 87 |
32 | 92 |
36 | 123 |
37 | 3 |
38 | 1223 |
39 | 1232 |
3.1.5 数据倾斜处理(偏度)
数据倾斜,指的是并行处理的过程中,
某些分区或节点处理的数据,显著高于其他分区或节点,
导致这部分的数据处理任务比其他任务要大很多,从而成为这个阶段执行最慢的部分,
进而成为整个作业执行的瓶颈,甚至直接导致作业失败。
# 检测倾斜API
from scipy import stats
def skew_check(df):
"""
检测倾斜
如果值大于1,则证明存在倾斜;
值越接近于0,越趋近于平缓。
返回: 倾斜的列名
"""
skew_attention = []
for column in df.columns:
if (df[column].dtype == 'int64') or (df[column].dtype == 'float64'):
skew = stats.mstats.skew(df[column]).data
if skew >= 1:
skew_attention.append(column)
print('检测到的倾斜的列名如下---------------------')
print(skew_attention)
return skew_attention
def skew_process(df, skew_attention):
"""
处理倾斜
"""
for column in skew_attention:
df[column] = np.log(df[column])
return df
if __name__ == '__main__':
# 创建数据
data = [1222, 87, 77, 92, 68, 80, 78,
84, 77, 81, 80, 80, 77, 92, 86,
76, 80, 81, 75, 77, 72, 81, 72,
84, 86, 80, 68, 77, 87, 76, 77,
78, 92, 75, 80, 78, 123, 3, 1223, 1232]
df = pd.DataFrame(data, columns=['value'])
skew_attention = skew_check(df)
df = skew_process(df, skew_attention)
display(df.head())
检测到的倾斜的列名如下---------------------
['value']
value | |
---|---|
0 | 7.108244 |
1 | 4.465908 |
2 | 4.343805 |
3 | 4.521789 |
4 | 4.219508 |
3.1.6 箱型图
当数据不符合正态分布, 可以使用箱型图检测异常值(符合也可以用)
def OutlierDetection(df, colname):
# 计算下四分位数和上四分位
Q1 = df[colname].quantile(q=0.25)
Q3 = df[colname].quantile(q=0.75)
# 基于1.5倍的四分位差计算上下须对应的值
low_whisker = Q1 - 1.5 * (Q3 - Q1)
up_whisker = Q3 + 1.5 * (Q3 - Q1)
# 寻找异常点
kk = df[(df[colname] > up_whisker) | (df[colname] < low_whisker)]
df = df[(df[colname] <= up_whisker) & (df[colname] >= low_whisker)]
print('箱线图检测到的异常值如下---------------------')
display(kk)
return df
if __name__ == '__main__':
# 创建数据
data = [1222, 87, 77, 92, 68, 80, 78,
84, 77, 81, 80, 80, 77, 92, 86,
76, 80, 81, 75, 77, 72, 81, 72,
84, 86, 80, 68, 77, 87, 76, 77,
78, 92, 75, 80, 78, 123, 3, 1223, 1232]
df = pd.DataFrame(data, columns=['value'])
result = OutlierDetection(df, 'value')
display(result.head())
箱线图检测到的异常值如下---------------------
value | |
---|---|
0 | 1222 |
36 | 123 |
37 | 3 |
38 | 1223 |
39 | 1232 |
value | |
---|---|
1 | 87 |
2 | 77 |
3 | 92 |
4 | 68 |
5 | 80 |
3.1.7 其它
基于聚类方法检测、基于密度的离群点检测、基于近邻度的离群点检测等。
(类似于缺失值拟合方法,调用sklearn的聚类方法对每列聚类,挑出离群点,这里不再赘述)
4. 类别特征编码
大部分机器学习算法要求输入的数据必须是数字,
这就要求将数据中的描述性变量(如性别)转换为数值型数据
有序编码 和 无序one-hot编码
from sklearn import preprocessing
def seek_object_column(df):
"""
寻找描述变量列
将其存储到cat_vars这个list中
"""
cat_vars = []
print('\n描述变量有:')
cols = df.columns.tolist()
for col in cols:
if df[col].dtype == 'object':
print(col)
cat_vars.append(col)
return cat_vars
def transform_object_to_num(data, cat_vars, ordered=False):
"""
转化描述变量列
ordered 划分数据为 有序 与 无序
有序 分类为 0-n
无序 分类为 one-hot 编码, 即 0,1
"""
print('\n开始转换描述变量...')
le = preprocessing.LabelEncoder()
if ordered:
##若变量是有序的##
#将描述变量自动转换为数值型变量,并将转换后的数据附加到原始数据上
for col in cat_vars:
tran = le.fit_transform(data[col].tolist())
tran_df = pd.DataFrame(tran,columns=['num_'+col])
print('{col}经过转化为{num_col}'.format(col=col,num_col='num_'+col))
data = pd.concat([data, tran_df], axis=1)
del data[col] #删除原来的列
else:
##若变量是无序变量##
#值得注意的是one-hot可能引发维度爆炸
for col in cat_vars:
onehot_tran = pd.get_dummies(data[col])
data = data.join(onehot_tran) #将one-hot后的数据添加到data中
del data[col] #删除原来的列
print('\n输出编码后的DataFrame:')
display(data.head())
return data
cat_vars = seek_object_column(data)
data = transform_object_to_num(data, cat_vars)
描述变量有:
sex
smoker
region
开始转换描述变量...
输出编码后的DataFrame:
age | bmi | children | charges | female | male | no | yes | northeast | northwest | southeast | southwest | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 19 | 27.900 | 0.0 | 16884.92400 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
1 | 18 | 33.770 | 1.0 | 1725.55230 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
2 | 28 | 33.000 | 3.0 | 4449.46200 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
3 | 33 | 22.705 | 0.0 | 21984.47061 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
4 | 32 | 28.880 | 0.0 | 3866.85520 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
5. 数据连续属性离散化
可以为分层抽样时提供比例参考
一些数据挖掘算法,别是分类算法,要求数据是分类属性形式。常常需要将连续属性变换成分类属性,即连续属性离散化。 常用的离散化方法:
- 等宽法:
将属性值域分成具有相同宽度的区间,区间的个数由数据本身的特点决定,或者由用户指定,类似于制作频率分布表。 - 等频法:
将相同数量的记录放进每个区间。 - 基于聚类分析的方法:
通过分箱离散化、通过直方图分析离散化、通过聚类、决策树和相关分析离散化、标称数据的概念分层产生。
前两种方法简单易操作,但需要人为规划区间,且等宽法对离群点敏感,容易分的不均,而等频法为了平均会把相同的分到不同区间,干扰决策。
cut 参数介绍
pd.cut( x, bins, right=True,
labels=None, retbins=False, precision=3,
include_lowest=False, duplicates='raise', ordered=True)
- x:待分箱 一维数组
- bins:
- 整数 则表示将x中的数值分成等宽的n份
- 标量序列 序列中的数值表示用来分档的分界值 [1, 2, 3, 4]
- 间隔索引 元组内的分界 如(0, 2), (3, 6)
- right:是否包含bins区间的最右边,默认为True,最右边为闭区间,False则不包含
- labels:指定返回的 bins 的标签 当bins为 间隔索引时, 忽略此参数
- retbins:是否返回bins的分组依据,默认为False
- precision:小数点位数 参数为 0 和 1 时一样
- include_lowest:是否包含最左边,左闭包 默认为False
- duplicates:{‘raise’, ‘drop’} drop去除bins里的重复分界
- ordered:需要配合labels一起使用 形成有序分类
# 画图
def classified_plot(data, d, k):
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(7,5))
for j in range(0, k):
plt.plot(data[d==j], [i for i in d[d==j]], 'o')
# 例子加载
data = pd.read_excel(r'../../data/practice/discretization_data.xls')
data = data['肝气郁结证型系数'].copy() #将数据集转化为集合
k = 4 #k值为组数
5.1 等宽法
def cut_bins_width(data, k):
"""
每组的宽度相同
处理单位为某一列
"""
d1 = pd.cut(data,k,labels=range(k)) #将集合分组
print('数值分布图---------')
classified_plot(data, d1, k)
plt.show()
print('数量分布图---------')
display(d1.hist())
plt.show()
return d1
d1 = cut_bins_width(data, k)
数值分布图---------
数量分布图---------
<AxesSubplot:>
5.2 等频法
def cut_bins_freq(data, k):
"""
每组的数量相同
处理单位为某一列
"""
w = [i/k for i in range(k+1)] #计算百分比
w = data.describe(percentiles=w)[4:4+k+1] #计算各个百分位数
w[0] = w[0]*(1-1e-10)
d2 = pd.cut(data,w,labels=range(k)) #将集合分组
print('数值分布图---------')
classified_plot(data, d2, k)
plt.show()
print('数量分布图---------')
display(d2.hist())
plt.show()
return d2
d2 = cut_bins_freq(data, k)
数值分布图---------
数量分布图---------
<AxesSubplot:>
5.3 k-means分组
from sklearn.cluster import KMeans #导入kmeans
def cut_bins_k_means(data, k):
"""
聚类分组
处理单位为某一列
"""
kmodel = KMeans(n_clusters = k) #确定族数
kmodel.fit(data.values.reshape(len(data),1)) #训练数据集
c = pd.DataFrame(np.sort(kmodel.cluster_centers_)) #确定中心并排序
w = c.rolling(2).mean().iloc[1:] #取移动平均值
w = [0]+list(w[0])+[data.max()] #加上最大最小值作为边界值
w = list(np.sort(w)) #再次排序
d3 = pd.cut(data,w,labels = range(k))
print('数值分布图---------')
classified_plot(data, d3, k)
plt.show()
print('数量分布图---------')
display(d3.hist())
plt.show()
return d3
d3 = cut_bins_k_means(data, k)
数值分布图---------
数量分布图---------
<AxesSubplot:>
6. 训练集测试集划分
# 例子加载
house_data = pd.read_csv(r'../../data/practice/housing.csv')
6.1 随机抽样
适用于庞大数据集
(样本个数远远大于属性个数)
from sklearn.model_selection import train_test_split
# 提供了随机种子 可以确定随机顺序
# 但数据的顺序如果更新,仍会变化,故仅解决在重复运行同一数据集的随机唯一性
# 参数 1.数据 2.比例 3.随机种子
train_set, test_set = train_test_split(data, test_size=0.2, random_state=42)
6.2 分层抽样
中小数据集抽样需要重要属性分层比例合理
如 人口男女比例 6:4,为了调查结果符合全体人口,
在只有 1000 人样本时 在抽取时会希望测试 和 训练 都满足 6:4 这个比例,
则需要将数据均匀划分层次, 每层抽取正确样本数量, 来确保测试 和 训练集合有这个比例
1. 如果之前分过层,不需要再分,直接使用属性作为分层依据即可。
分层使用先前的离散方法分层(3种任选一种)
from sklearn.cluster import KMeans #导入kmeans
def cut_bins_k_means(data, k):
"""
聚类分组
处理单位为某一列
"""
kmodel = KMeans(n_clusters = k) #确定族数
kmodel.fit(data.values.reshape(len(data),1)) #训练数据集
c = pd.DataFrame(np.sort(kmodel.cluster_centers_)) #确定中心并排序
w = c.rolling(2).mean().iloc[1:] #取移动平均值
w = [0]+list(w[0])+[data.max()] #加上最大最小值作为边界值
w = list(np.sort(w)) #再次排序
d3 = pd.cut(data,w,labels = range(k))
print('数值分布图---------')
classified_plot(data, d3, k)
plt.show()
print('数量分布图---------')
display(d3.hist())
plt.show()
return d3
house_data['income_cat'] = cut_bins_k_means(house_data['median_income'], 4)
house_data.head()
数值分布图---------
数量分布图---------
<AxesSubplot:>
longitude | latitude | housing_median_age | total_rooms | total_bedrooms | population | households | median_income | median_house_value | ocean_proximity | income_cat | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | -122.23 | 37.88 | 41.0 | 880.0 | 129.0 | 322.0 | 126.0 | 8.3252 | 452600.0 | NEAR BAY | 3 |
1 | -122.22 | 37.86 | 21.0 | 7099.0 | 1106.0 | 2401.0 | 1138.0 | 8.3014 | 358500.0 | NEAR BAY | 3 |
2 | -122.24 | 37.85 | 52.0 | 1467.0 | 190.0 | 496.0 | 177.0 | 7.2574 | 352100.0 | NEAR BAY | 3 |
3 | -122.25 | 37.85 | 52.0 | 1274.0 | 235.0 | 558.0 | 219.0 | 5.6431 | 341300.0 | NEAR BAY | 2 |
4 | -122.25 | 37.85 | 52.0 | 1627.0 | 280.0 | 565.0 | 259.0 | 3.8462 | 342200.0 | NEAR BAY | 0 |
2. sklearn 分层抽样
from sklearn.model_selection import StratifiedShuffleSplit
# 分层抽样
# 参数: 1. 训练和测试组的对数 2. 测试集在每对的比例 3. 随机种子
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
# split(分类数据, 类别依据)
for train_index, test_index in split.split(house_data, house_data['income_cat']):
stra_train_set = house_data.loc[train_index]
stra_test_set = house_data.loc[test_index]
3. 查验比例 与 比例偏差
def check_attribute_ratio(data, reference_col):
"""
查验属性比例
参数: data 数据
reference_col 类别依据
"""
return data[reference_col].value_counts() / len(data)
def ratio_error(overall_ratio, com_ratio):
"""
返回比例偏差
参数: 原始数据的比例
需要比较的数据比例
"""
return np.abs(com_ratio - overall_ratio.values)
## 比例依据
reference_col = 'income_cat'
## 属性比例比例
# 总体
overall_ratio = check_attribute_ratio(house_data, reference_col)
# 分层抽样
stratified_test_ratio = check_attribute_ratio(stra_test_set, reference_col)
stratified_train_ratio = check_attribute_ratio(stra_train_set, reference_col)
## 比例偏差
# 分层抽样
stratified_test_error = ratio_error(overall_ratio, stratified_test_ratio)
stratified_train_error = ratio_error(overall_ratio, stratified_train_ratio)
## df 打印
ratio_df = pd.DataFrame({
'总体':overall_ratio,
'测试分层':stratified_test_ratio,
'训练分层':stratified_train_ratio,
'测试偏差分层':stratified_test_error,
'训练偏差分层':stratified_train_error,
})
display(ratio_df)
总体 | 测试分层 | 训练分层 | 测试偏差分层 | 训练偏差分层 | |
---|---|---|---|---|---|
0 | 0.644816 | 0.644864 | 0.644804 | 0.000048 | 0.000012 |
2 | 0.167297 | 0.167393 | 0.167272 | 0.000097 | 0.000024 |
1 | 0.134060 | 0.133963 | 0.134084 | 0.000097 | 0.000024 |
3 | 0.053828 | 0.053779 | 0.053840 | 0.000048 | 0.000012 |
4. 删除分层属性,恢复数据原样
for set_ in (stra_train_set, stra_test_set):
set_.drop('income_cat', axis=1, inplace=True)
6.3 分割标签和属性
def split_labels_attribute(df, label):
"""
分割标签与属性
df 数据集
label 标签名称
"""
x = df.drop(label, axis=1)
y = df[label]
return x, y
# 这里以 charges 为标签
train_x, train_y = split_labels_attribute(train_set, 'charges')
test_x, test_y = split_labels_attribute(test_set, 'charges')
7.数据规范化(归一化)
数据的标准化(normalization)是将数据按比例缩放,使之落入一个小的特定区间。
去除数据的单位限制,将其转化为无量纲的纯数值,便于不同单位或量级的指标能够进行比较和加权。
一些需要数据归一化的算法:
LR、SVM、KNN、KMeans、AdaBoost、神经网络等,
一些不需要数据归一化的算法:
树形模型(决策树 DT、随机森林 RF、GBDT等)原因
注意
- 没有一种数据标准化的方法是万能的,对于不同的问题有不同的归一化方法。
- 先划分 测试集 与 训练集,再归一化。
- 标签 y 也可以归一化,具体是否实施措施看效果, 暂时没有找到相关介绍论文,
现在可以参考一下:一些浅显的解释
7.1 最大最小值归一化
对原始数据进行线性变换,变换到[0,1]区间。
计算公式为:x* = (x-x.min)/(x.max-x.min)
它的特点是不会对数据分布产生影响。
不过如果你的数据的最大最小值不是稳定的话,即异常值多,结果可能因此变得不稳定,不建议用。
min-max 归一化在图像处理上非常常用,因为大部分的像素值范围是 [0, 255]。
使用建议
- 对处理后的数据范围有严格要求,建议使用;
- 数据存在异常值和较多噪音,此时归一化受异常值影响,不建议使用;
- 在不涉及距离度量、协方差计算以及数据不符合正太分布的时候,建议使用。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
# 最大最小值归一化
train_x_sca = scaler.fit_transform(train_x)
test_x_sca = scaler.fit_transform(test_x)
7.2 标准归一化
对原始数据进行线性变换,经过处理的数据的均值为0,标准差为1。
计算方式是将特征值减去均值,除以标准差,计算公式为:x* = (x-x.mean)/σ
使用建议
- 如果数据存在异常值和较多噪音,用标准化,可以间接通过中心化避免异常值和极端值的影响
- 在分类、聚类算法中,需要使用距离来度量相似性的时候、或者使用PCA技术进行降维的时候,建议使用。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 标准归一化
train_x_sca = scaler.fit_transform(train_x)
test_x_sca = scaler.transform(test_x)
7.3 其他归一化
- 缩放稀疏数据
中心化稀疏数据会破坏数据的稀疏结构,因此明智的做法是少做这样的操作。但是,缩放稀疏输入是有意义的,尤其是当特征处于不同的缩放比例时。 - 缩放包含离群值的数据
如果数据中包含许多离群值,使用数据的均值和方差进行缩放可能效果不佳。
这一部分建议参考 sklearn归一化处理的 6.3.1.2 和 6.3.1.3:
sklearn归一化处理详细介绍
8. 降维
8.1 PCA主成分分析
用于连续属性的数据降维方法,旨在用少量新变量解释原数据,即主要成分。
sklearn PCA API介绍
PCA以及其他降维方式详解
from sklearn.decomposition import PCA
先默认看一下贡献率
pca = PCA()
pca.fit(train_x_sca)
print('\n模型的各个特征向量')
print(pca.components_)
print('\n返回各成分的方差百分比(贡献率)')
print(pca.explained_variance_ratio_)
模型的各个特征向量
[[-7.94175895e-03 3.74633341e-03 6.39264528e-03 -6.97717175e-01
6.97717175e-01 -1.12968712e-01 1.12968712e-01 8.18634362e-03
1.46567874e-02 -1.49902201e-03 -2.13441090e-02]
[-3.97575557e-02 9.84301203e-03 1.29919942e-03 1.10198731e-01
-1.10198731e-01 -6.76425713e-01 6.76425713e-01 1.73896789e-02
-1.03723489e-01 1.91730067e-01 -1.05396257e-01]
[ 6.45653175e-03 8.86642610e-02 -1.10589566e-02 -2.30937710e-02
2.30937710e-02 1.26600910e-01 -1.26600910e-01 -5.16825809e-01
-1.26710225e-01 8.05823140e-01 -1.62287106e-01]
[ 2.02356991e-02 1.52902430e-02 5.14718495e-03 8.81588025e-04
-8.81588025e-04 -9.70362187e-02 9.70362187e-02 -5.94698021e-01
2.22603820e-02 -1.94662109e-01 7.67099748e-01]
[ 5.72080699e-03 -2.49922743e-02 1.78304459e-02 2.19337811e-02
-2.19337811e-02 -5.92527445e-02 5.92527445e-02 -3.55518143e-01
8.49129901e-01 -1.42403404e-01 -3.51208354e-01]
[ 9.90879493e-01 8.45935423e-02 9.38143287e-02 -4.50515403e-04
4.50515403e-04 -2.60483409e-02 2.60483409e-02 2.22934354e-02
-7.16646949e-03 1.93464043e-03 -1.70616064e-02]
[-9.19797753e-02 -2.33027225e-02 9.95193738e-01 3.65456884e-03
-3.65456884e-03 7.06454162e-03 -7.06454162e-03 7.81408476e-04
-1.65834798e-02 1.33382145e-02 2.46385679e-03]
[-8.70052799e-02 9.91700680e-01 1.67039878e-02 4.27019303e-03
-4.27019303e-03 -1.78753556e-03 1.78753556e-03 4.43302523e-02
3.35805238e-02 -7.43820142e-02 -3.52876196e-03]
[ 5.13513206e-18 -2.06812965e-16 3.87942933e-17 7.75905231e-03
7.75905231e-03 4.99511115e-02 4.99511115e-02 -4.98720705e-01
-4.98720705e-01 -4.98720705e-01 -4.98720705e-01]
[ 5.87317919e-17 -3.83141025e-16 2.62054152e-16 5.67971427e-02
5.67971427e-02 7.03016694e-01 7.03016694e-01 3.56483668e-02
3.56483668e-02 3.56483668e-02 3.56483668e-02]
[ 1.18663144e-17 1.89796899e-16 9.82757689e-17 7.04779314e-01
7.04779314e-01 -5.72050172e-02 -5.72050172e-02 2.61766290e-03
2.61766290e-03 2.61766290e-03 2.61766290e-03]]
返回各成分的方差百分比(贡献率)
[2.89006618e-01 1.87154310e-01 1.48296902e-01 1.41500700e-01
1.38274884e-01 5.38189695e-02 2.81985615e-02 1.37490544e-02
4.82189608e-32 1.79450230e-32 1.11931405e-32]
根据贡献率 调整主成分个数 重新建立模型
# 上述看到 8 个基本就接近 90% 以上
pca = PCA(8)
pca.fit(train_x_sca)
# 降维
low_d = pca.transform(train_x_sca)
new_data = pd.DataFrame(low_d) # df 打印
display(new_data)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|---|
0 | -0.747525 | -0.281572 | -0.138311 | -0.012050 | 0.867988 | 0.115812 | 0.146058 | -0.254364 |
1 | -0.756073 | -0.160642 | -0.513806 | -0.628659 | -0.346014 | 0.142346 | -0.206365 | -0.135041 |
2 | -0.766567 | 0.009520 | 0.810833 | -0.226201 | -0.132640 | 0.230921 | -0.204145 | -0.248803 |
3 | -0.757507 | 0.024452 | 0.821433 | -0.223309 | -0.124267 | 0.058450 | 0.728542 | 0.043498 |
4 | -0.747581 | -0.287845 | -0.135591 | -0.006962 | 0.871232 | 0.308805 | 0.311633 | -0.225878 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
1065 | -0.745668 | -0.132763 | -0.509220 | -0.634748 | -0.341266 | -0.397515 | 0.577313 | 0.119626 |
1066 | -0.758560 | 0.021685 | 0.796506 | -0.227607 | -0.117240 | 0.034667 | 0.735093 | -0.235309 |
1067 | 0.637549 | -0.390321 | -0.464035 | -0.625232 | -0.389088 | 0.382143 | -0.236206 | -0.141575 |
1068 | -0.553248 | 1.084707 | -0.362397 | 0.934277 | -0.233555 | 0.027074 | 0.151638 | 0.466830 |
1069 | 0.609012 | -0.509262 | -0.098646 | 0.737190 | -0.388329 | 0.288919 | -0.231487 | -0.057696 |
1070 rows × 8 columns
# 必要时可以 逆向 PCA
old_data = pca.inverse_transform(low_d)
old_data = pd.DataFrame(old_data) # df 打印
display(old_data)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 6.086957e-01 | 0.107345 | 0.449782 | 1.000000e+00 | 5.551115e-16 | 1.000000e+00 | 6.106227e-16 | 4.163336e-16 | 1.000000e+00 | -2.775558e-16 | -3.330669e-16 |
1 | 6.304348e-01 | 0.224913 | 0.082969 | 1.000000e+00 | 9.992007e-16 | 1.000000e+00 | 2.220446e-16 | 1.000000e+00 | -1.387779e-16 | -4.440892e-16 | 3.608225e-16 |
2 | 7.391304e-01 | 0.239440 | 0.082969 | 1.000000e+00 | 7.771561e-16 | 1.000000e+00 | -7.216450e-16 | -5.828671e-16 | -9.159340e-16 | 1.000000e+00 | -2.498002e-16 |
3 | 4.565217e-01 | 0.493947 | 1.000000 | 1.000000e+00 | 2.220446e-16 | 1.000000e+00 | -9.159340e-16 | -4.163336e-16 | -7.216450e-16 | 1.000000e+00 | -1.942890e-16 |
4 | 7.826087e-01 | 0.148238 | 0.633188 | 1.000000e+00 | 6.661338e-16 | 1.000000e+00 | 6.106227e-16 | 4.163336e-16 | 1.000000e+00 | -3.330669e-16 | -3.330669e-16 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1065 | 2.220446e-16 | 0.414044 | 0.816594 | 1.000000e+00 | 6.661338e-16 | 1.000000e+00 | 1.110223e-16 | 1.000000e+00 | -2.775558e-17 | -2.775558e-16 | 3.608225e-16 |
1066 | 4.565217e-01 | 0.212806 | 1.000000 | 1.000000e+00 | 3.330669e-16 | 1.000000e+00 | -9.436896e-16 | -4.440892e-16 | -7.216450e-16 | 1.000000e+00 | -1.942890e-16 |
1067 | 8.695652e-01 | 0.247915 | 0.082969 | 1.665335e-16 | 1.000000e+00 | 1.000000e+00 | 2.775558e-16 | 1.000000e+00 | -1.110223e-16 | -4.440892e-16 | 1.110223e-16 |
1068 | 4.130435e-01 | 0.851224 | 0.449782 | 1.000000e+00 | 5.551115e-16 | 6.661338e-16 | 1.000000e+00 | -2.775558e-17 | 1.942890e-16 | -9.436896e-16 | 1.000000e+00 |
1069 | 8.043478e-01 | 0.375034 | 0.082969 | 5.551115e-16 | 1.000000e+00 | 1.000000e+00 | 7.494005e-16 | 6.938894e-16 | -6.938894e-16 | -2.220446e-16 | 1.000000e+00 |
1070 rows × 11 columns
这样就降低到 8 个维度,或者 利用降维知道前 8 个维度是主要影响因子。
9. 升维
9.1 升维本质
产生更多的特征角度去学习,提高模型的准确度,解决欠拟合的问题。
9.2 升维常见手段 (多项式回归)
已知维度 相乘 获取新的维度。
为何叫回归?
主要在回归任务中用的比较多,
且当 x 和 y 存在不存在线性关系,要训练模型,可以选择非线性算法去拟合数据;也可以选择升维 将数据变为线性关系后 继续使用线性算法,毕竟线性算法运算速度快一些。
9.3 升维代码实现 (多项式回归)
sklearn PolynomialFeatures API 详细介绍
from sklearn.preprocessing import PolynomialFeatures
X = np.arange(6).reshape(3, 2)
print(X)
>>> array([[0, 1],
[2, 3],
[4, 5]])
# degree 参数 多项式特征的程度
poly = PolynomialFeatures(degree=2)
poly.fit_transform(X)
>>> array([[ 1., 0., 1., 0., 0., 1.],
[ 1., 2., 3., 4., 6., 9.],
[ 1., 4., 5., 16., 20., 25.]])
可以看出 特征 X 已经从 ( X 1 , X 2 ) 转化为 ( 1 , X 1 , X 2 , X 1 2 , X 1 X 2 , X 2 2 ) \text{特征}X\,\,\text{已经从} \left( X_1, X_2 \right) \,\,\text{转化为}\left( 1, X_1, X_2, X_{1}^{2}, X_1X_2, X_{2}^{2} \right) 特征X已经从(X1,X2)转化为(1,X1,X2,X12,X1X2,X22)
更多详细用法,可参考 sklearn 6.3.7 生成多项式特征。