6.1 文本格式数据的读写
-
将表格型数据读取为DataFrame对象:read_csv()和read_table()
-
Pandas的解析函数
-
可选参数
- 索引:可以将一或多个列作为返回的DataFrame
- 类型推断和数据转换:包括用户自定义的值转换和自定义的缺失值符号列表。
- 日期时间解析:包括组合功能,包括将分散在多个列上的日期和时间信息组合成结果中的单个列
- 迭代:对大型文件的分块迭代
- 未清洗数据问题:跳过行、页脚、注释以及其他次要数据,比如使用逗号分隔千位的数字。
-
一些数据载入函数(如pandas.read_csv等)会进行类型推断,因为列的数据类型并不是数据格式的一部分,不必指定那一列是数值、整数、布尔值或字符串。
-
处理日期和自定义类型数据需要其他努力
-
pandas默认分配列名,也可以自己指定列名:
pd.read_csv(‘examples/ex2.csv’,names=[‘a’,’b’,’c’,’d’,’message’])
-
将message列成为DataFrame索引,可以指定位置4的列为索引,或将‘message’传给参数index_col
names = ['a', 'b', 'c', 'd', 'message'] pd.read_csv('examples/ex2.csv', names=names, index_col='message')
-
从多个列形成分层索引:传入一个包含列序号或列名的列表:
pd.read_csv(‘examples/csv_mindex.cav’,index_col=[‘key1’,’key2’])
-
-
表的分隔符不固定,使用空白或其他方式来分隔字段
-
向read_table传入一个正则表达式作为分隔符
result = pd.read_table('examples/ex3.txt',sep='\s+')
-
-
使用skiprows来跳过某些行(例:第一行、第三行和第四行)
pd.read_csv('examples/ex4.csv',skiprows=[0,2,3])
-
-
-
缺失值处理
-
缺失值不显示(空字符串)或用标识值显示(NA和Null等)
-
na_values选项可以传入一个列表或一组字符串来处理缺失值
pd.read_csv('examples/ex5.csv',na_values=['NULL'])
-
在字典中,每列可以指定不同的缺失值标识
sentinels = {'message': ['foo', 'NA'], 'something':['two']} pd.read_csv('examples/ex5.csv', na_values=sentinels)
-
-
一些read_csv/read_table函数常用参数
分块读入文本文件
处理大型文件时,常需要读入文件的一个小片段或者按小块遍历文件
-
在尝试大文件前,可先对pandas的显示设置进行调整,使之更为紧凑
pd.options.display.max_rows = 10 # 只读取文件的前五行 result = pd.read_csv('examples/ex6.csv',nrows=5)
-
设置chunksize指定每个分块的行数
chunker = pd.read_csv('examples/ex6.csv',chunksize=1000)
-
read_csv返回TextParser对象,可根据chunksize遍历文件
# 遍历ex6.csv,并对’key’列聚合获得计数值 chunker = pd.read_csv('examples/ex6.csv',chunksize=1000) tot = pd.Series([]) for piece in chunker: tot = tot.add(piece['key'].value_counts(),fill_value=0) tot = tot.sort_values(ascending=False)
-
TextParser还具有get_chunk方法,允许你按照任意大小读取数据块
将数据写入文本格式
- DataFrame的to_csv方法:将数据导出为逗号分隔的文件,可以通过sep设置其他分隔符
- 缺失值在输出时以空字符串出现,也可以用其他标识符对缺失值进行标注:
data.to_csv(sys.stdout,na_rep=‘NULL’)
- 默认列和标签都会被写入,可以禁止二者写入:
data.to_csv(sys.stdout,index=False,header=False)
- 按照指定顺序写入列的子集:
data.to_csv(sys.stdout,index=False,columns=[‘a’,’b’,’c’])
- Series也有to_csv方法
- 缺失值在输出时以空字符串出现,也可以用其他标识符对缺失值进行标注:
使用分隔格式
-
read_table无法接收一个带有一行或多行错误的文件
-
使用Python的csv模块处理带有单字符分隔符的文件,将打开的文件型对象传给csv.reader
import csv f = open('examples/ex7.csv') reader = csv.reader(f) # 像遍历文件一样,遍历reader会产生元组 for line in reader: print(line) # 元组的值为删除了引号的字符 # 将文件读取为行的列表 with open('examples/ex7.csv') as f: lines = list(csv.reader(f)) # 将数据拆分为列名行和数据行 header,values = lines[0],lines[1:] # 使用字典推导式和表达式zip(*values)生成一个包含数据列的字典,字典中行转置成列 data_dict = {h: v for h,v in zip(header,zip(*values))} data_dict # {'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}
-
使用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.Dialect中的一些属性及其用途
-
对于具有更复杂或固定的多字符分隔符的文件,你将无法使用csv模块.
使用字符串的split方法或正则表达式方法re.split进行拆分和其他清理工作
-
使用csv.writer手动写入被分隔的文件
- 接收 一个已打开可写入文件对象 和 csv.reader相同的CSV方言、格式选项
JSON数据
JSON(JavaScript Object Notation的简写)已经成为Web浏览器和其他应用间通过HTTP请求发送数据的标准格式。
-
除空值null和一些细微差别(例如不允许列表末尾的逗号)之外,JSON非常接近Python。
- 基本类型是对象(字典)、数组(列表)、字符串、数字、布尔值和空值
- 所有键都必须是字符串
-
JSON字符串和Python互相转换
-
json.loads方法将JSON字符串转换为Python形式
obj = """ {"name": "Wes", "places_lived": ["United States", "Spain", "Germany"], "pet": null, "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]}, {"name": "Katie", "age": 38, "pets": ["Sixes", "Stache", "Cisco"]}] } """ import json result = json.loads(obj) result """ {'name': 'Wes', 'places_lived': ['United States', 'Spain', 'Germany'], 'pet': None, 'siblings': [{'name': 'Scott', 'age': 30, 'pets': ['Zeus', 'Zuko']}, {'name': 'Katie', 'age': 38, 'pets': ['Sixes', 'Stache', 'Cisco']}]} """
-
json.dumps可以将Python对象转换回JSON:
asjson = json.dumps(result)
-
-
将JSON转换成其他数据结构,方便的是将字典构成的列表(之前是JSON对象)传入DataFrame构造函数,并选出数据字段的子集
-
import pandas as pd siblings = pd.DataFrame(result['siblings'], columns=['name', 'age']) siblings """ name age 0 Scott 30 1 Katie 38 """
-
pandas.read_json可以自动将JSON数据集按照指定次序转换为Series或DataFrame。
- pandas.read_json默认JSON数组中的每个对象是表里的一行
-
-
从pandas中将数据导出为JSON,对Series和DataFrame使用to_json方法
XML和HTML:网络抓取
Python有很多针对HTML和XML格式进行读取、写入数据的库
lxml(http://lxml.de)、Beautiful Soup、html5lib
lxml更快,其他库操作更多
html
-
pandas的内建函数read_html可以使用lxml和BeautifulSoup等库将HTML中的表自动解析为DataFrame对象
-
安装
conda install lxml pip install beautifulsoup4 html5lib
-
-
pandas.read_html函数存在很多选项,默认搜索并尝试解析所有包含在
标签中的表格型数据,返回的结果是DataFrame对象的列表 -
举例:从美国FDIC政府机构下载了显示银行倒闭数据的HTML文件(在pandas文档中使用)
tables = pd.read_html('examples/fdic_failed_bank_list.html') len(tables) # 1 failures = tables[0] failures.head() # failures有很多列,pandas在行内插入了换行符\ # 计算每年银行倒闭的数量 close_timestamps = pd.to_datetime(failures['Closing Date']) close_timestamps.dt.year.value_counts()
使用lxml.objectify解析XML
XML(eXtensible Markup Language)是结构化数据格式,它使用元数据支持分层、嵌套数据。
示例背景:纽约大都会交通局(MTA)发布了一份关于其公交、火车服务(http://www.mta.info/developers/down loadhtml)的数据集。每个火车或公交服务都有一个不同的文件(例如Performance_MNR.xml代表地铁-北铁路),文件中以一系列XML记录的方式包含了按月的数据。
-
使用lxml.objectify解析文件,用getroot获得XML文件根节点的引用
from lxml import objectify path = 'datasets/mta_perf/Performance_MNR.xml' parsed = objectify.parse(open(path)) root= parsed.getroot()
-
root.INDICATOR返回一个生成器,可以产生每一个XML元素
# root.INDICATOR返回一个生成器,可以产生每一个<INDICATOR>XML元素(不包括几个标签) 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)
-
XML标签可以更复杂,每个标签可以包含元数据。一个HYML连接标签,也是有效的XML
from io import StringIO tag = '<a href="http://www.google.com">Google</a>' root = objectify.parse(StringIO(tag)).getroot() # 现在可以访问标签或链接文本中的任何字段(如href) root # <Element a at 0x7f6b15817748> root.get('href') # 'http://www.google.com' root.text # 'Google'
6.2 二进制格式
pickle
-
使用Python内建的pickle序列化模块进行二进制格式操作
-
pandas对象使用to_pickle方法将数据以pickle格式写入硬盘
frame = pd.read_csv('examples/ex1.csv') frame.to_pickle('examples/frame_pickle')
-
使用pandas.read_pickle
pd.read_pickle('examples/frame_pickle')
-
pickle仅作为短期存储来使用,被pickle化的对象可能因为库版本更新而无法反序列化
-
-
Python支持的其他两个二进制格式:HDF5和MessagePack
-
pandas或NumPy其他存储格式
- bcolz(http://bcolz.blosc.org/):基于Blosc压缩库的可压缩列式二进制格式
- 基于Blosc压缩库的可压缩列式二进制格式
- R编程社区的Hadley Wickham(http://hadley.nz/)设计的跨语言列式文件格式
- Feather使用Apache箭头(http://arrow.apache.org)列式存储器格式
使用HDF5格式
存储大量的科学数组数据,以C库的形式提供,有很多其他语言接口
用于处理不适合在内存中存储的超大型数据,可以仅高效读写一小块
-
“HDF”代表分层数据格式,每个HDF5文件可以存储多个数据集并且支持元数据。
-
HDF5支持多种压缩模式的即时压缩,重复模式的数据可以更高效地存储
-
可使用PyTables或h5py等库直接访问HDF5文件,但pandas提供高阶接口:HDFStore类,可以像字典一样处理细节,简化Series和DataFrame的存储。
frame = pd.DataFrame({'a': np.random.randn(100)}) store = pd.HDFStore('mydata.h5') store['obj1'] = frame store['obj1_col'] = frame['a'] store
-
包含在HDF5文件中的对象可以用字典型API进行检索
-
HDFStore支持两种存储模式,‘fixed’和’table’(后者更慢,但支持一种特殊语法的查询操作)
# put是store ['obj2']=frame方法的显式版本,但允许我们设置其他选项,如存储格式 store.put('obj2', frame, format='table') store.select('obj2', where=['index >= 10 and index <= 15']) store.close()
-
pandas.read_hdf函数可以用作快捷方法
frame.to_hdf('mydata.h5', 'obj3', format='table') pd.read_hdf('mydata.h5', 'obj3', where=['index < 5'])
-
-
处理远程服务器上的数据时,使用专门为分布式存储而设计的二进制格式更为合适,比如Apache Parquet(http://parquet.apache.org)。
-
处理大量本地数据时,尝试使用PyTables和h5py。
-
很多数据分析的困难在于I/O密集(而不是CPU密集),使用类似HDF5可以加速
HDF5并不是数据库,它是一种适合一次写入多次读取的数据集.
多个写入者持续写入,文件可能会损坏
读取Microsoft Excel文件
pandas也支持通过ExcelFile类或pandas.read_excel函数来读取存储在Excel表格型数据
使用附加包xlrd和openpyxl来分别读取XLS和XLSX文件的
-
通过将xls或xlsx的路径传入ExcelFile,生成一个实例。
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') frame
-
将pandas数据写入到Excel格式中
- 先生成一个ExcelWriter
- 使用pandas对象的to_excel方法将数据写入
writer = pd.ExcelWriter('examples/ex2.xlsx') frame.to_excel(writer, 'Sheet1') writer.save()
- 也可以将文件路径传给to_excel,避免直接调用ExcelWriter
frame.to_excel('examples/ex2.xlsx')
6.3 与Web API交互
使用requests包(http://docs.python-requests.orgt)与Web API交互更简单
获取GitHub上最新的30条关于pandas的问题
-
用附加库requests发送一个HTTP GET请求
import requests url = 'https://api.github.com/repos/pandas-dev/pandas/issues' resp = requests.get(url) resp
-
Response(响应)对象的json方法将返回一个包含解析为本地Python对象的JSON的字典
# data中的每个元素都是一个包含GitHub问题页面上的所有数据的字典(注释除外) data = resp.json() data[0]['title']
-
将data直接传给DataFrame,并提取感兴趣的字段
issues = pd.DataFrame(data, columns=['number', 'title', 'labels', 'state']) issues
6.4 与数据库交互
业务场景中,基于SQL的关系型数据库(例如SQL Server、PostgreSQL和MySQL)使用广泛
pandas很多函数可以从SQL中将数据读取为DataFrame
SQLAlchemy项目(http://www.sqlalchemy.org/)是一个流行的PythonSQL工具包,抽象去除了SQL数据库之间的许多常见差异。
-
使用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) con.commit()
-
插入几行数据
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
-
将元组的列表传给DataFrame构造函数,要包含在游标的description属性中的列名
cursor.description pd.DataFrame(rows, columns=[x[0] for x in cursor.description])
-
pandas中read_sql函数可以从通用的SQLAlchemy连接中轻松地读取数据
# 使用SQLAlchemy连接到相同的SQLite数据库,并从之前创建的表中读取数据 import sqlalchemy as sqla db = sqla.create_engine('sqlite:///mydata.sqlite') pd.read_sql('select * from test', db)