pandas级联,合并

pandas的拼接分为两种:

  • 级联:pd.concat(), pd.append()
  • 合并:pd.merge(),pd.join()

概述:
pd.concat()
objs:(df1,df2,df3...)
axis:axis=0,1
join:"inner", "outer" 确定内连接还是外连接,默认外连接
ignore_index: ignore_index=True忽略原始索引
keys:设置多层级,保留原始索引

pd.Index(["D","E"])
df对象.reinex(Index对象): 取出指定的某几行

import numpy as np
import pandas as pd
from pandas import Series, DataFrame
# numpy的级联
# numpy级联维度上的形状要保持一致
arr1 = np.ones(shape=(2,3))
arr2 = np.zeros((4,3))
display(arr1, arr2)
np.concatenate((arr1,arr2), axis=0)  # 0是纵向,1是横向(会报错)
# 一、级联
# 1.使用pd.concat()级联

# 级联方向上形状不同,也可以级联
# 是以索引对齐的方式连接,与顺序无关
df1 = DataFrame(np.random.randint(0,100,(3,3)), list("ABC"), ["python", "java", "php"])
df2 = DataFrame(np.random.randint(0,100,(4,3)), list("DEFH"), ["java", "python", "php"])                
display(df1,df2)
pd.concat(objs=(df1,df2))
# 横向级联
pd.concat(objs=(df1,df2), axis=1)
# 索引对齐
df3 = DataFrame(np.random.randint(0,100,(3,3)), list("ABC"), ["python", "java", "php"])
df4 = DataFrame(np.random.randint(0,100,(4,3)), list("DBFC"), ["java", "python", "php"])                
display(df3,df4)
# 横向级联
pd.concat(objs=(df3,df4), axis=1)
# .ignore_index参数忽略索引
columns = ["成本", "费用", "销售额"]
order1 = DataFrame(np.random.randint(0,150,(5,3)), columns=columns)
order2 = DataFrame(np.random.randint(0,150,(8,3)), columns=columns)
display(order1,order2)
df5 = pd.concat((order1,order2))
df5.loc[0]  # 索引重复
# 原始表的索引没有实际意义,可以忽略处理,避免重复
pd.concat((order1,order2),ignore_index=True)
# 通过keys可以制造级联多层级
pd.concat((order1,order2), axis=1,keys=["第一季度", "第二季度"])
pd.concat((order1,order2), axis=1,keys=["第一季度", "第二季度"]).mean()
# 级联应用场景:一般是表结构相同或相似的两张表进行级联,如:商品表与商品表级联,价格表与价格表级联。

练习

  1. 使用昨天的知识,建立一个期中考试张三、李四的成绩表ddd
  2. 假设新增考试学科"计算机”,如何实现?
  3. 新增王老五同学的成绩,如何实现?
# 使用昨天的知识,建立一个期中考试张三、李四的成绩表ddd
df6 = DataFrame(np.random.randint(0,150,(2,3)), index=["张三", "李四"], columns=["语文", "数学", "英语"])
df6
# 假设新增考试学科"计算机”
df7 = DataFrame(np.random.randint(0,150,(2,1)), index=["张三", "李四"], columns=["计算机"])
df7 = pd.concat(objs=(df6,df7), axis=1)
df7
# 新增王老五同学的成绩
df8 = DataFrame(np.random.randint(0,150,(1,4)), index=["王老五"], columns=["语文", "数学", "英语", "计算机"])
df8
pd.concat(objs=(df7,df8))
# 不匹配级联
# 不匹配指的是级联的维度的索引|不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致

# 有3种连接方式
# 外连接:join="outer"(默认)
# 内连接:join="inner"
# 新版用df8.reindex( pd.Index(["D","E"]) )获取df8的"D","E"这两行                                                 # join_axes参数指定连接(已弃用)
df8 = DataFrame(np.random.randint(0,150,(3,5)), columns=list("ABCDE"))
df9 = DataFrame(np.random.randint(-100,0,(3,6)), columns=list("BCDEFG"))
display(df8,df9)
# 外连接
# outer连接,外连接,保存连接的表的所有字段,缺失值补空值
pd.concat(objs=(df8,df9))
# 内连接
pd.concat(objs=(df8,df9), join="inner")
# pd.Index(["D","E"])
# 3.df对象.reindex

# 扩展:可以这样定义df
df8 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['D', 'F'])
df9 = pd.DataFrame({'C': [5, 6], 'D': [7, 8]}, index=['D', 'G'])
display(df8,df9)

