「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」。
《数据载入、存储及文件格式》1.文本格式数据的读写
Pandas的解析函数
| 函数 | 描述 |
|---|---|
| read_csv | 从文件、URL或文件型对象读取分隔好的数据,逗号是默认分隔符 |
| read_table | 从文件、URL或文件型对象读取分隔好的数据,制表符('\t')是默认分隔符 |
| read_fwf | 从特定宽度格式的文件中读取数据(无分隔符) |
| read_clipboard | read_table的剪贴板版本,在将表格从Web页面上转换数据时有用 |
| read_excel | 从Excel的XLS或XLSX文件中读取表格数据 |
| read_hdf | 读取用pandas存储的HDF5文件 |
| read_html | 从HTML文件中读取所有表格数据 |
| read_json | 从JSON字符串中读取数据 |
| read_msgpack | 读取MessagePack二进制格式的pandas数据 |
| read_pickle | 读取以Python pickle格式存储的任意对象 |
| read_sas | 读取存储在SAS系统中定制存储格式的SAS数据集 |
| read_sql | 将SQL查询的结果(使用SQLAlchemy)读取为pandas的DataFrame |
| read_stata | 读取Stata格式的数据集 |
| read_feather | 读取Feather二进制格式 |
上述函数的可选参数主要有以下几种类型:
- 索引:可以将一列或多个列作为返回的DataFrame,从文件或用户处获得列明,或者没有列名;
- 类型推断和数据转换:包括用户自定义的值转换和自定义的缺失值符号列表;
- 日期时间解析:包括组合功能,也包括将分散在多个列上的日期和时间信息组合成结果中的单个列;
- 迭代:支持对大型文件的分块迭代;
- 未清洗数据问题:跳过行、页脚、注释以及其他次要数据,比如使用逗号分隔千位的数字。
读取文件内容相关说明:
- 对不包含表头行的文件,可以允许pandas自动分配默认列名,也可以自己指定列名:
- 设置header = None就是允许pandas自动分配默认列名;
- 设置names = [列名序列]就是指定列名。
- 如果要指定某列为返回DataFrame的索引,可以将列名传给参数index_col;
- 如果想要从多个列中形成一个分层索引,需要将一个包含序列号或列名的列表传给index_col;
- 某些情况下,一张表的分隔符并不是固定的,比如空白或者其他字符。当字段是以多种不同数量的空格分开时,可以向read_table传入一个正则表达式作为分隔符(正则表达式知识点回顾见正则笔记);
- 解析函数有很多附加参数帮助处理各种发生异常的文件格式,可以看下方函数参数表;
- 缺失值处理:通常情况下,缺失值要么不显示(空字符串),要么用一些标识值。默认情况下,pandas使用一些常见的标识,例如NA和NULL。
一些read_csv/read_table函数参数
| 参数 | 描述 |
|---|---|
| path | 表明文件系统位置的字符串、URL或文件型对象 |
| sep或delimiter | 用于分隔每行字段的字符序列或正则表达式 |
| header | 用作列名的行号,默认是0(第一行),如果没有列名的话,应该为None |
| index_col | 用作结果中行索引的列号或列名,可以是一个单一的名称/数字,也可以是一个分层索引 |
| names | 结果的列名列表,和header = None一起用 |
| skiprows | 从文件开头处起,需要跳过的行数或行号列表 |
| na_values | 需要用NA替换的值序列 |
| comment | 在行结尾处分隔注释的字符 |
| parse_dates | 尝试将数据解析为datetime,默认是False。如果为True,将尝试解析所有的列。也可以指定列号或列名列表来进行解析。如果列表的元素是元组或列表,将会把多个列组合在一起进行解析(例如日期/时间将拆分为两列) |
| keep_date_col | 如果链接列到解析日期上,保留被连接的列,默认是False |
| converters | 包含列名称映射到函数的字典(例如{'foo' : f}会把函数f应用到'foo'列 |
| dayfirst | 解析非明确日期时,按照国际格式处理(例如 10/29/2021 -> Oct,29,2021),默认为False |
| date_parser | 用于解析日期的函数 |
| nrows | 从文件开头处读入的行数 |
| iterator | 返回一个TextParser对象,用于零散地读入文件 |
| chunksize | 用于迭代的块大小 |
| skip_footer | 忽略文件尾部的行数 |
| verbose | 打印各种解析器输出的信息,比如位于非数值列中的缺失值的数量 |
| encoding | Unicode文本编码(例如'utf-8'用于表示UTF-8编码的文本) |
| squeeze | 如果解析数据只包含一列,返回一个Series |
| thousands | 千位分隔符(例如',',或'.') |
1.1 分块读入文本文件
- 在读取大文件之前,可以先对pandas的显示设置进行调整:pd.options.display.max_rows = 10;
- 如果只想读取一小部分行,可以指明nrows:nrows = 要读入的行数;
- 为了分块读入文件,可以指定chunksize作为每一块的行数;
- read_csv返回的TextParser对象允许根据chunksize遍历文件;
- TextParser对象还有get_chunk方法,可以按照任意大小读取数据块。
1.2 将数据写入文本文件
- 使用DataFrame的to_csv方法,可以将数据导出为逗号分隔的文件;
- 也可以自定义其他分隔符:sep = 新的分隔符;
- 使用其他标识值对缺失值进行标注:na_rep = 新的缺失标识值;
- 没有其他被指定的选项的话,行和列的标签都会被写入,也可以禁止写入二者:index/header = False;
- 也可以只写入列的自己,并且按照自定义顺序:index = False, columns = [指定的列顺序表];
- Series也有to_csv方法。
1.3 使用分隔格式
-
对于任何带有单字符分隔符的文件,可以使用Python的内建csv模块。要使用它,需要将任一打开的文件或文件型对象传递给csv.reader;
import csv f = open('example.csv') reader = csv.reader(f) -
遍历reader会产生元组,元组的值为删除了引号的字符;
-
CSV文件有多种不同的风格。如需根据不同的分隔符、字符串引用约定或行终止符定义一种新的格式时,可以使用csv.Dialect定义一个简单的子类;
class my_dialect(csv.Dialect): lineterminator = '\n' delimiter = ';' quotechar = '"' quoting = csv.QUOTE_MINIMAL reader = csv.reader(f, dialect = my_dialect) -
也可以不必定义子类,直接将CSV方言参数传入csv.reader的关键字参数;
reader = csv.reader(f, delimiter = '|') -
需要手动写入被分隔的文件时,可以使用csv.writer。这个函数接收一个已经打开的可写入文件对象以及和csv.reader相同的CSV方言。
with open('mydata.csv', 'w') as f: writer = csv.writer(f, dialect = my_dialect) writer.writerow(('one', 'two', 'three')) writer.writerow(('1', '2', '3')) writer.writerow(('4', '5', '6')) writer.writerow(('7', '8', '9'))
CSV方言选项
| 参数 | 描述 |
|---|---|
| delimiter | 一个用于分隔字段的字符,默认是‘,’ |
| lineterminator | 行终止符,默认是'\r\n',读取器会忽略行终止符并识别跨平台行终止符 |
| quotecher | 用在含有特殊字符字段中的引号,默认是' “ ' |
| quoting | 引用惯例。选项包括csv.QUOTE_ALL(应用所有的字段),csv.QUOTE_MINIMAL(只使用特殊字符,如分隔符),csv.QUOTE_NONNUMERIC和csv.QUOTE_NONE(不引用)。默认是QUOTE_MINIMAL |
| skipinitialspace | 忽略每个分隔符后的空白,默认是False |
| doublequote | 如何处理字段内部的引号。如果为True,则是双引号 |
| escapechar | 当引用设置为csv.QUOTE_NONE时用于转义分隔符的字符串,默认是禁用的 |
对于具有更复杂或固定的多字符分隔符的文件,将无法使用csv模块,在此类情况下,需要使用字符串的split方法或正则表达式方法re.split进行行拆分和其他清理工作
1.4 JSON数据
使用Python内置标准库:json。
- 将JSON字符串转换为Python形式时,使用json.loads方法;
- json.dumps方法可以将Python对象转回为JSON字符串;
- pandas.read_json方法可以自动将JSON数据集按照指定次序转换为Series或DataFrame;
- 如果需要从pandas中将数据导出为JSON,可以对Series和DataFrame使用to_json方法。
1.5 XML和HTML:网络抓取
pandas的内建函数read_html可以使用lxml和Beautiful Soup等库将HTML中的表自动解析为DataFrame对象。
conda/pip install lxml
pip install beautifulsoup4 html5lib
使用lxml.objectify解析XML:
from lxml import objectify
path = 'datasets/mta_perf/Performance_MNR.xml'
parsed = objectify.parse(open(path))
root = parsed.getroot()
root.INDICATOR返回一个生成器,可以产生每一个XML元素。对于每条记录,可以将标签名称的字典(如YTD_ACTUAL)填充为数据值(不包括几个标签):
data = []
skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ', 'DESIRED_CHANGE', 'DECIMAL_PLACES']
for elt in root.INDICATOR:
el_data = {}
for child in elt.getchildren():
if child.tag in skip_fields:
continue
el_data[child.tag] = child.pyval
data.append(el_data)
最后,将包含字典的列表转换为DataFrame:
pref = pd.DataFrame(data)
2.二进制格式
使用Python内建的pickle序列化模块进行二进制格式操作是存储数据(也称为序列化)最高效、最方便的方式之一。pandas对象拥有一个to_pickle方法可以将数据以pickle格式写入硬盘。
- 可以使用pandas.read_pickle读取文件中“pickle化“的对象:pd.read_pickle(pickle化的文件);
- pickle仅被推荐作为短期的存储格式,因为pickle很难确保格式的长期有效性,在使用pickle时尽可能保持向后兼容性;
- pandas内建支持其他两个二进制格式:HDF5和MessagePack;
- pandas或NumPy支持的其他存储格式包括:
- bcolz:基于Blosc压缩库的可压缩列式二进制格式;
- Feather:R编程社区的Hadly Wickham设计的跨语言列式文件格式,Feather使用Apache箭头列式存储器格式。
2.1 使用HDF5格式
- HDF5用于存储大量的科学数组数据;
- HDF5中的HDF代表分层数据格式;
- 每个HDF5文件可以存储多个数据集并且支持元数据;
- HDF5支持多种压缩模式的即时压缩,使得重复模式的数据可以更高效地存储;
- HDF5适用于处理不适合在内存中存储的超大型数据,可以高效读写大型数组的一小块。
pandas提供了一个高阶的接口,HDFStore类像字典一样工作并处理低级别细节。
frame = pd.DataFrame({'a': np.random.randn(100)})
store = pd.HDFStore('mydata.h5')
store['obj1'] = frame
store['obj1_col'] = frame['a']
[in]
store
[out]
<class 'pandas.io.pytables.HDFStore'>
File path: mydata.h5
-
HDFStore支持两种存储模式:'fixed'和'table',后者速度更慢,但支持一种特殊语法的查询操作:
store.put('obj2', frame, format='table') store.select('obj2', where=['index >= 10 and index <= 15'])- put是store['obj2'] = frame方法的显式版本,但允许设置其他选项,如存储格式。
-
pandas.read_hdf函数是这些工具的快捷方法:
frame.to_hdf('mydata.h5', 'obj3', format='table') pd.read_hdf('mydata.h5', 'obj3', where=['index < 5']) -
HDF5并不是数据库,它是一种适合一次写入多次读取的数据集。
2.2 读取Microsoft Excel文件
pandas也支持通过ExcelFile类或pandas.read_excel函数来读取存储在Excel文件中的表格型数据。这些工具内部是使用附加包xlrd和openpyxl来分别读取XLS和XLSX文件的。
-
使用ExcelFile时,通过将xls和xlsx的路径传入,生成一个实例:
xlsx = pd.ExcelFile('examples/ex1.xlsx') -
存储在表中的数据可以通过pandas.read_excel读取到DataFrame中;
pd.read_excel(xlsx, 'Sheet1') -
如果读取的是含有多个表的文件,生成ExcelFile更快,但也可以更简洁地将文件名传入pandas.read_excel:
frame = pd.read_excel('examples/ex1.xlsx', 'Sheet1') -
如需将pandas数据写入到excel中,必须先生成一个ExcelWriter,然后使用pandas对象的to_excel方法将数据写入:
writer = pd.ExcelWriter('examples/ex2.xlsx') frame.to_excel(writer, 'sheet2') writer.save() -
也可以直接将文件路径传给to_excel,避免直接调用ExcelWriter:
frame.to_excel('examples/ex2.xlsx')
2.3 与Web API交互
使用requests包来与网站公开的API交互,获取数据。
例如,通过requests发送一个HTTP GET请求:
import requests
url = 'https://api.github.com/repos/pandas-dev/pandas/issues'
resp = requests.get(url)
2.4 与数据库交互
从SQL中将数据读取为DataFrame,pandas有多个函数可以简化此过程:
举例:使用Python内建的sqlite3驱动来生成一个SQLite数据库来模拟此过程:
import sqlite3
query = """
CREATE TABLE test
(
a VARCHAR(20),
b VARCHAR(20),
c REAL,
d INTEGER
);
"""
con = sqlite3.connect('mydata.sqlite')
con.execute(query)
再插入几行数据:
data = [('Atlanta', 'Georgia', 1.25, 6),('Tallahassee', 'Florida', 2.6, 3), ('Sacramento', 'California', 1.7, 5)]
stmt = "INSERT INTO test VALUES(?,?,?,?)"
con.executemany(stmt, data)
con.commit()
当从数据库的表中选择数据时,大部分Python的SQL驱动(PyODBC,psycopg2,MySQLdb,pymssql等)返回的是元组列表:
cursor = con.execute('select * from test')
rows = cursor.fetchall()
rows
[out]
[('Atlanta', 'Georgia', 1.25, 6),
('Tallahassee', 'Florida', 2.6, 3),
('Sacramento', 'California', 1.7, 5)]
可以将元组的列表传给DataFrame构造函数,但还需要包含在游标的description属性中的列名:
cursor.description
[out]
(('a', None, None, None, None, None, None),
('b', None, None, None, None, None, None),
('c', None, None, None, None, None, None),
('d', None, None, None, None, None, None))
pd.DataFrame(rows, columns = [x[0] for x in cursor.description])
[out]
a b c d
0 Atlanta Georgia 1.25 6
1 Tallahassee Florida 2.60 3
2 Sacramento California 1.70 5
pandas有一个read_sql函数允许从通用的SQLAlchemy连接中轻松地读取数据。这里,将使用SQLAlchemy连接到相同的SQLite数据库,并从之前创建的表中读取数据(SQLAlchemy项目是一个流行的Python SQL工具包,抽象去除了SQL数据库之间的许多常见差异):
import sqlalchemy as sqla
db = sqla.create_engine('sqlite:///mydata.sqlite')
pd.read_sql('select * from test', db)