Python数据分析—清理数据

Python

# 一、清理数据注意事项

在评估之后,下一步是根据评估结果,对数据进行清洗。

# (一)、清洗数据之前

在清洗数据之前,我们要先看看索引或列名是否有意义。

如果索引或列名都是乱七八糟的,应该对它们进行重命名,或重新排序,以便我们理解数据。

# (二)、结构性问题

清洗数据,我们一般会先解决结构性问题,再处理内容性问题。

整洁数据,根据埃德加科德的第三范式,包括以下三个特点:

  1. 每列是一个变量
  2. 每行是一个观察值
  3. 每个单元格是一个值

任何不符合以上三个特点的数据都是乱数据。

# 1、每列是观察值,每行是变量

对行和列进行转置

# 2、每列包含多个变量

  • 对列进行拆分,把多的变量分到其它列去
  • 有的时候光拆分还不够,还要进行重塑,确保每列只包含一种变量

比如:许多列同时包含两个或多个变量的时候

# 3、每行包含多个观察值

  • 对行进行拆分,让每个观察值为独立的一行
  • 有的时候光拆分还不够,还要进行重塑,确保每列只包含一种观察值

比如:许多行同时包含两个或多个观察值的时候

很多时候,清理前的数据是宽数据,清理后的数据是长数据。

我们清理的目的,是为了后续能更高效地用程序处理数据,而不是更方便地让人类理解,所以清理前的宽数据更直观易懂也是正常的。

# (三)、内容性问题

在确保结构不存在问题后,我们再去深入到内容,处理脏数据。

# 1、缺失数据

针对缺失数据,处理方式需要具体情况具体分析。

  • 如果恰好知道空缺值的实际值,可以更新表格数据,人工把那个值填进去。
  • 如果我们不知道空缺值的实际值,而缺失值并不影响此次分析,最直接的办法是不处理缺失值。

Pandas在计算的时候,会自动忽略缺失值,所以很多时候放着不管不会造成什么问题。

  • 如果是关键变量缺失,我们可以把变量为空缺的行删掉,只留下对分析结果有意义的数据。
  • 如果是关键变量缺失,我们也可以用填充值的方式去处理,比如说把平均数、中位数、众数等填充进去,来代替空缺值

# 2、重复数据

针对重复数据,我们的处理方式就很简单了。

找到后删除即可,不删除的话,重复数据可能影响分析结论

# 3、不一致数据

针对不一致数据,我们的目标是对它们进行统一。针对同一含义,只保留一种表达方式,把其余的都进行替换

# 4、无效/错误数据

针对无效/错误数据,也有不同的清洗途径。

比如删除/替换,否则留下无效/错误数据,也可能影响分析结论。比如说一个负数的身高记录值,可以严重拉低平均值的分析。

  1. 把那条记录值进行删除。因为Pandas会自动忽略空缺值,所以NaN值反而不影响平均值计算
  2. 替换成其他值。比如说平均数。

# (四)、其他问题

除了数据本身的问题以外,我们清理数据时,有时候还要针对编程语言或库,做一些其他的处理,包括对数据类型进行转换。

比如:把手机号从数字类型转换成字符串类型;把'是'和'否'转换成布尔值True和False,能让我们之后针对这个变量的分析更加方便,包括能更简洁地进行逻辑判断

# 二、清理索引和列名

# (一)、 重命名索引和列名(使用字典)

DataFrame的rename方法,rename方法会返回一个新的DataFrame

适用于列名或索引数量少的情况

import pandas as pd
df1 = pd.read_csv('example1.csv', index_col=0)
df1
1
2
3
客户_姓名 客户 性别 age 邮箱
1 李十 58 [email protected]
2_ 冯三 27 [email protected]
3 吴十 32 [email protected]
4 王十 58 [email protected]
_5 钱十 27 [email protected]
6* 赵八 51 [email protected]

# 1、重命名索引

放入可选参数index,并赋值为一个字典。

字典中,把需要修改的老的名字作为键,把对应的新的名字作为值。

df1 = df1.rename(index={'2_': '2', '_5': '5', '6*': '6'})
df1
1
2
客户_姓名 客户 性别 age 邮箱
1 李十 58 [email protected]
2 冯三 27 [email protected]
3 吴十 32 [email protected]
4 王十 58 [email protected]
5 钱十 27 [email protected]
6 赵八 51 [email protected]

