Pandas数据处理---数据的读写、数据清洗、数据过滤、数据转换(映射、替换、字符串矢量级运算)、数据合并详细总结

该文从Pandas的数据的读写、数据清洗、数据过滤、数据转换(映射、替换、字符串矢量级运算)、数据合并等都进行了详细的总结和案例说明,是pandas的核心重点知识。

目录

一、数据读写处理文档

1.1、pandas数据的加载+写入

1.2、pandas写出数据到内存

二、数据清洗

2.1、缺失值处理

发现缺失值

丢弃缺失值

填充缺失值

2.2、重复值处理

发现重复值

删除重复值

2.3、无效值处理

检测无效值

三、数据过滤¶

四、数据转换(映射、替换、字符串矢量级运算)

4.1、应用 apply、map、applymap映射转换

Series的s.apply(function)转换

Series的s.map(map_series)

DataFrame调用apply,列级别的转换,df[[0, 1,...]].apply(function)

DtaFrame调用applymap:通过函数实现元素级的映射转换。

4.2、替换

4.3、字符串矢量级运算

五、数据合并

concat()

append()

merge()

join()


一、数据读写处理文档

1.1、pandas数据的加载+写入

我们常用的数据加载函数为(点击下划线部分跳转到详细文档):

  • read_csv :读取普通文件默认","分割,但可以指定为其他分隔符,如.csv .txt文件等
  • read_table :读取以‘/t’分割的文件到DataFrame 
  • read_sql :读取数据库数据,如mysqloracle数据库等
  • read_excel :读取excel文件
  • to_csv:写入csv文件
  • to_sql: 写入sql数据库
  • to_excel: 写入excel文件
  • ......

说明:

  • read_csv与read_table默认使用的分隔符不同。通常情况下我们直接使用read_csv代替read_table

加载+写入csv/tsv/:https://blog.csdn.net/weixin_41685388/article/details/103768116 

加载+写入txt:https://blog.csdn.net/weixin_41685388/article/details/103787971

加载+写入excel:https://blog.csdn.net/weixin_41685388/article/details/103773622

加载+写入mysql:https://blog.csdn.net/weixin_41685388/article/details/103770964

加载+写入oracle:https://blog.csdn.net/weixin_41685388/article/details/103791970

pandas官方手册:https://pandas.pydata.org/pandas-docs/stable/

pandas中文手册:https://www.pypandas.cn/docs/user_guide/io.html#io-sql

1.2、pandas写出数据到内存

# to_csv 不仅可以写入硬盘文件中,也可以写入内存(类文件对象)中。【处理速度更快,性能更好】
# 类文件对象:像文件那样具有read,write等功能的对象。

# StringIO 处理文本类型,如果是文本格式,都是字符串类型可解析
# BytesIO 处理二进制类型,如果是图片,视频,音频等,都是二进制格式。没法使用字符串来解析
from io import StringIO, BytesIO
# 创建一个类文件对象
str_io = StringIO()
df.to_csv(str_io)

# 查看文件指针的位置
display(str_io.tell())

# 调整文件指针的位置。将指针调整到文件的最前端。
str_io.seek(0)
display(str_io.read())

# 可以调用getvalue方法取出StringIO对象中的数据。(不用调整指针,再去读取)
display(str_io.getvalue())
print(str_io.getvalue())

二、数据清洗

数据清洗主要包含以下几方面内容:

  • 处理缺失值
  • 处理重复值
  • 处理异常值

准备数据,并读取数据:

import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings("ignore")

df = pd.read_csv(r"spider.csv", header=None)
display(df.sample(5))  #随机抽样df.head(n)/df.tail(n) #抽取前/后n行
display(df.shape,df.shape[0],df.shape[1])  #查看总的有n行,m列,这里有1369行,5列

2.1、缺失值处理

发现缺失值

Pandas中,会将float类型的nan与None视为缺失值,我们可以通过如下方法来检测缺失值:

  • info()
  • isnull()
  • notnull()

说明:

  • 判断是否存在空值,可以将isnull与any或all结合使用。

检测整体缺失值情况 df.info()

print(df.shape)
# 按照列来检测缺失值,首先可以调用info方法进行整体查看。
# info方法可以显示DataFrame中每列的相关信息。
print(df.info())  

判断空值的情况

