Python学习笔记(8-1):数据分析——数据读取
文章导读
- 课程难度:★★★☆☆
- 重要度:★★★☆☆
- 预计学习时间:1.5小时
- 简介:本节主要讲解了进行数据分析时读取数据文件、数据库等基本操作,包括:(1)在控制台和python编辑器下访问文件、路径的操作;(2)读取和保存csv、excel文件;(3)临时打开、读取文件的方法;
(4)连接、读取数据库;(5)读写JSON和pickle文件;(6)Dataframe数据的拼接、分组与聚合操作。
- 重点涉及的函数和内容:cd + 文件路径、pd.read_csv()、pd.to_csv()、cnxn.cursor()、crsr.fetchmany()、json.dumps()和json.loads()、json.dump()和json.load()、
josn不支持DataFrame数据的读写、pd.merge()、.join()、groupby()、.aggregrate()和.agg()。
前言
数据分析包括两个部分,一是做数据预处理,二是做具体的数据分析运算。首先讲数据预处理的前一个部分,即读取文件以及数据规整。这节讲数据的读取问题。读取文件部分包括读取txt、csv、excel,以及以access为例子讲解读取数据库文件。
一、文件路径操作
1、控制台下的基础文件访问
首先提一下我们在cmd下的路径变动的过程,一般也就用到两个:
(1)一个叫做dir
,输出当前目录下的所有文件:
dir
(2)另一个叫做cd
,访问一个新路径,访问当前路径下的下层文件夹直接cd + 文件夹名称
,访问上层文件夹用cd ..
cd + 文件路径
cd + 文件名称
cd..
在这里补充一个绝对路径和相对路径的概念。我们打开我们现在程序的路径,所在的这个文件夹就是我们当前的工作路径,一个完整的能访问到我们的程序的,是我们的绝对路径,例如:
# 一个斜杠表示转义字符,因此多加一个斜杠表示斜杠本身
'C:\\文件路径\\lesson8\\data_test.csv'
以当前的工作目录为起点,访问到我们程序的,是我们的相对路径:
'lesson8\\data_test.csv'
2、python下的文件路径操作
在python中,我们使用内置os
库进行文件路径操作,它的指令和我们在控制台下的一些指令比较相似:
import os
我们常用的是下面的几组工具:
(1)列举某个目录的所有文件
os.listdir() # 当前目录下的所有文件
os.listdir('FOLDER') # 访问该目录下某个文件夹的所有文件(夹)
os.listdir('..') # 访问当前目录上级目录的所有文件(夹)
(2)查看是否存在某个文件(夹)
os.path.exists(' FOLDER ') # 查看一个文件夹是否存在
os.path.exists('FILENAME') # 查看一个文件是否存在,需要带扩展名
(3)查看当前的工作目录
os.getcwd()
(4)变更工作目录至一个新目录
os.chdir('lesson8')
os.getcwd()
os.chdir('..')
os.getcwd()
(5)查看文件的各种路径
# 查看文件的绝对路径
os.path.abspath('lesson8\\data_test.csv')
# 文件所在目录的路径
os.path.dirname('C:\\文件路径\\lesson8\\data_test.csv')
# 查看文件名
os.path.basename('C:\\文件路径\\lesson8\\data_test.csv')
# 以元组形式分离目录和文件名
os.path.split('C:\\文件路径\\lesson8\\data_test.csv')
# 查看文件路径(包括目录)和扩展名
os.path.splitext('C:\\文件路径\\lesson8\\data_test.csv')
上面这些函数的应用场景主要例如,把所有待读取的一组数据全部存在一个文件夹里,通过listdir获取所有的文件,再一一地读取。还有例如访问一个文件,如果存在就读取并向其中追加,如果不存在就重新创建一个新的写入,等等。
二、读取和保存csv、excel文件
1、csv
读取:pd.read_csv()
读取一个csv文件,最常用的方式是利用pandas的read_csv()
函数,在数据读取之后产出的是DataFrame。带有常用参数的语法格式如下:
pd.read_csv(filepath,sep,encoding,header)
常用的参数如下:
1、filepath: 包含路径的文件对象,以字符串格式填写
2、sep: 字符串,指示分隔符的类型,默认为英文逗号(,)。特殊情况下,如果数据分析符号含多个,例如,多列之间通过“|+|”(三个符号)分隔的,分隔符sep的值可以设置为"\|\+\|",这是python的正则表达式语法。
3、encoding:用来指定编码类型。在读取纯英文时常用utf-8,而读取带中文的数据时一般使用gbk
4、header:它用来指示哪一行作为Dataframe的列索引。header=0,表示文件第0行(即第一行,索引从0开始)为列索引,这样加names会替换原来的列索引。header=None,即指明原始文件数据没有列索引,这样read_csv为自动加上列索引。
5、index_col:指定数据中哪一列作为Dataframe的行索引,也可以可指定多列,形成层次索引,默认为None,即不指定行索引,这样系统会自动加上行索引。index=0表示将文件第0行(即第一行,索引从0开始)为列索引,这样加names会替换原来的列索引。
具体使用示例如下:
import pandas as pd
csvfilename = 'lesson8\\data_test.csv'
csvfile = pd.read_csv(csvfilename, encoding = 'utf-8')
保存:pd.to_csv()
将数据保存成一个新文件,用.to_csv()
函数。含常用参数的语法格式如下:
pd.to_csv(data, encoding, index, columns)
常用的参数如下:
1、data: 要保存的数据
2、Index:将它设置为0(或者False)表示不写入这个dataframe的索引。
3、columns:指示将哪些列写入这个csv内,不过一般而言我们会新建一个有且只有待写入列的dataframe然后将之完整写入。
示例如下:
csvfilename_new = 'lesson8\\data_test_new.csv'
csvfile.to_csv(csvfilename_new, encoding = 'utf-8', index = 0, columns = csvfile.columns[:-5])
2、excel
读取:pd.read_excel()
这个函数在数据读取之后产出的也是DataFrame,带有常用参数的语法格式如下:
pd.resd_excel(io,sheet_name,header)
常用的参数如下:
1、io:字符串。包含文件路径的excel对象
2、sheet_name:指定了待读取的表。如果为数字,则按照表的顺序访问;为字符串,则按表名访问;如果传递一个列表,则读取列表中提到的所有表;如果为None,则表示读取所有表。读取多张表时返回的是表名-内容的键值对。这个参数的默认值为0,即读取第一张表。
3、header:整数或由整数构成的列表,默认为0。它指示由哪一行作为数据框的列名,默认0代表第1行。如果设置为整数列表,那么将使用多行信息组成联合索引。如果设置为None,则表示excel没有列表信息。
4、index_col:它指示哪一列作为index值(行索引)
其中,参数header
和index
使用方式相似。具体示例如下:
首先是读取整个excel文件:
excelfilename = 'lesson8\\data_test.xlsx'
excelfile = pd.read_excel(excelfilename, encoding = 'gbk', sheetname = None)
excelfile
按顺序读取excel文件中第0、1张表,按下标读取时,返回的表用下标访问:
excelfile = pd.read_excel(excelfilename, encoding = 'gbk', sheetname = [0, 1])
excelfile[1]
读名称为Sheet1、Sheet2两张表,按表名读取,返回的表用表名访问,sheetname
参数为None
时也用表名访问各表:
excelfile = pd.read_excel(excelfilename, encoding = 'gbk', sheetname = ['Sheet1', 'Sheet2'])
# excelfile = pd.read_excel(excelfilename, encoding = 'gbk', sheetname = None)
frame1 = excelfile['Sheet1']
frame2 = excelfile['Sheet2']
保存:pd.to_excel()
这个方法与to_csv()
方式的主要区别也是参数sheet_name
,它指示被写入的表名,如果没有给名字,它的表名就是’Sheet1’:我们尝试调用这个函数,先后将frame1和frame2保存:
excelfilename_new = 'lesson8\\data_test_new.xlsx'
frame1.to_excel(excelfilename_new, sheet_name = 'frame1', index = False)
frame2.to_excel(excelfilename_new, sheet_name = 'frame2', index = False)
但是,直接用to_excel
先后将两个dataframe写入一个表,后写的会直接覆盖原有的表。如果希望在一个Excel内写入两个DataFrame,可以利用文件路径创建一个pd.ExcelWriter
对象。此时,.to_excel()
函数不再传入保存的文件路径,而是传入这个pd.ExcelWriter
对象,可以将多个frame
写入一个文件:
writer = pd.ExcelWriter('lesson8\\data_test_new.xlsx')
frame1.to_excel(writer, sheet_name = 'frame1', index = False)
frame2.to_excel(writer, sheet_name = 'frame2', index = False)
若希望对excel执行更为精细的操作,可利用xlrd和xlwt[ 这个包将可以单独访问和处理每一个单元格(cell),同时也很好地维护了各个单元格的属性(ctype
属性),例如ctype
: 0 为empty
,1为string
,2为number
,3为date
,4为boolean
,5为error
。
三、文件读取操作
1、打开文件:.open()
首先,我们先讲利用系统中打开文件的功能,即open()
函数。我们先给出待打开文件的文件路径,将这个路径传递给open()
函数即可:
filepath = 'lesson8\\data_test.csv'
open(filepath)
返回结果如下:
<_io.TextIOWrapper name='lesson8\\data_test.csv' mode='r' encoding='cp936'>
现在这个文件已经打开了,返回的结果就是一个文件的读取结果。我们可以将返回结果赋给一个变量,然后借助循环依次读取文件的每一行内容。例如利用如下的代码将每一行分别保存在列表中,并在最后输出第一行:
file = open(filepath)
filecontents = []
for i in file:
filecontents.append(i)
filecontents[0]
输出结果如下:
'TOOL,210X1,210X2,210X3,210X4,210X5,210X6,210X7,210X8,210X9,210X10,210X11,210X12,210X13,210X14,Value\n'
对于利用open()
打开文件,这里有一重要的参数,即打开方式的参数mode
,例如只读、只写、读写模式等。在默认情况下的文件读取方式是'r'
,即只读模式。主要支持的的一些模式包括:
(1)r和r+,只读和读写模式,r只可读,r+既可读又可写,会在文件之后进行追加,对二者而言,原文件不存在则报错。
(2)w和w+,只写和读写模式,w只可写,w+既可读又可写,两者都会清除原有文件并重新创建(这是w+和r+的区别)。
(3)x和x+,也是只写和读写模式,与w不同的地方是若存在原有文件,x模式下不是清除而是报错。
(4)a和a+,只写和可读写,两者若文档不存在则创建,若存在文档,则在文档之后进行追加,原有内容保留。
上述参数都以字符串传入,每个都可以附加一个'b'
字符,写成例如'rb'
、'w+b'
或'ab+'
等组合,加入'b'
字符用来告诉open()
函数打开的文件为二进制文件,而非纯文本文件。
另外,pd.read_csv()
文件不仅可以接收待读取csv文件的文件路径,也可以接收一个已经被open()
函数打开的文件对象。对于有些csv文件,pandas在直接通过文件名读取时可能会报无法初始化文件的错误,这时先将这个文件用open()
打开,再将这个文件对象传递给pd.read_csv()
,一般可以解决问题。例如如下的代码:
file = open('lesson8\\data_test.csv')
csvfile = pd.read_csv(file, encoding = 'utf-8')
作为参考,利用pandas直接从文件路径读取csv的方式如下:
csvfilename = 'lesson8\\data_test.csv'
csvfile = pd.read_csv(csvfilename, encoding = 'utf-8')
2、with语句
在打开文件的时候,我们经常会应用到with
语句,在这里将之补充。我们就以刚刚的读取test.txt的过程为例讲解with的用法。对于刚刚读取test文件的过程,我们利用with语句可以写成如下形式:
filepath = 'lesson8\\test.txt'
filecontents = []
with open(filepath, 'r+') as file: # with...as语句读取了filepath作为file
for i in file:
filecontents.append(i)
filecontents
输出结果如下:
['1、Python的安装与编译环境\n',
'2、基础语法\n',
'3、内置数据结构\n',
'4、Numpy数据结构\n',
'5、Pandas数据结构\n',
'6、若干细节知识\n',
'7、若干高级功能\n',
'8、数据读取与规整\n',
'9、数据运算与统计\n',
'10、机器学习初涉\n',
'11、绘图工具\n']
上述代码块中with...as
语句实现了之前的读取功能。with语句的具体机制是,紧跟with后面的语句(例如这里的open()
)要求有__enter__()
方法,语句执行完毕后,返回对象的__enter__()
方法被调用,返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的 __exit__()
方法。
通过__enter__()
和__exit__()
的定义,就可以实现更为高效顺畅的清理资源,关闭文件等等操作。因为此特性,with语句常常被用在打开文件的场合
3、游标操作与文件读写
接下来,我们描述文件的读取机制,并讲解操作文件的.read()
、.seek()
和.tell()
以及写入的.write()
方法。在调用open函数之后会形成一个文件对象,当对这个文件对象做访问时,实际上有一个游标,或者说叫做句柄,指示了当前访问的位置。在这里有一个需要提示的地方,就是我们每做一次读取,游标都会移动。因此为了实现一些程序目的,有时需要人为地手动将游标重置。在读取文件中,有三个重要的函数与这个有所联系:
(1).read()
函数,是读取这个文件的内容,它需要传递一个数字,这个数字是将要读取的文件长度(以字符而非行为单位),读取的起点就是游标位置。
具体示例如下,我们连续读取三次,每次读取10个字符,这时候游标在每次读取之后都会移动:
filepath = 'lesson8\\test.txt' # 如果程序之前中断了,就重新从这里开始读取文件
file = open(filepath, 'r+')
file.read(10)
file.read(10)
file.read(10)
读取的结果如下:
'1、Python的安' # 在这里,英文、中文、转义字符
'装与编译环境\n2、基' # 每一个都占用1字符
'础语法\n3、内置数据'
(2).tell()
函数,用来是获取当前的游标位置。
例如刚刚连续三次通过read(10)读取文件之后,立即访问这个文件的当前游标位置:
file.tell()
输出结果如下。可以看到,当前游标的位置并不为30,它的返回值是基于当前编码格式,我们需要对游标之前的字符串进行编码所需的字节数。换言之,在这里.tell()
函数的返回值是对于已访问的30个字符,它编码所需的字节的总长度。
52
(3).seek()
函数,则是手动将原来的游标移动到指定新位置。
接下来,我们可以将游标重新设定在某个值上。但是由于字符串的游标是基于字节的,所以对于一些编码格式,.seek()
确定的游标位置未必会使.read()
成功返回我们想要的结果。有时候,基于游标的切开可能会将原有的编码切断,使编码不完整。
filepath = 'lesson8\\test.txt' # 如果程序之前中断了,就重新从这里开始读取文件
file = open(filepath, 'r+')
file.seek(3)
file.read(10)
file.seek(13)
file.read(10)
输出结果如下:
'Python的安装与'
'装与编译环境\n2、基'
如下两个游标设置就会报错:
file.seek(2)
file.read(10) # 报错1,无法解析对应元素
file.seek(14)
file.read(10) # 报错2,无法解析对应元素
【报错1】UnicodeDecodeError: 'gbk' codec can't decode byte 0xa2 in position 0: illegal multibyte sequence
【报错2】UnicodeDecodeError: 'gbk' codec can't decode byte 0xb3 in position 10: illegal multibyte sequence
因此,在利用.seek()
时,有时我们可能需要通过在访问的过程中用一些方法(如.tell()
)先找到所需的下标,之后再将游标移到这些下标。
4、关闭文件
所有文件在应执行的操作全部执行完毕之后,都应当关闭。关闭一个文件用.close()
函数。与此同时,还有一个.flush()
函数,它用来刷新缓冲区,即将缓冲区中的数据立刻写入文件,同时清空缓冲区。一般情况下,文件关闭后会自动刷新缓冲区,但我们可以用flush()
方法手动刷新缓冲区。
我们手动将缓冲区刷新一次,并关闭文件。在文件关闭之后,我们可以用closed
属性确认文件已经关闭:
file.flush()
file.close()
file.closed
True
再次强调,文件在处理完毕时需要将之关闭,如果不关闭就执行其他操作,可能导致一些重要问题。
四、读取数据库
这里我们利用pyodbc
库实现。作为例子,本节读取本地的一个access数据库,将读取结果放入一个pandas dataframe中,最终保存成一个csv文件。
import pyodbc
1、检查机器能支持数据库文件读取的类型
在读取数据库前,需要检查一下当前这台机器能支持哪些数据库文件的读取,可以利用pyodbc.drivers()
函数来查看:
pyodbc.drivers()
输出结果如下。当我们看见有Microsoft Access Driver (*.mdb, *.accdb)时,我们就知道我们能读mdb和accdb文件了。一般来说装了较新版本的office access之后,这一项就会有。
['SQL Server',
'Microsoft Access Driver (*.mdb, *.accdb)',
'Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)',
'Microsoft Access Text Driver (*.txt, *.csv)',
'SQL Server Native Client 11.0']
2、连接数据库
接下来,我们找到我们需要的这个数据库驱动,结合我们待读取的数据库的路径,构建数据库连接语句conn_str
。连接数据库语句分别需要指定数据库的驱动和读取数据库的文件位置。在构件好连接语句后,我们可以用pyodbc.connect()
函数建立一个数据库的连接。
建立一个数据库连接的实例代码如下:
import os # 需要通过os库访问当前的文件路径
# 以构建完整的字符串连接语句
filename = 'Wellstar_2016_01_07.mdb'
conn_str = (r'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)}; \
DBQ= ' + os.getcwd() + '\\lesson8\\' + filename)
cnxn = pyodbc.connect(conn_str)
3、读取数据库
读取数据库的过程就好像我们刚刚所说的读文件一样,是基于游标的。
(1)首先,我们定义一个游标,之后基于这个游标执行相应的SQL语句。这里的SQL语句就是只执行查询全表数据,TIMEDATA
是数据库中的数据表名称。当然,如果需要执行其他的操作,传入的SQL语句和我们操作数据库时编写的一样。
crsr = cnxn.cursor()
crsr = crsr.execute('SELECT * FROM ' + 'TIMEDATA')
(2)接下来,我们就用当前建立的游标去获取这个数据,就和读取文件的.read()
一样,有一组fetch
函数。因为数据量过大,在此便不输出查询的结果:
crsr.fetchone() # 自游标位置开始读取一行
crsr.fetchmany(3) # 自游标位置开始读取一定数目行
crsr.fetchall() # 自游标位置开始读取剩余所有行
如果我们需要去找列名称,可以在fetch
的结果的属性中找。但是在fetch
之后游标位置改变了,因为之前执行过fetchall()
,所以整个数据库已经取不出结果了。因此我们需要重新建立这个游标:
crsr = cnxn.cursor()
crsr = crsr.execute('SELECT * FROM ' + 'TIMEDATA')
(3)获取所有列名和值
在获取一行之后,通过访问结果的cursor_description
属性,便可获得各个列的全部属性信息,第一个值就是列名,因此我们可以写一个循环(或是推导式)获取所有列名:
row = crsr.fetchone()
row.cursor_description
rows = crsr.fetchmany(10)
rows[0].cursor_description # 访问下标为0的元素来获取配套属性
在了解了上述知识之后,我们便可以将我们的知识进行综合运用,访问整个数据库的所有列名和所有值。在这里就直接用列表推导式了:
crsr = cnxn.cursor()
crsr = crsr.execute('SELECT * FROM ' + 'TIMEDATA')
row_all = crsr.fetchall()
colname = [colinfo[0] for colinfo in row_all[0].cursor_description] # 找到数据库列名
colvalue = [list(row) for row in row_all]
(4)利用读取的列名和值建立dataframe
由于原数据库的名字是’Wellstar_2016_01_07.mdb’,这里要把扩展名改成csv。这里我们直接使用split
函数,面对例如带.
在文件名的更复杂的情况,我们可以用第7章所讲的reduce()
函数处理:
import pandas as pd
frame = pd.DataFrame(colvalue, columns = colname)
frame.to_csv( os.getcwd() + '\\lesson8\\' + filename.split('.')[0] + '.csv', index = 0)
(5)提交语句并关闭数据库
在访问一个数据库的最后,我们提交我们所有的语句,并关闭数据库,关闭的是connect()
建立的连接。只有提交了数据库才能更新(插入、删除等记录真正执行),而数据库操作完不关闭就会发生和文件不关闭一样的错误。
cnxn.commit()
cnxn.close()
五、读写JSON和pickle文件
1、 json文件读写
读写json(JavaScript Object Notation)文件相比我们读txt,方便的在于它虽然是以字符串保存,但是在读取时能够转换成python数据类型。
import json
(1)json.dumps()函数
利用json.dumps()
函数,我们可以将这个字典变成一个json字符串。它的反操作是json.loads()
函数,将json文件读取出来成为字典。但是这样转换并没有直接持久化到文件中。
具体示例如下:
scores = {
'data' : [35, 20, 24, 10, 15, 31, 40, 40, 20, 10],
'base' : 18,
'max' : 40,
'min' : 10,
'date' : '10-15'
}
str_scores = json.dumps(scores)
scores = json.loads(str_scores)
(2)json.dump()和json.load()
这对函数是处理json文件写入和读取的核心方法。利用json.dump()
,我们将现在的数据存储进入json文件里。我们首先建立一个文件,用json.dump()
将这段json数据写入,接着将文件关闭。
filename = 'lesson8\\scores_json.json'
file = open(filename, 'w') # 注意这里用的模式是w,即清除掉原有文件
json.dump(scores, file) # 写入时,传递待写入的数据以及文件对象
file.close()
当想重新使用这份数据时,便基于这个文件路径重新访问这个文件,利用.load()
函数进行读取即可。如此,我们便将之前存下的字典文件重新读取了出来:
file = open(filename,'r')
scores_read = json.load(file, encoding='utf-8')
file.close()
2、pickle文件读写
json文件可以用记事本打开,存储的也是字符串,但是它无法保存非python内置的数据类型。例如DataFrame或Series,json是无法保存的。这时候我们可以用另一个方法,即此处的pickle。
import pickle
scores = {
'data' : [35, 20, 24, 10, 15, 31, 40, 40, 20, 10],
'base' : 18,
'max' : 40,
'min' : 10,
'date' : '10-15'
}
仍然利用pickle.dumps()
和pickle.loads()
函数,我们将数据在原数据结构和二进制文本之间进行转换。
str_scores = pickle.dumps(scores)
scores = pickle.loads(str_scores)
同样,利用pickle.dump()
和pickle.load()
函数将数据以本地化文件保存和读取本地化的二进制文件。但是这里要仔细注意的一点是,此时保存的不是普通的字符串,而是二进制的文本。因此操作pickle写入文档的时候,要用二进制方式进行文本的写入和读取:
利用pickle.dump()
实现文件的写入(二进制方式):
filename = 'lesson8\\scores_pickle.pkl'
file = open(filename, 'wb')
pickle.dump(scores, file)
file.close()
在这个二进制文件保存之后,可以利用pickle.load()
进行读取(二进制方式):
file = open(filename,'rb')
scores_read = pickle.load(file, encoding='utf-8')
file.close()
3、补充:json和pickle的区别
json写的是字符串,能够支持在多种编程语言的数据传输,可以将通过json将现有的数据直接迁移到其他语言当中去处理。但是因此,它只能支持python基本类型的数据存储(例如列表、元组、字典等),例如DataFrame就是写不进去json的。
pickle写的则是二进制文件,则仅能支持python语言写入和读取,作为python对象的持久化或者python程序间进行互相传输对象。但反过来,它能够支持所有类型对象(例如numpy数组、DataFrame、各种函数,等等)的序列化,pickle反序列化后的对象与原对象是等值的副本对象。
六、数据库风格的dataframe操作
接下来,我们在此对DataFrame的拼接和聚合方式做一个扩展。
我们之前讲过DataFrame基础的一些访问和拼接方式,那时的拼接是根据索引的。而在此,则讲解按照数据库风格对DataFrame进行相似操作的方法。这里将会提到的是数据库风格的连接(merge
、join
)和聚合(groupby
)两种操作。
1、拼接函数:pd.merge()、.join()
之前讲过的pd.concat()
,它是基于行、列索引值进行的连接。例如纵向拼接时,可选连接方式参数join
为inner
或outer
,仅保存共有的索引值,或是使各自索引值上缺失的列置空。
pd.concat([frame1, frame2], axis = 1, join = 'inner')
(1)merge()
它用于关联并匹配2个数据框,语法格式如下:
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False, validate=None)
常用的参数如下:
1、left: 左侧拼接的DataFrame对象
2、right: 右侧拼接的DataFrame对象
3、on: 用于指定连接的列。 必须同时在左侧和右侧DataFrame中。
4、left_on:当两个DataFrame的连接列名不同时,可以用left_on和right_on分别指定左侧和右侧DataFrame的用于连接的列。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
5、right_on: 指定右侧DataFrame的用于连接的列。与left_on相似。
6、left_index: 如果为True,则使用左侧DataFrame中的索引(行标签)作为其连接键
7、right_index: 与left_index功能相似
8、how: 可选 ‘left’, ‘right’, ‘outer’, ‘inner’. 默认inner。**inner是取交集,outer取并集**
9、sort: 按字典顺序通过连接键对结果DataFrame进行排序。 默认为True,设置为False将在很多情况下显着提高性能。
10、suffixes:指示两个dataframe因为拼接产生的同名重复列各自的列名尾缀,默认是(_x, _y)。当前对于两个用于做连接的列,在拼接后都予以保留。
11、indicator:将一列添加到名为_merge的输出DataFrame,其中包含有关每行源的信息。 _merge是分类类型,并且对于其合并键仅出现在“左”DataFrame中的观察值,取得值为left_only,对于其合并键仅出现在“右”DataFrame中的观察值为right_only,并且如果在两者中都找到观察点的合并键,则为left_only。
(2)join()
join
函数是DataFrame的成员方法,功能是按照索引合并,类似于concat
。但是它要求左、右两个DataFrame没有重复的列,如果有则需要补充lsuffix
或rsuffix
(至少一个)参数作为列名后缀,对左表或右表的重复列进行修饰。
常用参数的语法格式为:
df1.join(df2,how='left',lsuffix='_x',rsuffix='_y') # how默认为'left',还有'outer'、'inner'、'right'
2、分组函数:groupby()
groupby
用于按指定的列作分类汇总。它是一个DataFrame的成员方法。它的调用本身产出了一个 GroupBy对象,在之后我们可以利用它附带的聚合函数进行运算。
语法格式如下:
data_frame.groupby(['col']) #按data_frame中的'col'列对数据进行聚合
具体示例如下:
data =pd.DataFrame({'col1':[2,1,0,4],'col2':['a','b','a','c'],'col3':[True,True,False,True]})
print(data)
print(data.groupby(['col2','col3'])['col1'].sum()) #按data中的'col2'和'col3'列对数据进行聚合,并以'col1'列为指标进行求和
输出结果如下:
col1 col2 col3
0 2 a True
1 1 b True
2 0 a False
3 4 c True
col2 col3
a False 0
True 2
b True 1
c True 4
Name: col1, dtype: int64
在.groupby()
函数的基本调用形式讲解完毕后,我们这里对其应用技巧进行如下补充:
(1)函数有一个补充参数as_index
,当它为True
,这聚合的两列以index
形式存储,而为False
时,两列存储为列数据。
data.groupby(['col2','col3'],as_index = False).sum()
输出结果如下:
col2 col3 col1
0 a False 0
1 a True 2
2 b True 1
3 c True 4
(2)在经过groupby
操作之后,产生的GroupBy对象可以通过.get_group()
函数单独访问某一个groupby
操作之后的组。需传分组列的值元组。
data.groupby(['col2']).get_group('a')
输出结果如下:
col1 col2 col3
0 2 a True
2 0 a False
(3)在形成GroupBy对象后,对象是一个包含分组列的值元组和对应聚合结果数据的迭代器,支持通过循环进行逐一访问:
for name, group in data.groupby(['col2'], as_index = False):
print(name) # 分组列的值元组
print(group.mean()) # 本组元素的聚合结果,一个Series
输出结果如下:
a
col1 1.0
col3 0.5
dtype: float64
b
col1 1.0
col3 1.0
dtype: float64
c
col1 4.0
col3 1.0
dtype: float64
3、聚合函数:.aggregrate()、.agg()
接下来我们就仔细讲讲我们这个聚合的方法。我们刚刚用的聚合方法是mean
,groupby
里面支持了许多聚合方法如下:
聚合方法 | 解释 |
---|---|
mean() | 组内平均数 |
sum() | 组内和 |
size() | 组内元素个数 |
std() | 组内标准差 |
var() | 组内方差 |
sem() | 组内标准误,标准误为标准差除以样本数的平方根 |
describe() | 组内一般统计信息 |
first() | 组内第一个值 |
last() | 组内最后一个值 |
nth() | 组内第N个值 |
min() | 组内的最小值 |
max() | 组内最大值 |
在上述补充内容以外,还有一个更加厉害的功能,这边是马上将要提到的.aggregrate()
函数以及衍生的.agg()
函数,它们是GroupBy对象的成员方法。
.aggregrate()
函数可以直接接一个函数名称作为聚合的方法,它也像map
、filter
等方法一样,支持lambda
匿名函数。首先我们让它接python内部的min
、max
等方法,接着让它接两个通过匿名函数定义的方法:
grouped = data.groupby(['col2'])
grouped.aggregate(max)
grouped.aggregate(lambda x: np.sum(x > np.mean(x))) # 统计大于列内均值的元素个数
输出结果如下:
col1 col3
col2
a 1 1
b 0 0
c 0 0
.agg()
函数则更进一步,可以一次性对多个列做聚合操作。当我们传入一个列表时,默认对所有列进行同样的操作,而传入字典,则对指定列传入各自不同的指定操作。我们也可以传递字符串,但主要针对groupby
内建函数进行使用。
data.groupby(['col2']).agg({'col1':np.sum,'col3':np.mean}) #在打data中以col2为维度,对col1求和,对col3求均值
输出结果如下:
col1 col3
col2
a 2 0.5
b 1 1.0
c 4 1.0
上述功能可以说是.groupby()
函数的非常有价值的一个应用方式。
对于数据分析的第一步——读取数据的部分就讲解结束了,下节我们将讲解数据分析中数据预处理的操作。
对于缺乏Python基础的同仁,可以通过免费专栏🔥《Python学习笔记(基础)》从零开始学习Python
结语
请始终相信“Done is better than perfect”
,不要过分追求完美,即刻行动就是最好的开始, 一个模块一个模块地积累和练习,必将有所收获。
还有其他的问题欢迎在评论区留言📝!
[版权申明] 非商业目的注明出处可自由转载,转载请标明出处!!!
博客:butterfly_701c