# 2、重命名列名

放入可选参数columns,并赋值为一个字典

字典中,把需要修改的老的名字作为键,把对应的新的名字作为值。

df1 = df1.rename(columns={'客户_姓名': '客户姓名', '客户 性别': '客户性别', 'age': '客户年龄', '邮箱': '客户邮箱'})
df1
1
2
客户姓名 客户性别 客户年龄 客户邮箱
1 李十 58 [email protected]
2 冯三 27 [email protected]
3 吴十 32 [email protected]
4 王十 58 [email protected]
5 钱十 27 [email protected]
6 赵八 51 [email protected]

# (二)、重命名索引和列名(函数/方法)

rename方法,除了传入字典,还可以传入一个针对Series的函数或方法,可以是Pandas库里面的,也可以是我们自行定义的

df2.rename(index=函数/方法)
df2.rename(columns=函数/方法)
1
2

这个函数/方法会把原本索引或列名作为参数,然后返回新的名字。

举个例子:

# 1、把所有列名变成大写

可以给columns这个可选参数,传入str.upper方法

str是Series类自带的一个属性,会返回一个包含了很多字符串相关操作方法的,StringMethods类的实例(返回实例才可以调用方法),对这个StringMethods实例调用upper方法,就会把Series里所有字符变成大写。

s1 = pd.Series(["hello", "this", "is", "Zheng"])
s1

0    hello
1     this
2       is
3    Zheng
dtype: object
1
2
3
4
5
6
7
8
s1.str
<pandas.core.strings.accessor.StringMethods at 0x1e6708d9710>
1
2
s1.str.upper()

0    HELLO
1     THIS
2       IS
3    ZHENG
dtype: object
1
2
3
4
5
6
7

作为columns参数的值,传给rename,执行后就会返回一个列名全变成了大写的DataFrame

df2 = pd.read_csv("example2.csv")
df2
1
2
Date Amount Salesperson Location
0 2022-01-01 1000 Alice New York
1 2022-01-02 1500 Bob San Francisco
2 2022-01-03 800 Charlie New York
3 2022-01-04 1200 David San Francisco
df2.rename(columns=str.upper)
1
DATE AMOUNT SALESPERSON LOCATION
0 2022-01-01 1000 Alice New York
1 2022-01-02 1500 Bob San Francisco
2 2022-01-03 800 Charlie New York
3 2022-01-04 1200 David San Francisco

# 2、其他操作

  1. 如果想了解其它对字符串Series调用的方法,可以查看Pandas官方文档

像这些所有以pandas.Series.str开头的,都是可以用在Pandas Series上的字符串操作

所有Series的方法相关文档:

https://pandas.pydata.org/docs/reference/api/pandas.Series.html (opens new window)

  1. 所有DataFrame的方法相关文档:

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html (opens new window)

  1. Pandas 官万文档: https://pandas.pydata.org/docs/reference (opens new window)

里面有所有 Pandas 库里面的类、类方法、类属性介绍等等。包括Series、DataFrame等

# (三)、设置索引

# 1、把某列设为索引

set_index方法,可以直接把某列属性的值用来作为索引,里面直接传入列名,会返回一个新的DataFrame,其索引变成了那列的值

df3 = df2.set_index('Salesperson')
df3
1
2
Date Amount Location
Salesperson
Alice 2022-01-01 1000 New York
Bob 2022-01-02 1500 San Francisco
Charlie 2022-01-03 800 New York
David 2022-01-04 1200 San Francisco

# 2、重置索引

reset_index方法,相当于做的和set_index相反的事,即把索引重设为初始默认的位置索引,并且把原本作为index的值变成单独一列

df3.reset_index()
1
Salesperson Date Amount Location
0 Alice 2022-01-01 1000 New York
1 Bob 2022-01-02 1500 San Francisco
2 Charlie 2022-01-03 800 New York
3 David 2022-01-04 1200 San Francisco

# (四)、对索引和列名重新排序

sort_index方法,适用于数据的索引或列名符合要求,但它的顺序是乱的情况

