pandas数据处理(三)合并数据、交叉透视表
1. 数据合并
对数据合并,可以使用concat、merge、join 等方法。
1. concat 方法
一般concat 用于上下数据堆叠合并。concat 有用的三个参数:
objs: 数据
axis: {0/‘index’, 1/‘columns’}要连接的轴。0 为上下堆叠,1为左右拼接
join:{‘inner’, ‘outer’}, 默认‘outer’。join='outer’表示外连接,保留两个表中的所有信息;join="inner"表示内连接,拼接结果只保留两个表共有的信息
- 引入pd 以及数据
import pandas as pd
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['打球','游戏','玩'],'性别':['男', '女', '男']})
df2=pd.DataFrame({'姓名':['张三','乔治'],'爱好':['麻将','扑克']})
- 上下堆叠合并数据集
df3 = pd.concat([df1,df2])
'''
姓名 爱好 性别
0 张三 打球 男
1 李四 游戏 女
2 王五 玩 男
0 张三 麻将 NaN
1 乔治 扑克 NaN
'''
- 左右合并
df3 = pd.concat([df1,df2], axis = 1)
'''
姓名 爱好 性别 姓名 爱好
0 张三 打球 男 张三 麻将
1 李四 游戏 女 乔治 扑克
2 王五 玩 男 NaN NaN
'''
- join 内外连接区别
outer 是默认值。 和RDB一样,outer是保留所有,inner 是保留共有的(这里是指属性共有)
df3 = pd.concat([df1,df2], join='inner')
'''
姓名 爱好
0 张三 打球
1 李四 游戏
2 王五 玩
0 张三 麻将
1 乔治 扑克
'''
2. merge 合并
erge 实现类似于数据库的join 操作。
有两种调用方式:pd.merge()和df1.merge(df2)。
语法以及参数:
def merge(
left: DataFrame | Series,
right: DataFrame | Series,
how: str = "inner",
on: IndexLabel | None = None,
left_on: IndexLabel | None = None,
right_on: IndexLabel | None = None,
left_index: bool = False,
right_index: bool = False,
sort: bool = False,
suffixes: Suffixes = ("_x", "_y"),
copy: bool = True,
indicator: bool = False,
validate: str | None = None,
)
'''
left、right:需要连接的两个DataFrame或Series,一左一右
how:两个数据连接方式,默认为inner,可设置inner、outer、left或right
on:作为连接键的字段,左右数据中都必须存在,否则需要用left_on和right_on来指定
left_on:左表的连接键字段
right_on:右表的连接键字段
left_index:为True时将左表的索引作为连接键,默认为False
right_index:为True时将右表的索引作为连接键,默认为False
suffixes:如果左右数据出现重复列,新数据表头会用此后缀进行区分,默认为_x和_y
'''
- on 指定用姓名连接
df3 = pd.merge(df1, df2, on='姓名')
'''
姓名 爱好_x 性别 爱好_y
0 张三 打球 男 麻将
'''
- 直接按索引进行连接
df3 = pd.merge(df1, df2, left_index=True, right_index=True, suffixes=('_1','_2'))
'''
姓名_1 爱好_1 性别 姓名_2 爱好_2
0 张三 打球 男 张三 麻将
1 李四 游戏 女 乔治 扑克
'''
- 多连接条件
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['麻将','游戏','玩'],'性别':['男', '女', '男']})
df2=pd.DataFrame({'姓名':['张三','乔治'],'爱好':['麻将','扑克']})
df3 = pd.merge(df1, df2, on=['姓名','爱好'])
'''
姓名 爱好 性别
0 张三 麻将 男
'''
- 使用外连接
- 右外
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['麻将','游戏','玩'],'性别':['男', '女', '男']})
df2=pd.DataFrame({'姓名':['张三','乔治', '王五'],'爱好':['麻将','扑克', '']})
df3 = pd.merge(df1, df2, on=['姓名'], how = 'right')
'''
姓名 爱好_x 性别 爱好_y
0 张三 麻将 男 麻将
1 乔治 NaN NaN 扑克
2 王五 玩 男
'''
- outter 外连接
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['麻将','游戏','玩'],'性别':['男', '女', '男']})
df2=pd.DataFrame({'姓名':['张三','乔治', '王五'],'爱好':['麻将','扑克', '']})
df3 = pd.merge(df1, df2, on=['姓名'], how = 'outer')
'''
姓名 爱好_x 性别 爱好_y
0 张三 麻将 男 麻将
1 李四 游戏 女 NaN
2 王五 玩 男
3 乔治 NaN NaN 扑克
'''
- 左外
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['麻将','游戏','玩'],'性别':['男', '女', '男']})
df2=pd.DataFrame({'姓名':['张三','乔治', '王五'],'爱好':['麻将','扑克', '']})
df3 = pd.merge(df1, df2, on=['姓名'], how = 'left')
'''
姓名 爱好_x 性别 爱好_y
0 张三 麻将 男 麻将
1 李四 游戏 女 NaN
2 王五 玩 男
'''
- 连接指示: 会加一列_merge 标识连接后是左表内容还是右表内容
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['麻将','游戏','玩'],'性别':['男', '女', '男']})
df2=pd.DataFrame({'姓名':['张三','乔治', '王五'],'爱好':['麻将','扑克', '']})
df3 = pd.merge(df1, df2, on=['姓名'], how = 'left', indicator=True)
'''
姓名 爱好_x 性别 爱好_y _merge
0 张三 麻将 男 麻将 both
1 李四 游戏 女 NaN left_only
2 王五 玩 男 both
'''
- 方法调用也可以通过 df 直接merge
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['麻将','游戏','玩'],'性别':['男', '女', '男']})
df2=pd.DataFrame({'姓名':['张三','乔治', '王五'],'爱好':['麻将','扑克', '']})
df3 = df1.merge(df2, on=['姓名'], how = 'left', indicator=True)
'''
结果同5
'''
3. join
看出join其实是省略了参数的merge,默认情况下是把行索引相同的数据合并到一起。参数如下:
def join(
self,
other: DataFrame | Series | list[DataFrame | Series],
on: IndexLabel | None = None,
how: str = "left",
lsuffix: str = "",
rsuffix: str = "",
sort: bool = False,
validate: str | None = None,
)
测试:
df1=pd.DataFrame({'姓名':['张三','李四','王五'],'爱好':['麻将','游戏','玩']}, index = [1, 2, 3])
df2=pd.DataFrame({'姓名':['张三','乔治', '王五'],'爱好':['麻将','扑克', '']}, index = [1, 2, 3])
df3 = df1.join(df2, lsuffix = '1', rsuffix = '2')
'''
姓名1 爱好1 姓名2 爱好2
1 张三 麻将 张三 麻将
2 李四 游戏 乔治 扑克
3 王五 玩 王五
'''
2. 交叉表、透视表
1. 交叉表crosstab
用于计算一列数据对于另外一列数据的分组个数(用于统计分组频率的特殊透视表), 一般用于探索两列数据之间的关系。
def crosstab(
index,
columns,
values=None,
rownames=None,
colnames=None,
aggfunc=None,
margins: bool = False,
margins_name: str = "All",
dropna: bool = True,
normalize=False,
) -> DataFrame:
index:类似数组、系列或数组/系列值的列表,行中分组依据的值
columns:类似数组、系列或数组/系列值的列表,列中要作为分组依据的值
values:类似数组,可选,要根据因素聚合的值数组,需要指定 aggfunc
rownames:序列,默认 None ,如果传递,必须匹配传递的行数组的数量
colnames:序列,默认 None ,如果传递,必须匹配传递的列数组的数量
aggfunc:function,可选,如果指定,则还需要指定值
margins:bool, 默认False 添加行/列边距(小计)
margins_name:str,默认为“All”,当边距为 True 时将包含总计的行/列的名称
dropna:bool, 默认为True,不包含条目均为 NaN 的列
normalize:bool, {‘all’, ‘index’, ‘columns’}, or {0,1}, 默认为False,通过将所有值除以值的总和来归一化
“all”或 True:将对所有值进行归一化
index:将对每一行进行归一化
columns:将对每一列进行归一化
若margins 为 True,也将标准化边距值
返回:数据的 DataFrame 交叉表
- 求count
a = np.array(["foo", "foo", "foo", "foo", "bar", "bar", "bar", "bar", "foo", "foo", "foo"], dtype=object)
b = np.array(["one", "one", "one", "two", "one", "one", "one", "two", "two", "two", "one"], dtype=object)
c = np.array(["dull", "dull", "shiny", "dull", "dull", "shiny", "shiny", "dull", "shiny", "shiny", "dull"], dtype=object)
count = pd.crosstab(a, [b, c], rownames=['a'], colnames=['b', 'c'])
count
结果:
- 求sum
# 算数运算,先求和。 默认按照列求和
sum = count.sum()
sum
'''
b c
one dull 4
shiny 3
two dull 2
shiny 2
dtype: int64
'''
# 按行求和
sum = count.sum(axis=1)
sum
'''
a
bar 4
foo 7
dtype: int64
'''
- 求比例
# 进行相除操作,得出比例
pro = count.div(sum, axis=0)
pro
- 画图
pro.plot(kind='bar', stacked=True)
2. 透视pivot
透视表是将原有的DataFrame的列分别作为行索引和列索引,然后对指定的列应用聚集函数,是一种可以对数据动态排布并且分类汇总的表格格式。
def pivot_table(
self,
values=None,
index=None,
columns=None,
aggfunc="mean",
fill_value=None,
margins=False,
dropna=True,
margins_name="All",
observed=False,
sort=True,
) -> DataFrame:
from pandas.core.reshape.pivot import pivot_table
values:要聚合的列,可选,默认对所有列操作
index:column, Grouper, array, or list of the previous 如果传递数组,它必须与数据的长度相同。该列表可以包含任何其他类型(列表除外)。在数据透视表索引上分组的键。如果传递一个数组,它的使用方式与列值相同
column:column, Grouper, array, or list of the previous 如果传递一个数组,它必须和数据一样长。该列表可以包含任何其他类型(列表除外)。在数据透视表列上分组的键。如果传递一个数组,它的使用方式与列值相同
aggfunc:function, list of functions, dict, 默认为numpy.mean 如果传递函数列表,则生成的数据透视表将具有分层列,其顶层是函数名称(从函数对象本身推断)如果传递dict,则键是列聚合和值是函数或函数列表
fill_value:scalar,默认 None 用于替换缺失值的值(在聚合后的结果数据透视表中)
margins:bool, 默认False 添加所有行/列(例如小计/总计)
dropna:bool, 默认为True,不包含条目均为 NaN 的列。如果为 True,则在计算边距之前将忽略任何列中具有 NaN 值的行
margins_name:str,默认为“All” 当边距为 True 时将包含总计的行/列的名称
observed:bool,默认为 False 这仅适用于任何groupers 是分类的。若为True:仅显示分类groupers 的观察值。否则显示分类groupers 的所有值。
sort:bool, default True 指定结果是否应该排序
测试数据:
df = pd.DataFrame({"A": ["aaa", "aaa", "aaa", "aaa", "aaa","aa", "aa", "aa"],
"B": ["bbb", "bbb", "bbb", "bb", "bb", "bbb", "bbb", "bb"],
"C": ["small", "large", "large", "small","small", "large", "small", "small"],
"D": [1, 2, 2, 3, 3, 4, 5, 6],
"E": [2, 4, 5, 5, 6, 6, 8, 9]})
df
结果:
- 求和
d1 = pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'], aggfunc=np.sum) # 通过求和来聚合值
d1
结果:
- 可以使用fill_value参数填充缺失的值
d2 = pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'], aggfunc=np.sum, fill_value=0) # 可以使用fill_value参数填充缺失的值
d2
- 对多个列取平均值进行聚合
d3 = pd.pivot_table(df, values=['D', 'E'], index=['A', 'C'], aggfunc={'D': np.mean, 'E': np.mean}) # 通过对多个列取平均值进行聚合
d3
- 为任何给定值列计算多种类型的聚合
d4 = pd.pivot_table(df, values=['D', 'E'], index=['A', 'C'], aggfunc={'D': np.mean, 'E': [min, max, np.mean]}) # 可以为任何给定值列计算多种类型的聚合
d4
- 默认mean 求均值
d4 = df.pivot_table(['D'], index=['A'])
d4
'''
D
A
aa 5.0
aaa 2.2
'''
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】