1
2
3
4
5
import pandas as pd
import numpy as np

data = pd.DataFrame([[1, np.nan, 3], [4, 5, np.nan], [7, 8, 9]])
print(data)

对缺失数据的处理

删除

1
2
3
4
5
6
data1 = data.dropna()  # 默认是按照行删除
data2 = data.dropna(axis=0) # 按照行删除
data3 = data.dropna(axis=1) # 按照列删除
print(data1)
print(data2)
print(data3)

note:前面几个虽然删除了空数据,但是并没有对data中的数据进行删除,需要我们指定inplace=True才会对在data上删除

1
2
data.dropna(axis=1, inplace=True)
print(data)

填充

指定值进行填充

1
2
3
4
5
data = pd.DataFrame([[1, np.nan, 3], [4, 5, np.nan], [7, 8, 9]])
print(data)

data1 = data.fillna(-1) # 用指定值进行填充
print(data1)

均值填充

1
2
3
print(data.mean())  # 默认是按照列求均值,计算有值的数值的和,然后统计有值的数值的个数,用和求个数得到每列均值
data2 = data.fillna(data.mean())
print(data2)

采用前面的值填充

1
2
data3 = data.fillna(method='pad')  # 使用数据的前一个值(所在列的前一个值,即上一行该列的元素值)对后一个值进行填充
print(data3)

采用后面的值进行填充

1
2
3
4
5
data4 = data.fillna(method='bfill')  # 使用数据的后一个值对前一个值进行填充
print(data4)

data5 = data.interpolate() # 采用差值法进行填充,默认是线性差值, 算是预测填充的一种
print(data5)

处理缺失值与重复值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 处理缺失值与重复值
def Data_preprocessing(x, feat_Remv):
# 删除重复值
x = x.drop_duplicates()

# 删除指定字段
x = x.drop(feat_Remv, axis=1)

# 补充缺失值
x = x.fillna(-1)
return x

feat_Remv = ['instance_id', 'creative_is_js', 'creative_is_voicead', 'app_paid']
train_data = Data_preprocessing(train_data, feat_Remv)
test_data = Data_preprocessing(test_data, feat_Remv)

特征缩放

  1. Min-Max normalization: (x - min) / (max - min)
  2. Mean normalization: (x - avg) / (max - min)
  3. Standardization: (x - avg) / (std)

如何做特征缩放

Min-Max normalization

Note:归一化不是让特征值范围是0-1之间,指所在一个特定范围内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.preprocessing import MinMaxScaler
data = [[1, 1, 1, 1], [1, 2, 3, 4], [2, 4, 9, 16]]
print(MinMaxScaler(feature_range=(0, 1)).fit_transform(data)) # feature_map选择归一化后的取值范围

data = [[1, 1, 1, 1], [1, 2, 3, 4], [2, 4, 9, 16]]
print(MinMaxScaler(feature_range=(-2, 2)).fit_transform(data)) # feature_map选择归一化后的取值范围为-2~2

### Mean normalization
data = [[1, 1, 1, 1], [1, 2, 3, 4], [2, 4, 9, 16]]
df = pd.DataFrame(data)
print(np.max(df)) # 求得每列的最大值,并返回一个ndarray
print(np.mean(df)) # 求得每列的均值,并返回一个ndarray
print((df - np.mean(df)) / (np.max(df) - (np.min(df))))

### Standard Normalization
from sklearn.preprocessing import StandardScaler
data = [[1, 1, 1, 1], [1, 2, 3, 4], [2, 4, 9, 16]]
print(StandardScaler().fit_transform(data)) # 其实这里包含两个步骤,一个训练成一个简单模型,之后转换

样本不均衡

对于样本不均衡,也会给我们进行相关的机器学习任务带来困难