df4 = pd.read_csv('example3.csv', index_col=0)
df4
1
2
客户姓名 客户性别 客户年龄 客户邮箱
a 李十 58 [email protected]
c 冯三 27 [email protected]
b 吴十 32 [email protected]
e 王十 58 [email protected]
d 钱十 27 [email protected]
f 赵八 51 [email protected]

# 1、对索引进行排序

默认axis=0,沿着索引纵向操作,对索引排序

df4 = df4.sort_index()
df4
1
2
客户姓名 客户性别 客户年龄 客户邮箱
a 李十 58 [email protected]
b 吴十 32 [email protected]
c 冯三 27 [email protected]
d 钱十 27 [email protected]
e 王十 58 [email protected]
f 赵八 51 [email protected]

# 2、对列名进行排序

指定axis=1,沿着列名纵向操作,对列名排序

df4.sort_index(axis=1)
1
客户姓名 客户年龄 客户性别 客户邮箱
a 李十 58 [email protected]
b 吴十 32 [email protected]
c 冯三 27 [email protected]
d 钱十 27 [email protected]
e 王十 58 [email protected]
f 赵八 51 [email protected]

# (五)、可选参数inplace

适用于rename、set_index、reset_index、sort_index方法

传入'inplace=True',表示直接原地修改原始DataFrame,就不用进行赋值了;

并且此时方法不返回新的DataFrame,如果赋值给变量,则变量变为空值。

# 三、清理乱数据

整洁数据,根据埃德加科德的第三范式,包括以下三个特点:

  1. 每列是一个变量
  2. 每行是一个观察值
  3. 每个单元格是一个值

任何不符合以上三个特点的数据都是乱数据。

# (一)、对数据进行转置

当数据的列和行是反的,也就是每列是观察值,每行是变量,需要对数据进行转置

DataFrame的T属性,能返回一个行和列调换后的DataFrame

import pandas as pd
df1 = pd.read_csv('example4.csv', index_col=0)
df1
1
2
3
0 1 2
姓名 张三 李四 王五
年龄 30 25 28
地址 北京 上海 广州
df1 = df1.T
df1
1
2
姓名 年龄 地址
0 张三 30 北京
1 李四 25 上海
2 王五 28 广州

# (二)、对列进行拆分

当一列有多个变量时,把一列拆分成多列

df2 = pd.read_csv("example5.csv")
df2
1
2
城市 人口密度
0 城市A 28.53万人/0.87平方公里
1 城市B 37.83万人/2.19平方公里
2 城市C 15.3万人/1.57平方公里

# 1、拆分列

1)针对Python字符串

split方法,参数可以指定分隔符,就会得到拆分后字符串所组成的列表

"28.53万人/0.87平方公里".split("/")

['28.53万人', '0.87平方公里']
1
2
3

2)针对Series

str.split方法,参数可以指定分隔符,拆分字符串,调用后返回一个新的Series,每一个元素是一个列表。

但这没有达到拆分列的效果,因为Series只能表示DataFrame的一列,而我们希望拆分成多列。

df2['人口密度'].str.split('/')
1
0    [28.53万人, 0.87平方公里]
1    [37.83万人, 2.19平方公里]
2     [15.3万人, 1.57平方公里]
Name: 人口密度, dtype: object
1
2
3
4

3)拆分列(其实也是针对Series)

str.split方法,额外指定一个可选参数expand=True,表示把分隔后的结果分别用单独的Series表示,这样输出结果就会变成一个包含多列的DataFrame

df2['人口密度'].str.split('/', expand=True)
1
0 1
0 28.53万人 0.87平方公里
1 37.83万人 2.19平方公里
2 15.3万人 1.57平方公里

# 2、把新生成的DataFrame添加进原先的DataFrame

其实就是添加多列

在DataFrame后面方括号里放入列表,列表里放入多个列名,赋值给要加入的新列所组成的DataFrame

df2[['人口', '面积']] = df2['人口密度'].str.split('/', expand=True)
df2
1
2
城市 人口密度 人口 面积
0 城市A 28.53万人/0.87平方公里 28.53万人 0.87平方公里
1 城市B 37.83万人/2.19平方公里 37.83万人 2.19平方公里
2 城市C 15.3万人/1.57平方公里 15.3万人 1.57平方公里

# 3、删除拆分前的列

drop方法,指定axis=1

