第3章 11条数据化运营不得不知道的数据预处理经验
目录:
3.1 数据清洗:缺失值、异常值和重复值的处理
3.2 将分类数据和顺序数据转化为标志变量
3.3 大数据时代的降维
3.4 解决样本类别分布不均衡的问题
3.5 解决运营数据源的冲突问题
3.6 数据化运营要抽样还是全量数据
3.7 解决运营数据的共线性问题
3.1 数据清洗:缺失值、异常值和重复值的处理
1.缺失值处理
import pandas as pd
import numpy as np
#显示所有列
pd.set_option('display.max_columns', None)
#显示所有行
pd.set_option('display.max_rows', None)
#生成缺失数据
df=pd.DataFrame(np.random.randn(6,4),columns=['col1','col2','col3','col4'])
df.iloc[1:3,1]=np.nan
df.iloc[4,3]=np.nan
print(df)
输出:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 NaN 0.974266 0.423142
2 0.426727 NaN -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 NaN
5 1.219927 -0.736849 0.567410 0.218980
查看缺失:
#查看哪些值缺失
nan_all=df.isnull()
输出:---------
col1 col2 col3 col4
0 False False False False
1 False True False False
2 False True False False
3 False False False False
4 False False False True
5 False False False False
#查看哪些列有缺失值
nan_col=df.isnull().any(axis=0)
输出:---------
col1 False
col2 True
col3 False
col4 True
dtype: bool
数据缺失的处理办法:
(1)丢弃
——注意不适用情况:缺失>10%;大量存在明显的数据分布规律和特征
#丢弃缺失值
df2=df.dropna()
输出:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
3 0.400797 -1.878966 -0.785209 -0.437353
5 1.219927 -0.736849 0.567410 0.218980
#丢弃缺失值后对index重新赋值
df2.reset_index(drop=True,inplace=True)
输出:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.400797 -1.878966 -0.785209 -0.437353
2 1.219927 -0.736849 0.567410 0.218980
(2)补全
——统计法:均值、中值、众数;
#使用sklearn将缺失值替换为特定值
from sklearn.preprocessing import Imputer
nan_model=Imputer(missing_values='NaN',strategy='mean',axis=0)
nan_result=nan_model.fit_transform(df)
输出:---------
[[-0.51457124 1.90388265 -0.11835919 0.13848576]
[ 0.44613788 -0.10575195 0.97426573 0.42314162]
[ 0.42672655 -0.10575195 -0.68370344 -0.72688408]
[ 0.40079702 -1.87896628 -0.78520927 -0.43735306]
[-0.17296966 0.28892473 -0.42190582 -0.07672596]
[ 1.21992651 -0.73684892 0.56741006 0.21897998]]
#使用pandas将缺失值替换为特定值
nan_result_pd1=df.fillna(method='backfill') #用后面的值替换缺失值
nan_result_pd2=df.fillna(method='pad') #用前面的值替换缺失值
输出1:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 -1.878966 0.974266 0.423142
2 0.426727 -1.878966 -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 0.218980
5 1.219927 -0.736849 0.567410 0.218980
输出2:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 1.903883 0.974266 0.423142
2 0.426727 1.903883 -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 -0.437353
5 1.219927 -0.736849 0.567410 0.218980
nan_result_pd3=df.fillna(0) #用0替换缺失值
nan_result=df.replace(np.nan,0) #用0替换缺失值
输出1:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 0.000000 0.974266 0.423142
2 0.426727 0.000000 -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 0.000000
5 1.219927 -0.736849 0.567410 0.218980
输出2:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 0.000000 0.974266 0.423142
2 0.426727 0.000000 -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 0.000000
5 1.219927 -0.736849 0.567410 0.218980
nan_result_pd4=df.fillna({'col2':1.1,'col4':1.2}) #用不同值替换不同列缺失值
输出:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 1.100000 0.974266 0.423142
2 0.426727 1.100000 -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 1.200000
5 1.219927 -0.736849 0.567410 0.218980
nan_result_pd5=df.fillna(df.mean()) #用平均值替换缺失值
nan_result_pd6=df.fillna(df.mean()['col2':'col4']) #用平均值替换缺失值
输出1:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 -0.105752 0.974266 0.423142
2 0.426727 -0.105752 -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 -0.076726
5 1.219927 -0.736849 0.567410 0.218980
输出2:---------
col1 col2 col3 col4
0 -0.514571 1.903883 -0.118359 0.138486
1 0.446138 -0.105752 0.974266 0.423142
2 0.426727 -0.105752 -0.683703 -0.726884
3 0.400797 -1.878966 -0.785209 -0.437353
4 -0.172970 0.288925 -0.421906 -0.076726
5 1.219927 -0.736849 0.567410 0.218980
——模型法:回归模型、分类模型
——专家补全
(3)真值转换(后面细说)
(4)不处理
——常见的能够自动处理缺失值的模型:KNN、决策树、随机森林、神经网络、朴素贝叶斯、DBSCAN等。
异常检测模型(无需抛弃):
常用于客户异常识别、信用卡欺诈、贷款审批识别、药物变异识别、恶劣气象预测、网络入侵检测、流量作弊检测等。
2.异常值处理
df=pd.DataFrame({'col1':[1,120,3,5,2,12,13],'col2':[12,17,31,53,22,32,43]}) #生成异常数据
输出:---------
col1 col2
0 1 12
1 120 17
2 3 31
3 5 53
4 2 22
5 12 32
6 13 43
df_zscore=df.copy()
cols=df.columns #获得数据框列名
for col in cols:
df_col=df[col]
z_score=(df_col-df_col.mean())/df_col.std() #计算每列的zscore得分
df_zscore[col]=z_score.abs()>2.2 #Zscore=2.2对应置信度98.6%
print(df_zscore)
输出:---------
col1 col2
0 False False
1 True False
2 False False
3 False False
4 False False
5 False False
6 False False
3.重复值处理
#重复值处理
data1=['a',3]
data2=['b',2]
data3=['a',3]
data4=['c',2]
df=pd.DataFrame([data1,data2,data3,data4],columns=['col1','col2']) #生成重复值
isDuplicated=df.duplicated() #判断重复数据记录
输出:---------
0 False
1 False
2 True
3 False
dtype: bool
new_df1=df.drop_duplicates()
#df.drop_duplicates(['col1','col2'])——这种方法可以指定列删除相同记录
输出:---------
col1 col2
0 a 3
1 b 2
3 c 2
3.2 将分类数据和顺序数据转化为标志变量
分类数据:没有逻辑上大小、高低、好坏和顺序的等级差别,如性别、颜色。
顺序数据:有排序规律和逻辑层次上的划分。如学历、用户价值等级。
将非数值型型数据转为数值型数据的最佳方法:将所有分类或顺序变量的值域从一列多值的形态转化为多列只包含真值的形态,其中的真值可通过0、1的方式来表示。
本书中介绍了手动转换、sklearn转换两种方法,但是sklearn提供的OneHotEncoder()方法的缺点是:1)无法使用字符串做转换,2)转换后列的顺序打乱,这样之后的变量不清楚对应之前的那个属性,比较麻烦。
解释模型中,建议使用手动转换。分析模型中,过程作为黑盒子,仅强调预测结果的准确性和实效性的话,可以选择sklearn转换。
from sklearn.preprocessing import OneHotEncoder #生成原始数据
df=pd.DataFrame({'id':[3566841,6541227,3512441],
'sex':['male','female','female'],
'level':['high','low','middle']})
输出:---------
id sex level
0 3566841 male high
1 6541227 female low
2 3512441 female middle
#自定义转化主过程
df_new=df.copy()
for col_num,col_name in enumerate(df):
col_data=df[col_name] #获得每列数据
col_dtype=col_data.dtype
if col_dtype=='object': #如果dtype类型是object(非数值型)
df_new=df_new.drop(col_name,1) #删除df数据框中要进行标志转换的列
value_sets=col_data.unique() #获取分类和顺序变量的唯一值域
for value_unique in value_sets:
col_name_new=col_name+'_'+value_unique
col_tmp=df.iloc[:,col_num]
df_new[col_name_new]=(col_tmp==value_unique)
print(df_new)
输出:---------
id sex_male sex_female level_high level_low level_middle
0 3566841 True False True False False
1 6541227 False True False True False
2 3512441 False True False False True
from sklearn.preprocessing import OneHotEncoder #生成原始数据
df2=pd.DataFrame({'id':[3566841,6541227,3512441],
'sex':[1,2,2],
'level':[3,1,2]})
输出:---------
id sex level
0 3566841 1 3
1 6541227 2 1
2 3512441 2 2
#使用sklearn进行标志转换
id_data=df2.values[:,:1] #获得ID列
transform_data=df2.values[:,1:] #指定要转换的列
enc=OneHotEncoder()
df2_new=enc.fit_transform(transform_data).toarray() #标志转换
df2_all=pd.concat((pd.DataFrame(id_data),pd.DataFrame(df2_new)),axis=1) #列合并组合成数据框
print(df2_all)
输出:---------
0 0 1 2 3 4
0 3566841 1.0 0.0 0.0 0.0 1.0
1 6541227 0.0 1.0 1.0 0.0 0.0
2 3512441 0.0 1.0 0.0 1.0 0.0
3.3 大数据时代的数据降维
解释模型需要完整保留原始特征,可以使用基于特征选择的降维。
分析模型可以使用基于唯独转换的降维。
1.基于特征选择的降维
如果没有业务的理解和支持,再好的数据、模型和算法都无法落地。基于特征选择的降维很好的保留了原始维度,便于业务方理解和直接使用模型输出规则(如决策树)。
方法:经验法、测算法、统计分析(线性相关性、互信息)、机器学习算法。
2.基于维度转换的降维
常用的代表算法:独立成分分析(ICA)、主成分分析(PCA)、因子分析(FA)、线性判别分析(LDA)、核主成分分析(Kernel PCA)。
PCA的适用场景
- 非监督式数据集(对于带标签的数据集则可以采用LDA)
- 根据方差自主选择特征数量(取决于解释方差比例)
- 更少的正则化处理
- 数据量较大的数据集
- 数据分布是位于相同平面上(非曲面),数据中存在线性结构。
下面就这两种方法举例:
from sklearn.tree import DecisionTreeClassifier
from sklearn.decomposition import PCA
data=np.loadtxt('data1.txt')
x=data[:,:-1] #获得输入变量x
y=data[:,-1] #获得目标变量y
print(x[0],y[0])
输出:---------
[ 1.88622997 1.31785876 -0.16480621 0.56536882 -1.11934542 -0.53218995
-0.6843102 1.24149827 1.00579225 0.45485041] 0.0
#使用sklearn的DecisionTreeClassifier判断变量重要性
model_tree=DecisionTreeClassifier(random_state=0)
model_tree.fit(x,y)
feature_importance=model_tree.feature_importances_
print(feature_importance)
输出:---------
[0.03331054 0.01513967 0.02199713 0.119727 0.47930312 0.04776297
0.17111746 0.02585441 0.02012725 0.06566044]
#使用sklearn的PCA进行维度转换
model_pca=PCA()
model_pca.fit(x)
model_pca.transform(x)
components=model_pca.components_ #转换后的所有主成分
components_var=model_pca.explained_variance_ #转换后的所有主成分的方差
components_var_ratio=model_pca.explained_variance_ratio_ #转换后的所有主成分的方差占比
print(components[:2]) #打印前两个主成分
print(components_var[:2]) #打印前两个主成分的方差
print(components_var_ratio) #打印前两个主成分的方差占比
print(components_var_ratio[:5].sum()) #打印前6项的主成分方差占比之和
输出:---------
[[ 7.18818316e-03 1.41619205e-02 1.00543847e-02 3.65097575e-01
6.38944537e-01 -1.95750380e-02 -1.73413378e-01 -3.80829974e-02
-2.87413113e-03 -6.52829504e-01]
[ 1.01307710e-02 -1.95270201e-04 -2.33689543e-02 -6.12915216e-01
5.08983971e-01 -2.23429533e-02 6.02958940e-01 -1.49061329e-02
-1.81362216e-02 -3.41623971e-03]]
[4.22602937 2.21149972]
[3.38339364e-01 1.77054475e-01 8.92753857e-02 8.73655166e-02
8.23542686e-02 8.03329836e-02 7.38094896e-02 7.14685179e-02
4.57861873e-32 4.40914865e-33]
0.774389008950177
前6个主成分方差占比之和达到77%,基本可以作为转换后的主成分参与模型计算。
3.4解决样本类别分布不均衡的问题
样本分类不均衡是指不同类别样本量差异非常大,主要出现在分类相关的建模问题上。样本量差异超过10倍就需要引起警觉,超过20倍就一定要解决。
哪些运营场景中容易出现样本不均衡?
- 异常检测场景。异常个案都是少量的,如恶意刷单、黄牛订单、信用卡欺诈、电力窃电、设备故障等。
- 客户流失场景。
- 罕见和低频事件。
如何解决样本不均衡问题?
(1)通过过抽样和欠抽样解决样本不均衡
过抽样:
简单复制少数类样本形成多条记录;
少数类中加入随机噪声、干扰数据;
通过一定规则产生新的合成样本(如SMOTE算法)
欠抽样:
减少多数类样本的数量来实现样本均衡。
(2)通过正负样本的惩罚权重解决样本不均衡
思想:小样本量类别权重高,大样本量类别权重低。
不需要对样本本身做额外处理,只需在算法模型的参数中进行相应设置即可。如sklearn中的SVM通过在class_weight:{dict, 'balanced'}中针对不同类别来手动指定权重。
(3)通过组合/集成方法解决样本不均衡
思想:每次生成训练集时使用小样本量,同时从大样本量中随机抽取数据与小样本量组合成训练集。
(4)通过特征选择解决样本不均衡
(这部分没看懂,略)
Python处理样本不均衡实操
准备工作:使用pip install imbalanced-learn
语句安装库。
#导入数据文件
df=pd.read_table('data2.txt', sep=' ',names=['col1','col2','col3','col4','col5','label'])
x=df.iloc[:,:-1]
y=df.iloc[:,-1]
groupby_data_original=df.groupby('label').count()
print(groupby_data_original)
输出:---------
col1 col2 col3 col4 col5
label
0.0 942 942 942 942 942
1.0 58 58 58 58 58
#使用SMOTE方法进行过抽样处理
model_smote=SMOTE()
x_smote_resampled,y_smote_resampled=model_smote.fit_sample(x,y) #输入数据并做抽样处理
x_smote_resampled=pd.DataFrame(x_smote_resampled,columns=['col1','col2','col3','col4','col5'])
y_smote_resampled=pd.DataFrame(y_smote_resampled,columns=['label'])
smote_resambled=pd.concat([x_smote_resampled,y_smote_resampled],axis=1)
groupby_data_smote=smote_resambled.groupby('label').count()
print(groupby_data_smote)
输出:---------
col1 col2 col3 col4 col5
label
0.0 942 942 942 942 942
1.0 942 942 942 942 942
#使用RandomUnderSampler方法进行欠抽样处理
model_rus=RandomUnderSampler()
x_rus_resampled,y_rus_resampled=model_rus.fit_sample(x,y)
x_rus_resampled=pd.DataFrame(x_rus_resampled,columns=['col1','col2','col3','col4','col5'])
y_rus_resampled=pd.DataFrame(y_rus_resampled,columns=['label'])
rus_resampled=pd.concat([x_rus_resampled,y_rus_resampled],axis=1)
groupby_data_rus=rus_resampled.groupby('label').count()
print(groupby_data_rus)
输出:---------
col1 col2 col3 col4 col5
label
0.0 58 58 58 58 58
1.0 58 58 58 58 58
#使用SVM的权重调节处理不均衡样本
model_svm=SVC(class_weight='balanced') #创建SVC模型对象并指定类别权重
model_svm.fit(x,y)
#使用集成方法EasyEnsemble处理不均衡样本
model_ee=EasyEnsemble()
x_ee_resambled,y_ee_resambled=model_ee.fit_sample(x,y)
print(x_ee_resambled.shape)
print(y_ee_resambled.shape)
#抽取一份样本做审查
index_num=1
x_ee_resambled_t=pd.DataFrame(x_ee_resambled[index_num],columns=['col1','col2','col3','col4','col5'])
y_ee_resambled_t=pd.DataFrame(y_ee_resambled[index_num],columns=['label'])
ee_resampled=pd.concat([x_ee_resambled_t,y_ee_resambled_t],axis=1)
groupby_data_ee=ee_resampled.groupby('label').count()
print(groupby_data_ee)
输出:---------
(10, 116, 5)
(10, 116)
col1 col2 col3 col4 col5
label
0.0 58 58 58 58 58
1.0 58 58 58 58 58
3.5 解决运营数据源的冲突问题
这部分我想略过,书中介绍的很全,不复制了。实际工作中最重要的还是熟悉数据逻辑、要跟产研对口径,然而这样也并不一定能找到(或者说确定)原因,数据上相差不要太大即可。
3.6 数据化运营要抽样还是全量数据
抽样样本量问题:
- 以时间为维度分布的,至少包含一个能满足预测的完整业务周期。
- 做预测分析建模的,通常:记录数>=100✖️∑特征值域。
- 做关联规则分析的,每个主体至少需要1000条数据。
- 对于异常检测类分析建模的,异常样本越多越好。
3.7 解决运营数据的共线性问题
共线性指的是输入的自变量之间存在较高的线性相关度。
共线性问题会导致回归模型的稳定性和准确性大大降低,同时也浪费计算资源和时间。
解决共线性的5种常用方法:
(1)增大样本量,消除数据量不足出现偶然共线性
(2)岭回归法(Ridge Regression)
(3)逐步回归法(Stepwise Regression)
(4)主成分回归(Principal Components Regression)
(5)人工去除