系列文章目录
- 分组对象操作
- 分组聚合
- 分组转换
- 分组过滤
- 分箱
- 合并操作
- 变形操作
文章目录
前言
本文通过案例分析的方式详解了Pandas函数进阶常用操作。
提示:以下是本篇文章正文内容,下面案例可供参考
1 分组对象操作
-
获取分组对象
import pandas as pd # 加载数据集 df = pd.read_csv('data/LJdata.csv') df # 实现分组操作是通过 groupby() 方法, 得到分组对象 # 对区域列进行分组操作 gs1 = df.groupby(by=['区域']) gs1 type(gs1) # 对多列进行分组操作 gs2 = df.groupby(by=['区域', '户型']) gs2 type(gs2)
-
分组对象聚合操作
# gs1是分组之后的df对象, 直接调用聚合函数是对所有数值列进行聚合操作 gs1.mean() # 获取分组df对象中某列数值列进行聚合操作 # 获取df中的列, df[列名] print(gs1['价格']) gs1[['价格', '面积']] gs1['价格'].max()
-
获取分组对象第一条或最后一条数据
# 获取每组中的第一条数据 gs2.first() gs1.first() # 获取每组中的最后一条数据 gs2.last()
-
获取分组对象分组信息
# 获取分组 gs2.grouper.result_index gs1.grouper.result_index # 获取每组数据的索引值 gs1.groups
-
获取分组对象分组数据
gs2.get_group(name=('七里庄租房', '1室0厅')) gs2.get_group(name=('CBD租房', '2室1厅')) gs1['价格'].get_group(name='CBD租房')
2 分组聚合
2.1 df分组对象直接调用聚合函数
# df分组后直接调用聚合函数, 对df分组对象的所有数值列进行相同的聚合操作
# df.groupby(by=[分组列名1, 分组列名2, ...]).max/min/mean/sum/count()
# 对区域进行分组, 统计面积,价格以及看房人数列的平均值
df.groupby(by='区域').mean()
df.groupby(by=['区域']).count()
2.2 df分组对象指定聚合列
# df分组后对单列或多列进行聚合操作
# df.groupby(by=[列名1, 列名2, ...])[[列名]].聚合函数() -> 一个[], 得到s对象; 两个[],得到df对象
# df.groupby(by=[列名1, 列名2, ...])[[列名1, 列名2, ...]].聚合函数()
# 对区域分组, 统计价格列的最小值, 一个[]返回s对象
df.groupby(by='区域')['价格'].min()
# 对区域分组, 统计价格列的最小值, 两个[]返回df对象
df.groupby(by='区域')[['价格']].min()
# 对区域分组, 统计价格和看房人数的求和
df.groupby(by=['区域'])[['价格', '看房人数']].sum()
2.3 df分组对象结合agg函数
-
多个聚合函数
# df分组后使用多个聚合函数 # df.groupby(by=[列名1. 列名2, ...])[[列名1, 列名2...]].agg([聚合函数名1, 聚合函数名2, ...]) # 对列名1进行多个聚合操作, 列名2进行多个聚合操作 # 聚合函数名可以是pandas内置的函数名 'max', 'min';也可以是numpy中的函数名 np.mean np.sum; 直接使用python的聚合函数 max min import numpy as np # 对户型分组, 统计面积和价格列的最小值,平均值以及求和 df.groupby(by='户型')[['面积', '价格']].agg([min, np.mean, 'sum'])
-
不同列使用不同聚合函数
# df分组后不同列使用不同聚合函数 # df.groupby(by=[列名1, 列名2, ...]).agg({列名1:聚合函数名1, 列名2:聚合函数名2, ...}) # 对户型分组, 统计面积的最大值以及价格的平均值 df.groupby('户型').agg({'面积':max, '价格':np.mean})
-
使用自定义函数
# df分组后使用自定义的函数进行聚合操作 参考apply()函数 # df.groupby(by=[列名1, 列名2, ...])[[列名1, 列名2, ...]].agg(自定义函数名, 参数2=, 参数3=) # 手写求平均聚合操作 # 自定义函数最少接收一个参数, 分组后每组的数据 def avg_func(x, arg1): print('x的值是->', x) # 平均值 = 求和值 / 个数 # x.sum()求和 # x.size 统计个数 m = x.sum() / x.size return m + arg1 df.groupby('户型')['价格'].agg(avg_func, arg1=10) # 手写求平均聚合操作 # 自定义函数最少接收一个参数, 分组后每组的数据 def avg_func(x, arg1): print('x的值是->', x) print(type(x)) # 平均值 = 求和值 / 个数 # sum()求和 # size 统计个数 m = x['价格'].sum() / x['价格'].size return m + arg1 # 自定义函数中的x参数是df分组数据 df.groupby('户型').agg(avg_func, arg1=10) def func(s): print('s的值是->', s) # 参数s是分组之后指定的每一列 s_mean = s.sum() / s.size return s_mean res = df.groupby('户型')[['价格', '看房人数']].agg(func) print(res)
3 分组转换
分组转换和分组聚合
分组转换是每组的聚合结果, 然后拼接到df后边, 不会改变df的行数; 分组之前的df有多少上数据, 分组转换之后还是多少行
分组聚合是每组的聚合结果和每组信息一同返回, 改变df的行数, 有多少分组返回多少行数据
# df分组之后对列进行转换(计算)操作
# df.groupby(by=[列名1, 列名2, ...])[[列名1, 列名2, ...]].transform(聚合函数)
df.groupby('户型')['价格'].agg('mean')
# 对户型分组, 统计价格列的平均值, 添加到原df后边
df.groupby('户型')['价格'].transform('mean')
# 类似于sql中的开窗函数
df['平均价格'] = df.groupby('户型')['价格'].transform('mean')
df
# transform(自定义函数名)
# 手写求平均值的函数
def func2(x, arg1):
m = x.sum() / x.size
return m + arg1
df.groupby(by='户型')['价格'].transform(func2, arg1=10)
4 分组过滤
# df.groupby(by=[列名1, 列名2, ...]).filter(lambda x: 判断条件)
# 判断条件返回True或False
# 对每组的数据结果进行判断, 留下满足条件的组数据, 删除掉不满足条件的组数据
# 根据户型分组, 保留平均价格大于1w的组数据
# x是df分组对象, 获取df中的价格列求平均值
# 判断条件是每组的价格平均值和10000比较, 不是每条数据的价格和10000比较
df.groupby(by='户型').filter(lambda x:x['价格'].mean() > 10000)
# x是df分组对象后的价格列 -> s对象
s1 = df.groupby(by='户型')['价格'].filter(lambda x: x.mean() > 10000)
s1.index
df.loc[s1.index]
5 分箱
# 分箱->根据df中的某列值进行分组操作
# pd.cut(x=s对象, bins=, labels=, right=, include_lowest=)
# x:分箱的列
# bins: 分箱的箱数(组数), 接受整数, 或分箱规则列表->[0, 20, 40] 0~20 20~40 40以上
# labels: 分箱的名称, 不写也会有默认值
# right: 分箱规则列表, 默认左开右闭 True, 左闭右开 False
# include_lowest: 是否包含第一组中的起始值, 默认False不包含
# 根据价格列对数据成3组
df['价格分组'] = pd.cut(x=df['价格'], bins=3)
df
# (0, 4500]->低 (4500, 8500]->中 (8500, 210000]->高
df['价格分组2'] = pd.cut(x=df['价格'], bins=[0, 4500, 8500, 210000], labels=['低', '中', '高'])
df
6 合并操作
6.1 纵向合并
类似于sql中的 union all 操作, 将多个df纵向连接到一起
# 导入模块
import pandas as pd
# 创建数据集
df1 = pd.DataFrame([[1, 2, 3], [1, 10, 20], [5, 6, 7], [3, 9, 0], [8, 0, 3]], columns=['x1', 'x2', 'x3'])
df2 = pd.DataFrame([[1, 2], [1, 10], [1, 3], [4, 6], [3, 9]], index=[0, 2, 4, 6, 8], columns=['x1', 'x4'])
print(df1)
print(df2)
# df1.append(df2), 根据列名相同进行追加, 列名不同用NaN值填充
# 在df1中追加df2
# 新df会保留各自df的索引值
df1.append(df2)
df1.append(df2).reset_index(drop=True)
# ignore_index=True:重置新df的索引值
df1.append(df2, ignore_index=True)
# pd.concat([df1, df2, df3, ...])
# 纵向合并多个df, 根据列名相同进行合并, 列名不同用NaN值填充
pd.concat([df1, df2], axis=0)
# 可以通过join参数, 设置保留df之间共有的数据, 默认是outer->保留所有数据, 类似于全连接
pd.concat([df1, df2], axis=0, join='inner', ignore_index=True)
6.2 横向合并
-
concat()
# pd.concat([df1, df2, df3, ...], axis=1, ignore_index=, join=) # 将多个df根据行索引值进行合并, 索引值相同的合并, 不相同的用NaN值填充 pd.concat([df1, df2], axis=1) # ignore_index=True: 重置列名 pd.concat([df1, df2], axis=1, ignore_index=True) # join='inner': 保留共有的行数据 pd.concat([df1, df2], axis=1, join='inner')
-
merge()
# pd.merge(left=, right=, on=, left_on=, right_on=, how=) 等同于sql的join/left join... # df1.merge(df2, on=, how=) # 根据指定的列值进行横向合并 # on=: 两个df相同的关联列名 # left_on/right_on: 两个df中没有相同的关联列名 # how: 合并方式, 默认是inner, 内连接 # 默认是内连接, how='innner' df1.merge(df2, on='x1') # left_on/right_on: 两个df中没有相同的关联列名 pd.merge(left=df1, right=df2, left_on='x3', right_on='x1') # 外连接, how='outer', 关联不上的用NaN值填充 pd.merge(df1, df2, on='x1', how='outer') # 左连接, how='left' df1.merge(df2, on='x1', how='left') # 右连接, how='right' df1.merge(df2, on='x1', how='right')
-
join()
# df1.join(df2, lsuffix=, rsuffix=, how=) # 根据两个df的行索引值进行横向合并, 索引值不同的用NaN值填充 # lsuffix/rsuffix: 如果横向合并df中有重名的列名,需要通过此参数设置列名的后缀 # how: 默认是left, 保留左df的所有数据; right, outer, inner df1.join(df2,lsuffix='_df1',rsuffix='_df2') df1.join(df2,lsuffix='_df1', rsuffix='_df2', how='inner') df1.join(df2,lsuffix='_df1', rsuffix='_df2', how='outer')
7 变形操作
7.1 转置
# 行变列, 列变行 -> 转置
# df.T
df1.T
7.2 stack和unstack
# 构造示例数据集
df3 = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns=['store1', 'store2', 'store3'], index=['street1', 'street2'])
df3
# stack() -> 将df表格转换成series花括号
df3.stack()
type(df3.stack())
# unstack() -> 将series花括号转换成df表格
# 对多列进行分组后, 可以通过unstack进行转换
df3.stack().unstack()
df = pd.read_csv('data/LJdata.csv')
temp = df.groupby(by=['户型', '朝向'])['价格'].mean()
temp
temp.unstack()
7.3 melt
# df.melt(id_vars=, value_vars=, var_name=, value_name=) 将数据实现宽变长操作
# id_vars:不变形的列名列表, 如果不指定,除了value_vars指定的变形列, 其余列都不变形(会被删除)
# value_vars: 变形的列名列表, 将指定的列名值保存到新列中, 如果不指定,除了id_vars不变形的列, 其余列都变形
# var_name: 指定变形列名保存到新列中的列名
# value_name: 指定变形列的值保存到新列的列名
df4 = pd.read_csv('data/music.csv')
df4
df4.melt(
id_vars=['artist', 'track'],
value_vars=['wk1', 'wk2']
)
df4.melt(
id_vars=['artist', 'track'],
value_vars=['wk1', 'wk2'],
var_name='week', # 修改存储变形列名列的列名
value_name='score' # 修改存储变形列值列的列名
)
# 只指定不变形的列, 剩余列需要变形
df4.melt(id_vars=['artist', 'track'])
# 只指定变形的列, 剩余列会被删除
df4.melt(value_vars=['wk1', 'wk2'])
temp.unstack().reset_index().melt(id_vars=['户型'])
7.4 pivot_table
# 透视表操作 -> 类似于分组聚合
# pd.pivot_table(data=, index=,columns=,values=,aggfunc=,margins=)
# index:分组列名, 作为新df的行索引值
# columns:分组列名, 作为新df的列名
# values:聚合列名
# aggfunc:聚合函数名
# margins: 是否在最后一行或一列添加求和结果
data = pd.read_csv('data/uniqlo.csv')
data.head()
# 获取销售渠道为线下的所有数据
new_data = data.query('channel=="线下"')
new_data
# 制作透视表, 获取不同城市(city->行索引)不同商品(product->列名)的销售总金额(revenue)
pd.pivot_table(data=new_data, index='city', columns='product', values='revenue', aggfunc='sum', margins=True)
# 分组聚合后unstack 等同于 透视表操作
new_data.groupby(by=['city', 'product'])[['revenue']].sum().unstack()
# index, columns, values 可以指定多列
pd.pivot_table(data=data, index=['city', 'channel'], columns=['product'], values=['revenue', 'quant'], aggfunc='sum')
pd.pivot_table(data=data, index=['city'], columns=['channel','product'], values=['revenue', 'quant'], aggfunc='sum')