Pandas高级操作

深入浅出Pandas读书笔记

5.1 复杂查询

5.1.1 逻辑运算

df.Q1 > 36
df.index == 1 # 返回array
df.loc[:, 'Q1':'Q4'] > 60 # 一般这种对一大片数字的判断, 都会与select_dtypes('number')组合使用
~(df.Q1 < 60) & (df.team == 'C') # Q1成绩不小于60, 且是C组成员, 若果是对整体的取反, 需要将&左右括号再取反

5.1.2 逻辑筛选数据

切片[], .loc, .iloc 均支持逻辑表达式

# 切片
df[df['Q1'] == 8] 
df[df.name == 'Ben']
df[df.Q1 > df.Q2]
# .loc
df.loc[df['Q1'] > 90, 'Q1':]
df.loc[(df.Q1 > 80) & (df.Q2 < 15)]
df.loc[(df.Q1 > 90) | (df.Q2 < 90)]
df.loc[df.Q1 == 8] # 这里等同于上面的切片, 筛选行
df.loc[df.Q1 > 90, Q1]

再进行与或非运算的时候, 各个独立的逻辑需要括号隔开
any和all对逻辑计算后的布尔序列再进行判断, 序列中所有值都为True, all才返回True
序列中只要有一个值为True, any就返回True

df[(df.loc[:, ['Q1', 'Q2']] > 80).all(1)] # 里层逻辑往外解决, 首先筛选Q1, Q2列的值, 判断所有的值是否>80, 在按照1轴的方向判断1轴的两个值是否都是True, 最后将True的值筛选出来, 也就是这个表达式最终会将Q1, Q2都>80的行筛选出来
df[(df.loc[:, ['Q1', 'Q2']] > 80).any(1)] # any -> 只要Q1, Q2上有一个>80就被筛选出

5.1.3 函数筛选

df.Q1[lambda s: max(s.index)] # df.Q1[max(df.Q1.index)]
df[lambda df: df['Q1'] == 8] # 表达式, 筛选行
df.loc[lambda df: df.Q1 == 8, 'Q1':'Q2']
df.loc[:, lambda df: df.columns.str.len() == 4]
df.loc[:, lambda df: [i for i in df.columns if 'Q' in i]]
df.iloc[:3, lambda df: df.columns.str.len() == 2]

5.1.4 比较函数

df[df.Q1.eq(60)] # = df[df.Q1 == 60]
df.ne() # != not equal
df.le() # <= less equal
df.lt() # < less than
df.ge() # >= greater equal
df.gt() # > greater than