desired_index = pd.Index(["D", "E"])
df8_reindexed = df8.reindex(desired_index)
df9_reindexed = df9.reindex(desired_index)
result = pd.concat([df8_reindexed, df9_reindexed])
result
type(result["A"])
# append()只支持纵向连接(已弃用)
# 优点:快
# 缺点:不灵活
# df8.append(df9)  # 报错,新版本已弃用              老师的pd版本是2.2.0好像

# join_axes参数指定连接
# 只保留特定的几个字段
# d.concat((df8,df9), join_axes=[pd.Index(["D","E"])])  # 报错,已弃用,用df对象.reindex()
# 二、合并

# 合并只跟列有关,两张表要去找内容相同的列来进行合并
# 合并的两张表一定存在至少一列,在内容上有对应关系,至少是一对一、一对多、多对多中一种
# 合并可以同时参考多列进行,这取决于你的业务需求
table1 = pd.read_excel("D:\Desktop\day3\代码+作业\关系表.xlsx", sheet_name=1, index_col=0)
table2 = pd.read_excel("D:\Desktop\day3\代码+作业\关系表.xlsx", sheet_name=2, index_col=0)
table3 = pd.read_excel("D:\Desktop\day3\代码+作业\关系表.xlsx", sheet_name=3, index_col=0)
table4 = pd.read_excel("D:\Desktop\day3\代码+作业\关系表.xlsx", sheet_name=4, index_col=0)
table1
table2
pd.merge(table1,table2)
pd.concat((table1,table2), axis=1)
table3
pd.merge(table1,table3)
table4
pd.merge(table3,table4)
# 指定用哪行合并
pd.merge(table3,table4,on="手机型号")

# 相当于用共同行合并(默认)
pd.merge(table3,table4,on=["手机型号","发货地区"])
table6 = table1.copy()
table6.columns = ["型号", "参考价格"]
table6
# 指定合并:用左表的手机型号与右表的型号合并
r1 = pd.merge(table2,table6, left_on="手机型号", right_on="型号")
r1
# 删除行/列(默认删行)

# axis=1删列
# r1.drop(labels=["型号"], axis=1, inplace=True)  # inplace=True表示修改并替换r1,r1就变了
r1.drop(labels=["型号"], axis=1)

# 删除第0行
r1.drop(labels=[0])
r1
# how合并方式
# inner内合并,只保留相同的【内容】
# concat的内连接:只保留相同的【标签】

# 外合并
pd.merge(table1,table2,how="outer")
# 只保留左表的合并
pd.merge(table1,table2,how="left")
# 只保留右表的合并
pd.merge(table1,table2,how="right")
table7 = pd.merge(table3,table4,on="手机型号",suffixes=["_上半年","_下半年"])
table7

pd.merge()总结

  1. 都是以列为合并项
  2. 参与合并的列必须满足一对一、一对多、多对多关系中的至少一种
  3. 参与合并的列的选择应该是选择离散型数据而不是连续型数据
  • how=inner'合并的方式:内合并,外合并,左合并,右合并
  • on指定参与合并的列用于有多列标签相同的情况
  • left_on\lright_on分别制定左右表参与合并的列用于两张表参与合并的列标签不同的情况
  • suffixes一般与on参数一起使用,给相同列标签但是没有参与合并的列添加后缀
  • left_indexlright_index指定索引I作为合并的参考值
# 将手机型号这一列设置为行索引
table7.set_index("手机型号")
# 用左表的索引列与右表的手机型号列进行合并
# pd.merge(table7, table2, left_index=True, right_on="手机型号")
# 练习
# 1.假设有两份成绩单,除了ddd是张三李四王老五之外,还有ddd4是张三和赵小六的成绩单,如何合并?
df8 = DataFrame(np.random.randint(0,150,(3,3)), columns=["张三", "李四", "王老五"], )
# df8 = DataFrame({
#     "张三":[11,22,33]
# })
df8["张三"] = [11,22,33]
df8
df9 = DataFrame({
    "张三":[11,22,33],
    "赵小六":[122,51,95],
})
df9
pd.merge(df8,df9)
# 2.如果ddd4中张三的名字被打错了,成为了张十三,怎么办?
ddd4 = DataFrame({
    "张十三":[11,22,33],
    "赵小六":[122,51,95],
})
ddd4

