【学习笔记】《深入浅出Pandas》第5章:Pandas高级操作

5.1 复杂查询

5.1.1 逻辑运算

(1)对DataFrame其中一列进行逻辑计算,会产生一个对应的由布尔值组成的Series,真假值由此位上的数据是否满足逻辑表达式决定。
(2)针对索引的逻辑表达式会产生一个array类型数组,该数组由布尔值组成:

df.index == 1
# 只有索引为1的值为True,其余均为False

(3)除了逻辑运算,还支持组合条件的Python位运算。

5.1.2 逻辑筛选数据

(1)切片、.loc和.iloc均支持逻辑表达式。
(2)逻辑表达式输出的结果必须是一个布尔序列或者符合其格式要求的数据形式。
(3)进行或(|)、与(&)、非(~)运算时,各个独立逻辑表达式需要用括号。
(4)any和all对逻辑运算后的布尔序列再进行判断,序列中所有值都是True时all才返回True,序列中只要有一个值为True时any就返回True。还可以传入参数axis,用于指定判断的方向,默认0为列方向,1为行方向。

5.1.3 函数筛选

在表达式处使用lambda函数,默认变量是操作的对象。如果操作对象是DataFrame,那么变量就是DataFrame;如果是Series,那么变量就是Series。

5.1.4 比较函数

df.eq() # 等于
df.ne() # 不等于
df.le() # 小于等于
df.lt() # 小于
df.ge() # 大于等于
df.gt() # 大于
isin() 
# 判断数据是否包含指定内容
# 可以传入一个列表,原数据只需要满足其中一个存在即可
# 也可以传入一个字典,键为列名,值为需要匹配的值

5.1.5 查询df.query()

df.query(expr)使用布尔表达式查询DataFrame列,表达式是一个字符串,类似SQL的where从句。

还支持使用@符引入变量:

a = df.Q1.mean()
df.query('Q1 > @a+40')

df.eval()与df.query()类似,也可以用于表达式筛选。

5.1.6 筛选df.filter()

df.filter()可以对行名和列名进行筛选,支持模糊匹配、正则表达式。

df.filter(items=['Q1', 'Q2']) # 选择两列
df.filter(regex='Q', axis=1) # 列名包含Q的列
df.filter(regex='e$', axis=1) # 以e结尾的列
df.filter(like='2', axis=0) # 索引中有2的
df.filter(regex='^2', axis=0).filter(like='Q', axis=1)

5.1.7 按数据类型查询

df.select_dtypes(include=None, exclude=None)
# 指定包含和不包含的数据类型
# 如果只有一个类型,传入字符
# 如果有多个类型,传入列表
# 如果没有满足条件的数据,会返回一个仅有索引的DataFrame

5.2 数据类型转换

# 对所有字段指定统一类型
df = pd.DataFrame(data, dtype='float32')
# 对每个字段分别指定
df = pd.read_excel(data, dtype={'team': 'string', 'Q1':'int32'})

5.2.1 推断类型

df.infer_objects() # 返回一个按推断修改后的DataFrame
df.infer_objects().dtypes # 返回每列的数据类型

# 更推荐,支持string类型
df.convert_dtypes() # 同上
df.convert_dtypes().dtypes

5.2.2 指定类型

pd.to_XXX系统方法可以将数据安全转换,error参数可以实现无法转换则转换为兜底类型:

pd.to_datetime(m) # 转为时间
pd.to_datetime(m, errors='coerce') # 错误处理
pd.to_datetime(m, errors='ignore') # 错误处理
pd.to_datetime(m, errors='coerce').fillna(0) # 兜底填充
pf.to_datetime(df[['year', 'month', 'day']]) # 组合成日期

转换为数字类型时,默认返回的dtype是float64还是int取决于提供的数据。使用downcast参数获得向下转换后的其他类型:

m = ['1', 2, 3]
pd.to_numeric(m, downcast='integer') # 至少为有符号int数据类型
# array([1, 2, 3], dtype=int8)

5.2.3 类型转换astype()

df.astype('int32') # 所有数据类型转换
df.astype({'col1': 'int32'}) # 指定字段转为指定类型
df['name'].astype('object')