df[df.Q1.ne(89)]
df.loc[df.Q1.gt(90) & df.Q2.lt(90) # Q1 > 90, Q2 < 90
# isin的比较
df[df.team.isin(['A', 'B'])] # 里面的表达式筛选了team列中的A和B, 返回一个bool的Series, 将这个bool作为df的切片, 筛选行, 返回一个DataFrame, 显示符合条件的行 
df[df.isin({'team':['C', 'D'], 'Q1': [36, 93]})] # 筛选出DataFrame中team列中C和D, Q1列中36和93, 返回一个DataFrame, 其中满足条件的显示值, 其他显示NaN

5.1.5 查询df.query()

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

# col名不需要加引号, 对比的值如果是字符串需要加引号
df.query('Q1 > Q2 > 90')
df.query('Q1 + Q2 > 180')
df.query('Q1 == Q2')
df.query('(Q1 < 50) & (Q2 >40) and (Q3 > 90)')
df.query('Q1 > Q2 > Q3 > Q4')
df.query('team != "C"')
df.query('team not in ["E", "A", "B"]') # query独有的not in的写法
# 支持@引入变量
a = df.Q1.mean()
df.query('Q1 > @a + 40') # Q1列>(Q1的平均值+40)
df.query('Q1 > Q2 + @a')
# df.eval与df.query类似, 但是eval不安全

5.1.6 筛选df.filter()

可以对行名和列名进行筛选, 支持模糊匹配, 正则表达式

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

5.1.7 按数据类型查询df.select_dtypes()

df.select_dtypes(include=None, exclude=None), 如果没有满足条件的数据, 会返回一个仅有索引的DataFrame

df.select_dtypes(include=['float64'])
df.select_dtypes(include=['bool'])
df.select_dtypes(include=['number'])
df.select_dtypes(exclude=['int'])
df.select_dtypes(exclude=['datetime64'])

5.2 数据类型转换

5.2.1 推断类型

df.infer_objects(), df.convert_dtypes()

5.2.2 指定类型

pd.to_XXX可以将数据安全转换, errors参数可以实现无法转换时候的兜底类型

m = ['1', 2, 3]
s = pd.to_numeric(m) # 转成数字
# pd.to_datetime(m) # 报错, 
pd.to_datetime(m, errors='coerce') # 遇到无法转换的, 转为NaT
pd.to_datetime(m, errors='ignore') # 遇到无法转换的, 返回input
pd.to_datetime(m, errors='coerce').fillna(0) # 兜底填充
pd.to_datetime(df[['year', 'month', 'day']]) # 组合成日期
# 转换为数字类型时, 默认返回的dtype是float64, 还是int64取决于提供的数据, 使用downcast参数获得向下转换后的其他类型
pd.to_numeric(m, downcast='integer') # 至少为有符号int数据类型, 这里会将'1'转为int

5.2.3 类型转换 astype()

df.Q1.astype('int32').dtypes # 这里dtypes = dtype
df.astype({'Q1': 'int32', 'Q2': 'int32'}).dtypes
df.index.astype('int64')

5.3.4 转为时间类型

我们通常用pd.to_datetime()和s.astype(‘datetime64’)来做时间类型转换

t = pd.Series(['20200801', '20200802'])
pd.to_datetime(t)
t.astype('datetime64') # 两者都可以转成datetime64[ns]类型

5.3 数据排序

5.3.1 索引排序 df.sort_index(), df.reindex()

df.sort_index(ascending=False)
df.sort_index(axis=1, ascending=False)
s.sort_index()
df.sort_index()
df.team.sort_index()
s.sort_index(inplace=True)
# 重新排列索引
s.sort_index(ignore_index=True)
s.sort_index(na_position='first') # 空值在前, 'last'空值在后
s.sort_index(level=1) # 多层索引排第一层
# 
df = pd.DataFrame({'A': [1, 2, 4], 'B': [3, 5, 6]}, index=['a', 'b', 'c'])
df.reindex(['c', 'b', 'a'])
df.reindex(['B', 'A'], axis=1)

5.3.2 数值索引 sort_values()

df.Q1.sort_values()
df.sort_values('Q4')
df.sort_values(by=['team', 'name'], ascending=[True, False])
s.sort_values(ascending=False)
s.sort_values(inplace=True)
s.sort_values(na_position='first')
df.sort_values(by=['team'])
df.sort_values(by=['team', 'Q1'], ascending=False) # 全降序
df.sort_values('team', ignore_index=True) # 重新索引

5.3.3 混合排序

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

df = df.set_index('name')
df.index.name = 's_name'
df.sort_values(by=['s_name', 'team'])
# 或
df.set_index('name').sort_values('team').sort_index()
# 通过reindex来重新索引
df.reindex(df.name.sort_values().index) # 根据排序后的index, 重新索引

5.3.4 按值大小排序 df.nsmallest(), df.nlargest()

# 先按Q1最小的在前, 如果相同, Q2小的在前
df.nsmallest(5, ['Q1', 'Q2']) # 最小的5个

5.4 添加修改

5.4.1 修改数值

print(df.iloc[0, 0]) # 查看原始值
df.iloc[0, 0] = 'Lily' # 修改值
print(df.iloc[0, 0]) # 查看修改后结果
df[df.Q1 < 60] = 60 # 将Q1列小于60的改为60
# 传一个同样形状的数据来修改值
v = list(range(1, 10, 2)) * 20
df.Q1 = v # Q1为100行
# 修改DataFrame, 会按照对应的索引进行修改
df.loc[1:3, 'Q1':'Q2']
df1 = pd.DataFrame({'Q1': [1, 2, 3], 'Q2': [4, 5, 6]}) # 这里的index是0-2
df.loc[1:3, 'Q1':'Q2'] = df1
print(df.loc[1:3, 'Q1':'Q2'])
'''
Q1	Q2
1	2.0	5.0
2	3.0	6.0
3	NaN	NaN
'''

5.4.2 替换数据 replace()

s.replace(0, 5) # 将Series中0替换为5
df.replace(0, 5) # 将DataFrame中0替换为5
df.replace([0, 1, 2, 3], 4) # 将0-3 替换为4
df.replace([0, 1, 2, 3], [4, 3, 2, 1]) # 对应替换
s.replace([1, 2], method='bfill') # backwardfill, 用下面的值填充1, 2, 'ffill' 用上面的填充
df.replace({0: 10, 1: 100}) # 字典对应修改
df.replace({'Q1': 0, 'Q2': 5}, 100) # 指定字段里的值修改为100
df.replace({'Q1': {0: 100, 4: 400}}) # 将指定列里的指定值替换为另一个指定的值
# 正则
df.replace(to_replace=r'^ba.$', value='new', regex=True)
df.replace(regex=[r'^ba.$', 'foo'], value='new'}) # 将列表中的替换为new
df.replace(regex={r'^ba.$': 'new', 'foo': 'xyz'})