pd.merge(df8, ddd4, left_on="张三", right_on="张十三").drop("张十三",axis=1)
# 三、案例分析:美国各州人口数据分析
abb = pd.read_csv("D:\Desktop\day3\作业数据\state-abbrevs.csv")
area = pd.read_csv("D:\Desktop\day3\作业数据\state-areas.csv")
pop = pd.read_csv("D:\Desktop\day3\作业数据\state-population.csv")
# 熟悉数据

# 查看下列字段类型
display(abb.head(),area.head(),pop.head())
# 查看下形状
# 可见第一张表有51行数据(表格第一行是列字段名称,总共52行)
display(abb.shape, area.shape, pop["state/region"].unique().shape)
# 查看数据中是否有NaN
display(abb.isnull().any(), area.isnull().any(), pop.isnull().any())
# 找不同
abb_state = set(abb["state"])
area_state = set(area["state"])
pop_state = set(pop["state/region"].unique())
display(abb_state, area_state, pop_state)
abb_state - area_state  # 返回state_abb中有而state_area中没有的元素  # {}
area_state - abb_state  # 返回state_area中有而state_abb中没有的元素  # {'Puerto Rico'}

# 更简便的方法
# set1对象.symmetric_difference(set2对象):获取两个集合中不重复的元素,也就是所有不共有的值。
abb_state.symmetric_difference(area_state)  # {'Puerto Rico'}
abb_state_abbreviation = set(abb["abbreviation"])
abb_state_abbreviation.symmetric_difference(pop_state)  # {'PR', 'USA'}
# 为了保留信息,使用外合并

# 合并abb, pop
abb_pop = pd.merge(abb,pop,how="outer",left_on="abbreviation", right_on="state/region")
abb_pop
abb_pop.isnull().any()
# 把所有有缺失值的行拿出来
bool_list = abb_pop.isnull().any(axis=1)
temp = abb_pop.loc[bool_list]
temp
# state 和 abbreviation 都是空
temp.isnull().all()
# 填充PR州

# 相当于abb_pop.loc[bool_list, column]
# 相当于abb_pop.loc[index, column]
abb_pop.loc[abb_pop["state/region"] == "PR", "state"] = "Puerto Rico"
abb_pop.loc[abb_pop["state/region"] == "USA", "state"] = "USA"
abb_pop.isnull().any()
# 删除abbreviation
abb_pop.drop("abbreviation", axis=1, inplace=True)
abb_pop.isnull().any()
display(area, abb_pop)
# 合并3张表

# 发现只有USA是存在面积缺失的,但是USA是整个美国的数据,所以不涉及州的人口密度,所以可以删除
total = pd.merge(area, abb_pop, how="outer")
total.isnull().any()
total
# 删除空area (sq. mi)的两种方式
# 方式一:drop()
empty_indexes = total.loc[total["area (sq. mi)"].isnull()].index
total1 = total.drop(empty_indexes)
total1
total1.isnull().any()
# 方式二:过滤
total2 = total[total["area (sq. mi)"].notnull()]
total2
total2.isnull().any()
# 查看哪些州有人口缺失
bool_list = total2["population"].notnull()
bool_list
# 因为人口数据官方没有统计,所以删除
# 这里采用过滤population非空的方式获取结果
result = total2.loc[bool_list]
result
# 判断是否有空值
result.isnull().any()
# 方式一:找出2010年的全民人口数据(普通方式)
condition = (result["year"] == 2010) & (result["ages"] == "total")
condition
total_2010 = result.loc[condition]
# 方式二:找出2010年的全民人口数据,df.query(查询语句)
total_2010_a = result.query("year == 2010 & ages == 'total'")
total_2010["population"] / total_2010["area (sq. mi)"]
# set_index("state") ,将州名作为索引,增加可读性
temp1 = total_2010_a.set_index("state") 
temp1
# density是Series对象
density = temp1["population"] / temp1["area (sq. mi)"]
# 排序,并找出人口密度最高的五个州

# Series的排序sort_values()
density.sort_values(ascending=True)[-5:]
# 找出人口密度最低的五个州
density.sort_values(ascending=True)[:5]

要点总结:

  • 统一用loc()索引
  • 善于使用.isnull().any()找到存在NaN的列
  • 善于使用.unique()确定该列中哪些key是我们需要的
  • 一般使用外合并、左合并,目的只有一个:宁愿该列是NaN也不要丢弃其他列的信息

作者:cloud-2-jane

出处:https://www.cnblogs.com/cloud-2-jane/articles/18609828

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   一只大学生  阅读(15)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示