df2 = df2.drop('人口密度', axis=1)
df2
1
2
城市 人口 面积
0 城市A 28.53万人 0.87平方公里
1 城市B 37.83万人 2.19平方公里
2 城市C 15.3万人 1.57平方公里

# (三)、把不同列合并成一列

df3 = pd.DataFrame({'姓': ['张', '李', '王'], '名': ['三', '四', '五'], '年龄': [30, 25, 35]})
df3
1
2
年龄
0 30
1 25
2 35
  1. 拼接字符串

str.cat方法,参数传入拼接的Series,可以拼接2个Series。

可以传入可选参数sep,来指定拼接时的分隔符

  1. 添加列
  2. 删除拼接前的列

df3.姓.str.cat(df3.名)

0    张三
1    李四
2    王五
Name:, dtype: object
1
2
3
4

df3['姓'].str.cat(df3['名'], sep='-')

0-1-2-五
Name:, dtype: object
1
2
3
4
df3['姓名'] = df3..str.cat(df3.)
df3 = df3.drop(['姓', '名'], axis=1)
df3
1
2
3
年龄 姓名
0 30 张三
1 25 李四
2 35 王五

# (四)、把宽数据转换成长数据

df4 = pd.read_csv('example6.csv')
df4
1
2
国家代码 年份 男性年龄组(0-4岁) 男性年龄组(5-14岁) 男性年龄组(15-24岁) 女性年龄组(0-4岁) 女性年龄组(5-14岁) 女性年龄组(15-24岁)
0 CN 2019 100 200 500 80 150 400
1 US 2019 50 150 300 40 100 200
2 JP 2019 30 120 250 20 80 150
3 IN 2019 80 180 400 60 120 300

即重塑列,具体来说是在把一部分列名的值,转换为变量的值

pd.melt方法,可以帮我们进行这方面的转换。

第一个参数传入要转换的DataFrame

可选参数id_vars传入一个列表,里面放入想保持原样的列,那么除了id_vars列表里面之外的列,都会被视为要进行转换的列

可选参数var_name,要被赋值为在转换后的DataFrame中,包含原本列名值的新列列名

可选参数value_name,要被赋值为在转换后的DataFrame中,包含原本变量值的新列列名

运行后,就会返回一个从原本宽数据转换成长数据的DataFrame

df4 = pd.melt(df4, 
              id_vars=['国家代码', '年份'], 
              var_name='年龄组', 
              value_name='肺结核病例数')
df4
1
2
3
4
5
国家代码 年份 年龄组 肺结核病例数
0 CN 2019 男性年龄组(0-4岁) 100
1 US 2019 男性年龄组(0-4岁) 50
2 JP 2019 男性年龄组(0-4岁) 30
3 IN 2019 男性年龄组(0-4岁) 80
4 CN 2019 男性年龄组(5-14岁) 200
5 US 2019 男性年龄组(5-14岁) 150
6 JP 2019 男性年龄组(5-14岁) 120
7 IN 2019 男性年龄组(5-14岁) 180
8 CN 2019 男性年龄组(15-24岁) 500
9 US 2019 男性年龄组(15-24岁) 300
10 JP 2019 男性年龄组(15-24岁) 250
11 IN 2019 男性年龄组(15-24岁) 400
12 CN 2019 女性年龄组(0-4岁) 80
13 US 2019 女性年龄组(0-4岁) 40
14 JP 2019 女性年龄组(0-4岁) 20
15 IN 2019 女性年龄组(0-4岁) 60
16 CN 2019 女性年龄组(5-14岁) 150
17 US 2019 女性年龄组(5-14岁) 100
18 JP 2019 女性年龄组(5-14岁) 80
19 IN 2019 女性年龄组(5-14岁) 120
20 CN 2019 女性年龄组(15-24岁) 400
21 US 2019 女性年龄组(15-24岁) 200
22 JP 2019 女性年龄组(15-24岁) 150
23 IN 2019 女性年龄组(15-24岁) 300

# (五)、对行进行拆分

df5 = pd.read_csv("example7.csv")
df5
1
2
学生姓名 学号 课程列表
0 张三 1 ['数学', '物理']
1 李四 2 ['英语', '化学', '历史']
2 王五 3 ['语文', '数学', '英语', '政治']
3 赵六 4 ['物理', '生物']

