复杂的数据处理过程(含清洗) 1. 数据解读 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 merchant = pd.read_csv('./eloData/merchants.csv' , header=0 ) merchant.head(5 ) merchant.info() ''' Output exceeds the size limit. Open the full output data in a text editor <class 'pandas.core.frame.DataFrame'> RangeIndex: 334696 entries, 0 to 334695 Data columns (total 22 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 merchant_id 334696 non-null object 1 merchant_group_id 334696 non-null int64 2 merchant_category_id 334696 non-null int64 3 subsector_id 334696 non-null int64 4 numerical_1 334696 non-null float64 5 numerical_2 334696 non-null float64 6 category_1 334696 non-null object 7 most_recent_sales_range 334696 non-null object 8 most_recent_purchases_range 334696 non-null object 9 avg_sales_lag3 334683 non-null float64 10 avg_purchases_lag3 334696 non-null float64 11 active_months_lag3 334696 non-null int64 12 avg_sales_lag6 334683 non-null float64 13 avg_purchases_lag6 334696 non-null float64 14 active_months_lag6 334696 non-null int64 15 avg_sales_lag12 334683 non-null float64 16 avg_purchases_lag12 334696 non-null float64 17 active_months_lag12 334696 non-null int64 18 category_4 334696 non-null object 19 city_id 334696 non-null int64 ... 20 state_id 334696 non-null int64 21 category_2 322809 non-null float64 dtypes: float64(9), int64(8), object(5) memory usage: 56.2+ Mb ''' df = pd.read_excel('./eloData/Data_Dictionary.xlsx' , header=2 , sheet_name='merchant' ) df
2. 数据探索
查看id出现次数是否唯一
1 2 3 4 5 6 7 8 print (merchant.shape, merchant['merchant_id' ].nunique())''' (334696, 22) 334633 能够看出,该表并不是一个id对应一条数据,存在一个商户有多条记录的情况。 ''' print (pd.Series(merchant.columns.tolist()).sort_values().values == pd.Series([va[0 ] for va in df.values]).sort_values().values)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 merchant.isnull().sum () ''' merchant_id 0 merchant_group_id 0 merchant_category_id 0 subsector_id 0 numerical_1 0 numerical_2 0 category_1 0 most_recent_sales_range 0 most_recent_purchases_range 0 avg_sales_lag3 13 avg_purchases_lag3 0 active_months_lag3 0 avg_sales_lag6 13 avg_purchases_lag6 0 active_months_lag6 0 avg_sales_lag12 13 avg_purchases_lag12 0 active_months_lag12 0 category_4 0 city_id 0 state_id 0 category_2 11887 dtype: int64 '''
能够发现,第二个匿名分类变量存在较多缺失值,而avg_sales_lag3/6/12缺失值数量一致,则很有可能是存在13个商户同时确实了这三方面信息。其他数据没有缺失,数据整体来看较为完整。
3. 数据预处理 3.1 离散/连续字段标注 由于商户数据集中特征同时存在分类变量和离散变量,因此我们首先可以根据字段的说明对不同属性特征进行统一的划分:
1 2 3 4 5 6 7 8 9 10 11 category_cols = ['merchant_id' , 'merchant_group_id' , 'merchant_category_id' , 'subsector_id' , 'category_1' , 'most_recent_sales_range' , 'most_recent_purchases_range' , 'category_4' , 'city_id' , 'state_id' , 'category_2' ] numeric_cols = ['numerical_1' , 'numerical_2' , 'avg_sales_lag3' , 'avg_purchases_lag3' , 'active_months_lag3' , 'avg_sales_lag6' , 'avg_purchases_lag6' , 'active_months_lag6' , 'avg_sales_lag12' , 'avg_purchases_lag12' , 'active_months_lag12' ] assert len (category_cols) + len (numeric_cols) == merchant.shape[1 ]
3.2 离散数据处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 merchant[category_cols].nunique() ''' merchant_id 334633 merchant_group_id 109391 merchant_category_id 324 subsector_id 41 category_1 2 most_recent_sales_range 5 most_recent_purchases_range 5 category_4 2 city_id 271 state_id 25 category_2 5 dtype: int64 ''' merchant[category_cols].dtypes ''' merchant_id object merchant_group_id int64 merchant_category_id int64 subsector_id int64 category_1 object most_recent_sales_range object most_recent_purchases_range object category_4 object city_id int64 state_id int64 category_2 float64 dtype: object ''' merchant[category_cols].isnull().sum () ''' merchant_id 0 merchant_group_id 0 merchant_category_id 0 subsector_id 0 category_1 0 most_recent_sales_range 0 most_recent_purchases_range 0 category_4 0 city_id 0 state_id 0 category_2 11887 dtype: int64 ''' 注意到离散变量中的category_2存在较多缺失值,由于该分类变量取值水平为1 -5 ,因此可以将缺失值先标注为-1 ,方便后续进行数据探索 merchant['category_2' ].unique() merchant['category_2' ] = merchant['category_2' ].fillna(-1 )
接下来对离散变量进行字典编码,即将object对象类型按照sort顺序进行数值化(整数)编码。例如原始category_1取值为Y/N,通过sort排序后N在Y之前,因此在重新编码时N取值会重编码为0、Y取值会重编码为1。以此类推。
需要注意的是,从严格角度来说,变量类型应该是有三类,分别是连续性变量、名义型变量以及有序变量。连续变量较好理解,所谓名义变量,指的是没有数值大小意义的分类变量,例如用1表示女、0表示男,0、1只是作为性别的指代,而没有1>0的含义。而所有有序变量,其也是离散型变量,但却有数值大小含义,如上述most_recent_purchases_range字段,销售等级中A>B>C>D>E,该离散变量的5个取值水平是有严格大小意义的,该变量就被称为有序变量。
在实际建模过程中,如果不需要提取有序变量的数值大小信息的话,可以考虑将其和名义变量一样进行独热编码。但本阶段初级预处理时暂时不考虑这些问题,先统一将object类型转化为数值型。==(object类型转换类型)==
1 2 3 4 5 def change_object_cols (se ): value = se.unique().tolist() value.sort() return se.map (pd.Series(range (len (value)), index=value)).values
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 merchant['category_1' ] ''' 0 N 1 N 2 N 3 Y 4 Y .. 334691 N 334692 Y 334693 N 334694 Y 334695 N Name: category_1, Length: 334696, dtype: object ''' change_object_cols(merchant['category_1' ])
3.3 连续变量数据探索 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 merchant[numeric_cols].dtypes ''' numerical_1 float64 numerical_2 float64 avg_sales_lag3 float64 avg_purchases_lag3 float64 active_months_lag3 int64 avg_sales_lag6 float64 avg_purchases_lag6 float64 active_months_lag6 int64 avg_sales_lag12 float64 avg_purchases_lag12 float64 active_months_lag12 int64 dtype: object ''' merchant[numeric_cols].isnull().sum () merchant[numeric_cols].describe()
据此我们发现连续型变量中存在部分缺失值,并且部分连续变量还存在无穷值inf,需要对其进行简单处理。
1 2 3 inf_cols = ['avg_purchases_lag3' , 'avg_purchases_lag6' , 'avg_purchases_lag12' ] merchant[inf_cols] = merchant[inf_cols].replace(np.inf, merchant[inf_cols].replace(np.inf, -99 ).max ().max ())
不同于无穷值的处理,缺失值处理方法有很多。但该数据集缺失数据较少,33万条数据中只有13条连续特征缺失值,此处我们先简单采用均值进行填补处理,后续若有需要再进行优化处理。
1 2 3 for col in numeric_cols: merchant[col] = merchant[col].fillna(merchant[col].mean()) merchant[numeric_cols].describe()`