print(df.isnull().any()) #按列检查每列是否有至少一个空
print(df[[3,4]].isnull().any()) #检查指定列是否有至少一个空

print(df.notnull().all()) #按列检查每列是否全部不为空
print(df[[3,4]].notnull().all())  #按列检查指定列是否全部不为空

    

丢弃缺失值

df.dropna(axis=0/1,inplace=True/False,how='any' / how='all' / thresh=n)

说明:

  • axis:指定丢弃行或者丢弃列(默认为丢弃行axis = 0)。axis = 0 / axis = 1
  • inplace:指定是否就地修改,默认为False。 是否就地操作
  • how:指定dropna丢弃缺失值的行为,默认为any(只要存在就删除)。all (该行(列)全部为空才删除)/ any
  • thresh:当非空数值达到该值时,保留数据,否则删除。如 thresh = 3,弥补how的不足(与how不同时使用)

df = pd.read_csv("spider.csv", header=None)
display(df.head())

# 处理空值。丢弃空值,使用dropna。
df1 = df.dropna() #默认<=>df2,# 这个行为表示: 把df中的 有缺失字段值的任意一行都删掉,但不使用就地删除
# <=> df2 = df.dropna(axis=0,inplace=False,how='any')
df1.info()   #处理后df1已经删除了有空值的行
df.info()    #由于inplace=False,所以df不变,后续继续处理和运算使用df1

df = pd.read_csv("spider.csv", header=None)
#display(df.head())

df1 = df.dropna(axis=0,inplace=False,thresh=4) #4个及以上字段不为空,则保留,否则删除
print(df.info())  
print(df1.shape)
print(df1.info())

 

填充缺失值

df.fillna(value / method ='ffill' [,limit=n] / method='bfill' [,limit=n] ,[inplace=True/False])

说明:

  • value:填充所使用的值。固定值填充,支持直接输入值,支持字典,支持Series,需要注意的是字典的key值,Series的index要与df的列名称对应。
  • method:指定前值(上一个有效值)填充(pad / ffill),还是后值(下一个有效值)填充(backfill / bfill)。
  • limit:如果指定method,表示最大连续NaN的填充数量,如果没有指定method,则表示最大的NaN填充数量。
  • inplace:指定是否就地修改,默认为False。

这些参数都很常用,联合起来控制进行替换的规则

df = pd.read_csv("spider.csv", header=None)
display(df.head())
df_mean = round(df.mean(),2)  #计算每列的均值
display(df_mean,type(df_mean))
#固定值填充,支持直接输入值,支持字典,支持Series,
#需要注意的是字典的key值,Series的index要与df的列名称对应
df1 = df.fillna(df_mean) 
display(df1.head())

df = pd.read_csv("spider.csv", header=None)
display(df[18:22])
#向下填充
df1 = df.fillna(method="ffill",inplace=False)
display(df1[18:22])
#向上填充
df2 = df.fillna(method='bfill',inplace=False)
display(df2[18:22])

# limit参数。如果指定method,则表示最多连续填充。
# 如果没有指定method,则表示总共填充。
df3 = df.fillna(method="bfill", limit=1)
display(df3[18:22])

2.2、重复值处理

在处理数据中,可能会出现重复的数据,我们通常需要将重复的记录删除。

df = pd.read_csv("spider.csv", header=None)   #导入数据
display(df.head())

发现重复值

我们可以通过duplicated方法来发现重复值。该方法返回Series类型的对象,值为布尔类型,表示是否与上一行重复。

r = df[df.duplicated( [subset=[0,1,...],keep='first'/'last'/False)] )]

display(r.sort_values([1,0,...], axis=0))

参数说明:

  • subset:指定依据哪些列判断是否重复,默认为所有列。
  • keep:指定记录被标记为重复(True)的规则。默认为first。

#默认不加参数的情况

# 检测重复值,, 这个函数的作用个,就是把每一行都映射成一个True或者false的结果
# 如果结果为True 表示: 这一行跟某一行重复了。默认全部字段重复才算重复
display(df.duplicated().sample(5))
display(df[df.duplicated()])  #这样输出的只是判断为True行

   

#指定标记重复记录的规则

# 如果需要查看所有重复的记录,可以使用keep参数。
# keep:
    # first:前面的记录标记为True。
    # last: 后面的记录标记为True。
    # False:所有记录都标记为True。