5.4.3 填充空值 fillna()

df.fillna(0)
df.fillna(method='ffill') # 将空值修改为前一个值
values = {'A': 0, 'B': 1, 'C': 2, 'D': 3}
df.fillna(value=values) # 为各列填充不同的值
df.fillna(value=values, limit=1) # 只填充1个

5.4.4 修改索引名 rename()

修改索引名最常用的办法就是将df.index, df.columns重新赋值为一个类似于列表的序列值, 也可以用rename

df.rename(columns={'team': 'class'})
df.rename(columns={'Q1': 'a', 'Q2': 'b'}) # 对表头进行修改
df.rename(index={0: 'x', 1: 'y', 2: 'z'}) # 对索引进行修改
df.rename(index=str)
df.rename(str.lower, axis='columns')
df.rename({1: 2, 2: 4}, axis=0)

5.4.5 增加列

df['total'] = df.Q1 + df.Q2 + df.Q3 + df.Q4
df['total'] = df.sum(1) # 按照1轴相加, 等同于上面的效果
# 增加一列foo, 所有值都为100
df['foo'] = 100
# 增加新列foo, 值为Q1+Q2
df['foo'] = df.Q1 + df.Q2
# 把所有数字相加
df['total'] = df.select_dtypes(include=['int']).sum(1)
df.loc[:, 'Q1':'Q4'].apply(lambda x: x.sum(), axis=1)
df.loc[:, 'Q10'] = 'new'
# 增加一列, 不满足条件的为NaN
df.loc[df.num >= 60, '成绩'] = '合格'

5.4.6 插入列 df.insert()

df.insert(loc=2, column='total2', value=df.select_dtypes('float').sum(1)) # 会修改原DataFrame, 如果增加的列名已经存在会报错, 默认参数allow_duplicates=False

5.4.7 指定列df.assign()

df.assign(k=v), k为新列明, v为此列的值, v必须是一个与原数据同索引的Series