解决办法:

  1. 上采样:增加分类中样本较少的类别的采样数量来实现样本的均衡
    1. 直接复制小样本数据,但是容易过拟合
    2. 通过一定规则生成新的变量,如smote
  2. 下采样:减少分类中多数类样本的样本数量来实现样本均衡
    1. 随机去掉一些多数类,缺点是可能丢失一些重要的信息
    2. 根据原始样本生成新样本来代替多数类样本
  3. 对每个类别设置权重:通常与数量比成反比
  4. 集成学习:生成多组样本来进行训练,例如选择全部的少数样本,每次从多数样本中选择与少数样本数量一致的多数样本进行训练,训练多次

生成一个不平衡的样本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 生成一个不平衡的样本
from sklearn.datasets import make_classification
from collections import Counter
X, y = make_classification(n_samples=5000, # 生成样本的数量
n_features=2, # 生成的样本的特征
n_informative=2,
n_redundant=0,
n_repeated=0,
n_classes=3, # 生成的样本的类别
n_clusters_per_class=1,
weights=[0.01, 0.05, 0.94], # 生成的每个样本类别的权重
class_sep=0.8,
random_state=0
)
print(X.shape)

方法一:上采样

通过增加分类样本中样本较少的类别的数量来实现平衡,直接复制小样本数据,缺点:容易过拟合
from imblearn.over_sampling import RandomOverSampler
ros = RandomOverSampler(random_state=0)
X_resampled, y_resampled = ros.fit_sample(X, y)
print(sorted(Counter(y_resampled).items()))

上采样的另一种方法:通过一定规则生成新变量,如smote
from imblearn.over_sampling import SMOTE, ADASYN
X_resampled, y_resampled = SMOTE(kind=’borderline1’).fit_sample(X, y)
print(sorted(Counter(y_resampled).items()))

方法二:下采样

随机去掉一些多数类,缺点是可能会丢失一些重要信息

1
2
3
4
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=0)
X_resampled, y_resampled = rus.fit_sample(X, y)
print(sorted(Counter(y_resampled).items()))

根据原始样本,生成新的样本来替换多数类样本, 通过聚类生成新的中心样本点来进行采样

1
2
3
4
from imblearn.under_sampling import ClusterCentroids
cc = ClusterCentroids(random_state=0)
X_resampled, y_resampled = cc.fit_sample(X, y)
print(sorted(Counter(y_resampled).items()))

方法三:集成方法,多次抽取样本的方法

1
2
3
4
from imblearn.datasets import make_imbalance
ratio = {0:20, 1:30, 2:40} # 抽取的不同样本类别的个数,但是不能超过样本数量,第1类样本20个,第2类样本30个,第3类样本40个
X_resampled, y_resampled = make_imbalance(X, y, ratio=ratio)
print(sorted(Counter(y_resampled).items()))

识别异常值

  1. 简单的统计分析:对属性值进行一个描述性的统计,从而查看哪些值是不合理的
  2. 3σ原则:根据正态分布的定义,距离平均值3σ之外的概率不超过0.003,属于极小概率事件
  3. 箱型图分析:箱型图提供了一个标准,即大于或小于箱型图设定的上下界的数值为异常值
  4. 聚类分析:如DBSCAN基于密度的聚类方法,可以用于离群点检测

Note : 找到异常值之后可以修正异常值,采用上面讲过的填充缺失值的方法



方法一:箱型图分析

1
2
3
4
5
6
7
import seaborn as sns
import matplotlib.pyplot as plt
arr = [[np.random.random(), i+np.random.random(), i+1+np.random.random()] for i in range(18)]
arr.append([11, 44, 77])
df = pd.DataFrame(arr)
df.columns = ['a', 'b', 'c']
print(df)

方法二:3σ原则

1
2
3
4
5
6
7
8
9
10