# 假如 1  3 4  这三行重复了
#    first    last      false
# 1   true    false     true
# 2   true    true      true
# 3  false    true      true
result = df[df.duplicated(keep=False)]
display(result.sort_values([2,0], axis=0)) #排序,按照第2列,第0列排序

#自定义判断重复的规则

# 可以使用subset参数来指定重复的规则。默认为所有列一致才认为是重复的。
# 规则改为:只要第0,与第1列相同,则认为是重复的。
r = df[df.duplicated(subset=[0,1],keep=False)]  #自定义用于判断重复的列
display(r.sort_values([1,0], axis=0))

删除重复值

通过drop_duplicates可以删除重复值。

df.drop_duplicates([subset=[0,1,...], keep='first'/'last', inplace=Ture/False])

参数说明:

  • subset:指定依据哪些列判断是否重复,默认为所有列。
  • keep:指定记录删除(或保留)的规则。默认为first,所以一般不要再自行指定keep啦。
  • inplace:指定是否就地修改,默认为False。
  • #如果想了解关于当前这个方法的更多参数。怎么办?
    df.drop_duplicates
    # 按组合键:shift + tab 建   4次

df = pd.read_csv("spider.csv", header=None)
display(df.shape)   #导入数据

r = df[df.duplicated(subset=[0,1,2],keep='first')]
display(r.shape)   #获取重复值
df1 = df.drop_duplicates(subset=[0,1,2],keep='first',inplace=False)
display(df1.shape)  #删除重复制后的结果

#结果:(1396, 5)       (18, 5)         (1378, 5)

2.3、无效值处理

检测无效值

可以通过DataFrame对象的describe方法查看数据的统计信息。不同类型的列,统计信息也不太相同。

无效值,跟缺失值的意思大致差不多,但是包含的范围更广泛 如:男 女 男 女 0

(1)、获取统计信息

df.describe() 获取数值列统计信息、df[[0,1,2]].describe()  获取指定的非数值列统计信息、

df.shape 获取行列数量 、df.info() 获取缺失值信息

df = pd.read_csv("spider.csv", header=None)
display(df.head())

display(df.describe()) #如果DataFrame当中存在数值列,则describe值显示数值列。
# describe()默认的规则:只针对数值列进行统计  describe() 描述   把数据的整体情况进行汇总汇报
# 数值列的统计与非数值列的统计,结果不同。
display(df[[0, 1, 2]].describe()) # 数值列 和 非数值列  的统计结果不一样!
display(df.shape) #行列个数
display(df.info()) #查看各列元素缺失值情况

  

(2)、根据统计信息,先处理缺失值(删除dropna,填充fillna)

(3)、处理重复值

  • r = df[df.duplicated( [subset=[0,1,...],keep='first'/'last'/False)] )]
  • display(r.sort_values([1,0,...], axis=0))
  • df1=df.drop_duplicates([subset=[0,1,...], keep='first'/'last', inplace=Ture/False])

(4)、处理其他异常值

三、数据过滤

可以使用布尔数组或者索引数组的方式来过滤数据。df[condition]
另外,也可以用DataFrame类的query方法来进行数据过滤 ,df.query()。在query方法中也可以使用外面定义的变量,需要在变量前加上@。

df = pd.read_csv("spider.csv", header=None,encoding='utf-8')
display(df.head())


# 数据过滤通常的方式:
# 通过判断条件生成一个布尔类型的数组,然后,DataFrame使用该布尔数组进行行过滤。
df[0] = pd.to_datetime(df[0],errors='coerce')
condition1 = df[0] < "2015-01-01"
condition2 = df[0].isnull()
display(df[condition1],df[condition2])

# 过滤的第二种方式。
df = pd.read_csv("spider.csv", header=None,encoding='utf-8')
df[0] = pd.to_datetime(df[0],errors='coerce')
df.columns = ["date", "url", "name", "num1", "num2"]
display(df.query("date < '2015-01-01'"))
display(df.query("date == 'NaT'"))

# 如果在query方法中,需要使用外面定义的变量,可以在变量名称前加上@符号,进行引用。
df = pd.read_csv("spider.csv", header=None,encoding='utf-8')
df.columns = ["date", "url", "name", "num1", "num2"]
df["date"] = pd.to_datetime(df["date"],errors='coerce')
s = ["2014-08-24 00:00:00","2014-04-02 05:10:26","NaT"]
display(df.query("date in @s"))