# 当数据格式不具备转换为目标类型的条件时,需要先处理数据
# eg: "89.3%" -> 浮点数
data.rate.apply(lambda x: x.replace('%', '')).astype('float')/1000

5.2.4 转为时间类型

pd.to_datetime()
s.astype('datetime64[ns]')

5.3 数据排序

5.3.1 索引排序

# 按索引排序,默认升序,降序:ascending=False
df.sort_index(ascending=True)

# 按列索引名排序(在列索引方向上排序)
df.sort_index(axis=1, ascending=False)

s.sort_index(inplace=True) # 排序后生效,改变原数据
# 索引重新0-(n-1)排,可以得到排序号
s.sort_index(ignore_index=True) 
# 空值在前,'last'表示空值在后
s.sort_index(na_position='first') 
# 如果多层,排一级
s.sort_index(level=1)
# 这层不排序
s.sort_index(level=1, sort_remaining=False)
# 行索引排序,表头排序
df.sort_index(axis=1) # 会把这列按照列名顺序排列

df.reindex() # 指定自己定义顺序的索引,实现行和列的顺序重新定义

5.3.2 数值排序

sort_values() # 数字按大小顺序,字符按字母顺序
# Series和DataFrame都支持
# DataFrame需要传入一个或多个排序的列名
# 默认排序是升序,但是可以指定排序方式
df.sort_values(by=['team', 'name'], ascending=[True, False])

# df按指定字段排序
df.sort_values(by=['team'])
df.sort_values('team')

# 索引重新0-(n-1)排
df.sort_values('team', ignore_index=True)

5.3.3 混合排序

有时候需要用索引和数据值混合排序。

# eg: 先按索引name排名,再按team排名
df.set_index('name', inplace=True)
df.index_names = ['s_name']
df.sort_values(by=['s_name', 'team'])

# 方法二 效果同上,注意顺序
df.set_index('name').sort_values('team').sort_index()
df.reindex() # 给定新的索引方式排名
# eg 
df.name.sort_values().index # 按姓名排序后取出排名的索引列表
df.reindex(df.name.sort_values().index) # 将新的索引应用到数据中

5.3.4 按值大小排序

# 实现数字列的排序,并可指定返回个数
df.nsmallest()
df.nlargest()

5.4 添加修改

5.4.1 修改数值

df.iloc[0, 0] = 'Lily' # 修改值
df[df.Q1 < 60] = 60
# 除了修改内容,还可以传入同样形状的数据来修改

# 对于DataFrame,会按对应的索引位进行修改

5.4.2 替换数据

replace(old, new) # 实现数据的批量替换
# 值可以是数字,也可以是列表、字典..

# 参数method='pad'/'ffill'/'bfill'/None

# 使用正则表达式
df.replace(to_replace=r'^ba.$', value='new', regex=True)

5.4.3 填充空值

df.fillna(0) # 将空值全部修改为0
# {'backfill', 'bfill', 'pad', 'ffill', None} 默认为None
df.fillna(method='ffill') # 将空值都修改为其前一个值
df.fillna(value=values, limit=1) # 只替换第一个

5.4.4 修改索引名

方法:将df.index和df.columns重新赋值为一个类似列表的序列值,这会将其覆盖为指定序列中的名称。
使用df.rename和df.rename_axis对轴名称进行修改。

df.rename(columns={'team': 'class'}) # 修改表头
df.rename(index={0: "x"}) # 修改索引

df.rename(index=str) # 修改类型
df.rename(str.lower, axis='columns') # 传索引类型

# 对索引名进行修改
s.rename_axis("animal")
df.rename_axis("animal") # 默认是列索引
df.rename_axis("limbs", axis="columns") # 指定行索引
# 索引为多层索引时可以将type修改为class
df.rename_axis(index={'type': 'class'})

# 可以用set_axis进行设置修改
s.set_axis(['a', 'b', 'c'], axis=0)
df.set_axis(['i', 'ii'], axis='columns', inplace=True) 

5.4.5 增加列

新列可以是一个定值,所有行都是该值,也可以是一个同等长度的序列数据,各行有不同的值。

df['total'] = df.sum(1) # 增加总成绩列
df['foo'] = 100 # 增加一列值全为100的列