虽然上面的课程列表变量看起来像是列表,但实际上是字符串

比如第一行里的课程列表实际为字符串"['数学', '物理']",而不是列表['数学', '物理']

所以我们需要先将字符串形式的列表转换为实际的列表对象,这样explode才能对列表进行拆分

import json
df5['课程列表'] = df5['课程列表'].apply(lambda x: json.loads(x.replace("'", "\"")))
df5
1
2
3
学生姓名 学号 课程列表
0 张三 1 [数学, 物理]
1 李四 2 [英语, 化学, 历史]
2 王五 3 [语文, 数学, 英语, 政治]
3 赵六 4 [物理, 生物]

当某列的值都是列表,而不是一个个独立的值

df.explode方法,传入需要拆分的变量名,就会返回一个把该列列表中每一个元素,转换为单独一行的新DataFrame

这个方法很实用,当每个单元格是一个值而不是列表时,更好进行统计

df5 = df5.explode('课程列表')
df5
1
2
学生姓名 学号 课程列表
0 张三 1 数学
0 张三 1 物理
1 李四 2 英语
1 李四 2 化学
1 李四 2 历史
2 王五 3 语文
2 王五 3 数学
2 王五 3 英语
2 王五 3 政治
3 赵六 4 物理
3 赵六 4 生物

# (六)、对行或列进行删除

当我们想清理无效或无用的行或列

drop方法

基本上,对DataFrame的操作方法,都是默认不改变原始DataFrame的,而是返回一个新的DataFrame

要么就得进行重新赋值,要么指定可选参数inplace=True

# 四、清理脏数据

import pandas as pd
import numpy as np
1
2

# (一)、处理缺失数据

# 1、对整列缺失值进行填充

# 创建示例 DataFrame
df1 = pd.DataFrame({
    '日期': ['2005-01-01', '2005-01-02', '2005-01-03', '2005-01-03'],
    '销售额': [1000, 1500, 800, 1200],
    '销售人员': ['李华', '王磊', '刘娜', '张洋'],
    '地址': ['苏州', '郑州', '南京', '西安']
})
df1 = df1.rename(index={
    0: '001',
    1: '002',
    2: '003',
    3: '004'
})
df1['国家'] = np.nan
df1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
日期 销售额 销售人员 地址 国家
001 2005-01-01 1000 李华 苏州 NaN
002 2005-01-02 1500 王磊 郑州 NaN
003 2005-01-03 800 刘娜 南京 NaN
004 2005-01-03 1200 张洋 西安 NaN
  1. 对全是缺失值的列填入统一值,可以通过针对列的赋值操作
  2. 对全是缺失值的列填入不同值

见更新DataFrame的一列值 2.15.一.1.

df1["国家"] = "中国"
df1
1
2
日期 销售额 销售人员 地址 国家
001 2005-01-01 1000 李华 苏州 中国
002 2005-01-02 1500 王磊 郑州 中国
003 2005-01-03 800 刘娜 南京 中国
004 2005-01-03 1200 张洋 西安 中国

# 2、对整行缺失值进行填充

  1. 对全是缺失值的行填入统一值,可以通过针对行的赋值操作
  2. 对全是缺失值的行填入不同值,见更新DataFrame的一行值
df1.iloc[3] = np.nan
df1
1
2
日期 销售额 销售人员 地址 国家
001 2005-01-01 1000.0 李华 苏州 中国
002 2005-01-02 1500.0 王磊 郑州 中国
003 2005-01-03 800.0 刘娜 南京 中国
004 NaN NaN NaN NaN NaN
df1.iloc[3] = "test"
df1
1
2
日期 销售额 销售人员 地址 国家
001 2005-01-01 1000.0 李华 苏州 中国
002 2005-01-02 1500.0 王磊 郑州 中国
003 2005-01-03 800.0 刘娜 南京 中国
004 test test test test test

# 3、对某个缺失值进行填充

# 创建示例 DataFrame
df2 = pd.DataFrame({
    '日期': ['2005-01-01', '2005-01-02', '2005-01-03', '2005-01-03'],
    '销售额': [1000, 1500, np.nan, 1200],
    '销售人员': ['李华', '王磊', '刘娜', '张洋'],
    '地址': ['苏州', '郑州', '南京', '西安']
})
df2 = df2.rename(index={
    0: '001',
    1: '002',
    2: '003',
    3: '004'
})
df2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
日期 销售额 销售人员 地址
001 2005-01-01 1000.0 李华 苏州
002 2005-01-02 1500.0 王磊 郑州
003 2005-01-03 NaN 刘娜 南京
004 2005-01-03 1200.0 张洋 西安