四、数据转换(映射、替换、字符串矢量级运算)

4.1、应用 apply、map、applymap映射转换

Series与DataFrame对象可以进行行(列)或元素级别的映射转换操作。

  • 对于Series,可以调用apply或map方法。
  • 对于DataFrame,可以调用apply或applymap方法。
  • map:对当前Series的值进行映射转换。参数可以是一个Series,一个字典或者是一个函数。
  • apply:通过函数实现映射转换。【Series传递元素,DataFrame传递行或列。】
  • applymap:通过函数实现元素级的映射转换。

案例:

Series的s.apply(function)转换

#对于Series,依次传递每一个元素。函数的参数只能一个,函数的返回值,用来表示处理的结果。
def fx(x):   #定义一个函数
    return 2*x+3
s = pd.Series([0,2,4])
s1 = s.apply(fx) #将Series中的每一个元素应用到函数中
display(s1)

# 对于非常简单的函数,我们可以使用lambda来实现。
s2 = s.apply(lambda x: x * 2 + 3)  #应用于匿名函数
display(s2)

   

Series的s.map(map_series)

# 参数可以是Series,则根据Series的index来进行映射,获取结果值。
s = pd.Series([2, 3, 4])
map_series = pd.Series(["a", "b", "c", "e"], index=[1,2,3,4])
s1 = s.map(map_series)
display(s1)

关于DataFrame的apply与applymap方法。参数x是就是一列

DataFrame调用apply,列级别的转换,df[[0, 1,...]].apply(function)

df = pd.read_csv(r"DataFrame.txt",header=0,encoding="utf-8")
display(df)
# 自行求均值。
df2 = df[["Sales", "price"]].apply(lambda x: x.mean())
display(df2)

def fx(x):
    return x.mean()
df3 = df[["Sales", "price"]].apply(fx)
display(df3)

DtaFrame调用applymap:通过函数实现元素级的映射转换。

# 参数为一个函数,DataFrame中的每个元素都会调用一次该函数(将元素传递给该函数),获得一个映射的结果(函数的返回值)。
# applymap函数是一个元素级的映射,类似与Series的map函数。
df = pd.read_csv(r"DataFrame.txt",header=0,encoding="utf-8")
display(df)
df1 = df[["Sales", "price"]].applymap(lambda x: x + 1000)
def fx(x):
    y = x+1000
    return y
df2 = df[["Sales", "price"]].applymap(fx)  # <=> df[["Sales", "price"]].apply(fx)
display(df1,df2)  #df1 == df2

4.2、替换

Series或DataFrame可以通过replace方法可以实现元素值的替换操作。

  • to_replace:被替换值,支持单一值,列表,字典,正则表达式。
  • regex:是否使用正则表达式,默认为False。

df = pd.read_csv(r"DataFrame.txt",header=0,encoding="utf-8")
display(df)
# 替换。支持单个值,字典,列表,正则表达式,替换都是拷贝后的替换,原来的df不变
df1 = df.replace("--", "xxx")  #单个值的替换
df2 = df.fillna(df["x"].mean()) #对于空值往往使用df.fillna()填充
df3 = df.replace({"500":"xxx","--":"xxx"})  # 字典 替换
df4 = df.replace(['500','--'],['aaa','bbb']) #列表对应替换
df5 = df.replace(['500','--'],'aaa')        #列表替换为同一个值
df6 = df.replace('a(.*)', "aa", regex=True) #正则表达式替换
# replace的操作我们也可以通过apply或map来实现。
def fx(item):
    if item >= 50:
        Z = "合格"
    else:
        Z = "不合格"
    return Z
# replace的操作我们也可以通过apply、map、applymap来实现。
df["Z"] = df["x"].map(fx)
display(df1,df2,df3,df4,df5,df6,df)

  

4.3、字符串矢量级运算

Series含有一个str属性,通过str能够进行字符串的矢量级运算。

使用Series的str属性时,需要Series元素的值是str(字符串)类型s.astype("str")。