5.4.6 插入列df.insert()

df.insert(loc, column, value)
# loc是一个数字,代表新列所在的位置,使用数字索引
# column为新的列名
# value为列的值,一般是Series

df.insert(2, 'total', df.sum(1))

# 如果已经存在相同的数据列会报错
# 可以传入allow_duplicates=True插入同名列
# 如果希望新列位于最后,可以在loc传入len(df.columns)

5.4.7 指定列df.assign()

df.assign(k=v)
# k为新列的列名,v为此列的值
# v必须是一个与原数据同索引的Series
# 该方法不用赋值也可以创建一个临时的列(不会影响原数据)

# 链式方法
df.assign(total=df.sum(1)) # 总成绩
.assign(Q=100) # 满分100
.assign(avg=df.mean(1)) # 平均值
.assign(avg2=lambda d: d.total/4)
# avg2:
# 由于实际上df并没有total这一列,如果需要使用total
# 就需要调用lambda, 变量d是代码执行到本行前的DataFrame内容
# 可以认为是一个虚拟的DataFrame实体

5.4.8 执行表达式df.eval()

df.eval()
# 功能同df.query()
# 以字符的形式传入表达式,增加列数据
# @可引入变量

# eg
df.eval('total=Q1+Q2+Q3+Q4')

5.4.9 增加行

df.loc[]
# 指定索引给出所有列的值来增加一行数据
# 无数据列值为NaN

5.4.10 追加合并

pd.append()	# 追加一个新行

pd.concat([s1, s2])	# 连接两个s或者df
pd.concat([s1, s2], ignore_index=True) # 索引重新编
pd.concat([s1, s2], keys=['s1', 's2'], names=['Series name', 'row ID'])
# 原数索引不变,增加一个一层索引(keys的内容),变成多层索引

# df同理
pd.concat([df1, df2])
pd.concat([df1, df3], sort=False)
pd.concat([df1, df3], join="inner") # 连接相同列
pd.concat([df1, df4], axis=1) # 连接列

5.4.11 删除

# 方法一: pop()
s.pop() # 删除指定索引的数据同时返回这个被删除的值
df.pop() # 删除指定列并返回这个被删除的列

# 方法二:反选法
# 将需要的数据筛选出来赋值给原变量,实现删除

5.4.12 删除空值

df.dropna() # 一行中有一个缺失值就删除
df.dropna(axis='columns') # 只保留全有值的列
df.dropna(how='all') # 行和列全没值才删除
df.dropna(thresh=2) # 至少有两个空值才删除
df.dropna(inplace=True) # 删除并替换生效

5.5 高级过滤

5.5.1 df.where()

df.where()
# 可以传入布尔表达式、布尔值的Series/DataFrame、序列或者可调用的对象
# 然后与原数据相比
# 返回一个行索引与列索引与原数据相同的数据
# 在满足条件的位置上保留原值,在不满足条件的位置填充NaN

df.where(lambda d: d.Q1>50) # 传入可调用对象
df.Q1.where(pd.Series([True]*3)) # 传入布尔值Series 前三个为真,其余为NaN
df.where(df>=60, '不合格') # 可以指定值或者算法来替换NaN

# NaN -> 算法
c = df%2 == 0 # 定义一个数是否为偶数的表达式
df.where(~c, -(df-20)) # 传入c, 为偶数的时候显示原值减去20的相反数

5.5.5 np.where()

# np.where()可以弥补df.where()的不足
# np.where()能够对满足条件的值进行替换
# 返回一个二维array
np.where(df>=60, '合格', '不合格')

# 让df.where()的条件为假,从而应用np.where()的计算结果
df.where(df==999999, np.where(df>=60, '合格', '不合格'))

5.5.3 df.mask()

# df.mask()的功能和df.where()基本相同
# 唯一区别是df.mask()将满足条件的位置填充为NaN
# 同样可以指定值进行填充

# df.mask()和df.where()还可以通过数据筛选返回布尔序列
(df.where((df.team=='A') & (df.Q1>60)) == df).Q1 # 返回布尔序列,符合条件的行值为True 
(df.mask((df.team=='A') & (df.Q1>60)) == df).Q1 # 返回布尔序列,符合条件的行值为False