可以先用loc或iloc先去定位到那个值,然后重新赋值

用loc或iloc先去定位值,见Dataframe章节

df2.loc['003', '销售额'] = 800
df2
1
2
日期 销售额 销售人员 地址
001 2005-01-01 1000.0 李华 苏州
002 2005-01-02 1500.0 王磊 郑州
003 2005-01-03 800.0 刘娜 南京
004 2005-01-03 1200.0 张洋 西安

# 4、对部分缺失值进行填充

df3 = pd.DataFrame({
    '日期': ['2005-01-01', '2005-01-02', np.nan, np.nan],
    '销售额': [1000, 1500, 800, 1200],
    '销售人员': ['李华', '王磊', '刘娜', '张洋'],
    '地址': ['苏州', '郑州', '南京', '西安']
})
df3 = df3.rename(index={
    0: '001',
    1: '002',
    2: '003',
    3: '004'
})
df3
1
2
3
4
5
6
7
8
9
10
11
12
13
日期 销售额 销售人员 地址
001 2005-01-01 1000 李华 苏州
002 2005-01-02 1500 王磊 郑州
003 NaN 800 刘娜 南京
004 NaN 1200 张洋 西安
# 1)填入统一值

如果要替换某一部分的值,也是一样的道理,能用loc或iloc提取出来的部分,都可以通过赋值来填充或替换值。

df3.loc['003': '004', '日期'] = '2005-01-03'
df3
1
2
日期 销售额 销售人员 地址
001 2005-01-01 1000 李华 苏州
002 2005-01-02 1500 王磊 郑州
003 2005-01-03 800 刘娜 南京
004 2005-01-03 1200 张洋 西安
# 2)分别填入值

如果要对部分缺失值,分别填入不同值,可以用元组或列表的形式来赋值

缺失值的个数和元组或列表里元素的个数,要一致

df3.loc['003': '004', '日期'] = ['2005-01-03', '2023-9-15']
df3
1
2
日期 销售额 销售人员 地址
001 2005-01-01 1000 李华 苏州
002 2005-01-02 1500 王磊 郑州
003 2005-01-03 800 刘娜 南京
004 2023-9-15 1200 张洋 西安

# 5、自动找到缺失值进行填充

以上方法都需要我们自行定位缺失值的位置,但在数据量很大,缺失值很多的情况下,更需要程序能自动找到所有为NaN的值,然后根据我们的指示进行填充

df4 = pd.DataFrame({'A': [1, 2, np.nan, 4],
                    'B': [5, np.nan, 7, np.nan],
                    'C': [8, 9, 10, 11]})
df4
1
2
3
4
A B C
0 1.0 5.0 8
1 2.0 NaN 9
2 NaN 7.0 10
3 4.0 NaN 11
# 1)Series的fillna方法

Series有个叫fillna的方法,调用时列里面所有NaN值都会被替换成传入的参数,就不需要一个个去定位缺失值的位置了

a、对缺失值填入确定的值

df4['B'].fillna(0)

0    5.0
1    0.0
2    7.0
3    0.0
Name: B, dtype: float64
1
2
3
4
5
6
7

b、对缺失值填入某个计算结果

fillna方法里,除了传入确定的参数,还可以替换成某个计算结果,如平均值

df4['B'].fillna(df4['B'].mean())

0    5.0
1    6.0
2    7.0
3    6.0
Name: B, dtype: float64
1
2
3
4
5
6
7
# 2)DataFrame的fillna方法

DataFrame也有fillna方法,所以除了能填充某列的缺失值,还能直接对整个DataFrame里的缺失值进行填充。

a、对缺失值填入统一值

传入某个值后,返回的新DataFrame里,所有缺失值都会变成那个值

df4.fillna(0)
1
A B C
0 1.0 5.0 8
1 2.0 0.0 9
2 0.0 7.0 10
3 4.0 0.0 11

b、对缺失值按列填入不同值

