# Pandas_Dataframe
# 一、DataFrame简介
相比Series,由多个Series组成的DataFrame,才是我们分析数据是最常打交道的数据结构。
DataFrame看起来像是一个表格,它的不同列可以是不同数据类型,而不是像NumPy二维数组那样,要求数据类型全部保持一致。
相比起Series,DataFrame每个值不止有索引,也有列名。换个角度来看,DataFrame就像是由Series组成的字典,每个Series对应一个键名,也就是列名。
# 二、创建DataFrame
创建DataFrame时,会自动进行索引对齐
# (一)参数传入一个字典,值是Series
既然DataFrame可以看成由Series组成的字典,那么第一个创建方法就是,参数传入一个字典。键是各个Series所对应的列名。
创建好后,Jupyter Notebook会输出一个,排版好看的DataFrame表格。索引就对应了Series的索引,而列名对应我们传入的键。
参数传入一个字典,值是Series
import pandas as pd
s_id = pd.Series(["01", "02", "03", "04", "05"])
s_class = pd.Series(["二班", "一班", "二班", "三班", "一班"])
s_grade = pd.Series([92, 67, 70, 88, 76])
df1 = pd.DataFrame({"学号": s_id, "班级": s_class, "成绩": s_grade})
df1
2
3
4
5
6
7
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
0 | 01 | 二班 | 92 |
1 | 02 | 一班 | 67 |
2 | 03 | 二班 | 70 |
3 | 04 | 三班 | 88 |
4 | 05 | 一班 | 76 |
# (二)参数传入一个字典,值是列表
l_id = ["01", "02", "03", "04", "05"]
l_class = ["二班", "一班", "二班", "三班", "一班"]
l_grade = [92, 67, 70, 88, 76]
df2 = pd.DataFrame({"学号": s_id, "班级": s_class, "成绩": s_grade})
df2
2
3
4
5
6
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
0 | 01 | 二班 | 92 |
1 | 02 | 一班 | 67 |
2 | 03 | 二班 | 70 |
3 | 04 | 三班 | 88 |
4 | 05 | 一班 | 76 |
Dataframe默认的索引和Series一样,都是从0开始依次递增的整数,来表示位置。但如果传入的Series有标签索引的话,DataFrame的索引也会变成相应的标签。
s_id = pd.Series(["01", "02", "03", "04", "05"], index=["小明", "小红", "小杰", "小丽", "小华"])
s_class = pd.Series(["二班", "一班", "二班", "三班", "一班"], index=["小明", "小红", "小杰", "小丽", "小华"])
s_grade = pd.Series([92, 67, 70, 88, 76],
index=["小明", "小红", "小杰", "小丽", "小华"])
df3 = pd.DataFrame({"学号": s_id, "班级": s_class, "成绩": s_grade})
df3
2
3
4
5
6
7
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小明 | 01 | 二班 | 92 |
小红 | 02 | 一班 | 67 |
小杰 | 03 | 二班 | 70 |
小丽 | 04 | 三班 | 88 |
小华 | 05 | 一班 | 76 |
# (三)参数传入一个嵌套字典
参数传入一个嵌套字典,可以一次性创建出既有标签索引,也有列名的DataFrame
最外层的键仍然对应各个列名,而值则对应每列的Series;里层字典的键对应Series的标签索引
df4 = pd.DataFrame({"学号": {"小明": "01", "小红": "02", "小杰": "03", "小丽": "04", "小华": "05"},
"班级": {"小明": "二班", "小红": "一班", "小杰": "二班", "小丽": "三班", "小华": "一班"},
"成绩": {"小明": 92, "小红": 67, "小杰": 70, "小丽": 88, "小华": 76}})
df4
2
3
4
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小明 | 01 | 二班 | 92 |
小红 | 02 | 一班 | 67 |
小杰 | 03 | 二班 | 70 |
小丽 | 04 | 三班 | 88 |
小华 | 05 | 一班 | 76 |
# 三、DataFrame的常用属性
# (一)index属性与columns属性
要获得DataFrame的索引,可以用index属性;要获取所有列名,可以用columns属性。
返回的索引和列名的数据类型,都是pandas库的Index类
df4.index
Index(['小明', '小红', '小杰', '小丽', '小华'], dtype='object')
df4.columns
Index(['学号', '班级', '成绩'], dtype='object')
2
3
4
5
6
7
# (二)values属性
要获取所有的值,可以用values属性
返回的类型是NumPy数组,那所有针对NumPy数组的操作,都可以用在values属性上了
df4.values
array([['01', '二班', 92],
['02', '一班', 67],
['03', '二班', 70],
['04', '三班', 88],
['05', '一班', 76]], dtype=object)
2
3
4
5
6
7
# (三)T属性
对DataFrame进行转置,可以用T属性,大写的T。
返回的结果会把行和列进行转置。
df4.T
运行结果
小明 | 小红 | 小杰 | 小丽 | 小华 | |
---|---|---|---|---|---|
学号 | 01 | 02 | 03 | 04 | 05 |
班级 | 二班 | 一班 | 二班 | 三班 | 一班 |
成绩 | 92 | 67 | 70 | 88 | 76 |
获取转置后的属性
df5 = df4.T
print(df5.index)
print(df5.columns)
print(df5.values)
2
3
4
运行结果
Index(['学号', '班级', '成绩'], dtype='object')
Index(['小明', '小红', '小杰', '小丽', '小华'], dtype='object')
[['01' '02' '03' '04' '05']
['二班' '一班' '二班' '三班' '一班']
[92 67 70 88 76]]
2
3
4
5
转置后,索引、列名、值都相应发生了变化
# 四、从DataFrame中提取数据
# (一)提取DataFrame的列
# 1、提取某个列
1)在DataFrame后面跟上方括号,里面放上列名,就能把列名对应的列提取出来。
列的类型是Series,索引对应DataFrame原本的索引。
这就跟我们在Python字典后面跟上方括号,里面放上键名,就能把键对应的值提取出来是类似的
df4['成绩']
小明 92
小红 67
小杰 70
小丽 88
小华 76
Name: 成绩, dtype: int64
2
3
4
5
6
7
8
df4['班级']
小明 二班
小红 一班
小杰 二班
小丽 三班
小华 一班
Name: 班级, dtype: object
2
3
4
5
6
7
8
2)也可以通过".列名" ,来获取对应的列,因为每列Series其实也是DataFrame的属性
如果列名里面有空格或特殊符号的话,就不能通过属性名来获取了,只能通过方括号
Pandas不允许通过属性来添加/更新列
df4.成绩
小明 92
小红 67
小杰 70
小丽 88
小华 76
Name: 成绩, dtype: int64
2
3
4
5
6
7
8
df4.班级
小明 二班
小红 一班
小杰 二班
小丽 三班
小华 一班
Name: 班级, dtype: object
2
3
4
5
6
7
8
# 2、提取任意多列
可以在方括号里放入列表,再在列表里面传入多个列名。
这样做返回的就不是Series,而是DataFrame了
df4[["成绩", "班级"]]
运行结果
成绩 | 班级 | |
---|---|---|
小明 | 92 | 二班 |
小红 | 67 | 一班 |
小杰 | 70 | 二班 |
小丽 | 88 | 三班 |
小华 | 76 | 一班 |
# (二)提取DataFrame的行
提取列用列名,提取行就应该用索引
# 1、提取某个行
每行数据是以Series类型进行返回的
1)loc 按照标签索引提取行
2)iloc 按照位置索引提取行
df4.loc["小丽"]
学号 04
班级 三班
成绩 88
Name: 小丽, dtype: object
2
3
4
5
6
df4.iloc[3]
学号 04
班级 三班
成绩 88
Name: 小丽, dtype: object
2
3
4
5
6
# 2、提取部分行
与Series切片类似,给loc标签索引范围,或者给iloc位置索引范围,可以获得多行数据
标签索引做切片是包含结束值的
df4.loc["小红": "小丽"]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小红 | 02 | 一班 | 67 |
小杰 | 03 | 二班 | 70 |
小丽 | 04 | 三班 | 88 |
df4.iloc[1: 3]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小红 | 02 | 一班 | 67 |
小杰 | 03 | 二班 | 70 |
# 3、提取任意多行
可以给loc或iloc后面的方括号里,放入一个列表,里面是想提取出的行的标签或位置索引。与之前提取任意列类似。
df4.loc[["小丽", "小红"]]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小丽 | 04 | 三班 | 88 |
小红 | 02 | 一班 | 67 |
df4.iloc[[3, 1]]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小丽 | 04 | 三班 | 88 |
小红 | 02 | 一班 | 67 |
# (三)提取DataFrame的值
# 1、提取某个DataFrame的元素
可以在loc或iloc后面的方括号里,放上2个参数,第一个表示行,第二个表示列,就能提取出表格某个位置上的值
df4.loc["小杰", "学号"]
'03'
df4.iloc[2, 0]
'03'
2
3
4
5
6
7
# 2、提取部分DataFrame
提取部分表格数据类似,只需要在loc或iloc后面的方括号里放上2个参数,第一个表示行的切片,第二个表示列的切片,就可以把表格的一部分给切出来
标签索引做切片会包含结束值
df4.loc["小红": "小丽", "学号": "成绩"]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小红 | 02 | 一班 | 67 |
小杰 | 03 | 二班 | 70 |
小丽 | 04 | 三班 | 88 |
df4.iloc[1: 3, 0: 2]
运行结果
学号 | 班级 | |
---|---|---|
小红 | 02 | 一班 |
小杰 | 03 | 二班 |
# 3、提取部分列,同时保留所有行;提取部分行,同时保留所有列
一个省事的方法是,省略希望保留所有的切片里冒号前后的值,直接放上一个冒号,这能默认表示全部范围的索引
df4.loc[:, "班级": "成绩"]
运行结果
班级 | 成绩 | |
---|---|---|
小明 | 二班 | 92 |
小红 | 一班 | 67 |
小杰 | 二班 | 70 |
小丽 | 三班 | 88 |
小华 | 一班 | 76 |
df4.iloc[0: 3, :]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小明 | 01 | 二班 | 92 |
小红 | 02 | 一班 | 67 |
小杰 | 03 | 二班 | 70 |
# 4、提取不相邻的行或列
通过往loc或iloc后面的方括号里放入列表,通过标签或位置指明提取哪些
df4.loc[["小红", "小丽"], "学号": "班级"]
运行结果
学号 | 班级 | |
---|---|---|
小红 | 02 | 一班 |
小丽 | 04 | 三班 |
df4.iloc[[1, 3], 0: 2]
运行结果
学号 | 班级 | |
---|---|---|
小红 | 02 | 一班 |
小丽 | 04 | 三班 |
# 五、根据条件筛选DataFrame中的行
# (一)根据条件筛选行,相比于根据条件筛选列来说,是更加常见和合理的。
因为一般每一行代表一个实例,比如一个城市、一个学生,而每一列代表数据实例的属性,比如说城市的人口、学生的身高。那我们筛选符合条件的行,就相当于从已有数据里,提取符合条件的实例,比如人口在1000万以上的城市,身高在1.6米以上的学生。
# (二)语法
和NumPy的数组以及Pandas的Series是很类似的,在DataFrame后面跟一个方括号,里面放上针对列的条件。列的条件包括:数据类型是Series的列和条件。返回列的两种方法:通过"["列名"]",或者通过".属性名",都可以应用在列的条件中。
# (三)原理
DataFrame的列是Series类型,而Series和条件结合起来,会返回一个布尔值组成的Series,它的长度和DataFrame的行数相对应。DataFrame会用布尔值的Series进行索引,保留True所对应的索引的行。
df4['成绩'] > 80
小明 True
小红 False
小杰 False
小丽 True
小华 False
Name: 成绩, dtype: bool
2
3
4
5
6
7
8
df4[df4["成绩"] > 80]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小明 | 01 | 二班 | 92 |
小丽 | 04 | 三班 | 88 |
df4[df4.成绩 > 80]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小明 | 01 | 二班 | 92 |
小丽 | 04 | 三班 | 88 |
# (四)结合逻辑运算
条件也可以结合逻辑运算,因为DataFrame有不同列,所以不同条件里,可以根据不同列的变量进行筛选。
df4[(df4.成绩 > 80) & (df4["班级"] == "三班")]
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小丽 | 04 | 三班 | 88 |
# 六、head方法和tail方法
基本上,对DataFrame的操作方法,都是默认不改变原始DataFrame的,而是返回一个新的DataFrame
要操作生效的话,要么就得进行重新赋值,要么指定可选参数inplace=True
# (一)head方法
1、DataFrame.head(num)会返回给我们DataFrame前num行的内容,num是可选参数,默认是前5行内容。
2、用处:当我们和实际数据打交道的时候,可能动辄几千、几万甚至几十万数据,这种时候,这个方法就很实用了。可以看一眼开头几行,快速了解数据包含的信息,以及各列里面变量的特点。
df4.head()
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小明 | 01 | 二班 | 92 |
小红 | 02 | 一班 | 67 |
小杰 | 03 | 二班 | 70 |
小丽 | 04 | 三班 | 88 |
小华 | 05 | 一班 | 76 |
增加指定行数
df4.head(2)
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小明 | 01 | 二班 | 92 |
小红 | 02 | 一班 | 67 |
# (二)tail方法
DataFrame.tail(num)会返回给我们DataFrame后num行的内容,num是可选参数,默认是后5行内容
df4.tail(2)
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小丽 | 04 | 三班 | 88 |
小华 | 05 | 一班 | 76 |
# (三)sample方法
DataFrame.sample(num)会返回给我们DataFrame随机num行的内容,num是可选参数,默认是随机1行内容
df4.sample()
运行结果
学号 | 班级 | 成绩 | |
---|---|---|---|
小华 | 05 | 一班 | 76 |
# 七、更新/增加DataFrame的一列值
更新或者增加,取决于列名是否已经存在
import pandas as pd
name = pd.Series(["小陈", "小李", "小王", "小张", "小赵", "小周"], index=["001", "002", "003", "004", "005", "006"])
gender = pd.Series(["女", "女", "男", "男", "女", "男"], index=["006", "005", "004", "003", "002", "001"])
height = pd.Series([172.5, 168.0, 178.2, 181.3, 161.7, 159.8], index=["001", "002", "003", "004", "005", "006"])
grade = pd.Series([89, 92, 82, 96, 93, 84], index=["001", "002", "003", "004", "005", "006"])
df1 = pd.DataFrame({"姓名": name, "性别": gender, "身高": height, "成绩": grade})
df1
2
3
4
5
6
7
姓名 | 性别 | 身高 | 成绩 | |
---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 89 |
002 | 小李 | 女 | 168.0 | 92 |
003 | 小王 | 男 | 178.2 | 82 |
004 | 小张 | 男 | 181.3 | 96 |
005 | 小赵 | 女 | 161.7 | 93 |
006 | 小周 | 女 | 159.8 | 84 |
# (一)、Series处理列值
dataframe['列名'] = Series
如果DataFrame有标签索引,那被赋值的Series也要有index属性,才能成功和DataFrame的索引对齐,否则所有标签无法对齐的地方,都会产生缺失值
df1['成绩'] = pd.Series([90, 91, 83, 95, 94, 85])
df1
2
姓名 | 性别 | 身高 | 成绩 | |
---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | NaN |
002 | 小李 | 女 | 168.0 | NaN |
003 | 小王 | 男 | 178.2 | NaN |
004 | 小张 | 男 | 181.3 | NaN |
005 | 小赵 | 女 | 161.7 | NaN |
006 | 小周 | 女 | 159.8 | NaN |
df1['成绩'] = pd.Series([90, 91, 83, 95, 94, 85], index=['001', '002', '003', '004', '005', '006'])
df1
2
姓名 | 性别 | 身高 | 成绩 | |
---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 |
002 | 小李 | 女 | 168.0 | 91 |
003 | 小王 | 男 | 178.2 | 83 |
004 | 小张 | 男 | 181.3 | 95 |
005 | 小赵 | 女 | 161.7 | 94 |
006 | 小周 | 女 | 159.8 | 85 |
# (二)、列表处理列值
dataframe['列名'] = 列表
用列表的好处是不需要指明标签,会自动按顺序对齐,列表的长度和DataFrame本身的行数温和就可以
df1['成绩'] = [90, 91, 83, 95, 94, 85]
df1
2
姓名 | 性别 | 身高 | 成绩 | |
---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 |
002 | 小李 | 女 | 168.0 | 91 |
003 | 小王 | 男 | 178.2 | 83 |
004 | 小张 | 男 | 181.3 | 95 |
005 | 小赵 | 女 | 161.7 | 94 |
006 | 小周 | 女 | 159.8 | 85 |
# 增加DataFrame的一列值
df1["班级"] = ["一班", "三班", "二班", "三班", "一班", "二班"]
df1
2
3
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
003 | 小王 | 男 | 178.2 | 83 | 二班 |
004 | 小张 | 男 | 181.3 | 95 | 三班 |
005 | 小赵 | 女 | 161.7 | 94 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
# 八、更新/增加DataFrame的一行值
更新或者增加,取决于列名是否已经存在
既然可以通过loc或iloc提取出一行数据,那就可以以同样的方式更新那行
# (一)、Series处理行值
dataframe.loc/iloc['索引'] = Series
需要把Series的标签与列名进行对应,否则会产生缺失值
df1.loc["005"] = pd.Series(["小赵", "女", 162.7, 95, "一班"],
index=["姓名", "性别", "身高",
"成绩", "班级"])
df1
2
3
4
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
003 | 小王 | 男 | 178.2 | 83 | 二班 |
004 | 小张 | 男 | 181.3 | 95 | 三班 |
005 | 小赵 | 女 | 162.7 | 95 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
# (二)、列表处理行值
dataframe.loc/iloc['索引'] = 列表
不需要指明标签,会自动按顺序对齐
df1.loc["005"] = ["小赵", "女", 162.7, 95, "一班"]
df1
2
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
003 | 小王 | 男 | 178.2 | 83 | 二班 |
004 | 小张 | 男 | 181.3 | 95 | 三班 |
005 | 小赵 | 女 | 162.7 | 95 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
添加行,只能用loc。用iloc,会报错,因为给iloc里面的位置索引得是存在的
# 增加DataFrame的一行值
df1.loc["007"] = ["小孙", "男", 182.7, 71, "一班"]
df1
2
3
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
003 | 小王 | 男 | 178.2 | 83 | 二班 |
004 | 小张 | 男 | 181.3 | 95 | 三班 |
005 | 小赵 | 女 | 162.7 | 95 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
007 | 小孙 | 男 | 182.7 | 71 | 一班 |
# 九、删除DataFrame的行或列
用drop函数
drop函数只会返回删除后的DataFrame,但不会改变原始的DataFrame。假如让原本DataFrame的行被删除,可以把drop返回的结果赋值给原本的DataFrame,来实现更新。
# (一)、删除DataFrame的行
# 1、删除DataFrame的单行
dataframe.drop('标签索引')
df1.drop('003')
2
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
004 | 小张 | 男 | 181.3 | 95 | 三班 |
005 | 小赵 | 女 | 162.7 | 95 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
007 | 小孙 | 男 | 182.7 | 71 | 一班 |
# 2、删除DataFrame的任意多行
dataframe.drop(['标签索引1', '标签索引2'...])
df1.drop(['003', '007'])
2
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
004 | 小张 | 男 | 181.3 | 95 | 三班 |
005 | 小赵 | 女 | 162.7 | 95 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
# (二)、删除DataFrame的列
# 1、删除DataFrame的单列
dataframe.drop('列名', axis=1)
传入可选参数'axis=1'
因为DataFrame是二维的,可以看成是有两个轴线,axis=0表示沿着索引纵向进行操作,axis=1,表示沿着列名横向进行操作。一般针对DataFrame的方法,默认axis为0,因为沿着索引纵向进行操作的时候,数据的变量含义和类型一般是相同的,比如都是表示身高的浮点数,所以适合进行统计操作。drop,默认axis也是0,那沿着索引纵向操作,删除的就是行,当我们指明axis为1时,是沿着列名横向进行操作,所以删除的就是列。正是因为DataFrame二维的特征,针对很多操作我们都可以指明axis到底是0还是1。
df1.drop('身高', axis=1)
姓名 | 性别 | 成绩 | 班级 | |
---|---|---|---|---|
001 | 小陈 | 男 | 90 | 一班 |
002 | 小李 | 女 | 91 | 三班 |
003 | 小王 | 男 | 83 | 二班 |
004 | 小张 | 男 | 95 | 三班 |
005 | 小赵 | 女 | 95 | 一班 |
006 | 小周 | 女 | 85 | 二班 |
007 | 小孙 | 男 | 71 | 一班 |
# 2、删除DataFrame的任意多列
dataframe.drop(['列名1', '列名2'...], axis=1)
df1.drop(['身高', '性别'], axis=1)
2
姓名 | 成绩 | 班级 | |
---|---|---|---|
001 | 小陈 | 90 | 一班 |
002 | 小李 | 91 | 三班 |
003 | 小王 | 83 | 二班 |
004 | 小张 | 95 | 三班 |
005 | 小赵 | 95 | 一班 |
006 | 小周 | 85 | 二班 |
007 | 小孙 | 71 | 一班 |
# (三)、根据条件删除行或列
- 可以反向操作,把不符合条件的行或列筛选出来,重新赋值给原始DataFrame
# 删除成绩小于75的同学的档案
df1 = df1[df1.成绩 > 75]
df1
2
3
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
003 | 小王 | 男 | 178.2 | 83 | 二班 |
004 | 小张 | 男 | 181.3 | 95 | 三班 |
005 | 小赵 | 女 | 162.7 | 95 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
- 正向操作。由于drop函数中传入
'标签索引'
作为参数。所以我们先根据条件筛选出要删除的行,再用.index
获取作为属性的标签索引,最后作为参数放入DataFrame的drop函数中。
# 删除身高大于180的同学的档案
df1.drop(df1[df1.身高 > 180].index, inplace=True)
df1
2
3
C:\Users\stube\AppData\Local\Temp\ipykernel_26868\3609063738.py:2: 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
df1.drop(df1[df1.身高 > 180].index, inplace=True)
2
3
4
5
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
2
3
4
5
6
7
姓名 | 性别 | 身高 | 成绩 | 班级 | |
---|---|---|---|---|---|
001 | 小陈 | 男 | 172.5 | 90 | 一班 |
002 | 小李 | 女 | 168.0 | 91 | 三班 |
003 | 小王 | 男 | 178.2 | 83 | 二班 |
005 | 小赵 | 女 | 162.7 | 95 | 一班 |
006 | 小周 | 女 | 159.8 | 85 | 二班 |
# 十、DataFrame和DataFrame之间的运算
与Series与Series之间的运算类似。
DataFrame和DataFrame之间的运算,索引和索引、列名和列名会自动对齐。
# 1、使用符号运算
准备df1数据
df1 = pd.DataFrame({"a": {"001": 0, "003": 4, "005": 8, "007": 12},
"b": {"001": 1, "003": 5, "005": 9, "007": 13},
"c": {"001": 2, "003": 6, "005": 10, "007": 14},
"d": {"001": 3, "003": 7, "005": 11, "007": 15}})
df1
2
3
4
5
a | b | c | d | |
---|---|---|---|---|
001 | 0 | 1 | 2 | 3 |
003 | 4 | 5 | 6 | 7 |
005 | 8 | 9 | 10 | 11 |
007 | 12 | 13 | 14 | 15 |
准备df2数据
df2 = pd.DataFrame({"a": [0, 3, 6],
"b": [1, 4, 7],
"c": [2, 5, 8]})
df2
2
3
4
a | b | c | |
---|---|---|---|
0 | 0 | 1 | 2 |
1 | 3 | 4 | 5 |
2 | 6 | 7 | 8 |
相加
df1 + df2
a | b | c | d | |
---|---|---|---|---|
001 | NaN | NaN | NaN | NaN |
003 | NaN | NaN | NaN | NaN |
005 | NaN | NaN | NaN | NaN |
007 | NaN | NaN | NaN | NaN |
0 | NaN | NaN | NaN | NaN |
1 | NaN | NaN | NaN | NaN |
2 | NaN | NaN | NaN | NaN |
分析:df1与df2是有重合的列名的,a, b, c这三列是可以对齐上,而d列会全是缺失值;但除了列,行也要对齐,df1的标签是001、003、005、007,而df2的标签是0、1、2,没有一个对得上。可以看到,计算结果会保留双方全部索引和列名,所以每一个计算结果都是NaN
假如一个DataFrame没有标签,另一个DataFrame有标签,两个DataFrame之间运算是会拿标签索引和位置索引对齐,不一样则会保留全部索引
df2 = pd.DataFrame({"a": {"001": 0, "003": 3, "006": 6},
"b": {"001": 1, "003": 4, "006": 7},
"c": {"001": 2, "003": 5, "006": 8}})
df2
2
3
4
a | b | c | |
---|---|---|---|
001 | 0 | 1 | 2 |
003 | 3 | 4 | 5 |
006 | 6 | 7 | 8 |
df1 + df2
a | b | c | d | |
---|---|---|---|---|
001 | 0.0 | 2.0 | 4.0 | NaN |
003 | 7.0 | 9.0 | 11.0 | NaN |
005 | NaN | NaN | NaN | NaN |
006 | NaN | NaN | NaN | NaN |
007 | NaN | NaN | NaN | NaN |
分析:有一部分数据对齐成功,不全是缺失值了
# 2、用方法而不是符号运算
如果你希望给缺失的值一个默认值的话,可以给fill_value这个参数传一个值
df1.add(df2, fill_value=0)
a | b | c | d | |
---|---|---|---|---|
001 | 0.0 | 2.0 | 4.0 | 3.0 |
003 | 7.0 | 9.0 | 11.0 | 7.0 |
005 | 8.0 | 9.0 | 10.0 | 11.0 |
006 | 6.0 | 7.0 | 8.0 | NaN |
007 | 12.0 | 13.0 | 14.0 | 15.0 |
分析:索引006、列名d的值不存在,是由于这个位置的值,在两个DataFrame中都不存在。因此没有相加操作,也不存在替换。但因为结果会保留所有索引和列名,导致那个位置会有个占位的NaN值。
df1.sub(df2, fill_value=0)
a | b | c | d | |
---|---|---|---|---|
001 | 0.0 | 0.0 | 0.0 | 3.0 |
003 | 1.0 | 1.0 | 1.0 | 7.0 |
005 | 8.0 | 9.0 | 10.0 | 11.0 |
006 | -6.0 | -7.0 | -8.0 | NaN |
007 | 12.0 | 13.0 | 14.0 | 15.0 |
df1.mul(df2, fill_value=0)
a | b | c | d | |
---|---|---|---|---|
001 | 0.0 | 1.0 | 4.0 | 0.0 |
003 | 12.0 | 20.0 | 30.0 | 0.0 |
005 | 0.0 | 0.0 | 0.0 | 0.0 |
006 | 0.0 | 0.0 | 0.0 | NaN |
007 | 0.0 | 0.0 | 0.0 | 0.0 |
df1.div(df2, fill_value=0)
a | b | c | d | |
---|---|---|---|---|
001 | NaN | 1.00 | 1.0 | inf |
003 | 1.333333 | 1.25 | 1.2 | inf |
005 | inf | inf | inf | inf |
006 | 0.000000 | 0.00 | 0.0 | NaN |
007 | inf | inf | inf | inf |
# 十一、DataFrame和Series之间运算
DataFrame和Series之间运算同样会自动对齐,是用Series的索引和DataFrame的列名对齐,然后把操作运用在DataFrame的每一行上,对齐不了的就是NaN。
比如:
对齐的情况
s1 = pd.Series([0.1, 0.2, 0.3, 0.4, 0.5], index=["a", "b", "c", "d", "e"])
s1
2
运行结果
a 0.1
b 0.2
c 0.3
d 0.4
e 0.5
dtype: float64
2
3
4
5
6
不对齐的情况
s1 * df1
运行结果
a | b | c | d | e | |
---|---|---|---|---|---|
001 | 0.0 | 0.2 | 0.6 | 1.2 | NaN |
003 | 0.4 | 1.0 | 1.8 | 2.8 | NaN |
005 | 0.8 | 1.8 | 3.0 | 4.4 | NaN |
007 | 1.2 | 2.6 | 4.2 | 6.0 | NaN |
这里s1里索引a对应的值,会与df1里a列下面的所有值进行相乘;索引b对应的值,会与df1里b列下面的所有值进行相乘。
广播机制,Series和DataFrame之间的操作也是广播机制的体现,因为广播机制值得就是不同维度数据之间的运算机制。
# 十二、DataFrame和单个数字之间运算
name = pd.Series(["小陈", "小李", "小王", "小张", "小赵", "小周"], index=["001", "002", "003", "004", "005", "006"])
gender = pd.Series(["女", "女", "男", "男", "女", "男"], index=["006", "005", "004", "003", "002", "001"])
height = pd.Series([172.5, 168.0, 178.2, 181.3, 161.7], index=["001", "002", "003", "004", "005"])
students = pd.DataFrame({"姓名": name, "性别": gender, "身高": height})
students
2
3
4
5
姓名 | 性别 | 身高 | |
---|---|---|---|
001 | 小陈 | 男 | 172.5 |
002 | 小李 | 女 | 168.0 |
003 | 小王 | 男 | 178.2 |
004 | 小张 | 男 | 181.3 |
005 | 小赵 | 女 | 161.7 |
006 | 小周 | 女 | NaN |
# 1、运算运用到DataFrame的每一项数据上
运算会被运用到DataFrame的每一项数据上,所以要确保操作确实能被运用到里面的所有数据类型上。因为DataFrame包含多种数据类型,同一个操作放到不同类型上可能会有不同效果,也可能会报错
students * 5
姓名 | 性别 | 身高 | |
---|---|---|---|
001 | 小陈小陈小陈小陈小陈 | 男男男男男 | 862.5 |
002 | 小李小李小李小李小李 | 女女女女女 | 840.0 |
003 | 小王小王小王小王小王 | 男男男男男 | 891.0 |
004 | 小张小张小张小张小张 | 男男男男男 | 906.5 |
005 | 小赵小赵小赵小赵小赵 | 女女女女女 | 808.5 |
006 | 小周小周小周小周小周 | 女女女女女 | NaN |
# 2、先提取出需要的DataFrame数据,再进行操作
也可以先提取出需要的DataFrame数据,用数据类型一致的那一部分,再拿去进行操作
students[['姓名', '性别']] * 5
姓名 | 性别 | |
---|---|---|
001 | 小陈小陈小陈小陈小陈 | 男男男男男 |
002 | 小李小李小李小李小李 | 女女女女女 |
003 | 小王小王小王小王小王 | 男男男男男 |
004 | 小张小张小张小张小张 | 男男男男男 |
005 | 小赵小赵小赵小赵小赵 | 女女女女女 |
006 | 小周小周小周小周小周 | 女女女女女 |
# 十三、聚合运算
# (一)、统计方法
NumPy数组和Pandas的Series的统计方法,包括max, min, sum, mean,在DataFrame中也有相同名字的方法
不同之处在于,由于DataFrame是二维的,我们可以指定是沿着索引纵向操作,还是沿着列名横向操作。
import pandas as pd
player1 = pd.Series([8.5, 7.9, 8.2, 7.6, 8.8, 7.4], index=["001", "002", "003", "004", "005", "006"])
player2 = pd.Series([9.0, 8.3, 8.6, 7.7, 8.9, 7.8], index=["001", "002", "003", "004", "005", "006"])
player3 = pd.Series([8.7, 8.1, 8.4, 7.9, 8.6, 7.3], index=["001", "002", "003", "004", "005", "006"])
df1 = pd.DataFrame({"选手1": player1, "选手2": player2, "选手3": player3})
df1
2
3
4
5
6
选手1 | 选手2 | 选手3 | |
---|---|---|---|
001 | 8.5 | 9.0 | 8.7 |
002 | 7.9 | 8.3 | 8.1 |
003 | 8.2 | 8.6 | 8.4 |
004 | 7.6 | 7.7 | 7.9 |
005 | 8.8 | 8.9 | 8.6 |
006 | 7.4 | 7.8 | 7.3 |
df1.mean()
选手1 8.066667
选手2 8.383333
选手3 8.166667
dtype: float64
2
3
4
5
6
默认沿着索引纵向操作,计算各列的统计值,因此返回结果的Series索引和列名对应
df1.mean(axis=1)
001 8.733333
002 8.100000
003 8.400000
004 7.733333
005 8.766667
006 7.500000
dtype: float64
2
3
4
5
6
7
8
9
如果用可选参数指定axis=1的话,就变成横向操作,计算各行的统计值,因此返回结果的Series索引和DataFrame索引对应。
# (二)、describe方法
与Series的describe方法类似。用在DataFrame上,能得到各列的统计信息
如果DataFrame既有数字列也有非数字列,我们不需要吧数字列提取出来再调用这个方法,describe方法会自动忽略掉所有非数字列,只计算数字列的统计信息
如果DataFrame数据类型全都都是object,那么describe方法会展现针对object类型数据的信息
df1["姓名"] = ["小陈", "小李", "小王", "小张", "小赵", "小周"]
df1
2
选手1 | 选手2 | 选手3 | 姓名 | |
---|---|---|---|---|
001 | 8.5 | 9.0 | 8.7 | 小陈 |
002 | 7.9 | 8.3 | 8.1 | 小李 |
003 | 8.2 | 8.6 | 8.4 | 小王 |
004 | 7.6 | 7.7 | 7.9 | 小张 |
005 | 8.8 | 8.9 | 8.6 | 小赵 |
006 | 7.4 | 7.8 | 7.3 | 小周 |
df1.describe()
选手1 | 选手2 | 选手3 | |
---|---|---|---|
count | 6.000000 | 6.000000 | 6.000000 |
mean | 8.066667 | 8.383333 | 8.166667 |
std | 0.535413 | 0.549242 | 0.520256 |
min | 7.400000 | 7.700000 | 7.300000 |
25% | 7.675000 | 7.925000 | 7.950000 |
50% | 8.050000 | 8.450000 | 8.250000 |
75% | 8.425000 | 8.825000 | 8.550000 |
max | 8.800000 | 9.000000 | 8.700000 |
# 十四、apply方法
与Series的apply方法类似,DataFrame中也有相同名字的方法。
参数接受一个函数,调用后把DataFrame的每列或每行的Series数据,分别作为那个函数的参数,默认对列进行操作。返回的Series里的元素,就是那个函数对原始Series里各列或各行调用后的结果。
apply方法不改变原始DataFrame,而是会返回一个新的DataFrame
# 写出一个函数,去掉选手最高分和最低分后,求平均分
def trim_mean(data):
data_len = len(data)
data_sum = data.sum()
max_num = data.max()
min_num = data.min()
return (data_sum - max_num - min_num) / data_len
2
3
4
5
6
7
df1.apply(trim_mean)
选手1 5.366667
选手2 5.600000
选手3 5.500000
dtype: float64
2
3
4
5
6
df1.apply(trim_mean, axis=1)
001 2.900000
002 2.700000
003 2.800000
004 2.566667
005 2.933333
006 2.466667
dtype: float64
2
3
4
5
6
7
8
9
# 十五、applymap方法
与apply方法区别在于,传给apply的函数,会运用在每列或每行上;而传给applymap的函数,会运用在每一个元素上
applymap方法不改变原始DataFrame,而是会返回一个新的DataFrame
df1.applymap(lambda x: x + 5)
选手1 | 选手2 | 选手3 | |
---|---|---|---|
001 | 13.5 | 14.0 | 13.7 |
002 | 12.9 | 13.3 | 13.1 |
003 | 13.2 | 13.6 | 13.4 |
004 | 12.6 | 12.7 | 12.9 |
005 | 13.8 | 13.9 | 13.6 |
006 | 12.4 | 12.8 | 12.3 |