上篇文章我们针对ML闲扯了一番,并在最后又借鉴Data Mining的CRISP-DM模型分析了一个ML项目的开发过程。
今天说点什么呢?我犹豫了,我迷茫了!先给大家讲个故事吧!
有一天你的boss找到你说:XX听说你对ML很熟悉啊,正好我们公司有很多**方面的数据,你看看能不能搞一个ML项目为我们公司提供一些决策参考。你听到这里是高兴还是悲伤,具体因人而异(要是小弟听到了,绝对会很高兴)。我这里假设你很高兴,接到boss的圣旨以后你就屁颠屁颠的找公司的相关数据。毕竟手持boss的圣旨,所以数据获取应该不是太难,拿到数据你就开始疯狂的想使用什么模型呢?逻辑回归,GBDT,随机森林,SVM、贝叶斯等等模型(别急,后面都会提到),你通过“认真”思考后选择了一个模型,迫不及待的把数据往里面喂(亲爱的张嘴)。当你信心满满的点击run后,你会看到下面一行,一行,一行的红色字体,大体意思是这里数字无效,那里数据为空……这时候就问你崩溃吗?
故事说完了,正事还是要干的,今天在网上浪了一番,发现一个妹子(CSDN的账户名为Charlotte77)根据自己的实际情况总结了一个ML项目的流程,如下图所示(此图纯属盗图,妹子不会怪罪我的吧):
其实,我盗用妹子的图是有目的的,对,你没有听错,我是带着目的来的,现在话不多说——开干。根据妹子的图,我的思绪回到了扯上一篇文章的时候,突然发现上一篇没有什么干货,全文都是我在扯淡。其实,我上一篇的目的性很明确就是让大家了解一下什么叫ML以及在一个ML项目开始前我们需要做或者准备的东西,这对一个ML项目来说至关重要的。这一篇我打算说一下关于数据预处理的问题,在ML中有数据、好数据才是王道。无论对于数据挖掘,还是ML,还是DL,数据都占据很重要的地位,例如(臭不要脸的又盗图Jim Liang的图):
上图来自美帝的“纽约时报”的一篇报道,其实就是说数据科学家在他们的时间中有50%到80%的时间花费在收集和准备不规则数据的更为平凡的任务中,然后才能探索有用的金块。就看上图,不用我多废话大家就会明白数据预处理的重要性(臭不要脸的谁让你废话的,滚!)。说到数据预处理,我们就说一些稍微正式的,何为数据处理?(老毛病,先说定义吧)数据预处理主要是指在对数据主要处理以前对数据进行的一些处理(就问你是不是我的回答简单易懂)。数据预处理有四个任务,数据清洗、数据集成、数据变换和数据规约。
各位大佬,各位看官,请听说继续扯:
从惯性思维来讲,如果一件事情放到第一位就说明这件事情很重要,例如软件开发中的需求分析,所以数据清洗在数据预处理中占据很重要的地位(谁让人家是老大呢)。各位大佬,各位看官,如果你已经对ML有一定的了解,你就会发现有几个数据集(例如,iris数据集,房价数据,电影评分数据集)已经被数据挖掘、ML等领域的人玩烂了。这些数据集毕竟是为学习的人提供练手的玩物(看到这两个字是不是,我邪恶了),所以这些数据的质量都相对较好。然而,在我们实际项目中的数据往往是混乱的,也可以说杂乱无章的(这里说乱主要是指数据不完整、数据重复、数据值为空等)。如果你想要你的努力获得效果(模型获得更好的预测),你就必须对你的数据做预处理。其实,我听很多高机器学习的朋友告诉我,机器学习数据时王道,较好的数据经过不同的模型训练后,其预测结果差距不是太大(这是真的你没有听错)。在真实数据中,我们拿到的数据可能包含了大量的缺失值,可能包含大量的噪音,也可能因为人工录入错误(比如,医生的就医记录)导致有异常点存在,对我们挖据出有效信息造成了一定的困扰,所以我们需要通过一些方法,尽量提高数据的质量。
数据清洗是检测和去除数据集中的噪声数据和无关数据,处理遗漏数据,去除空白数据域和知识背景下的白噪声。数据清洗分为有监督清洗和无监督清洗两类(参考CSDN昵称:树先生海浪):
- 有监督清洗:在对应领域专家的指导下,收集分析数据,手工去除明显的噪声数据和重复记录,填补缺值数据等清洗动作
- 无监督清洗:根据一定的业务规则,预先定义好数据清洗算法,由计算机自动执行算法,对数据集进行清洗,然后产生清洗报告
在实际项目中,完全有监督的清洗估计不太可能(你让去人工清洗数以万计的数据你愿意吗?反正我是不干),因此,一般是先进行无监督清洗并产生相应的清洗报告,然后再让专家根据清晰报告对清洗的结果进行人工整理。在数据清理前,我们需要分析数据的特点并定义数据清洗规则,清洗结束后为了保证清洗的质量,我们还需要验证清洗结果。数据的分析是根据相关的业务知识和相应的技术,如统计学,数据挖掘的方法,分析出数据源中数据的特点,为定义数据清洗规则奠定基础。常用的清洗规则主要包括:空值的检查和处理;非法值的检测和处理;不一致数据的检测和处理;相似重复记录的检测和处理。执行数据清洗规则时需要检查拼写错误,去掉重复的(duplicate)记录,补上不完全的(incomplete)记录,解决不一致的(inconsistent)记录,用测试查询来验证数据,最后需要生成数据清晰报告。在清洗结果验证中,需要对定义的清洗转换规则的正确性和效率进行验证和评估,当不满足清洗要求时要对清洗规则或系统参数进行调整和改进,数据清洗过程中往往需要多次迭代的进行分析,设计和验证。
杂乱无章的数据,问题多如麻,因此针对数据中存在的不同问题,有不同的处理方法:
空值数据的处理方法
- 删除包含空值的记录,这种方式主要是针对包含空值的数据占总体比例较低,删除这些数据对于数据整体影响不大。
- 自动补全方法,这种方法通过统计学原理,根据数据集中记录的取值分布情况来对一个空值进行自动填充,可以用平均值,最大值,最小值等基于统计学的客观知识来填充字段。
- 手工的补全缺失值,这种方法仅适用于数据量比较小的情况,对于大数据量只能说no way,并且对空值不正确的填充往往将新的噪声引入数据中,使知识获取产生错误的结果。
不一致数据处理的基本方法
清洗方法主要在分析不一致产生原因的基础上,利用各种变换函数,格式化函数,汇总分解函数去实现清洗。
噪声数据的基本处理方法
- 分箱:将存储的值分布到一些箱中,用箱中的数据值来局部平滑存储数据的值,包括按箱平均值平滑,按箱中值平滑和按箱边界值平滑。
- 回归:找到恰当的回归函数来平滑数据。线性回归要找出适合两个变量的“最佳”直线,使得一个变量能预测另一个。多线性回归涉及多个变量,数据要适合一个多维面。
- 计算机检查和人工检查相结合:可以通过计算机将被判定数据与已知的正常值比较,将差异程度大于某个阈值的模式输出到一个表中,人工审核后识别出噪声数据。
- 聚类:将类似的值组成群或“聚类”,落在聚类集合之外的值被视为孤立点。孤立点可能是垃圾数据,也可能是提供信息的重要数据。垃圾数据将清除。
哔哔半天了,来点实际的吧!
我们经常用来学习的数据比较完整不适合这里的闲扯淡,所以我特意从kaggle找了一个数据。这个数据集分为训练数据集和测试数据集,训练数据集的大小为(1272, 161),总计161个特征,数据存储格式为csv格式。我们这里选择使用pandas(pandas教程,这个教程基本上是按照官网翻译的,如果你想看看官方文档可以在这里下载,一个积分哦)处理数据。下面为大家展示一下这个数据集的数据分布情况(由于特征比较多,我只是简单展示了部分特征):
首先,我们来看一下csv文件里面的数据,在使用pandas之前导入使用到的模块:
import numpy as np
import pandas as pd
打开数据,显示前2条数据:
df = pd.read_csv('./learn/2016 School Explorer.csv')
df.head(2)
#df.describe() 如果数据主体为数字,这里也可以使用df.describe()显示更具体的情况
由于数据主要是使用字符串保存,并且里面有6个特征包含%,不方便处理数据。这里我们写一个方法,用于处理这些数据生成float数据:
def p2f(x):
return float(x.strip('%'))/100
写完函数了,进一步处理数据:
# astype(type)是将数据转换成制定的type类型
# apply(function)将会在DataFrame中行或列中的数据应用function函数
df['Percent of Students Chronically Absent']=df['Percent of Students Chronically Absent'].astype(str).apply(p2f)
df['Rigorous Instruction %'] = df['Rigorous Instruction %'].astype(str).apply(p2f)
df['Collaborative Teachers %'] = df['Collaborative Teachers %'].astype(str).apply(p2f)
df['Supportive Environment %'] = df['Supportive Environment %'].astype(str).apply(p2f)
df['Effective School Leadership %'] = df['Effective School Leadership %'].astype(str).apply(p2f)
df['Strong Family-Community Ties %'] = df['Strong Family-Community Ties %'].astype(str).apply(p2f)
df['Trust %'] = df['Trust %'].astype(str).apply(p2f)
下面我们通过df[‘School Income Estimate’]来看一下School Income Estimate特征的值:
0 $31,141.72
1 $56,462.88
2 $44,342.61
3 $31,454.00
4 $46,435.59
5 $39,415.45
6 $43,706.73
7 $28,820.67
8 $34,889.24
9 $35,545.10
10 $40,809.90
11 $27,881.59
12 NaN
13 NaN
14 $63,760.00
15 NaN
16 $62,519.57
17 $57,504.48
18 $56,787.20
19 NaN
20 NaN
21 $76,833.96
22 NaN
23 $32,817.79
24 $26,114.78
有没有发现这个数据里面不仅有NaN,而且还有$$(其实是一个刀了,如果不写成两个,就显不出来),不便于我们计算,下面我们先将数据中的$,英文“,”以及空格替换掉,并将数据转换成float(NaN后面在处理):
df['School Income Estimate'] = df['School Income Estimate'].str.replace(',', '')
df['School Income Estimate'] = df['School Income Estimate'].str.replace('$', '')
df['School Income Estimate'] = df['School Income Estimate'].str.replace(' ', '')
df['School Income Estimate'] = df['School Income Estimate'].astype(float)
处理后的结果(是不是很好了):
0 31141.72
1 56462.88
2 44342.61
3 31454.00
4 46435.59
5 39415.45
6 43706.73
7 28820.67
8 34889.24
9 35545.10
10 40809.90
11 27881.59
12 NaN
13 NaN
14 63760.00
15 NaN
16 62519.57
17 57504.48
18 56787.20
19 NaN
20 NaN
21 76833.96
22 NaN
23 32817.79
24 26114.78
下面我们使用matplotlib和pandas一起显示部分特征分布信息,同样首先导入matplotlib模块:
import matplotlib.pyplot as plt
画图如下(画图内容就是使用不同的特征表示图中的不同信息,其他的我就不废话了):
df.plot(kind="scatter", x="Longitude", y="Latitude",
s=df['School Income Estimate']/1210, c="Economic Need Index", cmap=plt.get_cmap("jet"),
label='Schools', title='New York School Population Map',colorbar=True, alpha=0.4, figsize=(15,7))
plt.legend()
plt.show()
data = [
{
'x': df["Longitude"],
'y': df["Latitude"],
'text': df["School Name"],
'mode': 'markers',
'marker': {
'color': df["Economic Need Index"],
'size': df["School Income Estimate"]/4500,
'showscale': True,
'colorscale':'Portland'
}
}
]
layout= go.Layout(
title= 'New York School Population (Economic Need Index)',
xaxis= dict(
title= 'Longitude'
),
yaxis=dict(
title='Latitude'
)
)
fig = go.Figure(data=data, layout=layout)
iplot(fig, filename='scatter_hover_labels')
对于数据,将字符转化成float:
df['Percent Asian'] = df['Percent Asian'].apply(p2f)
df['Percent Black'] = df['Percent Black'].apply(p2f)
df['Percent Hispanic'] = df['Percent Hispanic'].apply(p2f)
df['Percent White'] = df['Percent White'].apply(p2f)
df['Percent Black / Hispanic'] = df['Percent Black / Hispanic'].apply(p2f)
再画一个图:
data = [
{
'x': df["Longitude"],
'y': df["Latitude"],
'text': df["School Name"],
'mode': 'markers',
'marker': {
'color': df["Percent Black"],
'size': df["School Income Estimate"]/4500,
'showscale': True,
'colorscale':'Portland'
}
}
]
layout= go.Layout(
title= 'New York Black Student Ratio Of School',
xaxis= dict(
title= 'Longitude'
),
yaxis=dict(
title='Latitude'
)
)
fig = go.Figure(data=data, layout=layout)
iplot(fig, filename='scatter_hover_labels')
再画一个图:
f, axes = plt.subplots(2, 2, figsize=(19, 9), sharex=True)
sns.despine(left=True)
sns.regplot(x=df["Economic Need Index"], y=df["Percent Asian"], color='purple', ax=axes[0, 0], line_kws={"color": "black"})
sns.regplot(x=df["Economic Need Index"], y=df["Percent White"], color='g', ax=axes[0, 1], line_kws={"color": "black"})
sns.regplot(x=df["Economic Need Index"], y=df["Percent Black"], color='b', ax=axes[1, 0], line_kws={"color": "black"})
sns.regplot(x=df["Economic Need Index"], y=df["Percent Hispanic"], color='r', ax=axes[1, 1], line_kws={"color": "black"})
axes[0,0].set_title('Ecnomic Need Index (Asian)')
axes[0,1].set_title('Ecnomic Need Index (White)')
axes[1,0].set_title('Ecnomic Need Index (Black)')
axes[1,1].set_title('Ecnomic Need Index (Hispanic)')
plt.subplots_adjust(hspace=0.4)
就说到这里吧,下次继续扯!
参考文献:
https://blog.csdn.net/qq_38360675/article/details/78284329
https://www.cnblogs.com/charlotte77/p/5606926.html
好像还有,忘了……
(如果发现出现引用,并未标注的请联系本文作者。)