函数表
矢量化字符串方法
No 方法 RE 说明
0 使用方式:s.str.cat()   返回序列索引或其他
1 capitalize() no 字符串转大写
2 cat([others, sep, na_rep, join]) no 用分隔符连接字符串;返回str或原对象构架
3 center(width[, fillchar]) no 用附加字符填充字符串的左侧和右侧
4 contains(‘'is') yes 判断字符串中是否包含子串true; pat str或正则表达式
5 count(pat[, flags]) yes 子串出现次数
6 decode(encoding[, errors]) no 字节解码
7 encode(encoding[, errors]) no 字符串编码
8 endswith(pat[, na]) yes 字符串结尾是否是特定子串 true
9 extract(pat[, flags, expand]) yes 从正则表达式pat中提取第一个匹配字符;结果为1个字符
10 extractall(pat[, flags]) yes 从正则表达式pat提取所有匹配的,返回组
11 find(sub[, start, end]) no 查子串首索引,子串包含在[start:end];无返回-1
12 findall(pat[, flags]) yes 查找所有符合正则表达式的字符,以数组形式返回
13 get(i) no 从指定位置提取字符
14 get_dummies([sep]) no 用sep拆分每个字符串,返回一个虚拟/指示dataFrame
15 index(sub[, start, end]) no 子串最低索引,子串范围[start:end];无抛异常ValueError
16 isalnum() no 检查所有字符是否为字母数字
17 isalpha() no 检查是否都是字母
18 isdecimal() no 检查是否都是十进制
19 isdigit() no 检查是否都是数字
20 islower() no 检查是否都是小写
21 isnumeric() no 检查是否都是数字
22 isspace() no 检查是否都是空格
23 istitle() no 检查是否都是标题
24 isupper() no 检查是否都是大写
25 join(sep) no 用分隔符连接所有字符;同一级有非str返回Na
26 len() no 计算每个字符串的长度
27 ljust(width[, fillchar]) no 使用附加字符填充字符串的右侧
28 lower() no 字符串转小写
29 lstrip([to_strip]) no 左侧删除空格(包括换行符)或其他str
30 match(pat[, case, flags, na, …]) yes 确定每个字符串是否与正则表达式匹配。
31 normalize(form) no 返回字符串的Unicode普通表单
32 pad(width[, side, fillchar]) no 指定左或右填充字符补齐字符串
33 partition([pat, expand]) no 分隔符拆分为3部分,分隔符左,分隔符,分隔符右
34 repeat(repeats) no 重复每个元素指定的次数
35 replace(pat,b) yes 将值pat替换为值b。
36 rfind(sub[, start, end]) no 右边查找子串索引,子串包含在[start:end];无返回-1
37 rindex(sub[, start, end]) no 返回子串的最高索引,子串范围[start:end];无抛出异常
38 rjust(width[, fillchar]) no 使用附加字符填充字符串的左侧
39 rpartition([pat, expand]) no 右拆分成3部分含分隔符
40 rsplit([pat, n, expand]) yes 分隔符字符串右边拆分字符串
41 rstrip([to_strip]) no 右侧删除空格(包括换行符)
42 slice([start, stop, step]) no 切片截取字符串
43 slice_replace([start, stop, repl]) no 用另一个值替换字符串的位置切片;比较复杂看实例
44 split([pat=None, n, expand]) yes 按分隔符或子串拆分字符串
45 startswith('st') yes 字符串开头是否匹配子串True
46 strip('') no 删除字符串左右空白(包括换行符)或删除其他左右字符串
47 swapcase() no 变换字母大小写
48 title() no 字符串转标题
49 translate(table[, deletechars]) no 通过映射表映射字符串中的所有字符
50 upper() no 字符串转大写
51 wrap(width, **kwargs) no 长字符串换行,#结果插入换行符\n
52 zfill(width) no 用0填充字符串的左侧
说明:类似Python中str忽略Na;适用于Series / Index

列举几个常用案例:

s = pd.Series(["10 1 ",1001,10001])
s = s.astype("str")   #先转换为字符串类型
len_s = s.str.len()   #计算每个元素的长度
isdigit_s = s.str.isdigit() #查看每个元素是否只包含数字,返回True/False
cut_s = s.str[0:2]   #切片截取字符串
split_s = s.str.split(" ") #指定字符进行切割,返回列表
strip_s = s.str.strip(' ') #删除左右两端的空格,中间部分依然保留
replace_s = s.str.replace(" ","") #将指定的字符替换为其他字符,这里将空格替换删除
display(len_s,isdigit_s,cut_s,split_s,strip_s,replace_s)

五、数据合并