还可以传入一个字典,键为列名,值为替换值,能实现把不同列里的空缺值,替换成不同值的效果。这个相比Series的fillna方法逐列操作要高效得多

df4.fillna({'A': 0, 'B': 10, 'C': 20})
1
A B C
0 1.0 5.0 8
1 2.0 10.0 9
2 0.0 7.0 10
3 4.0 10.0 11

只返回替换好的新DataFrame

# 6、删除存在缺失值的行

如果丢失的是对分析目标有关键意义的数据,同时又没有合适的值进行填充,这时我们或许需要抛弃存在空缺值的观察值,调用dropna方法,被用于删除有缺失值的数据

df5 = pd.DataFrame({
    '姓名': ['John', 'Alice', 'Bob', 'Mary'],
    '年龄': [25, 30, np.nan, 40],
    '工资': [50000, np.nan, 70000, 60000],
    '性别': ['M', 'F', 'M', 'F']
})
df5
1
2
3
4
5
6
7
姓名 年龄 工资 性别
0 John 25.0 50000.0 M
1 Alice 30.0 NaN F
2 Bob NaN 70000.0 M
3 Mary 40.0 60000.0 F
# 1)删除所有有缺失值的行

DataFrame在dropna方法直接调用时,会返回没有任何缺失值的行,所组成的新DataFrame。也就是说,只要某行任何一个值是空缺的,调用dropna后就见不到它了。

df5.dropna()
1
姓名 年龄 工资 性别
0 John 25.0 50000.0 M
3 Mary 40.0 60000.0 F
# 2)删除关键值缺失的行

但我们一般清理数据的时候,更关注的是某些有关键信息的列,不太关键的变量,即使缺失也问题不大

那我们调用dropna方法时,可以传入一个可选参数subset=['关键值缺失的列的列名1',...]。

df5.dropna(subset=['工资'])
1
姓名 年龄 工资 性别
0 John 25.0 50000.0 M
2 Bob NaN 70000.0 M
3 Mary 40.0 60000.0 F
# 3)删除有缺失值的列

指定axis=1

df5.dropna(axis=1)
1
姓名 性别
0 John M
1 Alice F
2 Bob M
3 Mary F
df5.dropna(axis=1, subset=[0, 1])
1
姓名 年龄 性别
0 John 25.0 M
1 Alice 30.0 F
2 Bob NaN M
3 Mary 40.0 F

# (二)、删除重复数据

df6 = pd.DataFrame({
    '姓名': ['John', 'Alice', 'Bob', 'Alice', 'John'],
    '年龄': [25, 30, 35, 30, 40],
    '性别': ['M', 'F', 'M', 'F', 'M']
})
df6
1
2
3
4
5
6
姓名 年龄 性别
0 John 25 M
1 Alice 30 F
2 Bob 35 M
3 Alice 30 F
4 John 40 M

drop_duplicates方法,可以用于删除Series里的重复值,或是DataFrame里的重复行

# 1、删除所有变量的值都重复的行

直接调用DataFrame的drop_duplicates时,只有当所有变量都一样的时候,才会被视为重复,也因此才会被删除掉。

df6['姓名'].drop_duplicates()

0     John
1    Alice
2      Bob
Name: 姓名, dtype: object
1
2
3
4
5
6
df6.drop_duplicates()
1
姓名 年龄 性别
0 John 25 M
1 Alice 30 F
2 Bob 35 M
4 John 40 M

# 2、删除特定变量的值重复的行

也可以用subset=['变量1', '变量2'...],只要这个列表里的变量同时重复,就会被删除

df6.drop_duplicates(subset=['姓名', '性别'])
1
姓名 年龄 性别
0 John 25 M
1 Alice 30 F
2 Bob 35 M

# 3、可选参数keep

删除的时候,默认是删除第二次及之后出现的值。

但我们也可以把让可选参数keep='last',这样会保留最后一个出现的值,删除掉之前出现的重复值

df6.drop_duplicates(subset=['姓名', '性别'], keep='last')
1
姓名 年龄 性别
2 Bob 35 M
3 Alice 30 F
4 John 40 M

# (三)、处理不一致数据

不一致数据是指,存在不同的值实际含义相同,或指代的是同一目标

处理方法是,把数值都替换成统一的。

