# 一、清理数据注意事项
在评估之后,下一步是根据评估结果,对数据进行清洗。
# (一)、清洗数据之前
在清洗数据之前,我们要先看看索引或列名是否有意义。
如果索引或列名都是乱七八糟的,应该对它们进行重命名,或重新排序,以便我们理解数据。
# (二)、结构性问题
清洗数据,我们一般会先解决结构性问题,再处理内容性问题。
整洁数据,根据埃德加科德的第三范式,包括以下三个特点:
- 每列是一个变量
- 每行是一个观察值
- 每个单元格是一个值
任何不符合以上三个特点的数据都是乱数据。
# 1、每列是观察值,每行是变量
对行和列进行转置
# 2、每列包含多个变量
- 对列进行拆分,把多的变量分到其它列去
- 有的时候光拆分还不够,还要进行重塑,确保每列只包含一种变量
比如:许多列同时包含两个或多个变量的时候
# 3、每行包含多个观察值
- 对行进行拆分,让每个观察值为独立的一行
- 有的时候光拆分还不够,还要进行重塑,确保每列只包含一种观察值
比如:许多行同时包含两个或多个观察值的时候
很多时候,清理前的数据是宽数据,清理后的数据是长数据。
我们清理的目的,是为了后续能更高效地用程序处理数据,而不是更方便地让人类理解,所以清理前的宽数据更直观易懂也是正常的。
# (三)、内容性问题
在确保结构不存在问题后,我们再去深入到内容,处理脏数据。
# 1、缺失数据
针对缺失数据,处理方式需要具体情况具体分析。
- 如果恰好知道空缺值的实际值,可以更新表格数据,人工把那个值填进去。
- 如果我们不知道空缺值的实际值,而缺失值并不影响此次分析,最直接的办法是不处理缺失值。
Pandas在计算的时候,会自动忽略缺失值,所以很多时候放着不管不会造成什么问题。
- 如果是关键变量缺失,我们可以把变量为空缺的行删掉,只留下对分析结果有意义的数据。
- 如果是关键变量缺失,我们也可以用填充值的方式去处理,比如说把平均数、中位数、众数等填充进去,来代替空缺值
# 2、重复数据
针对重复数据,我们的处理方式就很简单了。
找到后删除即可,不删除的话,重复数据可能影响分析结论
# 3、不一致数据
针对不一致数据,我们的目标是对它们进行统一。针对同一含义,只保留一种表达方式,把其余的都进行替换
# 4、无效/错误数据
针对无效/错误数据,也有不同的清洗途径。
比如删除/替换,否则留下无效/错误数据,也可能影响分析结论。比如说一个负数的身高记录值,可以严重拉低平均值的分析。
- 把那条记录值进行删除。因为Pandas会自动忽略空缺值,所以NaN值反而不影响平均值计算
- 替换成其他值。比如说平均数。
# (四)、其他问题
除了数据本身的问题以外,我们清理数据时,有时候还要针对编程语言或库,做一些其他的处理,包括对数据类型进行转换。
比如:把手机号从数字类型转换成字符串类型;把'是'和'否'转换成布尔值True和False,能让我们之后针对这个变量的分析更加方便,包括能更简洁地进行逻辑判断
# 二、清理索引和列名
# (一)、 重命名索引和列名(使用字典)
DataFrame的rename
方法,rename
方法会返回一个新的DataFrame
适用于列名或索引数量少的情况
import pandas as pd
df1 = pd.read_csv('example1.csv', index_col=0)
df1
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
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
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=函数/方法)
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
2
3
4
5
6
7
8
s1.str
<pandas.core.strings.accessor.StringMethods at 0x1e6708d9710>
2
s1.str.upper()
0 HELLO
1 THIS
2 IS
3 ZHENG
dtype: object
2
3
4
5
6
7
作为columns参数的值,传给rename,执行后就会返回一个列名全变成了大写的DataFrame
df2 = pd.read_csv("example2.csv")
df2
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)
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、其他操作
- 如果想了解其它对字符串Series调用的方法,可以查看Pandas官方文档
像这些所有以pandas.Series.str
开头的,都是可以用在Pandas Series
上的字符串操作
所有Series的方法相关文档:
https://pandas.pydata.org/docs/reference/api/pandas.Series.html (opens new window)
- 所有DataFrame的方法相关文档:
https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html (opens new window)
里面有所有 Pandas 库里面的类、类方法、类属性介绍等等。包括Series、DataFrame等
# (三)、设置索引
# 1、把某列设为索引
set_index方法,可以直接把某列属性的值用来作为索引,里面直接传入列名,会返回一个新的DataFrame,其索引变成了那列的值
df3 = df2.set_index('Salesperson')
df3
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()
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
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
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)
客户姓名 | 客户年龄 | 客户性别 | 客户邮箱 | |
---|---|---|---|---|
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,如果赋值给变量,则变量变为空值。
# 三、清理乱数据
整洁数据,根据埃德加科德的第三范式,包括以下三个特点:
- 每列是一个变量
- 每行是一个观察值
- 每个单元格是一个值
任何不符合以上三个特点的数据都是乱数据。
# (一)、对数据进行转置
当数据的列和行是反的,也就是每列是观察值,每行是变量,需要对数据进行转置
DataFrame的T属性,能返回一个行和列调换后的DataFrame
import pandas as pd
df1 = pd.read_csv('example4.csv', index_col=0)
df1
2
3
0 | 1 | 2 | |
---|---|---|---|
姓名 | 张三 | 李四 | 王五 |
年龄 | 30 | 25 | 28 |
地址 | 北京 | 上海 | 广州 |
df1 = df1.T
df1
2
姓名 | 年龄 | 地址 | |
---|---|---|---|
0 | 张三 | 30 | 北京 |
1 | 李四 | 25 | 上海 |
2 | 王五 | 28 | 广州 |
# (二)、对列进行拆分
当一列有多个变量时,把一列拆分成多列
df2 = pd.read_csv("example5.csv")
df2
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平方公里']
2
3
2)针对Series
str.split方法,参数可以指定分隔符,拆分字符串,调用后返回一个新的Series,每一个元素是一个列表。
但这没有达到拆分列的效果,因为Series只能表示DataFrame的一列,而我们希望拆分成多列。
df2['人口密度'].str.split('/')
0 [28.53万人, 0.87平方公里]
1 [37.83万人, 2.19平方公里]
2 [15.3万人, 1.57平方公里]
Name: 人口密度, dtype: object
2
3
4
3)拆分列(其实也是针对Series)
str.split方法,额外指定一个可选参数expand=True,表示把分隔后的结果分别用单独的Series表示,这样输出结果就会变成一个包含多列的DataFrame
df2['人口密度'].str.split('/', expand=True)
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
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
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
2
姓 | 名 | 年龄 | |
---|---|---|---|
0 | 张 | 三 | 30 |
1 | 李 | 四 | 25 |
2 | 王 | 五 | 35 |
- 拼接字符串
str.cat方法,参数传入拼接的Series,可以拼接2个Series。
可以传入可选参数sep,来指定拼接时的分隔符
- 添加列
- 删除拼接前的列
df3.姓.str.cat(df3.名)
0 张三
1 李四
2 王五
Name: 姓, dtype: object
2
3
4
df3['姓'].str.cat(df3['名'], sep='-')
0 张-三
1 李-四
2 王-五
Name: 姓, dtype: object
2
3
4
df3['姓名'] = df3.姓.str.cat(df3.名)
df3 = df3.drop(['姓', '名'], axis=1)
df3
2
3
年龄 | 姓名 | |
---|---|---|
0 | 30 | 张三 |
1 | 25 | 李四 |
2 | 35 | 王五 |
# (四)、把宽数据转换成长数据
df4 = pd.read_csv('example6.csv')
df4
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
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
2
学生姓名 | 学号 | 课程列表 | |
---|---|---|---|
0 | 张三 | 1 | ['数学', '物理'] |
1 | 李四 | 2 | ['英语', '化学', '历史'] |
2 | 王五 | 3 | ['语文', '数学', '英语', '政治'] |
3 | 赵六 | 4 | ['物理', '生物'] |
虽然上面的课程列表变量看起来像是列表,但实际上是字符串
比如第一行里的课程列表实际为字符串"['数学', '物理']",而不是列表['数学', '物理']
所以我们需要先将字符串形式的列表转换为实际的列表对象,这样explode才能对列表进行拆分
import json
df5['课程列表'] = df5['课程列表'].apply(lambda x: json.loads(x.replace("'", "\"")))
df5
2
3
学生姓名 | 学号 | 课程列表 | |
---|---|---|---|
0 | 张三 | 1 | [数学, 物理] |
1 | 李四 | 2 | [英语, 化学, 历史] |
2 | 王五 | 3 | [语文, 数学, 英语, 政治] |
3 | 赵六 | 4 | [物理, 生物] |
当某列的值都是列表,而不是一个个独立的值
df.explode方法,传入需要拆分的变量名,就会返回一个把该列列表中每一个元素,转换为单独一行的新DataFrame
这个方法很实用,当每个单元格是一个值而不是列表时,更好进行统计
df5 = df5.explode('课程列表')
df5
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
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
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 |
- 对全是缺失值的列填入统一值,可以通过针对列的赋值操作
- 对全是缺失值的列填入不同值
见更新DataFrame的一列值 2.15.一.1.
df1["国家"] = "中国"
df1
2
日期 | 销售额 | 销售人员 | 地址 | 国家 | |
---|---|---|---|---|---|
001 | 2005-01-01 | 1000 | 李华 | 苏州 | 中国 |
002 | 2005-01-02 | 1500 | 王磊 | 郑州 | 中国 |
003 | 2005-01-03 | 800 | 刘娜 | 南京 | 中国 |
004 | 2005-01-03 | 1200 | 张洋 | 西安 | 中国 |
# 2、对整行缺失值进行填充
- 对全是缺失值的行填入统一值,可以通过针对行的赋值操作
- 对全是缺失值的行填入不同值,见更新DataFrame的一行值
df1.iloc[3] = np.nan
df1
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
# 2)DataFrame的fillna方法
DataFrame也有fillna方法,所以除了能填充某列的缺失值,还能直接对整个DataFrame里的缺失值进行填充。
a、对缺失值填入统一值
传入某个值后,返回的新DataFrame里,所有缺失值都会变成那个值
df4.fillna(0)
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})
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
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()
姓名 | 年龄 | 工资 | 性别 | |
---|---|---|---|---|
0 | John | 25.0 | 50000.0 | M |
3 | Mary | 40.0 | 60000.0 | F |
# 2)删除关键值缺失的行
但我们一般清理数据的时候,更关注的是某些有关键信息的列,不太关键的变量,即使缺失也问题不大
那我们调用dropna方法时,可以传入一个可选参数subset=['关键值缺失的列的列名1',...]。
df5.dropna(subset=['工资'])
姓名 | 年龄 | 工资 | 性别 | |
---|---|---|---|---|
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)
姓名 | 性别 | |
---|---|---|
0 | John | M |
1 | Alice | F |
2 | Bob | M |
3 | Mary | F |
df5.dropna(axis=1, subset=[0, 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
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
2
3
4
5
6
df6.drop_duplicates()
姓名 | 年龄 | 性别 | |
---|---|---|---|
0 | John | 25 | M |
1 | Alice | 30 | F |
2 | Bob | 35 | M |
4 | John | 40 | M |
# 2、删除特定变量的值重复的行
也可以用subset=['变量1', '变量2'...],只要这个列表里的变量同时重复,就会被删除
df6.drop_duplicates(subset=['姓名', '性别'])
姓名 | 年龄 | 性别 | |
---|---|---|---|
0 | John | 25 | M |
1 | Alice | 30 | F |
2 | Bob | 35 | M |
# 3、可选参数keep
删除的时候,默认是删除第二次及之后出现的值。
但我们也可以把让可选参数keep='last',这样会保留最后一个出现的值,删除掉之前出现的重复值
df6.drop_duplicates(subset=['姓名', '性别'], keep='last')
姓名 | 年龄 | 性别 | |
---|---|---|---|
2 | Bob | 35 | M |
3 | Alice | 30 | F |
4 | John | 40 | M |
# (三)、处理不一致数据
不一致数据是指,存在不同的值实际含义相同,或指代的是同一目标
处理方法是,把数值都替换成统一的。
data = {'姓名': ['小明', '小红', '小张', '小李'],
'家乡': ['北京', '上海', '广州', '深圳'],
'学校': ['北京大学', '清华大学', '华南理工', '清华']}
df7 = pd.DataFrame(data)
df7
2
3
4
5
姓名 | 家乡 | 学校 | |
---|---|---|---|
0 | 小明 | 北京 | 北京大学 |
1 | 小红 | 上海 | 清华大学 |
2 | 小张 | 广州 | 华南理工 |
3 | 小李 | 深圳 | 清华 |
可以用Series或DataFrame的replace方法,在Series和DataFrame上用法差不多
# 1、replace方法参数是两个数字或字符串
表示想把前面值全部替换成后面值
df7['学校'].replace('清华', '清华大学')
0 北京大学
1 清华大学
2 华南理工
3 清华大学
Name: 学校, dtype: object
2
3
4
5
6
7
df7.replace('清华', '清华大学')
姓名 | 家乡 | 学校 | |
---|---|---|---|
0 | 小明 | 北京 | 北京大学 |
1 | 小红 | 上海 | 清华大学 |
2 | 小张 | 广州 | 华南理工 |
3 | 小李 | 深圳 | 清华大学 |
# 2、replace方法第一个参数是一个列表
那么列表里面所有值都会被替换成后面那个,这样就可以一次性,对多个不同指代词进行统一
df7['学校'].replace(['清华', '五道口职业技术学院', 'Tsinghua University'],'清华大学')
0 北京大学
1 清华大学
2 华南理工
3 清华大学
Name: 学校, dtype: object
2
3
4
5
6
7
df7.replace(['清华', '五道口职业技术学院', 'Tsinghua University'],'清华大学')
姓名 | 家乡 | 学校 | |
---|---|---|---|
0 | 小明 | 北京 | 北京大学 |
1 | 小红 | 上海 | 清华大学 |
2 | 小张 | 广州 | 华南理工 |
3 | 小李 | 深圳 | 清华大学 |
# 3、replace方法参数是单个字典
去指定不同的值要被什么值替换
replace_dict = {'华南理工': '华南理工大学',
'清华': '清华大学',
'北大': '北京大学',
'中大': '中山大学'}
df7.replace(replace_dict)
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)
2
0 1.0
1 2.0
2 3.0
dtype: float64
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')
2
0 红色
1 红色
2 橙色
3 蓝色
dtype: category
Categories (3, object): ['橙色', '红色', '蓝色']
2
3
4
5
6
# 4、转换成datetime日期时间类型
可以用Pandas的to_datetime方法,参数传入Series,可以返回转换成日期时间类型的Series
df3['日期'] = pd.to_datetime(df3['日期'])
df3.日期
2
001 2005-01-01
002 2005-01-02
003 2005-01-03
004 2023-09-15
Name: 日期, dtype: datetime64[ns]
2
3
4
5