特征工程(一)数据清理
现实的数据是多种多样的,即使它们已经是结构化的,仍可能存在各种问题,如数据不完整、丢失、类型错误、前后不一致等。因此需要进行数据清理(Data cleaning),也译为数据清洗。
1.1 基本概念
通常以二维表的方式表示数据,Pandas的DataFrame类型的数据是最常见的。
import pandas as pd
df = pd.read_csv("datasets/pm2.csv")
df.sample(10)
RANK | CITY_ID | CITY_NAME | Exposed days | |
---|---|---|---|---|
126 | 138 | 622 | 陇南 | 96 |
54 | 62 | 44 | 朔州 | 58 |
68 | 76 | 562 | 贵阳 | 65 |
89 | 99 | 284 | 景德镇 | 80 |
150 | 171 | 501 | 桂林 | 112 |
211 | 234 | 390 | 武汉 | 158 |
225 | 248 | 359 | 洛阳 | 178 |
144 | 161 | 245 | 铜陵 | 108 |
177 | 200 | 538 | 德阳 | 130 |
186 | 209 | 428 | 株洲 | 137 |
上面显示的二维表格中,每行是一个对象,它被冠以的名称有“记录Record”、“样本Sample”、“实例Instance”,甚至干脆就是“行Row”。
二维表中,列就是描述对象的“属性Attribute”,这是一个常用的名称,还有一个常用名称叫“特征Feature”,也是列的常用名称,此外还有“维度Dimension”和“变量Variable”等称呼。
df.shape
(264, 4)
该数据集有264个样本、4个特征,每个特征的数据也各有不同的类型
df.dtypes
RANK int64
CITY_ID int64
CITY_NAME object
Exposed days int64
dtype: object
1.2 转化数据类型
基础知识
数据集中的数据、有的是整数或浮点数类型,有的是字符串类型、布尔类型等。数据分析和机器学习算法喜欢的是整数或浮点数(数值类型),如果数据集中出现非数值类型的数据,就需要对其进行适当转化。
import pandas as pd
df = pd.DataFrame([{'col1':'a', 'col2':'1'},
{'col1':'b', 'col2':'2'}])
df.dtypes
col1 object
col2 object
dtype: object
“col2”特征的值貌似是整数,但通过df.dtypes查看却是字符串类型,可以通过astype方法进行强转。
# 转int 类型
df['col2-int'] = df['col2'].astype(int)
但用astype方法,只能转化全部由数字组成的数据,如果遇到下面的问题,就无能为力了。
# could not convert string to float: 'pandas'
s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
s.astype(float)
Pandas提供了另外一个实现类型转化的函数to_numeric
# Unable to parse string "pandas" at position 3
pd.to_numeric(s,errors='coerce')
0 1.0
1 2.0
2 4.7
3 NaN
4 10.0
dtype: float64
可以看到,这次将字符串类型的数据转化为浮点数类型,并且将原来数据中由字母组成的字符型强制转化为了NaN——表示缺失值,但它是一个浮点数。
项目案例
去取数据集sales_types.csv中的数据,根据要求对特征数据类型进行转化。
- 将"Customer Number"的数据转化为字符类型
- 将“2016”和“2017”的数据转化为浮点数类型
- 将“Percent Growth”的数据转化为浮点数类型
- 将“Jan Units ”中的数据转化为浮点数类型
- 将“Month”、“Day”、“Year“三个特征的数据合并为一个日期类型的特征
- 将“Active”的数据用1和0表示
import pandas as pd
import numpy as np
def convert_money(value):
new_value = value.replace(",","").replace("$","")
return float(new_value)
df2 = pd.read_csv("datasets/sales_types.csv",
dtype = {'Customer Number': 'int'},
converters = {'2016': convert_money,
'2017': convert_money,
'Percent Growth': lambda x: float(x.replace("%", "")) / 100,
'Jan Units': lambda x: pd.to_numeric(x, errors='coerce'),
'Active': lambda x: np.where(x =='Y', 1, 0),
})
df2['Date'] = pd.to_datetime(df[['Month', 'Day', 'Year']])
df2
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | Date | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 10002 | Quest Industries | 125000.0 | 162500.0 | 0.30 | 500.0 | 1 | 10 | 2015 | 1 | 2015-01-10 |
1 | 552278 | Smith Plumbing | 920000.0 | 1012000.0 | 0.10 | 700.0 | 6 | 15 | 2014 | 1 | 2014-06-15 |
2 | 23477 | ACME Industrial | 50000.0 | 62500.0 | 0.25 | 125.0 | 3 | 29 | 2016 | 1 | 2016-03-29 |
3 | 24900 | Brekke LTD | 350000.0 | 490000.0 | 0.04 | 75.0 | 10 | 27 | 2015 | 1 | 2015-10-27 |
4 | 651029 | Harbor Co | 15000.0 | 12750.0 | -0.15 | NaN | 2 | 2 | 2014 |
其中用到的特殊函数有
- replace是字符串的方法
- Python高级特性lambda函数
- np.where能够实现条件判断功能
1.3 处理重复数据
基础知识
如果数据集中的某个特征下的重复数据比例较高,则会造成该特征标准差降低,如下面的数据:
import pandas as pd
d = {'Name':['Newton', 'Galilei', 'Einstein', 'Feynman', 'Newton', 'Maxwell', 'Galilei'],
'Age':[26, 30, 28, 28, 26, 39, 40],
'Score':[90, 80, 90, 100, 90, 70, 90]}
df = pd.DataFrame(d,columns=['Name','Age','Score'])
df
Name | Age | Score | |
---|---|---|---|
0 | Newton | 26 | 90 |
1 | Galilei | 30 | 80 |
2 | Einstein | 28 | 90 |
3 | Feynman | 28 | 100 |
4 | Newton | 26 | 90 |
5 | Maxwell | 39 | 70 |
6 | Galilei | 40 | 90 |
duplicated()方法可以用来检查是否有重复数据
Considering certain columns is optional.
- subset:用它可以指明数据子集,即某个特征或某几个特征
- keep='first':当遇到重复数据时保留哪一个,被保留的数据标记为False,其他标记为True
- keep='last':表示要保留重复数据中的最后一个。
检查df的特征“Age”中是否有重复数据,并且用keep='last'的方式声明保留最后一个数据。
df.drop_duplicates('Age', keep='last')
Name | Age | Score | |
---|---|---|---|
1 | Galilei | 30 | 80 |
3 | Feynman | 28 | 100 |
4 | Newton | 26 | 90 |
5 | Maxwell | 39 | 70 |
6 | Galilei | 40 | 90 |
项目案例
计算CPI有关数据的重复率
cpi = pd.read_excel("datasets/cpi.xls")
cpi.columns = cpi.iloc[1]
cpi = cpi[2:]
cpi.drop([11, 12], axis=0, inplace=True)
cpi['cpi_index'] = ['总体消费', '食品烟酒', '衣着', '居住', '生活服务', '交通通信', '教育娱乐', '医保', '其他']
cpi.drop(['指标'], axis=1, inplace=True)
cpi.reset_index(drop=True, inplace=True)
cpi.columns.rename('', inplace=True)
cpi
2019年3月 | 2019年2月 | 2019年1月 | 2018年12月 | 2018年11月 | 2018年10月 | 2018年9月 | 2018年8月 | 2018年7月 | 2018年6月 | 2018年5月 | 2018年4月 | cpi_index | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 102.3 | 101.5 | 101.7 | 101.9 | 102.2 | 102.5 | 102.5 | 102.3 | 102.1 | 101.9 | 101.8 | 101.8 | 总体消费 |
1 | 103.5 | 101.2 | 102 | 102.4 | 102.5 | 102.9 | 103 | 101.9 | 101 | 100.8 | 100.7 | 101.1 | 食品烟酒 |
2 | 102 | 102 | 101.6 | 101.5 | 101.4 | 101.4 | 101.2 | 101.3 | 101.2 | 101.1 | 101.1 | 101.1 | 衣着 |
3 | 102.1 | 102.2 | 102.1 | 102.2 | 102.4 | 102.5 | 102.6 | 102.5 | 102.4 | 102.3 | 102.2 | 102.2 | 居住 |
4 | 101.2 | 101.3 | 101.5 | 101.4 | 101.5 | 101.5 | 101.6 | 101.6 | 101.6 | 101.5 | 101.5 | 101.5 | 生活服务 |
5 | 100.1 | 98.8 | 98.7 | 99.3 | 101.6 | 103.2 | 102.8 | 102.7 | 103 | 102.4 | 101.8 | 101.1 | 交通通信 |
6 | 102.4 | 102.4 | 102.9 | 102.3 | 102.5 | 102.5 | 102.2 | 102.6 | 102.3 | 101.8 | 101.9 | 102 | 教育娱乐 |
7 | 102.7 | 102.8 | 102.7 | 102.5 | 102.6 | 102.6 | 102.7 | 104.3 | 104.6 | 105 | 105.1 | 105.2 | 医保 |
8 | 101.9 | 102 | 102.3 | 101.6 | 101.5 | 101.3 | 100.7 | 101.2 | 101.2 | 100.9 | 101 | 100.9 | 其他 |
dup_ratio = []
for column in cpi.columns:
col = cpi[column]
ratio = col[col.duplicated()].count() / col.count()
dup_ratio.append(round(ratio, 2))
dr = pd.Series(dup_ratio, index=cpi.columns)
dr
2019年3月 0.00
2019年2月 0.11
2019年1月 0.00
2018年12月 0.00
2018年11月 0.22
2018年10月 0.22
2018年9月 0.00
2018年8月 0.00
2018年7月 0.11
2018年6月 0.00
2018年5月 0.11
2018年4月 0.22
cpi_index 0.00
dtype: float64
1.4 处理缺失数据
出于各种原因,现实的数据总会出现缺失现象,这似乎是很难避免的,其中有主观原因,也有客观原因。
检查缺失数据
Python中有一个特殊对象None,可以用来表示“缺失” “没有”
Numpy提供一种表示“缺失”数据的对象np.nan
它可以与数字进行运算,因为它本身就是浮点数类型
import pandas as pd
s = pd.Series([1, 2, None, np.nan])
s
0 1.0
1 2.0
2 NaN
3 NaN
dtype: float64
s中虽然有缺失值,但不影响计算过程
s.sum()
3.0
检测是否缺失
0 False
1 False
2 True
3 True
dtype: bool
也可以用DataFrame类型的对象调用
df = pd.DataFrame({"one":[1, 2, np.nan], "two":[np.nan, 3, 4]})
df.isna()
one | two | |
---|---|---|
0 | False | True |
1 | False | False |
2 | True | False |
使用DataFrame实例的dropna方法可以实现对缺失数据的删除
df.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
主要参数说明:
Parameters
----------
axis : {0 or 'index', 1 or 'columns'}, default 0
Determine if rows or columns which contain missing values are
removed.
* 0, or 'index' : Drop rows which contain missing values.
* 1, or 'columns' : Drop columns which contain missing value.
.. versionchanged:: 1.0.0
Pass tuple or list to drop on multiple axes.
Only a single axis is allowed.
how : {'any', 'all'}, default 'any'
Determine if row or column is removed from DataFrame, when we have
at least one NA or all NA.
* 'any' : If any NA values are present, drop that row or column.
* 'all' : If all values are NA, drop that row or column.
thresh : int, optional
Require that many non-NA values.
subset : array-like, optional
Labels along other axis to consider, e.g. if you are dropping rows
these would be a list of columns to include.
inplace : bool, default False
If True, do operation inplace and return None.
how声明删除条件
- 默认any 表示行或列中只要有缺失值,就删除该行或列
- 如果为all,则要求都是缺失值才删除
非缺失值小于2的删除
df.dropna(thresh=2)
用指定值填补
某些数据集中,可以用指定数值填补
df = pd.DataFrame({'ColA':[1, np.nan, np.nan, 4, 5, 6, 7], 'ColB':[1, 1, 1, 1, 2, 2, 2]})
df
ColA | ColB | |
---|---|---|
0 | 1.0 | 1 |
1 | NaN | 1 |
2 | NaN | 1 |
3 | 4.0 | 1 |
4 | 5.0 | 2 |
5 | 6.0 | 2 |
6 | 7.0 | 2 |
fillna可以指定数值填补缺失值
- method='ffill' 表示用当前缺失值前面的值填补
- method='bfill' 表示用当前缺失值后面的值填补
df['ColA'].fillna(method='ffill')
0 1.0
1 1.0
2 1.0
3 4.0
4 5.0
5 6.0
6 7.0
Name: ColA, dtype: float64
df['ColA'].fillna(method='bfill')
0 1.0
1 4.0
2 4.0
3 4.0
4 5.0
5 6.0
6 7.0
Name: ColA, dtype: float64
scikit-learn专门提供了名为SimpleImputer的模块,用来做填充
from sklearn.impute import SimpleImputer
df = pd.DataFrame({"name": ["Google", "Huawei", "Facebook", "Alibaba"],
"price": [100, -1, -1, 90]
})
imp = SimpleImputer(missing_values=-1, strategy='constant', fill_value=110)
imp.fit_transform(df['price'].values.reshape((-1, 1)))
array([[100],[110],[110],[ 90]], dtype=int64)
- missing_values = -1 声明将整数-1作为缺失值
- fill_value 声明填补数值
- strategy 填补策略可选,mean平均值,median中位数,most_frequent众数,constant用参数fill_value指定的数值填补缺失数据
根据规律填补缺失值
用指定数值填补缺失数据,因为所有填补的数值都是一样的,这样做会导致数据标准差降低,对模型的泛化能力有一定影响。如果能发现特征中所有数据的规律,比如符合某个线性关系,就不会因为用特定数值填补而使标准差降低了。
df = pd.DataFrame({"one":np.random.randint(1, 100, 10),
"two": [2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
"three":[5, 9, 13, np.nan, 21, np.nan, 29, 33, 37, 41]})
df
one | two | three | |
---|---|---|---|
0 | 52 | 2 | 5.0 |
1 | 48 | 4 | 9.0 |
2 | 60 | 6 | 13.0 |
3 | 60 | 8 | NaN |
4 | 8 | 10 | 21.0 |
5 | 71 | 12 | NaN |
6 | 61 | 14 | 29.0 |
7 | 60 | 16 | 33.0 |
8 | 69 | 18 | 37.0 |
9 | 52 | 20 | 41.0 |
对简单的数据可以凭观察找到规律,对于复杂的数据可用机器学习模型来解决
下面是使用线性回归模型填充缺失值的过程,将此处的缺失值填补问题转化为了预测问题,将特征“three”视为每个样本的标签,缺失值就是要预测的值。
from sklearn.linear_model import LinearRegression
df_train = df.dropna() #训练集
df_test = df[df['three'].isnull()] #测试集
regr = LinearRegression() #线性回归模型
regr.fit(df_train['two'].values.reshape(-1, 1), df_train['three'].values.reshape(-1, 1)) # ⑦
df_three_pred = regr.predict(df_test['two'].values.reshape(-1, 1))
# 将所得数值填补到原数据集中
df.loc[(df.three.isnull()), 'three'] = df_three_pred
df
one | two | three | |
---|---|---|---|
0 | 52 | 2 | 5.0 |
1 | 48 | 4 | 9.0 |
2 | 60 | 6 | 13.0 |
3 | 60 | 8 | 17.0 |
4 | 8 | 10 | 21.0 |
5 | 71 | 12 | 25.0 |
6 | 61 | 14 | 29.0 |
7 | 60 | 16 | 33.0 |
8 | 69 | 18 | 37.0 |
9 | 52 | 20 | 41.0 |
项目案例
读取train.csv数据,找到有缺失值数据的特征,并对数据进行填充
import pandas as pd
train_data = pd.read_csv("datasets/train.csv")
train_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
不难看出来,有一些特征是存在缺失值的,比如Age计数只有714
train_data.isna().any()
PassengerId False
Survived False
Pclass False
Name False
Sex False
Age True
SibSp False
Parch False
Ticket False
Fare False
Cabin True
Embarked True
dtype: bool
df = train_data[['Age','Fare', 'Parch', 'SibSp', 'Pclass']] #可能跟年龄有关的特征
known_age = df[df['Age'].notnull()].values
unknown_age = df[df['Age'].isnull()].values
y = known_age[:, 0]
X = known_age[:, 1:]
引入随机森林回归模型,准备用它来预测缺失值
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
rfr.fit(X, y)
pred_age = rfr.predict(unknown_age[:, 1:])
pred_age.mean()
29.43784237405462
用预测结果填充缺失数据
train_data.loc[(train_data.Age.isnull()), 'Age'] = pred_age
train_data.isna().any()
PassengerId False
Survived False
Pclass False
Name False
Sex False
Age False
SibSp False
Parch False
Ticket False
Fare False
Cabin True
Embarked True
dtype: bool
为直观地查看填补之后对原有数据分布是否造成太大影响,分别对填补前后的数据绘制直方图
#填补前数据分布
%matplotlib inline
import seaborn as sns
sns.distplot(y)
填补后数据分布
sns.distplot(train_data['Age'])
填补前后的Age数据没有太大的变化,说明填补的数据几乎是符合原数据分布特点的,似乎也可以使用平均填充,分布图如下:
df_mean = df['Age'].fillna(df['Age'].mean())
sns.distplot(df_mean)
显然,使用平均填充之后,数据分布有了较大到的变化,那么使用平均填充并不是一个很好地选择。
1.5 处理离群数据
所谓离群数据,是指少量数据显著不同于其他数据。 下图中,可以看到右上角部分数据明显不符合某种直线关系,这些数据就是离群数据。
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv("datasets/experiment.csv", index_col=0)
fig, ax = plt.subplots()
ax.scatter(df['alpha'], df['belta'])
一般离群数据划分为以下三种:
- 全局离群数据
- 情景离群数据
- 集体离群数据
在实际业务中,一个数据集可能包含多种类型的离群数据。此外,一个离群数据可能数据多种类型。检验不同类型的离群值,可能有不同的目的和应用。
基础知识
箱线图示一种检验离群数据的常用方法
箱形图(Box-plot)又称为盒须图、盒式图或箱线图,是一种用作显示一组数据分散情况资料的统计图。因形状如箱子而得名。在各种领域也经常被使用,常见于品质管理。它主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比 较。箱线图的绘制方法是:先找出一组数据的上边缘、下边缘、中位数和两个四分位数;然后, 连接两个四分位数画出箱体;再将上边缘和下边缘与箱体相连接,中位数在箱体中间。
import seaborn as sns
sns.set(style="whitegrid")
tips = sns.load_dataset("tips") #加载数据集
tips.sample(5)
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
36 | 16.31 | 2.00 | Male | No | Sat | Dinner | 3 |
134 | 18.26 | 3.25 | Female | No | Thur | Lunch | 2 |
198 | 13.00 | 2.00 | Female | Yes | Thur | Lunch | 2 |
184 | 40.55 | 3.00 | Male | Yes | Sun | Dinner | 2 |
165 | 24.52 | 3.48 | Male | No | Sun | Dinner | 3 |
tips变量引用的是一个关于顾客给消费的数据集,下面用箱线图检查顾客给小费的金额中有没有离群值
sns.boxplot(x="day", y="tip", data=tips, palette="Set3")
显然,Sat有比较明显的离群特征。餐厅服务员在周六工作比较合算。
sns.swarmplot以不重叠的点表示数据分布情况
ax = sns.boxplot(x="day", y="tip", data=tips)
ax = sns.swarmplot(x="day", y="tip", data=tips, color=".25")
项目案例
Boston房价数据集中是否有离群值,如果有,则将离群值从数据集中剔除
加载数据集
from sklearn.datasets import load_boston
import pandas as pd
boston = load_boston()
x = boston.data
y = boston.target
columns = boston.feature_names
#为了操作方便,将数据集转化为DataFrame类型
boston_df = pd.DataFrame(boston.data)
boston_df.columns = columns
boston_df.head()
CRIM | ZN | INDUS | CHAS | NOX | RM | AGE | DIS | RAD | TAX | PTRATIO | B | LSTAT | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.00632 | 18.0 | 2.31 | 0.0 | 0.538 | 6.575 | 65.2 | 4.0900 | 1.0 | 296.0 | 15.3 | 396.90 | 4.98 |
1 | 0.02731 | 0.0 | 7.07 | 0.0 | 0.469 | 6.421 | 78.9 | 4.9671 | 2.0 | 242.0 | 17.8 | 396.90 | 9.14 |
2 | 0.02729 | 0.0 | 7.07 | 0.0 | 0.469 | 7.185 | 61.1 | 4.9671 | 2.0 | 242.0 | 17.8 | 392.83 | 4.03 |
3 | 0.03237 | 0.0 | 2.18 | 0.0 | 0.458 | 6.998 | 45.8 | 6.0622 | 3.0 | 222.0 | 18.7 | 394.63 | 2.94 |
4 | 0.06905 | 0.0 | 2.18 | 0.0 | 0.458 | 7.147 | 54.2 | 6.0622 | 3.0 | 222.0 | 18.7 | 396.90 | 5.33 |
boston_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 CRIM 506 non-null float64
1 ZN 506 non-null float64
2 INDUS 506 non-null float64
3 CHAS 506 non-null float64
4 NOX 506 non-null float64
5 RM 506 non-null float64
6 AGE 506 non-null float64
7 DIS 506 non-null float64
8 RAD 506 non-null float64
9 TAX 506 non-null float64
10 PTRATIO 506 non-null float64
11 B 506 non-null float64
12 LSTAT 506 non-null float64
dtypes: float64(13)
memory usage: 51.5 KB
计算箱线图的IQR(四分位距),即数据集的75%的值和25%的值的差
# axis=0 表示是计算各个特征(列)数据的四分位值
percentlier = boston_df.quantile([0, 0.25, 0.5, 0.75, 1], axis=0)
IQR = percentlier.iloc[3] - percentlier.iloc[1]
IQR
CRIM 3.595038
ZN 12.500000
INDUS 12.910000
CHAS 0.000000
NOX 0.175000
RM 0.738000
AGE 49.050000
DIS 3.088250
RAD 20.000000
TAX 387.000000
PTRATIO 2.800000
B 20.847500
LSTAT 10.005000
dtype: float64
在箱型图中,判断某数据是否为离群值,常以(Q1-1.5IQR)和(Q3+1.5IQR)两个数为界限。
Q1 = percentlier.iloc[1] #第1四分位
Q3 = percentlier.iloc[3] #第3四分位
(boston_df < (Q1 - 1.5 * IQR)).any()
CRIM False
ZN False
INDUS False
CHAS False
NOX False
RM True
AGE False
DIS False
RAD False
TAX False
PTRATIO True
B True
LSTAT False
dtype: bool
(boston_df > (Q3 + 1.5 * IQR)).any()
CRIM True
ZN True
INDUS False
CHAS True
NOX False
RM True
AGE False
DIS True
RAD False
TAX False
PTRATIO False
B False
LSTAT True
dtype: bool
利用两个边界计算,将数据集的离群值剔除
boston_df_out = boston_df[~((boston_df < (Q1 - 1.5 * IQR)) |(boston_df > (Q3 + 1.5 * IQR))).any(axis=1)]
boston_df_out.shape
(274, 13)
经剔除后,还剩下274条记录,安装箱线图的原则,现有的数据集boston_df_out中就没有离群值了
如果数据是正态分布的,还可以依据正态分布的有关统计理论检查、处理离群数据。
# 计算z值
from scipy import stats #统计专用模块
import numpy as np
rm = boston_df['RM']
z = np.abs(stats.zscore(rm)) # 计算RM特征下所有数据的Z分数
st = boston_df['RM'].std() # 计算RM特征的标准差
st
0.7026171434153237
threshold = 3 * st #阈值
print(np.where(z > threshold)) # 如果Z分数大于三个标准差,则认为所对应的原数据为离群值
(array([ 97, 98, 162, 163, 166, 180, 186, 195, 203, 204, 224, 225, 226,232, 233, 253, 257, 262, 267, 280, 283, 364, 365, 367, 374, 384,386, 406, 412, 414], dtype=int64),)
保留所有Z分数绝对值大于三个标准差的数据
rm_in = rm[(z < threshold)]
rm_in.shape
(476,)