data = [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
df = pd.DataFrame(data)
df.columns = ['a', 'b', 'c']
print(df.std()) # 求得标准差
th1 = df.mean() + 3 * df.std() # 上界
th2 = df.mean() - 3 * df.std() # 下界
df['a'] = df[df['a'] > th2['a']]
df['a'] = df[df['a'] < th1['a']]
print(df)

方法三:识别异常值–DBSCAN

1
2
3
4
5
6
7
8
9
10
11
from sklearn import datasets
from sklearn.cluster import DBSCAN
arr = [[np.random.random(), i+np.random.random()] for i in range(18)]
arr.append([11, 44])
arr = np.array(arr)
plt.scatter(arr[:, 0], arr[:, 1], marker='o')
plt.show()

y_pred = DBSCAN(eps = 3).fit_predict(arr)
plt.scatter(arr[:, 0], arr[:, 1], c=y_pred)
plt.show()

特征工程

  1. 特征处理
  2. 特征构建
  3. 特征选择

特征处理:

特征类型:

  1. 数值类型: 连续型,离散型
  2. 类别类型: 也就是枚举类型
  3. 时间类型:
  4. 文本类型: 例如爬下来的一段评论
  5. 图形类型:


数值类型的特征:

  1. 特征缩放的几种方法:标准化,归一化,区间缩放
  2. 连续变量的离散化, 分箱

类别特征:

  1. one-hot编码
  2. LabelEncoder

时间特征:

  1. 持续时间
  2. 时间间隔

连续变量的离散化:分箱(bin)

1
2
3
4
5
6
7
8
9
df = pd.DataFrame()
df['Age'] = np.random.randint(1, 100, 100)
df['Age'].hist() # 显示一个直方图来统计年龄段
bin_ranges = [0, 20, 30, 40, 50, 60, 100]
bin_names = [1, 2, 3, 4, 5, 6]
# 将(0, 20]作为一个段,并给定标签1,(20, 30]作为一个段,并给定一个标签2,以此类推
df['Age_bin_custom_range'] = pd.cut(np.array(df['Age']), bins=bin_ranges)
df['Age_bin_custom_label'] = pd.cut(np.array(df['Age']), bins=bin_ranges, labels=bin_names)
df[['Age', 'Age_bin_custom_range', 'Age_bin_custom_label']].head()

上面是手动分箱,难免会出现一些不规则的划分

1
2
3
4
5
6
7
8
9
### 基于4分位数的自适应二进制方案
quantile_list = [0, .25, .5, .75, 1.]
quantiles = df['income'].quantile(quantile_list)
### 可视化这些分位数
fig, ax = plt.subplots()
df['income'].hist(bins=30, color='#A9C5D3', edgecolor='black', grid=False)

for quantile in quantile_list:
qvl = plt.axvline(quantile, color='r')

类别特征:LabelEncoder

1
2
3
4
5
6
7
from sklearn.preprocessing import LabelEncoder
lb = LabelEncoder()
lb.fit(['male', 'female'])
x_data = pd.DataFrame()
x_data['gender'] = np.array(['male', 'female', 'male', 'male', 'female', 'female', 'male'])
x_data['gender_encode'] = lb.transform(x_data['gender'])
print(x_data)

类别特征:one-hot

在样本之间距离的计算和相似度的度量非常重要,one-hot将离散的取值扩展到了欧式空间

1
2
3
4
5
6
7
from sklearn import preprocessing
enc = preprocessing.OneHotEncoder(categories='auto')
df = pd.DataFrame([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])
enc.fit(df)
# 注意:如果不写toarray,是用一个稀疏矩阵进行表示,写了toarray之后才可以展示出来
array = enc.transform([[0, 0, 0], [1, 1, 1], [0, 2, 2], [1, 2, 3]]).toarray()
print(array)

特征构建

特征选取:

SelectKBest的运用:

这里是通过卡方检验选取特征,k=20指定选择20个特征


1
2
3
4
5
6
from sklearn.datasets import load_digits
from sklearn.feature_selection import SelectKBest, chi2
X, y = load_digits(return_X_y=True)
print(X.shape)
X_new = SelectKBest(chi2, k=20).fit_transform(X, y)
X_new.shape