concat()

我们可以通过DataFrame或Series类型的concat方法,来进行连接操作,连接时,会根据索引进行对齐。

  • axis:指定连接轴,默认为0(上下)。【axis=0/1】
  • join:指定连接方式,默认为外连接。【join='outer':并集,join='inner':交集】
  • keys:可以用来区分不同的数据组。形成层级索引【这个稍微难理解一点】
  • join_axes:指定连接结果集中保留的索引。默认全部保留【如:join_axes=[df1.columns]】
  • ignore_index:忽略原来连接的索引,创建新的整数序列索引,默认为False。【ignore_index=True/False】
  • sort:concat之后,是否按照列索引排序,sort=True/False

df1=pd.DataFrame({"date":[2015,2016,2017,2018,2019],"x1":[2000,3000,5000,8000,10000],"x2":[np.nan,"d","d","c","c"]})
df2=pd.DataFrame({'date':[2017,2018,2019,2020],"y1":[1000,2000,3000,2000]})
# display(df1,df2)
df3=pd.concat([df1,df2],keys=["df1","df2"])
display(df3)
#索引层级索引元素时,先外再内
df3.loc["df2",3].loc["y1"]

append()

在对行进行连接时,也可以使用Series或DataFrame的append方法。

append是concat的简略形式,只不过只能在axis=0上进行合并

df1=pd.DataFrame({"date":[2015,2016,2017,2018,2019],"x1":[2000,3000,5000,8000,10000],"x2":[np.nan,"d","d","c","c"]})
df2=pd.DataFrame({'date':[2017,2018,2019,2020],"y1":[1000,2000,3000,2000]})
# display(df1,df2)
df3=df1.append(df2)
display(df3)

merge()

通过pandas或DataFrame的merge方法,可以进行两个DataFrame的连接,这种连接类似于SQL中对两张表进行的join连接。

  • how:指定连接方式。可以是inner, outer, left, right,默认为inner。
  • on 指定连接使用的列(该列必须同时出现在两个DataFrame中),默认使用两个DataFrame中的所有同名列进行连接。
  • left_on / right_on:指定左右DataFrame中连接所使用的列。
  • left_index / right_index:是否将左边(右边)DataFrame中的索引作为连接列,默认为False。
  • suffixes:当两个DataFrame列名相同时,指定每个列名的后缀(用来区分),默认为x与y。

df1=pd.DataFrame({"date":[2015,2016,2017,2018,2019],"x1":[2000,3000,5000,8000,10000],"x2":[np.nan,"d","d","c","c"]})
df2=pd.DataFrame({'date':[2017,2018,2019,2020],"y1":[1000,2000,3000,2000]})
# display(df1,df2)
df3=df1.merge(df2,how='left',on="date")
display(df3)

join()

与merge方法类似,但是默认使用索引进行连接。

  • how:指定连接方式。可以是inner, outer, left, right,默认为left。
  • on:设置当前DataFrame对象使用哪个列与参数对象的索引进行连接。
  • lsuffix / rsuffix:当两个DataFrame列名相同时,指定每个列名的后缀(用来区分),如果不指定,列名相同会产生错误。

join与merge类似,都是进行两张表的连接。

不同点:

  • merge默认进行的内连接(inner),join默认进行的左外连接(left)。
  • 当出现同名字段(列索引)时,merge可以自动补后缀(_x, _y),但是join不会自动补后缀,而是会产生错误。
  • merge默认使用同名的列进行等值连接。join默认使用左右两表的索引进行连接。
  • merge中on参数,指定两张表中共同的字段,而join中on参数,仅指定左表中的字段(右表依然使用索引)。

merge与join侧重点不同,merge侧重的是使用字段进行连接,而join侧重的是使用索引进行连接。

df1=pd.DataFrame({"date":[2015,2016,2017,2018,2019],"x1":[2000,3000,5000,8000,10000],"x2":[np.nan,"d","d","c","c"]})
df2=pd.DataFrame({'date':[2017,2018,2019,2020],"y1":[1000,2000,3000,2000]})
# display(df1,df2)
df3=df1.join(df2,how='left',lsuffix='_x',rsuffix='_y')#根据索引对齐
display(df3)


数据分析更多知识点关注博客,共同分享交流学习。

发布了103 篇原创文章 · 获赞 75 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_41685388/article/details/103841296