随着 Facebook、Twitter等社交网络平台的流行,越来越多的青少年用户会在这些平台发布消息。本案例使用Pandas包和sklearn的预处理模块中的一些类,对青少年社交网络信息数据集进行预处理。
我们使用一份包含30000个样本的美国高中生社交网络信息数据集。 数据均匀采样于2006年到2009年,每个样本包含40个变量,其中gradyear
、gender
、age
和friends
四个变量代表高中生的毕业年份、性别、年龄和好友数等基本信息,剩余36个关键词代表了高中生的5大兴趣类:课外活动、时尚、宗教、浪漫和反社会行为,具体描述如下:
变量 | 描述 |
---|---|
gradyear (毕业年份) |
2006-2009分别对应高中一年级到四年级 |
gender (性别) |
M(男)、F(女) |
age (年龄) |
按出生日期计算的年龄,保留小数点后三位 |
friends (朋友) |
社交网络好友数 |
36个关键词 | 取值表示该词语在高中生社交网络服务平台发布信息中出现的频次 |
我们首先导入必要的库:
import numpy as np
import pandas as pd # 读取数据、离散化
import matplotlib.pyplot as plt # 可视化
from sklearn.preprocessing import Imputer # 缺失值填补
from sklearn.preprocessing import StandardScaler # Z-Score标准化
from sklearn.preprocessing import MinMaxScaler # Min-Max标准化
from sklearn.preprocessing import LabelEncoder # 数字编码
from sklearn.preprocessing import OneHotEncoder # One-Hot编码
from sklearn.preprocessing import Binarizer # 二值化
再从本地读取数据,并查看数据的前5行:
teenager_sns = pd.read_csv("./input/teenager_sns.csv")
teenager_sns.head()
利用info()
函数可以从宏观上查看数据集整体情况:
teenager_sns.info()
我们发现,gender
和age
的记录不足30000条,意味着gender
和age
两个变量存在缺失值。相比于gender
二值变量,age
变量的情况更加复杂,因此我们调用describe()
函数进一步查看age
变量整体情况:
print('age变量缺失值数目:', len(teenager_sns["age"]) - teenager_sns["age"].count())
teenager_sns["age"].describe()
根据上面输出的结果,我们注意到最大值106.927岁和最小值3.086岁是不合常理的。青少年的年龄限定在13-20岁,我们将不在此范围内的数据记为缺失值,重新统计缺失值数目:
def tag_nan(value):
if (value >= 13) & (value < 20):
return value
else:
return np.NaN
teenager_sns["age"] = teenager_sns["age"].map(tag_nan)
print('age变量缺失值数目:', len(teenager_sns["age"]) - teenager_sns["age"].count())
teenager_sns["age"].describe()
用这种方法可以初步规范数据定义,但也为age
变量引入更多缺失值。缺失值比例较大,后续我们将考虑使用填补法处理。
在上一步,我们使用info()
方法发现age
和gender
变量都有缺失值。接下来,我们将使用sklearn中的Imputer
方法,将数据集teenager_sns中age
列利用均值进行填充:
# 缺失值填补
imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
imp.fit(teenager_sns[["age"]])
teenager_sns["age_imputed"]=imp.transform(teenager_sns[["age"]])
# 显示年龄缺失的行,和插补缺失值之后的列"age_imputed"
teenager_sns[teenager_sns['age'].isnull()].head()
查看性别age
一列的缺失值数量:
# 对性别(离散变量)进行处理
print('gender变量缺失值数目:', len(teenager_sns["gender"]) - teenager_sns["gender"].count())
由于性别缺失数据较少,故可以考虑直接删除缺失值:
teenager_sns = teenager_sns.dropna(subset=['gender'])
检查缺失数据:
teenager_sns.isnull().sum().sort_values(ascending=False) # age填补后在age_imputed变量,已不存在缺失值
接下来,我们将对friends
列数据进行异常值检测。
# 画箱线图
fig = plt.figure(figsize = (20,8))
plt.subplot(1,2,1)
plt.boxplot(x = teenager_sns.friends)
plt.xlabel('friends',fontsize = 20)
plt.ylabel("Count",fontsize = 20)
# 查看分布情况
plt.subplot(1,2,2)
plt.hist(teenager_sns.friends,bins = 15)
plt.xlabel('friends',fontsize = 20)
plt.ylabel("Count",fontsize = 20)
plt.show()
由图可知,friends
变量整体呈右偏,大于100左右为异常值。下面我们将剔除异常值:
# 剔除异常值
'''
规定:超过上四分位+1.5倍IQR距离,或者下四分位-1.5倍IQR距离的点为异常值
# 四分位距(IQR)就是上四分位与下四分位的差值,我们以IQR的1.5倍为标准
'''
quantile = np.percentile(teenager_sns.friends,[0,25,50,75,100])
friendsIQR = quantile[3] - quantile[1]
UpLimit = quantile[3]+friendsIQR*1.5
DownLimit = quantile[1]-friendsIQR*1.5
teenager_sns = teenager_sns[(teenager_sns['friends'] > DownLimit) & (teenager_sns['friends'] < UpLimit)]
查看异常值剔除后的数据分布情况:
# 画箱线图
fig = plt.figure(figsize = (20,8))
plt.subplot(1,2,1)
plt.boxplot(x = teenager_sns.friends)
plt.xlabel('friends',fontsize = 20)
plt.ylabel("Count",fontsize = 20)
# 查看分布情况
plt.subplot(1,2,2)
plt.hist(teenager_sns.friends,bins = 15)
plt.xlabel('friends',fontsize = 20)
plt.ylabel("Count",fontsize = 20)
plt.show()
# 删去数据后需要重置索引
teenager_sns = teenager_sns.reset_index()
下面我们将使用sklearn中的StandardScaler
方法,对数据集teenager_sns中的friends
列做Z-Score标准化,使得处理后的数据具有固定均值和标准差。
# Z-Score标准化
scaler = StandardScaler(copy=True)
teenager_sns_zscore = pd.DataFrame(scaler.fit_transform(teenager_sns[["friends"]]),columns =["friends_StandardScaled"] )
teenager_sns_zscore["friends"] = teenager_sns["friends"]
print("均值:",teenager_sns_zscore["friends_StandardScaled"].mean(axis=0))
print("方差:",teenager_sns_zscore["friends_StandardScaled"].std(axis=0))
teenager_sns_zscore.head()
接下来,我们使用sklearn中的MinMaxScaler
方法,对数据集teenager_sns中的friends
列做Min-Max标准化,使得处理后的数据取值分布在[0,1]区间上。
# Min-Max标准化
filtered_columns = ["friends"]
scaler = MinMaxScaler(copy=False)
teenager_sns_minmaxscore = pd.DataFrame(scaler.fit_transform(teenager_sns[["friends"]]),
columns = ["friends_MinMaxScaled"])
teenager_sns_minmaxscore["friends"] = teenager_sns["friends"]
teenager_sns_minmaxscore.head()
我们使用sklearn中的LabelEncoder
方法,对数据集teenager_sns中的gender
进行特征编码:
# 数字编码
le = LabelEncoder()
# 打印前4个人的性别
print(teenager_sns["gender"][:4])
# 数字编码
print(le.fit_transform(teenager_sns["gender"][:4]))
结果显示 M
编码为1, F
编码为0。
接下来,我们尝试对gender
一列进行One-Hot编码。在进行One-Hot编码前,需要先进行数字编码, M
编为1, F
编为2,随后用One-Hot编码将1转换为(1,0),2转换为(0,1)。
# 对性别进行数字编码
teenager_sns['gender']=teenager_sns['gender'].map({'M':1,'F':2,np.NaN:3})
enc = OneHotEncoder()
# 对性别用OneHotEncoder进行拟合
enc.fit(teenager_sns[['gender']])
# active_features_表示训练集中实际出现的值
print(enc.active_features_)
# 使用One-Hot编码转换
print(enc.transform([[2]]).toarray())
我们使用sklearn中的Binarizer
方法,对数据集teenager_sns中的friends
列进行二值特征离散化:
# 二值化
# 阈值设置为3,大于3的映射为1,小于等于3的映射为0
scaler = Binarizer(threshold=3)
teenager_sns_binarizer = pd.DataFrame(scaler.fit_transform(teenager_sns[["friends"]]),columns = ["friends_Binarized"])
teenager_sns_binarizer["friends"] = teenager_sns["friends"]
teenager_sns_binarizer.head()
使用Pandas中的cut
方法,实现等距离散化:
# 等距离散化
data = teenager_sns[['friends']].copy()
k = 4
# 等距离散化,各个类别依次命名为0,1,2,3
d = pd.cut(data['friends'], k, labels = range(k))
data['f_cut'] = d
# 查看各组频数
print(data.f_cut.value_counts())
# 检验分组是否正确
print('最大好友数', max(teenager_sns.friends), ';最小好友数', min(teenager_sns.friends))
data.head()
等距离散化各组频数不一定相等,根据前面的右偏分布,各组频数逐渐减小。另外我们可以验证,0-103分为4个区间,69应分在第三个区间,经验证分组正确。
使用Pandas中的qcut
方法,实现等频离散化:
# 等频离散化
data = teenager_sns[['friends']].copy()
k = 4
# 等频离散化,各个类比依次命名为'A','B','C','D'
d = pd.qcut(data['friends'], k, labels = ['A','B','C','D'])
data['f_qcut'] = d
# 查看各组频数
print(data.f_qcut.value_counts())
data.head()
由各组频数可知,等频离散化各组频数大致相等。