第5章 数据清理

目录

  • 5.1 数据清理的概述
    • 5.1.1 常见的三种数据问题
    • 5.1.2 常见三种数据问题的处理办法
  • 5.2 缺失值的检测与处理
    • 5.2.1 缺失值的检测
    • 5.2.2 缺失值的处理
      • 5.2.2.1 丢弃有缺失值的数据记录
      • 5.2.2.2 填充记录的缺失值
      • 5.2.2.3 插补记录的缺失值
  • 5.3 重复值处理
    • 5.3.1 重复值的检测
    • 5.3.2 重复值的处理
  • 5.4 异常值的处理:
    • 5.4.1 异常值的检测
      • 5.4.1.1 3σ原则
      • 5.4.1.2 箱形图异常值检测。
        • 5.4.1.2.1 箱形图的绘制。
        • 5.4.1.2.2 利用箱形图检测异常值
    • 5.4.2 异常值的处理
      • 5.4.2.1 删除异常值
      • 5.4.2.2 替换异常值
      • 5.4.2.3 补充对pandas的.replace()方法详解
        • 5.4.2.3.1 替换全部的某个值
        • 5.4.2.3.2 替换全部的多个值
        • 5.4.2.3.3 替换某列里的内容,而不改变其它列
        • 5.4.2.3.4 替换某行内容
        • 5.4.2.3.5 使用正则表达式替换
        • 5.4.2.3.6 使用条件替换

本文的jupyter notebook版本及数据资料文件下载地址 百度网盘

5.1 数据清理的概述

5.1.1 常见的三种数据问题

  • 缺失值
  • 重复值
  • 异常值

5.1.2 常见三种数据问题的处理办法

  • 缺失值处理:

    • 丢弃有缺失值的数据记录
    • 填充记录的缺失值: 均值填充、中位数填充、众数填充、前值填充、后值填充
    • 插补记录的缺失值。
  • 重复值处理:

    • 删除重复的记录
    • 保留重复的记录
  • 异常值的处理:

    • 删除带异常值的记录
    • 保留带异常值的记录
    • 替换掉记录中的异常值

5.2 缺失值的检测与处理

5.2.1 缺失值的检测

pandas中通常用nan或者是none来代表缺失值。
常用的检测的方法有:

  • isnull(): True则有缺失值
  • notnull():False则有缺失值
  • isna(): True则有缺失值
  • notna(): False则有缺失值
import pandas as pd
import numpy as np

na_df = pd.DataFrame({'A':[1,2,np.NaN,4],
                      'B':[3,4,4,5],
                      'C':[5,6,7,8],
                      'D':[7,5,np.NaN, np.NaN]})
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
# isnull(): True则有缺失值
na_df.isnull()
A B C D
0 False False False False
1 False False False False
2 True False False True
3 False False False True
# notnull():False则有缺失值
na_df.notnull()
A B C D
0 True True True True
1 True True True True
2 False True True False
3 True True True False
#isna(): True则有缺失值
na_df.isna()
A B C D
0 False False False False
1 False False False False
2 True False False True
3 False False False True
#notna(): False则有缺失值
na_df.notna()
A B C D
0 True True True True
1 True True True True
2 False True True False
3 True True True False
mr_df = na_df.isna()
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
mr_df
A B C D
0 False False False False
1 False False False False
2 True False False True
3 False False False True
na_df[mr_df]
A B C D
0 NaN NaN NaN NaN
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN NaN NaN NaN
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
na_df>3
A B C D
0 False False True True
1 False True True True
2 False True True False
3 True True True False
na_df[na_df>3]
A B C D
0 NaN NaN 5 7.0
1 NaN 4.0 6 5.0
2 NaN 4.0 7 NaN
3 4.0 5.0 8 NaN

5.2.2 缺失值的处理

5.2.2.1 丢弃有缺失值的数据记录

pandas中用dropna() 删除有缺失值的一行或者一列。

语法格式:

DataFrame.dropna(axis=0, how=‘any’, thresh=None, subset=None,inplace=False)