df.assign(total=df.select_dtypes('number').sum(1)) # 原DataFrame没有变化
# 增加两列
df.assign(total=df.sum(1), Q=100)
df.assign(total=df.sum(1)).assign(Q=100) # 效果同上
# 在增加两列name_len和avg
# 正下面多个assign的链式过程中, 如果想要使用对已经.assign出来的字段, 需要使用lambda, 对原字段的使用直接用df即可
(
    df.assign(total=df.sum(1))
    .assign(Q=100)
    .assign(name_len=df.name.str.len()) # Series是无法直接调用.str的方法的
    .assign(avg=df.mean(1))
    .assign(avg2=lambda d: d.total/4) # 这里的d是代码执行到本行前的DataFrame内容, 可以认为是一个虚拟的DataFrame
    .assign(Q100=lambda d: d.Q-100) 
)
#
df.assign(Q5=[100]*100) # 赋值的内容是有100个100的列表, 返回一个新的DataFrame
df.assign(Q6=df.Q2/df.Q1)
df.assign(Q7=lambda d: d.Q1*9/5+32)
df.assign(tag=df.Q1>df.Q2) # 逻辑判断
df.assign(tag=df.Q1>60).map({True: '及格', False: '不及格'})
dit = {True: '及格', False: '不及格'}
df.assign(tag=(df.Q1>60).apply(lambda x: dit[x])) # 使用lambda映射的话, 这里使用apply或者map都一样
df.assign(Q8=lambda d: d.Q1*2, Q9=lambda d: d.Q8+1) # 在一个assign中赋值两次, 第二次使用了第一次的结果Q8

5.4.8 执行表达式 df.eval()

df.eval()与df.qeury()一样, 传入字符的形式传入表达式

df.eval('total = Q2 +Q3') # 相当于直接生成了新的列
# 使用变量
a = df.Q1.mean()
df.eval('C3 = Q3 + @a')
df.eval('C3 = Q2 > (Q3 + @a)') # 判断Q2 > Q3+a

5.4.9 增加行

可以使用loc[]指定索引给出所有列的值来增加一行数据

df.loc[102] = ['tom', 'A', 88, 88, 88, 88] # 原df有100行, 指定102后, 会在原99后直接加上102, 如果列名与col数量不匹配, 报错
df.loc[104] = {'Q1': 100, 'Q2': 102} # 字典会根据键值匹配col, 无数据列为NaN, 如果更新的字典中有键值无法在col列名上找到, 则被忽略
df.loc[df.shape[0]+1] = {...} # 自动增加索引
# 批量增加数据
rows = [[1, 2], [3, 4], [5, 6]]
for row in rows:
    df.loc[len(df)] = row # len()比索引大一, 可以批量自动新增索引

5.4.10 追加合并 df.append(), df.concat()

append使用场景较少, 一般使用concat

pd.concat([s1, s2], axis=0, ignore_index=True)

5.4.11 删除 df.pop()

Series会删除指定索引的数据同时返回这个被删除的值, DataFrame会删除指定列并返回这个被删除的列
不建议删除, 建议使用重新所以并赋值给新的变量

5.4.12 删除空值 df.dropna()

df.dropna() # 一行中有一个缺失值就删除整行
df.dropna(how='all', axis=1) # 整列全是空, 删除列
df.dropna(thresh=2, asxis=0) # <=2个非空时, 删除行

5.5 高级过滤

5.5.1 df.where()

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

df = df.select_dtypes('number') # 选出所有的数值
df.where(df > 70) # 布尔表达式
df.where(lambda d: d.Q1>50) # 可调用的对象
df.where(pd.Series([True] * 3)) # bool的Series
# 上面不满足条件的都会返回NaN, 指定一个值或者算法来替换NaN
df.where(df >= 60, '不及格') # 指定一个值
# 定义一个数是否为偶数的表达式
cond = (df%2 == 0)
df.where(~cond, -(df-20)) # 这里有两个否定, -> 满足cond的时候用原DataFrame, 不满足cond的时候用-(df-20)

5.5.2 np.where()

df.where()无法对满足条件的值进行替换, 而np.where()实现了这种功能
np.where()返回的是一个二维array

np.where(df>=60, '及格', '不及格') # 返回array, array是没有col名字的
# 可以使用df.where来应用他
df.where(df==99999, np.where(df>=60, '及格', '不及格')) # 给df.where()一个一定不能成立的条件, 
# 需求: 对原DataFrame判断他们的avg是否及格, 及格返回`是`, 不及格返回`否`
(
    df.assign(avg=df.select_dtypes('number').mean(1))
    .assign(及格=lambda d: np.where(d.avg>60, '是', '否')) # 如果要使用链式表达式中生成的列名, 需要使用lambda
)