5.5.4 df.lookup()

df.lookup(行标签, 列标签)
# 返回一个numpy.ndarray 标签必须是一个序列
# 行列相同数量

5.5.5 小结

本节介绍了数据过滤函数。
df.where()和df.mask()都可以按条件筛选数据,df.where()将不满足条件的值替换成NaN,df.mask()将满足条件的值替换成NaN。np.where()在满足和不满足条件的情况下都可以指定填充值。

5.6 数据迭代

Pandas的迭代操作可以将数据按行或者列遍历。

5.6.1 迭代Series

Series本身是一个可迭代的对象,Series df.name.values()返回array结构,数据可用于迭代,不过可直接对Series使用for语句来遍历。
迭代索引和指定的多列,使用Python内置的zip函数将其打包为可迭代的zip对象:

for i, n, q in zip(df.index, df.name, df.Q1):
	print(i, n, q)

5.6.2 df.iterrows()

(1)df.iterrows()生成一个可迭代对象,将DataFrame行作为(索引,行数据)组成的Series数据对进行迭代。
(2)在for语句中需要两个变量来承接数据:一个为索引变量,即使索引在迭代中不会使用(这种情况可以用useless作为变量名);另一个为数据变量,读取具体列时,可以使用字典的方法和对象属性的方法。

# 迭代,使用name、Q1数据
for index, row in df.iterrows():
	print(index, row['name'], row.Q1)

5.6.3 df.itertuples()

df.itertuples()
# 生成一个namedtuples类型数据,name默认名为Pandas,可以在参数中指定

在这里插入图片描述

# 不包含索引数据
for row in df.itertuples(index=False):
	print(row)
# Pandas(name='Liver', team='E', Q1=89, Q2=21, Q3=24, Q4=64)

# 自定义name
for row in df.itertuples(index=False, name='Gairuo'): # namedtuples
	print(row)
# Gairuo(name='Liver', team='E', Q1=89, Q2=21, Q3=24, Q4=64)

# 使用数据
for row in df.itertuples():
	print(row.Index, row.name)

5.6.4 df.items()

# df.items()和df.iteritems()功能相同
# 迭代时返回一个(列名,本列的Series结构数据),实现对列的迭代:
for label, ser in df.items(): # label:表头
	print(label)
	print(ser[:3], end='\n\n')
'''
name
0    Liver
1     Arry
2      Ack
Name: name, dtype: object

team
0    E
1    C
2    A
Name: team, dtype: object

Q1
0    89
1    36
2    57
Name: Q1, dtype: int64

Q2
0    21
1    37
2    60
Name: Q2, dtype: int64

Q3
0    24
1    37
2    18
Name: Q3, dtype: int64

Q4
0    64
1    57
2    84
Name: Q4, dtype: int64
'''

5.6.5 按列迭代

除了df.items(),如需要迭代一个DataFrame的列,可以直接对DataFrame迭代,会循环得到列名:
在这里插入图片描述

# 再利用df[列名]的方法迭代列:

# 依次取出每个列
for column in df:
	print(df[column])

# 可对每个列的内容进行迭代
for column in df:
    for i in df[column]:
        print(i) 

# 可以迭代指定列
for i in df.name:
	print(i)

# 只迭代想要的列
l = ['name', 'Q1']
cols = df.columns.intersection(l)
for col in cols:
	print(col)

5.6.6 小结

本节介绍了Pandas各个维度的数据迭代方法,DataFrame和Series本身就是可迭代对象,以上专门的迭代函数为我们提供了十分方便的迭代功能。
与df.iterrows()相比,df.itertuples()运行速度会更快一些,推荐在数据量庞大的情况下优先使用。
迭代的优势是可以把大量重复的事务按规定的逻辑依次处理,处理逻辑部分的也能随心所欲地去发挥,同时它简单清晰。

5.7 函数应用

pipe() # 应用在整个DataFrame或Series上
apply() # 应用在整个DataFrame的行或列中,默认为列
applymap() # 应用在DataFrame的每个元素中
map() # 应用在Series或DataFrame的一列的每个元素中  

