数据预处理的过程包括数据清洗、数据集成、数据变换和规约。获取到数据后的第一步,是要进行数据清洗,主要是删除原始数据集中的无关数据,重复数据,筛选掉与挖掘主题无关的数据,处理缺失值、异常值。
缺失值处理的方法可以分为三类:删除记录、数据插补和不处理。
删除记录,如果记录是空行,可以在读取数据之前通过Pandas来过滤:
df.dropna(how='all',inplace=True) # 删除全空的行
数据中是否存在重复记录。如果存在重复记录,就使用 Pandas 提供的 drop_duplicates() 来删除重复数据。
df.drop_duplicates(how='all',inplace=True) # 删除重复数据行
常见的插补方法如下:
插补方法 | 方法描述 |
---|---|
均值/中位数/众数插补 | 根据属性值的类型,用该属性取值的均值/中位数/众数插补 |
使用固定值 | 将缺失值用常量替换 |
最近临插补 | 在记录中找到与缺失样本最接近的样本的该属性值插补 |
回归方法 | 对含有缺失值的变量,根据已有数据和与有关的其他变量(因变量)的数据建立拟合模型来预测缺失值 |
插值法 | 插值法是利用已知点建立合适的插值函数f(x),未知值由对应点Xi求出的函数值f(Xi)近似代替 |
如果是使用均值进行插补,可以使用:
df['column'].fillna(df['column'].mean(), inplace=True)
如果用最高频的数据或是众数(如果列数据时数值)进行填充,可以先通过value_counts获取column字段最高频次column_maxf,然后再对字段中缺失的数据用column_maxf进行填充:
column_maxf = train_features['cloumn'].value_counts().index[0]
train_features['column'].fillna(column_maxf, inplace=True)
如果缺失值的数量很少我们可以考虑删除缺失值并且对目标不产生影响,这样的方法简便。但丢弃缺失值会对结果产生影响,具有局限性。将会舍弃掉缺失值中含有的影藏信息,尤其在数据集本身含有的记录就很少的时候,删除少量记录可能会影响到结果的客观性和正确性。一些模型可以将缺失值是做一种特殊的取值,允许直接在含有缺失值的数据上进行建模。
我们在这里介绍两个插值法,分别是拉格朗日插值法和牛顿插值法。其他的插值法还有Hermite插值、分段插值、样条插值等。下面的数学方法,阅读后了解原理即可,会有相应包的模块帮助我们实现。
(1) 拉格朗日插值法
根据数学知识可知,对于平面上已知的n个点 (无两点在一条直线上)可以找到一个n-1次多项式 y=a0 +a1 x+a2 x2 +...+an-1 xn-1 ,使此多项式曲线过这n个点。
1)求已知的过n个点的n-1次多项式:
y=a0 +a1 x+a2 x +...+an-1 x (4-1)
将n个点的坐标(x1 ,y1 ),(x2 ,y2 )...(xn ,yn )代入多项式函数,得
解出拉格朗日插值多项式为:
2)将缺失的函数值对应的点x代入插值多项式得到缺失值的近似值L(x)。
拉格朗日插值公式结构紧凑,在理论分析中很方便,但是当插值节点增减时,插值多项式就会随之变化,在实际计算中是很不方便的,为了克服这一缺点,提出了牛顿插值法。
(2)牛顿插值法
1)求已知的n个点对(x1 ,y1 ),(x2 ,y2 )...(xn ,yn )的所有阶差商公式
2)联立以上差商公式建立如下插值多项式f(x)
P(x)是牛顿插值逼近函数,R(x)是误差函数。
3)将缺失的函数值对应的点x代入插值多项式得到缺失值的近似值f(x)。
牛顿插值法也是多项式插值,但采用了另一种构造插值多项式的方法,与拉格朗日插值相比,具有承袭性和易于变动节点的特点。从本质上来说,两者给出的结果是一样的(相同次数、相同系数的多项式),只不过表示的形式不同。因此,在Python的Scipy库中,只提供了拉格朗日插值法的函数(因为实现上比较容易),如果需要牛顿插值法,则需要自行编写函数。
餐饮系统中的销量数据可能会出现缺失值,如下表为某餐厅一段时间的销量表,其中2020年2月14日的数据缺失,用拉格朗日插值对缺失值进行插补的Python程序实现。
公众号回复DT12,获取数据,获取数据后请将后缀名.csv改为.xls。
#拉格朗日插值代码
import pandas as pd #导入数据分析库Pandas
from scipy.interpolate import lagrange #导入拉格朗日插值函数
inputfile = 'catering_sale.xls' #销量数据路径
outputfile = 'sales.xls' #输出数据路径
data = pd.read_excel(inputfile) #读入数据
data[u'销量'][(data[u'销量'] < 400) | (data[u'销量'] > 5000)] = None #过滤异常值,将其变为空值
#自定义列向量插值函数
#s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5
def ployinterp_column(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_column(data[i], j)
data.to_excel(outputfile) #输出结果,写入文件
应用拉格朗日插值法算对表中的缺失值和异常值进行插补,发现2/21/2020的原销量6607.4插值为4275.255;2/14/2020的原销量为空值插值为4156.86。
在进行插值之前会对数据进行异常值检测,2020/2/21日的数据是异常的(数据大于5000),所以也把此日期数据定义为空缺值,进行补数。利用拉格朗日插值对这2015/2/21和 2015/2/14的数据进行插补,结果是4275.255和4156.86,这两天都是周末,而周末的销售额一般要比周一到周五要多,所以插值结果比较符合实际情况。
在数据预处理时,异常值是否剔除,需视具体情况而定,因为有些异常值可能蕴含着有用的信息。异常值处理常用方法如下表。
异常值处理方法 | 方法描述 |
---|---|
删除含有异常值的记录 | 直接将异常值的记录删除 |
视作缺失值 | 利用缺失值的方法处理 |
平均值修正 | 可用前后两个观测值的平均值修正 |
不处理 | 直接在具有异常值的数据集上进行分析和挖掘 |
将含有异常值的记录直接删除的方法简单易行,但缺点也很明显,在观测值很少的情况下,这种删除会造成样本量不足,可能会改变变量的原有分布,从而造成分析结果的不准确。视 为缺失值处理的好处是可以利用现有变量的信息,对异常值(缺失值)进行填补。
在很多情况下,要先分析异常值出现的可能原因,再判断异常值是否应该舍弃,如果是正确的数据,可以直接在具有异常值的数据集上进行挖掘建模。
数据清洗除了对异常值和缺失值进行处理外,本身数据的质量还存在一些问题,也要观察数据后对其进行变换,这里涉及一小不分的变换,下面仅列出两种常见的情况:
1.列数据的单位不统一
我们在统计重量时会发现,有的时候重量单位是一磅,有的时候是一公斤,此时我们需要对单位进行统一,这里使用千克。
# 获取 weight 数据列中单位为 lbs 的数据
rows_with_lbs = df['weight'].str.contains('lbs').fillna(False)
# 将 lbs转换为 kgs, 2.2lbs=1kgs
for i,lbs_row in df[rows_with_lbs].iterrows(): #遍历df,iterrows()返回值为元组,(index,row)
# 截取倒数三个字符,即去掉lbs。
weight = int(float(lbs_row['weight'][:-3])/2.2)
df.at[i,'weight'] = '{}kgs'.format(weight)
2.拆分列,一列有多个参数
如果我们想分别分析姓和名,我们可以将一列name拆分成两列firstname 和 lastname:
# 切分名字,删除源数据列
df[['first_name','last_name']] = df['name'].str.split(expand=True)
df.drop('name', axis=1, inplace=True)