data = {'姓名': ['小明', '小红', '小张', '小李'],
        '家乡': ['北京', '上海', '广州', '深圳'],
        '学校': ['北京大学', '清华大学', '华南理工', '清华']}
df7 = pd.DataFrame(data)
df7
1
2
3
4
5
姓名 家乡 学校
0 小明 北京 北京大学
1 小红 上海 清华大学
2 小张 广州 华南理工
3 小李 深圳 清华

可以用Series或DataFrame的replace方法,在Series和DataFrame上用法差不多

# 1、replace方法参数是两个数字或字符串

表示想把前面值全部替换成后面值

df7['学校'].replace('清华', '清华大学')

0    北京大学
1    清华大学
2    华南理工
3    清华大学
Name: 学校, dtype: object
1
2
3
4
5
6
7
df7.replace('清华', '清华大学')
1
姓名 家乡 学校
0 小明 北京 北京大学
1 小红 上海 清华大学
2 小张 广州 华南理工
3 小李 深圳 清华大学

# 2、replace方法第一个参数是一个列表

那么列表里面所有值都会被替换成后面那个,这样就可以一次性,对多个不同指代词进行统一

df7['学校'].replace(['清华', '五道口职业技术学院', 'Tsinghua University'],'清华大学')

0    北京大学
1    清华大学
2    华南理工
3    清华大学
Name: 学校, dtype: object
1
2
3
4
5
6
7
df7.replace(['清华', '五道口职业技术学院', 'Tsinghua University'],'清华大学')
1
姓名 家乡 学校
0 小明 北京 北京大学
1 小红 上海 清华大学
2 小张 广州 华南理工
3 小李 深圳 清华大学

# 3、replace方法参数是单个字典

去指定不同的值要被什么值替换

replace_dict = {'华南理工': '华南理工大学', 
                '清华': '清华大学', 
                '北大': '北京大学', 
                '中大': '中山大学'}
df7.replace(replace_dict)
1
2
3
4
5
姓名 家乡 学校
0 小明 北京 北京大学
1 小红 上海 清华大学
2 小张 广州 华南理工大学
3 小李 深圳 清华大学

# (四)、进行数据类型转换

数据类型转换,在很多时候是有必要,因为某些方法只能用在特定类型的数据上。比如说:不能对字符串求平均值

# 1、Series的astype方法

参数为数据类型,比如int、float、str、bool等,调用后,返回的新Series里的数据就被转换成了那个类型

s1 = pd.Series([1, 2, 3])
s1.astype(float)
1
2
0    1.0
1    2.0
2    3.0
dtype: float64
1
2
3
4

# 2、type函数

在Python里面,如果你想知道数据类型,可以调用type函数

# 3、Pandas的数据类型category

数据可以被划分为两种类型,分类数据和数值数据。

分类数据,指的是包含有限数量的不同类别的数据,比如:出生时的性别,就两种,是有限的;奥运会的奖牌,金银铜,也是有限的

数值数据,指的是测量出的观测值,是某个具体的数值,种类不受限制。比如:0到1之间就有无限个数字,那对它进行求和或求平均值等数学运算也是有意义的

当分类数据种类有限时,Pandas会推荐把它们转换成category这个数据类型。

好处:节约内存空间,并且在后续分析或可视化的时候,也有利于让Pandas自动选用合适的统计方法或图标类型

category不是Python自带的类型名称,而是Pandas库里面的,所以我们在用astype进行转换的时候,要传入一个用引号包围的category,不然Python不认

s2 = pd.Series(["红色", "红色", "橙色", "蓝色"])
s2.astype('category')
1
2
0    红色
1    红色
2    橙色
3    蓝色
dtype: category
Categories (3, object): ['橙色', '红色', '蓝色']
1
2
3
4
5
6

# 4、转换成datetime日期时间类型

可以用Pandas的to_datetime方法,参数传入Series,可以返回转换成日期时间类型的Series

df3['日期'] = pd.to_datetime(df3['日期'])
df3.日期
1
2
001   2005-01-01
002   2005-01-02
003   2005-01-03
004   2023-09-15
Name: 日期, dtype: datetime64[ns]
1
2
3
4
5
最近修改于: 2024/12/11 00:00:05
和宇宙温柔的关联
房东的猫