5.7.1 pipe()

df.pipe(<函数名>, <传给函数的参数列表或字典>)
# pipe管道方法,可以让分析过程标准化、流水线化,达到复用目标
# DataFrame和Series都支持pipe()
# DataFrame和Series作为函数的第一个参数,可以根据需求返回自己定义的任意类型数据

在这里插入图片描述

# pipe()可以将复杂的调用简化
# eg:对df多重应用多个函数
f(g(h(df), arg1=a), arg2=b, arg3=c)
# 将它们用pipe连接起来
(df.pipe(h)
   .pipe(g, arg1=a)
   .pipe(f, arg2=b, arg3=c))
# 以下是将'arg2'参数传给函数f,然后作为函数整体接受后面的参数
(df.pipe(h)
   .pipe(g, arg1=a)
   .pipe((f, 'arg2'), arg1=a, arg3=c)

5.7.2 apply()

apply()可以对DataFrame按行和列进行函数处理,也支持Series
如果是Series,逐个传入具体值
如果是DataFrame,逐行或逐列传入

在这里插入图片描述

#eg
# 将name全部变为小写
df.name.apply(lambda x: x.lower())

# 去掉一个最高分和一个最低分再算出平均分
def my_mean(s):
	max_min_ser = pd.Series([-s.max(),  -s.min()])
	return s.append(max_min_ser).sum() / (s.count()-2)
# 对数字列应用函数
df.select_dtypes(include='number').apply(my_mean)
# apply()可以应用的函数类型如下
df.apply(fun) # 自定义
df.apply(max) # Python内置函数
df.apply(lambda x: x*2) # lambda
df.apply(np.mean) # Numpy等其他库函数
df.apply(pd.Series.first_valid_index) # Pandas自带的函数

5.7.3 applymap()

applymap()实现元素级函数应用,可以对DataFrame的所有元素(不包含索引)应用函数处理

在这里插入图片描述

# 使用lambda时,变量指的是每一个具体的值
def mylen(x):
	return len(str(x))
df.applymap(lambda x:mylen(x))
df.applymap(mylen) # 效果同上

5.7.4 map()

map()根据输入对应关系映射值返回最终数据,用于Series对象或者DataFrame对象的一列
传入的值可以是一个字典,键为原数据值,值为替换后的值
可以传入一个函数(参数为Series的每个值),还可以传入字符格式化表达式来格式化数据内容

# eg: df.team.map({'A': '一班'}) # A -> 一班

5.7.5 agg()

agg()一般用于使用指定轴上的一项或多项操作进行汇总,
可以传入一个函数或函数的字符
还可以用列表的形式传入多个函数

在这里插入图片描述
在这里插入图片描述

agg()还支持传入函数的位置参数和关键字参数
支持每个列分别用不同的方法聚合,支持指定轴的方向

5.7.6 transform()

DataFrame和Series自身调用函数并返回一个与自身长度相同的数据

在这里插入图片描述
分组后,直接使用计算函数并按分组显示合计数据。
使用transform()调用计算函数,返回的是原数据的结构,但在指定位置上显示聚合计算后的结果,这样方便了我们了解数据所在组的情况。

5.7.7 copy()

df.copy()返回一个新对象,与原对象没有关系
当deep=True时(默认),将创建一个新对象,其中包含调用对象的数据和索引的副本,
对副本数据或索引的修改不会反映在原始对象中;
当deep=False时,将创建一个新对象,不复制调用对象的数据或索引(仅复制对数据和索引的引用),
原始数据的任何更改都将对浅拷贝的副本进行同步更改

5.7.8 小结

本节介绍了一些实用的DataFrame和Series函数,熟练使用函数可以帮助我们抽象问题,复用解决方案,同时大大减少代码量。

5.8本章小结

本章介绍了Pandas的一些高级应用功能。我们可以利用本章介绍的高级筛选技巧对数据进行任意逻辑的查询;利用类型转换功能,将数据转换为方便使用的类型;对数据进行个性化排序,探索数据的变化规律;对数据进行增删修改操作,修正异常数据;以迭代形成编写复杂的数据处理逻辑;利用函数完成重复工作,让代码更高效。

  • 0
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值