数据处理方法
主要采用编写数据处理代码来对数据进行处理步骤,主要采用Python语言,使用相关的Pandas、Numpy、Scikit-learn等库来进行操作,一些代码实例如下。
1、缺失数据处理
数据缺失是指数据集中某行记录或某列特征的变量值存在空值的情况。常用的缺失值处理方法主要包括以下几种:
(1)删除法,若数据集中某行记录或某一列特征的数据缺失比率大于指定阅值时,可以认为该行数据或该列特征为无效数据或无效特征,直接删除含缺失数据的记录即可。
# 参考代码
# 缺失值进行判断
df.isnull()
# 缺失值删除
df.dropna(axis=0, how=‘any’, thresh=None, subset=None, inplace=False)
# pandas删除全为0值的行
df.drop( index = df.age[df1.age == 0].index )
# 参数:
# axis:轴。0或’index’,表示按行删除;1或’columns’,表示按列删除。
# how:筛选方式。‘any’,表示该行/列只要有一个以上的空值,就删除该行/列;‘all’,表示该行/列全部都为空值,就删除该行/列。
# thresh:非空元素最低数量。int型,默认为None。如果该行/列中,非空元素数量小于这个值,就删除该行/列。
# subset:子集。列表,元素为行或者列的索引。如果axis=0或者‘index’,subset中元素为列的索引;如果axis=1或者‘column’,subset中元素为行的索引。由subset限制的子区域,是判断是否删除该行/列的条件判断区域。
# inplace:是否原地替换。布尔值,默认为False。如果为True,则在原DataFrame上进行操作,返回值为None。
(2)基于统计学变量的填充法,这类方法需要根据特征的类型和分布情况决定采用哪种统计学变量进行填充。例如:特征是离散型的,可以直接通过众数对缺失值进行填充;特征是连续数值型并且数据分布比较均匀,可以采用平均数对缺失值进行填充,用全局变量或是属性的平均值来代替所有缺失数据;特征是连续数值型但分布倾斜,可以采用中位数进行填充等。
# 参考代码
# 方法一:直接用Pandas库
# 均值填补数据
data['空缺值所在列名'] = data['空缺值所在列名'].fillna(data['空缺值所在列名'].mean())
catering_sale = "catering_sale.xls"
# 众数填补数据
data['空缺值所在列名'] = data['空缺值所在列名'].fillna(data['空缺值所在列名'].mode()[0])
catering_sale = "catering_sale.xls"
# 中位数填补数据
data['空缺值所在列名'] = data['空缺值所在列名'].fillna(data['空缺值所在列名'].median())
catering_sale = "catering_sale.xls"
# 前一个数据填补数据
data['空缺值所在列名'] = data['空缺值所在列名'].fillna(method='pad')
catering_sale = "catering_sale.xls"
# 后一个数据填补数据
data['空缺值所在列名'] = data['空缺值所在列名'].fillna(method='bfill')
# 方法二:计算平均值等,再进行填充
# 对‘column’列用平均值带替代缺失值NaN计算平均值
c = avg = 0
for ele in df['column']:
if str(ele).isnumeric():
c += 1
avg += ele
avg /= c
# 替换缺失值
df = df.replace(to_replace="NaN",
value=avg)
# 展示数据
df
(3)基于插值的填充法,这类方法主要是通过随机插值、拉格朗日插值、多项式插值等方法对缺失的变量值进行填充。例如:多项式插值法是通过构建多项式来拟合现有的数据,使得所有的样本数据都符合该多项式的分布,需要获取某个样本的缺失值时,通过求解该多项式来获得。
# 参考代码
# 使用库函数进行插值
# Scipy库中的interp1d函数可以实现一维数据的插值,包括线性插值、多项式曲线插值和邻近值插值。
# 定义x,y分别为数据的‘时间轴’和需要插补的‘列名’
x = data['time']
y = data['column']
# xnew构建新的完整的时间序列 = [1,2,3,...23]
xnew = np.linspace(1,23,num=23)
# 线性插值
f1 = interp1d(x,y,kind='linear') # kind替换不同的种类用不同方法进行插值,多项式曲线插值-‘cubic’,邻近值插值-‘nearest’
ynew1 = f1(xnew)
# 自定义函数进行插值
from scipy.interpolate import lagrange # 拉格朗日函数
data=pd.read_excel('data.xls')
# 自定义列向量插值函数
def ploy(s,n,k=6):
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]=ploy(data[i],j)
data.to_excel('data_1.xls')
(4)基于模型的填充法,这类方法是利用有监督的模型或者无监督的模型来实现缺失值的填充。例如:K近邻填充是利用聚类的方式来获得某个缺失样本邻近的若干个样本点,通过对这些样本点计算均值或加权平均来进行缺失值填充。
# 参考代码
# scikit-learn发布0.22版本中,新增的非常好用的缺失值插补方法:KNNImputer。基于KNN算法使得可以更便捷地处理缺失值,并且与直接用均值、中位数相比更为可靠。
from sklearn.impute import KNNImputer
# n_neighbors为选择“邻居”样本的个数,这里n_neighbors=2。
imputer = KNNImputer(n_neighbors=2)
imputer.fit_transform(data)
# IterativeImputer多变量缺失值填补-虑数据在高维空间中的整体分布情况,然后在对有缺失值的样本进行填充。
# IterativeImputer多变量缺失值填补方法
iterimp = IterativeImputer(random_state = 123)
oceandfiter = iterimp.fit_transform(oceandf)
# 获取填充后的变量-column为第5列
column = oceandfiter[:,4]
# MissForest缺失值填补-利用随机森林的思想,进行缺失值填充
# MissForest缺失值填补方法
forestimp = MissForest(n_estimators = 100,random_state = 123)
oceandfforest = forestimp.fit_transform(oceandf)
# 获取填充后的变量-column为第5列
column = oceandfforest[:,4]
(5)热卡填充法,这类方法是在数据集中寻找与缺失样本最相似的样本点,并利用该最相似样本的变量值对缺失数据进行填充。问题关键是不同的问题可能会选用不同的标准来对相似进行判定,以及如何制定这个判定标准。该方法概念上很简单,且利用了数据间的关系来进行空值估计,但缺点在于难以定义相似标准,主观因素较多。
# 参考代码
def hot_deck_imputation(dataframe:pd.DataFrame):
from sklearn.impute import KNNImputer
hot_deck_imputer = KNNlmputer(n_neighbors=2,weights="uniform")#虽然看着是用 KNN,但是参数固定:n neighbors=2
new_df= hot_deck_imputer.fit_transform(dataframe)
return new_df
(6)预测法,这类方法是用预测模型来预测每一个缺失数据。用已有数据作为训练样本来建立预测模型,预测缺失数据。该方法最大限度地利用已知的相关数据,是比较流行的缺失数据处理技术。
# 参考代码
# 一些采用ML或DL方法进行预测来对缺失值进行插补,如RNN方法
# 南开大学Yonghong Luo等人提出的用GAN来进行时间序列插值的算法-算法框架(E2GAN)(文章发表于NIPS 2018 和IJCAI 2019)
1. Luo, Yonghong, et al. "Multivariate time series imputation with generative adversarial networks."Advances in Neural Information Processing Systems. 2018.
2. Luo, Yonghong, et al. "E 2 GAN: end-to-end generative adversarial network for multivariate time series imputation."Proceedings of the 28th International Joint Conference on Artificial Intelligence. AAAl Press, 2019.
2、数据重采样
针对时序数据可采用数据重采样方法,将时间序列从一个频率转换至另一个频率的过程,它主要有两种实现方式,分别是降采样和升采样,降采样指将高频率的数据转换为低频率,升采样则与其恰好相反,将低频率数据转换到高频率。
对于一些高密度传感器,会在毫秒级别产生海量时序数据,因此对此类大量数据进行采样,将数据压缩到秒、分钟、小时等级别,来对数据进行压缩,较少数据量。
可采用Pandas 提供的 resample() 函数来实现数据的重采样。
# 参考代码
DataFrame.resample(rule, on='索引列名', axis=0, fill_method=None, label='left', closed='left')
# on:可以指定某一列使用resample,例如:例如:resample(‘3D’, on=’交易日期’).sum()
# axis=0:默认是纵轴,横轴设置axis=1
# fill_method = None:升采样时如何插值,比如‘ffill’(向上采样)、‘bfill’(向下采样)等
# label= ‘right’:在降采样时,如何设置聚合值的标签,例如,9:30-9:35会被标记成9:30还是9:35,默认9:35
# closed = ‘right’:在降采样时,各时间段的哪一段是闭合的,‘right’或‘left’,默认‘right’
# label=’left’, closed=’left’,建议统一设置成’left’
# rule的取值对应如下:
B 工作日频率
C 自定义工作日频率
D 日历日频率
W 每周频率
M 月末频率
SM 半月末频率(15日和月末)
BM 营业月末频率
CBM 自定义业务月末频率
MS 月初频率
SMS 半个月开始频率(第1次和第15次)
BMS 营业月开始频率
CBMS 自定义营业月开始频率
Q 四分之一端频率
BQ 业务季度末频率
QS 四分之一起始频率
BQS 业务季度开始频率
A 年终频率
BA 营业年末频率
AS 年份起始频率
BAS 业务年度开始频率
BH 营业时间频率
H 每小时频率
T 分钟频率
S 第二频率
L 毫秒
U 微秒
N 纳秒
data_h =data_index.resample('H').first()
data_h
3、离群值处理
当数据中的某个数据点明显偏离于其他数据点的分布或者某个数据点明显区别于其他的数据点时,将其判定为离群点(异常值),对离群值可采用异常数据检测的方法,检测异常值并将其进行去除。
异常数据检测主要包括以下几种方法:
(1)基于统计分析的方法,通过特征的描述信息以及特征值范围来判断数据是否异常。例如,对于年龄特征,规约其值的范围是[0,200],当出现了负数或者大于200的数,则判断为异常数据。
(2)基于密度的方法,通过离群点的局部密度显著低于大部分近邻点的特点进行判定,适用于非均匀的数据集。
(3)基于聚类的方法,一般正常的数据点呈现“物以类聚”的聚合形态,正常数据出现在密集的邻域周围,而异常点偏离较远,以此来对数据进行判定异常。
(4)基于树的方法,通过划分来判定异常。如孤立森林(Isolation Forest,iForest)被认为是最有效的异常检测方法之一,该方法是通过计算样本点的异常关联度分数来进行异常判定,若某样本得到的异常关联度分数较高,且大于阅值时可以判定其为异常。
(5)基于预测的方法,对时序数据根据其预测出来的时序曲线和真实的数据相比,来判定异常值的出现。
具体一些方法介绍如下:
(1) 基于统计分布的异常检测
数据分布模型可以通过估计概率分布的参数来创建。如果一个对象不能很好地同该模型拟合,即如果它很可能不服从该分布,则它是一个异常。
3σ-法则
假设一组检测数据只含有随机误差,对原始数据进行计算处理得到标准差,然后按一定的概率确定一个区间,认为误差超过这个区间的就属于异常值。
(μ−3σ,μ+3σ)区间内的概率为99.74。所以可以认为,当数据分布区间超过这个区间时,即可认为是异常数据。
假设数据集由一个正太分布产生,该分布可以用 N(μ,σ) 表示,其中 μ 是序列的均值,σ是序列的标准差,数据落在 (μ-3σ,μ+3σ) 之外的概率仅有0.27%,落在 (μ-4σ,μ+4σ) 之外的区域的概率仅有0.01%,可以根据对业务的理解和时序曲线,找到合适的K值用来作为不同级别的异常报警。
# 参考代码
#3-sigma识别异常值
def three_sigma(df_col): #df_col:DataFrame数据的某一列
rule = (df_col.mean() - 3 * df_col.std() > df_col) | (df_col.mean() + 3 * df_col.std() < df_col)
index = np.arange(df_col.shape[0])[rule]
outrange = df_col.iloc[index]
return outrange
# 另一种方式识别低值和高值
def three_sigma(s):
mu, std = np.mean(s), np.std(s)
lower, upper = mu-3*std, mu+3*std
return lower, upper
Z-score
Z-score为标准分数,测量数据点和平均值的距离,若A与平均值相差2个标准差,Z-score为2。当把Z-score=3作为阈值去剔除异常点时,便相当于3sigma。
# 参考代码
def z_score(s):
z_score = (s - np.mean(s)) / np.std(s)
return z_score
MA滑动平均法
识别数据不规则性的最简单的方法是标记偏离分布的数据点,包括平均值、中值、分位数和模式。
假定异常数据点是偏离平均值的某个标准偏差,那么我们可以计算时间序列数据滑动窗口下的局部平均值,通过平均值来确定偏离程度。这被技术称为滑动平均法(moving average,MA),旨在平滑短期波动并突出长期波动。滑动平均还包括累加移动平均、加权移动平均、指数加权移动平均、双指数平滑、三指数平滑等,在数学上,nn周期简单移动平均也可以定义为“低通滤波器”。
该方法有明显缺陷:数据中可能存在与异常行为类似的噪声数据,所以正常行为和异常行为之间的界限通常不明显;异常或正常的定义可能经常发生变化,基于移动平均值的阈值可能并不总是适用。
# 参考代码
# 简单移动平均线(Simple Moving Average,SMA)|加权移动平均(Weighted Moving Average,WMA)|指数加权移动平均(exponentially weighted moving average,EWMA)
# python pandas包中,对于ewma和sma有现成的实现方法,这里对于wma进行代码的编写处理
class WMA(object):
"""
加权移动平均实现
"""
def get_wma_weights(span, flag=True):
"""
计算每个数值点的wma权重值
"""
paras = range(1, span + 1)
count = sum(paras)
if flag:
return [float(para) / count for para in paras]
else:
return [float(para) / count for para in paras][::-1]
def get_wma_values(self, datas):
"""
计算wma数值
"""
wma_values = []
wma_keys = datas.index
for length in range(1, len(datas) + 1):
wma_value = 0
weights = self.get_wma_weights(length)
for index, weight in zip(datas.index, weights):
wma_value += datas[index] * weight
wma_values.append(wma_value)
return pd.Series(wma_values, wma_keys)
# 计算异常值
def calculate_variance(data, moving_average):
variance = 0
flag_list = moving_average.isnull()
count = 0
for index in range(len(data)):
if flag_list[index]:
count += 1
continue
variance += (data[index] - moving_average[index]) ** 2
variance /= (len(data) - count)
return variance
# ewma进行拟合
ewma_line = pd.ewma(data, span=4)
# 简单移动平均
sma_line = pd.rolling_mean(data, window=4)
# wma加权移动平均
wma_line = WMA().get_wma_values(data)
sma_var = calculate_variance(data, sma_line)
wma_var = calculate_variance(data, wma_line)
ewma_var = calculate_variance(data, ewma_line)
boxplot箱型图(分位数异常检测)
箱型图,是一种用作显示一组数据分散情况资料的统计图。主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比较,其绘制方法是:先找出一组数据的最大值、最小值、中位数和上下两个四分位数。通过不同分位数来划分异常值和疑似异常值。
IQR是第三四分位数减去第一四分位数,大于Q3+1.5IQR之外的数和小于Q1-1.5*IQR的值被认为是异常值。
# 参考代码
# 定义箱线图识别异常值函数
def box_plot(Ser):
'''
Ser:进行异常值分析的DataFrame的某一列
'''
Low = Ser.quantile(0.25)-1.5*(Ser.quantile(0.75)-Ser.quantile(0.25))
Up = Ser.quantile(0.75)+1.5*(Ser.quantile(0.75)-Ser.quantile(0.25))
index = (Ser< Low) | (Ser>Up)
Outlier = Ser.loc[index]
return(Outlier)
box_plot(df['counts']).head(8)
# 另一种形式识别低值和高值
def boxplot(s):
q1, q3 = s.quantile(.25), s.quantile(.75)
iqr = q3 - q1
lower, upper = q1 - 1.5*iqr, q3 + 1.5*iqr
return lower, upper
Grubbs异常检验
Grubbs测试是一种从样本中找出outlier的方法,所谓outlier,是指样本中偏离平均值过远的数据,他们有可能是极端情况下的正常数据,也有可能是测量过程中的错误数据。使用Grubbs测试需要总体是正态分布的。
算法流程:
①.样本从小到大排序
②.求样本的mean和std.dev [均值和标准差]
③.计算min/max与mean的差距,更大的那个为可疑值
④.求可疑值的z-score (standard score),如果大于Grubbs临界值,那么就是outlier;
⑤.Grubbs临界值可以查表得到[它由两个值决定:检出水平α(越严格越小)、样本数量n],排除outlier,对剩余序列循环做①-④步骤。
由于这里需要的是异常判定,只需要判断tail_avg是否outlier即可。
# 参考代码
# 使用grubbs对应的库直接调用
# 添加包 pip install outlier_utils==0.0.3
from outliers import smirnov_grubbs as grubbs
grubbs.test(data, alpha) # 默认双边检验
# https://github.com/c-bata/outlier-utils
(2) 基于密度的异常检测
基于密度的异常检测有一个先决条件,即正常的数据点呈现“物以类聚”的聚合形态,正常数据出现在密集的邻域周围,而异常点偏离较远。
对于这种场景,我们可以计算得分来评估最近的数据点集,这种得分可以使用Eucledian距离或其它的距离计算方法,具体情况需要根据数据类型来定:类别型或是数字型。
对象的密度估计(1)可以相对直接地计算,特别是当对象之间存在邻近性度量时,低密度区域中的对象相对远离近邻,可能被看作异常。一种更复杂的方法考虑到(2)数据集可能有不同密度区域这一事实,仅当一个点的局部密度显著地低于它的大部分近邻时才将其分类为离群点。
- 基于密度的异常分的计算公式:
density ( x , k ) = ( ∑ y ∈ N ( x , k ) distance ( x , y ) ∣ N ( x , k ) ∣ ) − 1 \operatorname{density}(x, k)=\left(\frac{\sum_{y \in N(x, k)} \operatorname{distance}(x, y)}{|N(x, k)|}\right)^{-1} density(x,k)=(∣N(x,k)∣∑y∈N(x,k)distance(x,y))−1
其中 N ( x , k ) N(x,k) N(x,k)指的是x的k个最近的邻居的集合, ∣ N ( x , k ) ∣ |N(x,k)| ∣N(x,k)∣表示该集合的大小,y是x最近的邻居。
LOF
局部异常因子算法,全称Local Outlier Factor(简写LOF)。LOF算法是一种无监督的异常检测方法,它计算给定数据点相对于其邻居的局部密度偏差。每个样本的异常分数称为局部异常因子。异常分数是局部的,取决于样本相对于周围邻域的隔离程度。确切地说,局部性由k近邻给出,并使用距离估计局部密度。通过将样本的局部密度与其邻居的局部密度进行比较,可以识别密度明显低于其邻居的样本,,这些样本就被当做是异常样本点。
数据点 P \mathrm{P} P 的局部相对密度(局部异常因子)为点 P \mathrm{P} P 邻域内点的平均局部可达密度跟数据点 的 P \mathrm{P} P局部可达密度的比值, 即:
L O F k ( P ) = ∑ O ∋ N k ( P ) lr ( O ) lr ( P ) ∣ N k ( P ) ∣ = ∑ O ∋ N k ( P ) lrd ( O ) ∣ N k ( P ) ∣ / lrd ( P ) L O F_k(P)=\frac{\sum_{O \ni N_k(P)} \frac{\operatorname{lr}(O)}{\operatorname{lr}(P)}}{\left|N_k(P)\right|}=\frac{\sum_{O \ni N_k(P)} \operatorname{lrd}(O)}{\left|N_k(P)\right|} / \operatorname{lrd}(P) LOFk(P)=∣Nk(P)∣∑O∋Nk(P)lr(