商户数据解读与探索(包含较为复杂的数据处理)


复杂的数据处理过程(含清洗)

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)
# 查看头5行数据
merchant.head(5)
# 采用info()函数打印数据帧的完整摘要。
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
# 查看id出现次数是否唯一
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()
# array([ 1., 5., nan, 2., 3., 4.])
# 缺失值填充为-1
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改为最大的显式数值。代码实现流程如下
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()`