5.5.3 df.mask()

df.mask()用法与df.where()基本相同, 唯一的区别就是df.mask()将满足条件的位置填充为NaN

df.mask(s>80) # 满足条件的显示NaN
df.Q1.mask(s>80, '优秀') # 满足条件的显示为优秀

5.5.4 df.lookup()

使用loc / iloc

5.6 数据迭代

5.6.1 迭代Series

5.6.2 df.iterrows()

将DataFrame行作为组成的Series数据进行迭代

5.6.3 df.itertuples()

5.6.4 df.items()

5.6.5 按列迭代

5.7 函数应用

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

5.7.1 pipe()

管道方法/链式方法, 语法为df.pipe(<函数名>, <传给函数的参数列表或字典>), 它将DataFrame或Series作为函数的第一个参数, 可以根据需求返回自己定义的任意类型数据

# 对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)
)
# 案列
# 定义一个函数, 给所有季度的成绩加n, 然后增加一列平均数
def add_mean(rdf, n):
    df = rdf.copy()
    df = df.loc[:, 'Q1':'Q4'].applymap(lambda x: x+n)
    df['avg'] = df.loc[:, 'Q1':'Q4'].mean(1)
    return df

df.pipe(add_mean, 100) # 调用
# 筛选出Q1大于80且Q2大于90的数据
df.pipe(lambda df_, x, y: df_[(df_.Q1 >= x) & (df_.Q2 >= y)], 80, 90) 

5.7.2 apply()

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

df.name.apply(lambda x: x.lower())
# 计算去掉一个最高分一个最低分的平均分
def my_mean(s):
    return (s.sum() - s.max() - s.min()) / (len(s) - 2) 
df.select_dtypes('number').apply(my_mean)
# 以学生为维度计算
(
    df.set_index('name')
    .select_dtypes('number')
    .apply(my_mean, axis=1)
)
# 判断一列是否包含在另一列数据中
df.apply(lambda d: d.s in d.s_list, axis=1)
# 与np.where()配合使用
fun = lambda x: np.where((x.team == 'A') & (x.Q1 > 90), 'good', 'other')
df.apply(fun, axis=1)
# 同上效果
(
    df.apply(lambda x: x.team == 'A' and x.Q1>90, axis=1)
    .map({True: 'good', False: 'other'})
)
df.apply(lambda x: 'good' if x.team == 'A' amd x.Q1>90 'other')

5.7.3 applymap()

df.applymap()实现元素级函数应用

def mylen(x):
    return len(str(x))
df.applymap(mylen)

5.7.4 map()

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

df.team.map({'A': '一班', 'B': '二班', 'C': '三班', 'D': '四班'})
df.team.map('I am a {}'.format, na_action='ignore')
'''
0     I am a E
1     I am a C
2     I am a A
3     I am a C
4     I am a D
        ...   
95    I am a C
96    I am a C
97    I am a C
98    I am a E
99    I am a E
Name: team, Length: 100, dtype: object
'''
df.team.map(str) # team列的每个元素调用str

5.7.5 agg()

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

df.agg(max) # 每列的最大值
df.agg([sum, min])
df.agg({'Q1': [sum, min], 'Q2': [min, max]})
df.groupby('team').agg(max)
df.Q1.agg([sum, 'mean'])
# 将行命名为特定名字
df.agg(a=('Q1', max),
       b=('Q2', min)
      )

5.7.6 transform()

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

df.transform(lambda x: x*2) # 应用匿名函数
df.transform([np.sqrt, np.exp]) # 调用多个函数
df.transform({'A': np.abs, 'B': lambda x: x+1})
# 
df.groupby('team').sum()
df.groupby('team').transform(sum) # 返回的是原数据的结构, 在指定位置上显示聚合计算后的结果

5.7.7 copy()

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值