参数说明:

  • axis:表示是否删除包含缺失值的行(axis=0)或列(axis=1)。
  • how:表示删除缺失值的方式('any’表示存在任何一个空值则删除整行或列;‘all’表示所有都是空值时才删除)。
  • thresh:表示保留至少有N个非NaN值的行或列。
  • subset:表示删除指定列的缺失值。
  • inplace:表示是否操作原数据。
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
#DataFrame.dropna(axis=0, how='any', thresh=None, subset=None,inplace=False)
# axis=0是删除整行
# 默认how=any,则表示有任何一个为nan就删除
na_df.dropna()
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
# how=all要所有元素是nan才删除
na_df.dropna(how='all')
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
# axis=1是删除列
na_df.dropna(axis=1)
B C
0 3 5
1 4 6
2 4 7
3 5 8
# thresh=3表示至少保留3行或者3列
na_df.dropna(thresh=3)
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
3 4.0 5 8 NaN
na_df.dropna(axis=1,thresh=3)
A B C
0 1.0 3 5
1 2.0 4 6
2 NaN 4 7
3 4.0 5 8
new_df = na_df.dropna()
new_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
# inplace=True则更改了原dataframe,不返回值
na_df.dropna(inplace=True)
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
import numpy as np
import pandas as pd
 
a = np.ones((11,10))
for i in range(len(a)):
    a[i,:i] = np.nan
d = pd.DataFrame(data=a)
print(d)
      0    1    2    3    4    5    6    7    8    9
0   1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
1   NaN  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
2   NaN  NaN  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
3   NaN  NaN  NaN  1.0  1.0  1.0  1.0  1.0  1.0  1.0
4   NaN  NaN  NaN  NaN  1.0  1.0  1.0  1.0  1.0  1.0
5   NaN  NaN  NaN  NaN  NaN  1.0  1.0  1.0  1.0  1.0
6   NaN  NaN  NaN  NaN  NaN  NaN  1.0  1.0  1.0  1.0
7   NaN  NaN  NaN  NaN  NaN  NaN  NaN  1.0  1.0  1.0
8   NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  1.0  1.0
9   NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  1.0
10  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
# 设置子集:删除第0、5、6、7列都为空的行
print(d.dropna(axis='index', how='all', subset=[0,5,6,7]))
     0    1    2    3    4    5    6    7    8    9
0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
1  NaN  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
2  NaN  NaN  1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0
3  NaN  NaN  NaN  1.0  1.0  1.0  1.0  1.0  1.0  1.0
4  NaN  NaN  NaN  NaN  1.0  1.0  1.0  1.0  1.0  1.0
5  NaN  NaN  NaN  NaN  NaN  1.0  1.0  1.0  1.0  1.0
6  NaN  NaN  NaN  NaN  NaN  NaN  1.0  1.0  1.0  1.0
7  NaN  NaN  NaN  NaN  NaN  NaN  NaN  1.0  1.0  1.0

5.2.2.2 填充记录的缺失值

pandas中用fillna()进行缺失值的填充

语法格式:

DataFrame.fillna(value=None, method=None, axis=None, inplace=False,
limit=None, downcast=None)

参数说明:

  • method:表示填充的方式,默认值为None。该参数还支持 'pad’或’ffill’和’backfill’或’bfill’几种取值
    • 'pad’或’ffill’表示将最后一个有效值向后传播,也就是说使用缺失值前面的有效值填充缺失值;
    • 'backfill’或’bfill’表示将最后一个有效值向前传播,也就是说使用缺失值后面的有效值填充缺失值。
  • limit:表示可以连续填充的最大数量。
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
np.mean(na_df['A'])
2.3333333333333335
# 计算A列的非空值的平均值,并保留一位小数
col_a = np.around(np.mean(na_df['A']),1)
col_a
2.3
# 计算D列的非空值的平均值,并保留一位小数
col_d = np.around(np.mean(na_df['D']),1)
col_d
6.0
na_df.fillna(value={'A':col_a,'D':col_d})
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 2.3 4 7 6.0
3 4.0 5 8 6.0
# value是一个字典时,对不同列采用不同值进行填充
na_df.fillna(value={'A':1000,'D':9999})
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 1000.0 4 7 9999.0
3 4.0 5 8 9999.0
# value是一个变量或标量时,对整个dataframe采用该值填充nan
na_df.fillna(8888)
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 8888.0 4 7 8888.0
3 4.0 5 8 8888.0
na_df
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 NaN 4 7 NaN
3 4.0 5 8 NaN
# method = 'ffill',表示用nan的记录的前一个记录的该列值填充
# 用前值填充时第一行的nan无法填充
na_df.fillna(method='ffill')
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 2.0 4 7 5.0
3 4.0 5 8 5.0
# method = 'bfill',表示用nan的记录的后一个记录的该列值填充
# 用后值填充时,最后一行的nan无法填充
na_df.fillna(method='bfill')
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 4.0 4 7 NaN
3 4.0 5 8 NaN

5.2.2.3 插补记录的缺失值

pandas中提供了插补缺失值的方法interpolate(),interpolate() 会根据相应的插值方法求得的值进行填充。

语法格式:

DataFrame.interpolate(method=‘linear’, axis=0, limit=None, inplace=False,
limit_direction=None, limit_area=None, downcast=None, **kwargs)

参数说明:

  • method:表示使用的插值方法,该参数支持’linear’(默认值)、‘time’、 ‘index’、‘values’、 ‘nearest’ 、'barycentric’共6种取值,
    • 'linear’代表采用线性插值法进行填充;
    • 'time’代表根据时间长短进行填充;
    • ‘index’、'values’代表采用索引的实际数值进行填充;
    • ‘nearest’代表采用最临近插值法进行填充;’
    • barycentric’代表采用重心坐标插值法进行填充。
  • limit_direction:表示按照指定方向对连续的NaN进行填充。
na_df.interpolate(method='linear')
A B C D
0 1.0 3 5 7.0
1 2.0 4 6 5.0
2 3.0 4 7 5.0
3 4.0 5 8 5.0

例子练习

txtData = pd.read_csv("cj.txt",sep="\t")
txtData
学号 班级 姓名 性别 英语 体育 军训 数分 高代 解几
0 2308024241 23080242 成龙 76 78 77 40 23 60
1 2308024244 23080242 周怡 66 91 75 47 47 44
2 2308024251 23080242 张波 85 81 75 45 45 60
3 2308024249 23080242 朱浩 65 50 80 72 62 71
4 2308024219 23080242 封印 73 88 92 61 47 46
5 2308024201 23080242 迟培 60 50 89 71 76 71
6 2308024347 23080243 李华 67 61 84 61 65 78
7 2308024307 23080243 陈田 76 79 86 69 40 69
8 2308024326 23080243 余皓 66 67 85 65 61 71
9 2308024320 23080243 李嘉 62 作弊 90 60 67 77
10 2308024342 23080243 李上初 76 90 84 60 66 60
11 2308024310 23080243 郭窦 79 67 84 64 64 79
12 2308024435 23080244 姜毅涛 77 71 缺考 61 73 76
13 2308024432 23080244 赵宇 74 74 88 68 70 71
14 2308024446 23080244 周路 76 80 77 61 74 80
15 2308024421 23080244 林建祥 72 72 81 63 90 75
16 2308024433 23080244 李大强 79 76 77 78 70 70
17 2308024428 23080244 李侧通 64 96 91 69 60 77
18 2308024402 23080244 王慧 73 74 93 70 71 75
19 2308024422 23080244 李晓亮 85 60 85 72 72 83
20 2308024201 23080242 迟培 60 50 89 71 76 71
txtData.info()

RangeIndex: 21 entries, 0 to 20
Data columns (total 10 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   学号      21 non-null     int64 
 1   班级      21 non-null     int64 
 2   姓名      21 non-null     object
 3   性别      21 non-null     object
 4   英语      21 non-null     int64 
 5   体育      21 non-null     object
 6   军训      21 non-null     object
 7   数分      21 non-null     int64 
 8   高代      21 non-null     int64 
 9   解几      21 non-null     int64 
dtypes: int64(6), object(4)
memory usage: 1.8+ KB
df = txtData
df['体育']=pd.to_numeric(df['体育'],errors='coerce')
df['军训']=pd.to_numeric(df['军训'],errors='coerce')
df
学号 班级 姓名 性别 英语 体育 军训 数分 高代 解几
0 2308024241 23080242 成龙 76 78.0 77.0 40 23 60
1 2308024244 23080242 周怡 66 91.0 75.0 47 47 44
2 2308024251 23080242 张波 85 81.0 75.0 45 45 60
3 2308024249 23080242 朱浩 65 50.0 80.0 72 62 71
4 2308024219 23080242 封印 73 88.0 92.0 61 47 46
5 2308024201 23080242 迟培 60 50.0 89.0 71 76 71
6 2308024347 23080243 李华 67 61.0 84.0 61 65 78
7 2308024307 23080243 陈田 76 79.0 86.0 69 40 69
8 2308024326 23080243 余皓 66 67.0 85.0 65 61 71
9 2308024320 23080243 李嘉 62 NaN 90.0 60 67 77
10 2308024342 23080243 李上初 76 90.0 84.0 60 66 60
11 2308024310 23080243 郭窦 79 67.0 84.0 64 64 79
12 2308024435 23080244 姜毅涛 77 71.0 NaN 61 73 76
13 2308024432 23080244 赵宇 74 74.0 88.0 68 70 71
14 2308024446 23080244 周路 76 80.0 77.0 61 74 80
15 2308024421 23080244 林建祥 72 72.0 81.0 63 90 75
16 2308024433 23080244 李大强 79 76.0 77.0 78 70 70
17 2308024428 23080244 李侧通 64 96.0 91.0 69 60 77
18 2308024402 23080244 王慧 73 74.0 93.0 70 71 75
19 2308024422 23080244 李晓亮 85 60.0 85.0 72 72 83
20 2308024201 23080242 迟培 60 50.0 89.0 71 76 71
df['体育'].mean()
72.75
df.fillna(value={'体育':df['体育'].mean(),'军训':df['军训'].min()})
学号 班级 姓名 性别 英语 体育 军训 数分 高代 解几
0 2308024241 23080242 成龙 76 78.00 77.0 40 23 60
1 2308024244 23080242 周怡 66 91.00 75.0 47 47 44
2 2308024251 23080242 张波 85 81.00 75.0 45 45 60
3 2308024249 23080242 朱浩 65 50.00 80.0 72 62 71
4 2308024219 23080242 封印 73 88.00 92.0 61 47 46
5 2308024201 23080242 迟培 60 50.00 89.0 71 76 71
6 2308024347 23080243 李华 67 61.00 84.0 61 65 78
7 2308024307 23080243 陈田 76 79.00 86.0 69 40 69
8 2308024326 23080243 余皓 66 67.00 85.0 65 61 71
9 2308024320 23080243 李嘉 62 72.75 90.0 60 67 77
10 2308024342 23080243 李上初 76 90.00 84.0 60 66 60
11 2308024310 23080243 郭窦 79 67.00 84.0 64 64 79
12 2308024435 23080244 姜毅涛 77 71.00 75.0 61 73 76
13 2308024432 23080244 赵宇 74 74.00 88.0 68 70 71
14 2308024446 23080244 周路 76 80.00 77.0 61 74 80
15 2308024421 23080244 林建祥 72 72.00 81.0 63 90 75
16 2308024433 23080244 李大强 79 76.00 77.0 78 70 70
17 2308024428 23080244 李侧通 64 96.00 91.0 69 60 77
18 2308024402 23080244 王慧 73 74.00 93.0 70 71 75
19 2308024422 23080244 李晓亮 85 60.00 85.0 72 72 83
20 2308024201 23080242 迟培 60 50.00 89.0 71 76 71

5.3 重复值处理

  • 删除重复的记录
  • 保留重复的记录

5.3.1 重复值的检测

pandas中使用duplicated()方法来检测数据中的重复值。

语法格式:

DataFrame.duplicated(subset=None, keep=‘first’)

参数说明:

  • subset:表示识别重复项的列索引或列索引序列,默认标识所有的列索引。
  • keep:表示采用哪种方式保留重复项,该参数可以取值为’first’(默认值)、 'last '和 ‘False’.
    • 'first’代表删除重复项,仅保留第一次出现的数据项;
    • 'last '代表删除重复项,仅保留最后一次出现的数据项;
    • 'False’表示所有相同的数据都被标记为重复项。
import pandas as pd
person_info = pd.DataFrame({'name':['刘婷婷','王淼','彭岩','刘华','刘华','周华'],
                            'age':[24,23,29,22,22,27],
                            'height':[162,165,175,175,175,178],
                            'gender':['女','女','男','男','男','男']})
person_info
name age height gender
0 刘婷婷 24 162
1 王淼 23 165
2 彭岩 29 175
3 刘华 22 175
4 刘华 22 175
5 周华 27 178
# 检测person_info中是否有重复的行
# subset=None, keep='first'
person_info.duplicated()
0    False
1    False
2    False
3    False
4     True
5    False
dtype: bool
# 检测person_info中是否有重复的行
# subset=None, keep='last'
person_info.duplicated(keep='last')
0    False
1    False
2    False
3     True
4    False
5    False
dtype: bool
# 检测person_info中是否有重复的行
# subset=None, keep=False
person_info.duplicated(keep=False)
0    False
1    False
2    False
3     True
4     True
5    False
dtype: bool
# 检测person_info中是否有重复的行
# subset=['height','gender'], keep=False
person_info.duplicated(subset=['height','gender'],keep=False)
0    False
1    False
2     True
3     True
4     True
5    False
dtype: bool

5.3.2 重复值的处理

重复值的一般处理方式是删除,pandas中使用drop_duplicates()方法删除重复值。

语法格式:

DataFrame.drop_duplicates(subset=None, keep=‘first’, inplace=False,
ignore_index=False)

参数说明:

  • keep:表示采用哪种方式保留重复项,该参数可以取值为’first’(默认值)、 'last ‘和’False’.
    • 'first’代表删除重复项,仅保留第一次出现的数据项;
    • 'last '代表删除重复项,仅保留最后一次出现的数据项;
    • 'False’表示删除所有的重复项。
  • inplace:表示是否放弃副本数据,返回新的数据,默认为False。
  • ignore_index:表示是否对删除重复值后的对象的行索引重新排序,默认为Flase。
person_info
name age height gender
0 刘婷婷 24 162
1 王淼 23 165
2 彭岩 29 175
3 刘华 22 175
4 刘华 22 175
5 周华 27 178
# 删除person_info对象的重复值
# subset=None, keep='first', inplace=False,ignore_index=False
person_info.drop_duplicates()
name age height gender
0 刘婷婷 24 162
1 王淼 23 165
2 彭岩 29 175
3 刘华 22 175
5 周华 27 178
person_info
name age height gender
0 刘婷婷 24 162
1 王淼 23 165
2 彭岩 29 175
3 刘华 22 175
4 刘华 22 175
5 周华 27 178
# 删除person_info对象的重复值
# subset=None, keep='first', inplace=False,ignore_index=True
person_info.drop_duplicates(ignore_index=True)
name age height gender
0 刘婷婷 24 162
1 王淼 23 165
2 彭岩 29 175
3 刘华 22 175
4 周华 27 178
# 删除person_info对象的重复值
# subset=None, keep='first', inplace=True,ignore_index=True
person_info.drop_duplicates(inplace=True,ignore_index=True)
person_info
name age height gender
0 刘婷婷 24 162
1 王淼 23 165
2 彭岩 29 175
3 刘华 22 175
4 周华 27 178
# 删除person_info对象的重复值
# subset=['height','gender'], keep=False, inplace=False,ignore_index=False
person_info.drop_duplicates(subset=['height','gender'],keep=False,ignore_index=False)
name age height gender
0 刘婷婷 24 162
1 王淼 23 165
4 周华 27 178

5.4 异常值的处理:

  • 删除带异常值的记录
  • 保留带异常值的记录
  • 替换掉记录中的异常值

5.4.1 异常值的检测

5.4.1.1 3σ原则

3σ原则,又称为拉依达原则,它是先假设一组检测数据只含有随机误差,对该组数据进行计算处理得到标准偏差,按一定概率确定一个区间,凡是超过这个区间的误差不属于随机误差而是粗大误差,含有粗大误差范围内的数据(视为异常值)应予以剔除

  • 3σ原则并不适用于任意数据集,而只适用于符合或近似正态分布的数据集。
  • 正态分布也称高斯分布,是统计学中十分重要的概率分布,它有两个比较重要的参数:μ和σ,其中μ是遵从正态分布的随机变量(值无法预先确定仅以一定的概率取值的变量)的均值,σ是此随机变量的标准差。
    第5章 数据清理_第1张图片

实例:3σ原则检测数据data.xlsx里面的异常值

import numpy as np
import pandas as pd

def three_sigma(ser):
    """
    ser参数:被检测的数据,是一个Series
    返回值:异常值及其对应的行索引
    """
    
    # 计算平均值
    mean_data = ser.mean()
    # 计算标准差sigma
    std_data = ser.std()
    
    #小于(均值-3倍标准差) 或者 大于 (均值+3倍标准差)的数值都是异常的
    rule = ((mean_data-3*std_data)>ser) | ((mean_data+3*std_data)<ser)
    
    # 返回异常值的行索引
    index = np.arange(ser.shape[0])[rule]
    # 获取异常值
    outliers = ser.iloc[index]
    return outliers
    
    
# 读data.xlsx
excel_data = pd.read_excel('data.xlsx',index_col=0)
excel_data
value
0 12.7
1 9.9
2 10.2
3 10.6
4 8.6
... ...
995 10.3
996 10.1
997 10.0
998 9.3
999 9.9

1000 rows × 1 columns

three_sigma(excel_data['value'])
121    13.2
710    13.1
Name: value, dtype: float64

5.4.1.2 箱形图异常值检测。

除了使用3σ原则检测异常值之外,还可以使用箱形图检测异常值。
需要说明的是,箱形图对检测数据没有任何要求,即使不符合正态分布的数据集是能被检测的。
箱形图是一种用于显示一组数据分散情况的统计图,它通常由上边缘、上四分位数、中位数、下四分位数、下边缘和异常值组成。箱形图能直观地反映出一组数据的分散情况,一旦图中出现离群点(远离大多数值的点),就认为该离群点可能为异常值。
第5章 数据清理_第2张图片

5.4.1.2.1 箱形图的绘制。

为了能够直观地从箱形图中查看异常值,pandas中提供了两个绘制箱形图的函数:plot()boxplot(),其中plot()函数用于根据Series和DataFrame类对象绘制箱形图,该箱形图中默认不会显示网格线; boxplot()函数用于根据DataFrame类对象绘制箱形图,该箱形图中默认会显示网格线。

语法格式:

DataFrame.boxplot(column=None, by=None, ax=None, fontsize=None,
rot=0, grid=True, figsize=None, layout=None, return_type=None,
backend=None, **kwargs)

参数说明:

  • column,默认为None,输入为str 或由str构成的list,其作用是指定要进行箱型图分析的列
  • by,默认为None,str or array-like,其作用为pandas的group by,通过指定by=‘columns’,可进行多组合箱型图分析
  • ax,matplotlib.axes.Axes的对象,没有太大作用
  • fontsize,箱型图坐标轴字体大小
  • rot,箱型图坐标轴旋转角度
  • grid,箱型图网格线是否显示
  • figsize,箱型图窗口尺寸大小
  • layout,必须配合by 一起使用,类似于subplot 的画布分区域功能
  • return_type,指定返回对象的类型,默认为None,可输入的参数为‘axes’,‘dict’,‘both’,当与by一起使用是,返回的对象为Series或array(for return_type = None)
import pandas as pd

# 读data.xlsx
excel_data = pd.read_excel('data.xlsx',index_col=0)
excel_data.boxplot(column='value')

第5章 数据清理_第3张图片

5.4.1.2.2 利用箱形图检测异常值
import pandas as pd
import numpy as np

def box_outliers(ser):
    # 对待检测的数据集进行排序
    new_ser = ser.sort_values()
    # 判断待检测数据集数的个数数偶数还是奇数
    if new_ser.count() %2==0:
        #计算Q3,Q1
        Q3 = new_ser[int(len(new_ser)/2):].median()
        Q1 = new_ser[:int(len(new_ser)/2)].median()
    else:
        #计算Q3,Q1
        Q3 = new_ser[int((len(new_ser)-1)/2):].median()
        Q1 = new_ser[:int((len(new_ser)-1)/2)].median()
        
    #计算IQR
    IQR = round(Q3-Q1,1)
    # 制定异常值规则
    rule = (round(Q3+1.5*IQR,1)<ser)|(round(Q1-1.5*IQR,1)>ser)
    
    # 返回异常值的行索引
    index = np.arange(ser.shape[0])[rule]
    # 获取异常值
    outliers = ser.iloc[index]
    return outliers
                         
# 读data.xlsx
excel_data = pd.read_excel('data.xlsx',index_col=0)
box_outliers(excel_data['value'])
0      12.7
121    13.2
255    12.7
353    13.0
694    12.8
710    13.1
724    12.9
Name: value, dtype: float64

5.4.2 异常值的处理

异常值被检测出来之后,需要进一步确认其是否为真正的异常值,等确认完之后再选用合适的方式进行处理。异常值有3种处理方式,分别为:

  • 保留异常值
  • 删除异常值
  • 替换异常值

5.4.2.1 删除异常值

pandas中提供了删除数据的drop()方法,使用该方法可以根据指定的行标签索引或列标签索引来删除异常值。

语法格式:

DataFrame.drop(labels=None, axis=0, index=None, columns=None, level=None,
inplace=False, errors=‘raise’)

参数说明:

  • labels:表示要删除行标签索引或列标签索引,可以是一个或多个。
  • axis:指定删除行或删除列,其中0或index表示删除行;1或columns表示删除列。
  • index:指定要删除的行。
  • columns:指定要删除的列。
# 根据行索引进行删除
# 前面根据3σ原则检测到的异常的行时121,710行
clean_data = excel_data.drop([121,710])
# 对清理后的干净数据再次使用3σ检测
three_sigma(clean_data['value'])
Series([], Name: value, dtype: float64)

5.4.2.2 替换异常值

pandas中提供了替换值的replace()方法,replace()方法可以对单个或多个值进行替换。

语法格式:

DataFrame.replace(to_replace=None, value=None, inplace=False, limit=None, regex=False, method=‘pad’)

参数说明:

  • to_replace:被替换的值
  • value:替换后的值
  • inplace:是否要改变原数据,False是不改变,True是改变,默认是False
  • limit:控制填充次数
  • regex:是否使用正则,False是不使用,True是使用,默认是False
  • method:填充方式,pad,ffill,bfill分别是向前、向前、向后填充。
# 前面根据箱形图方法检测到的异常的行:
“”“
0      12.7
121    13.2
255    12.7
353    13.0
694    12.8
710    13.1
724    12.9
”“”

# 按值替换
replace_data = excel_data.replace({13.2:10.2,13.1:10.5})
#查看替换后的值
print(replace_data.loc[121])
value    10.2
Name: 121, dtype: float64
print(replace_data.loc[710])
value    10.5
Name: 710, dtype: float64
# 替换后再用箱型图方法检测异常值
box_outliers(replace_data['value'])
0      12.7
255    12.7
353    13.0
694    12.8
724    12.9
Name: value, dtype: float64

5.4.2.3 补充对pandas的.replace()方法详解

0. 准备数据

import requests
import pandas as pd 

html_data = requests.get("http://www.weather.com.cn/textFC/hb.shtml")
html_tables = pd.read_html(html_data.content,header=0)
tqDf = html_tables[5]
tqDf
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 西南风 <3级 27 西南风 3-4级 13 详情
2 北京 海淀 南风 3-4级 27 西南风 3-4级 13 详情
3 北京 朝阳 西南风 <3级 27 西南风 3-4级 11 详情
4 北京 顺义 南风 <3级 27 西南风 3-4级 13 详情
5 北京 怀柔 西南风 <3级 28 西南风 <3级 10 详情
6 北京 通州 西南风 <3级 27 西南风 3-4级 11 详情
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 西南风 3-4级 27 西南风 3-4级 12 详情
10 北京 石景山 南风 3-4级 26 西南风 <3级 12 详情
11 北京 大兴 西南风 <3级 27 西南风 3-4级 12 详情
12 北京 房山 西南风 <3级 27 西南风 <3级 12 详情
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 西南风 <3级 12 详情
15 北京 平谷 西南风 <3级 27 南风 <3级 11 详情
16 北京 东城 西南风 <3级 27 西南风 3-4级 11 详情
17 北京 西城 南风 3-4级 27 西南风 3-4级 13 详情
tqDf.columns
Index(['省/直辖市', '城市', '周二(4月19日)白天', '周二(4月19日)白天.1', '周二(4月19日)白天.2',
       '周二(4月19日)夜间', '周二(4月19日)夜间.1', '周二(4月19日)夜间.2', 'Unnamed: 8'],
      dtype='object')
5.4.2.3.1 替换全部的某个值

replace的基本结构是:python df.replace(to_replace, value)前面是需要替换的值,后面是替换后的值。

# 将“西南风<3级”替换为“微风”
tqDf.replace("西南风 <3级","微风")
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 微风 27 西南风 3-4级 13 详情
2 北京 海淀 南风 3-4级 27 西南风 3-4级 13 详情
3 北京 朝阳 微风 27 西南风 3-4级 11 详情
4 北京 顺义 南风 <3级 27 西南风 3-4级 13 详情
5 北京 怀柔 微风 28 微风 10 详情
6 北京 通州 微风 27 西南风 3-4级 11 详情
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 西南风 3-4级 27 西南风 3-4级 12 详情
10 北京 石景山 南风 3-4级 26 微风 12 详情
11 北京 大兴 微风 27 西南风 3-4级 12 详情
12 北京 房山 微风 27 微风 12 详情
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 微风 12 详情
15 北京 平谷 微风 27 南风 <3级 11 详情
16 北京 东城 微风 27 西南风 3-4级 11 详情
17 北京 西城 南风 3-4级 27 西南风 3-4级 13 详情

要注意这样的操作并没有改变文档的源数据,要改变源数据需要使用inplace = True

5.4.2.3.2 替换全部的多个值
  • 采用字典的形式,字典的key是要替换的值,value是替换后的值
  • 采用列表的形式,前面列表保存原值,后面列表保存新值。
# 将“西南风<3级”替换为“微风”,将"西南风3-4级”替换为“小风"
tqDf.replace({"西南风 <3级":"微风","西南风 3-4级":"小风"})
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 微风 27 小风 13 详情
2 北京 海淀 南风 3-4级 27 小风 13 详情
3 北京 朝阳 微风 27 小风 11 详情
4 北京 顺义 南风 <3级 27 小风 13 详情
5 北京 怀柔 微风 28 微风 10 详情
6 北京 通州 微风 27 小风 11 详情
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 小风 27 小风 12 详情
10 北京 石景山 南风 3-4级 26 微风 12 详情
11 北京 大兴 微风 27 小风 12 详情
12 北京 房山 微风 27 微风 12 详情
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 微风 12 详情
15 北京 平谷 微风 27 南风 <3级 11 详情
16 北京 东城 微风 27 小风 11 详情
17 北京 西城 南风 3-4级 27 小风 13 详情
# 将“西南风<3级”替换为“微风”,将"西南风3-4级”替换为“小风"
tqDf.replace(["西南风 <3级","西南风 3-4级"],["微风","小风"])
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 微风 27 小风 13 详情
2 北京 海淀 南风 3-4级 27 小风 13 详情
3 北京 朝阳 微风 27 小风 11 详情
4 北京 顺义 南风 <3级 27 小风 13 详情
5 北京 怀柔 微风 28 微风 10 详情
6 北京 通州 微风 27 小风 11 详情
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 小风 27 小风 12 详情
10 北京 石景山 南风 3-4级 26 微风 12 详情
11 北京 大兴 微风 27 小风 12 详情
12 北京 房山 微风 27 微风 12 详情
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 微风 12 详情
15 北京 平谷 微风 27 南风 <3级 11 详情
16 北京 东城 微风 27 小风 11 详情
17 北京 西城 南风 3-4级 27 小风 13 详情
5.4.2.3.3 替换某列里的内容,而不改变其它列
# 仅将'周二(4月19日)白天.1'列里面的“西南风<3级”替换为“微风”
tmpDf = tqDf.copy()
tmpDf['周二(4月19日)白天.1'].replace("西南风 <3级","微风",inplace=True)
tmpDf
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 微风 27 西南风 3-4级 13 详情
2 北京 海淀 南风 3-4级 27 西南风 3-4级 13 详情
3 北京 朝阳 微风 27 西南风 3-4级 11 详情
4 北京 顺义 南风 <3级 27 西南风 3-4级 13 详情
5 北京 怀柔 微风 28 西南风 <3级 10 详情
6 北京 通州 微风 27 西南风 3-4级 11 详情
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 西南风 3-4级 27 西南风 3-4级 12 详情
10 北京 石景山 南风 3-4级 26 西南风 <3级 12 详情
11 北京 大兴 微风 27 西南风 3-4级 12 详情
12 北京 房山 微风 27 西南风 <3级 12 详情
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 西南风 <3级 12 详情
15 北京 平谷 微风 27 南风 <3级 11 详情
16 北京 东城 微风 27 西南风 3-4级 11 详情
17 北京 西城 南风 3-4级 27 西南风 3-4级 13 详情
5.4.2.3.4 替换某行内容
#仅替换index=3行(朝阳区)的“西南风<3级”替换为“微风”
tmpDf = tqDf.copy()
tmpDf.iloc[3].replace("西南风 <3级","微风",inplace=True)
tmpDf
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 西南风 <3级 27 西南风 3-4级 13 详情
2 北京 海淀 南风 3-4级 27 西南风 3-4级 13 详情
3 北京 朝阳 微风 27 西南风 3-4级 11 详情
4 北京 顺义 南风 <3级 27 西南风 3-4级 13 详情
5 北京 怀柔 西南风 <3级 28 西南风 <3级 10 详情
6 北京 通州 西南风 <3级 27 西南风 3-4级 11 详情
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 西南风 3-4级 27 西南风 3-4级 12 详情
10 北京 石景山 南风 3-4级 26 西南风 <3级 12 详情
11 北京 大兴 西南风 <3级 27 西南风 3-4级 12 详情
12 北京 房山 西南风 <3级 27 西南风 <3级 12 详情
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 西南风 <3级 12 详情
15 北京 平谷 西南风 <3级 27 南风 <3级 11 详情
16 北京 东城 西南风 <3级 27 西南风 3-4级 11 详情
17 北京 西城 南风 3-4级 27 西南风 3-4级 13 详情
#仅替换index=[3:6]行(朝阳区、顺义区、怀柔区)的“西南风<3级”替换为“微风”
tmpDf = tqDf.copy()
tmpDf.iloc[3:6].replace("西南风 <3级","微风",inplace=True)
tmpDf
D:\ProgramFiles\Anaconda3\lib\site-packages\pandas\core\frame.py:5244: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  method=method,
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 西南风 <3级 27 西南风 3-4级 13 详情
2 北京 海淀 南风 3-4级 27 西南风 3-4级 13 详情
3 北京 朝阳 微风 27 西南风 3-4级 11 详情
4 北京 顺义 南风 <3级 27 西南风 3-4级 13 详情
5 北京 怀柔 微风 28 微风 10 详情
6 北京 通州 西南风 <3级 27 西南风 3-4级 11 详情
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 西南风 3-4级 27 西南风 3-4级 12 详情
10 北京 石景山 南风 3-4级 26 西南风 <3级 12 详情
11 北京 大兴 西南风 <3级 27 西南风 3-4级 12 详情
12 北京 房山 西南风 <3级 27 西南风 <3级 12 详情
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 西南风 <3级 12 详情
15 北京 平谷 西南风 <3级 27 南风 <3级 11 详情
16 北京 东城 西南风 <3级 27 西南风 3-4级 11 详情
17 北京 西城 南风 3-4级 27 西南风 3-4级 13 详情
5.4.2.3.5 使用正则表达式替换

正则表达式很强大,能够让我们实现一次替换很多很多个不同的值

# 把气温数字27或28标注为“高温”
tmpDf = tqDf.copy()
tmpDf.replace("[2][78]","高温", regex = True, inplace = True)
tmpDf
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 西南风 <3级 高温 西南风 3-4级 13 详情
2 北京 海淀 南风 3-4级 高温 西南风 3-4级 13 详情
3 北京 朝阳 西南风 <3级 高温 西南风 3-4级 11 详情
4 北京 顺义 南风 <3级 高温 西南风 3-4级 13 详情
5 北京 怀柔 西南风 <3级 高温 西南风 <3级 10 详情
6 北京 通州 西南风 <3级 高温 西南风 3-4级 11 详情
7 北京 昌平 南风 <3级 高温 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 西南风 3-4级 高温 西南风 3-4级 12 详情
10 北京 石景山 南风 3-4级 26 西南风 <3级 12 详情
11 北京 大兴 西南风 <3级 高温 西南风 3-4级 12 详情
12 北京 房山 西南风 <3级 高温 西南风 <3级 12 详情
13 北京 密云 南风 <3级 高温 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 西南风 <3级 12 详情
15 北京 平谷 西南风 <3级 高温 南风 <3级 11 详情
16 北京 东城 西南风 <3级 高温 西南风 3-4级 11 详情
17 北京 西城 南风 3-4级 高温 西南风 3-4级 13 详情
5.4.2.3.6 使用条件替换
# 判断[“周二(4月19日)白天.1”] 列的字符串为 “西南风 <3级"时,将本行的 [“Unnamed: 8”]列,替换为”西南风,微风"
tmpDf = tqDf.copy()
tmpDf.loc[tmpDf["周二(4月19日)白天.1"]=="西南风 <3级","Unnamed: 8"]="西南风,微风"
tmpDf
省/直辖市 城市 周二(4月19日)白天 周二(4月19日)白天.1 周二(4月19日)白天.2 周二(4月19日)夜间 周二(4月19日)夜间.1 周二(4月19日)夜间.2 Unnamed: 8
0 省/直辖市 城市 天气现象 风向风力 最高气温 天气现象 风向风力 最低气温 NaN
1 北京 北京 西南风 <3级 27 西南风 3-4级 13 西南风,微风
2 北京 海淀 南风 3-4级 27 西南风 3-4级 13 详情
3 北京 朝阳 西南风 <3级 27 西南风 3-4级 11 西南风,微风
4 北京 顺义 南风 <3级 27 西南风 3-4级 13 详情
5 北京 怀柔 西南风 <3级 28 西南风 <3级 10 西南风,微风
6 北京 通州 西南风 <3级 27 西南风 3-4级 11 西南风,微风
7 北京 昌平 南风 <3级 28 北风 <3级 11 详情
8 北京 延庆 西风 <3级 24 西风 <3级 8 详情
9 北京 丰台 西南风 3-4级 27 西南风 3-4级 12 详情
10 北京 石景山 南风 3-4级 26 西南风 <3级 12 详情
11 北京 大兴 西南风 <3级 27 西南风 3-4级 12 西南风,微风
12 北京 房山 西南风 <3级 27 西南风 <3级 12 西南风,微风
13 北京 密云 南风 <3级 27 东南风 <3级 10 详情
14 北京 门头沟 南风 3-4级 26 西南风 <3级 12 详情
15 北京 平谷 西南风 <3级 27 南风 <3级 11 西南风,微风
16 北京 东城 西南风 <3级 27 西南风 3-4级 11 西南风,微风
17 北京 西城 南风 3-4级 27 西南风 3-4级 13 详情

你可能感兴趣的:(Python基础及应用,python)