第六章 数据加载、存储与文件格式
- 二进制格式的数据[2/2]
- Web API交互操作[2/2]
- 数据库交互操作[2/2]
6.2 二进制格式
Python内建的pickle序列化模块进行二进制格式化操作是存储数据(也称序列化)最高效、最方便的方式之一。pandas对象的to_pickle方法可以将数据以pickle格式写入硬盘:
In [3]: frame = pd.read_csv('examples/ex1.csv')
In [4]: frame
Out[4]:
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
In [6]: frame.to_pickle('examples/frame_pickle')
In [7]: pd.read_pickle('examples/frame_pickle')
Out[7]:
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
可以直接用内建的pickle读取文件中的“pickle化”的对象,示例中更方便的使用了pandas.read_pickle进行操作。
pickle仅被推荐作为短期的存储格式。因为一个被pickle序列化的对象可能明天因为库的新版本而无法反序列化。
pandas內建支持其他两个二进制格式:HDF5和MessagePack。
pandas和numpy其他的存储格式:
- bcolz(http://bcolz.blosc.org/),基于Blosc压缩库的可压缩列式二进制格式。
- Feather(http://github.com/wesm/feather),R编程社区Hadley Wickham(http://hadley.nz/)设计的跨语言列式文件格式。Feather使用Apache箭头(http://arrow.apache.org)列式存储器格式。
使用HDF5格式
HDF5用于存储大量的科学数组数据,备受好评,以C库的形式提供,具有多语言接口。
HDF代表分层数据格式(Hierarchical Data Format)。
- HDF5文件可以存储多个数据集并支持元数据。
- 支持多种压缩模式的即时压缩,是重复模式的数据更高效的存储。
- 适合处理不适合在内存中存储的超大型数据,可以高效读写大型数组的一小块。
通过PyTables或h5py等库可直接访问HDF5文件,但pandas提供了一个高阶的接口,可简化Series和DataFrame的存储。HDFStore类像字典一样工作并处理低级别细节:
In [8]: frame = pd.DataFrame({'a': np.random.randn(100)})
In [11]: store = pd.HDFStore('mydata.h5')
In [12]: store['obj1'] = frame
In [13]: store['obj1_col'] = frame['a']
In [14]: store
Out[14]:
<class 'pandas.io.pytables.HDFStore'>
File path: mydata.h5
包含在HDF5文件中的对象可以使用相同的字典型API进行检索:
In [16]: store['obj1']
Out[16]:
a
0 1.834601
1 -0.825105
2 0.057644
3 -0.271681
4 -0.284001
.. ...
95 0.212565
96 0.943591
97 -0.386988
98 -0.097866
99 -0.385368
[100 rows x 1 columns]
HDFStore支持两种存储格式,‘fixed’和‘table’。后者速度更慢,但支持一种特殊语法的查询操作:
In [17]: store.put('obj2',frame, format='table')
In [18]: store.select('obj2', where=['index>=10 and index<=15'])
Out[18]:
a
10 1.299110
11 -0.508155
12 -0.537229
13 -0.658068
14 0.650192
15 -0.434512
In [19]: store.close()
put是store['obj2']=frame方法的显式版本,但允许设置其他选项,如存储格式。
pandas.read_hdf函数是这些工具的快捷用法:
In [20]: frame.to_hdf('mydata.h5','obj3',format='table')
In [21]: pd.read_hdf('mydata.h5','obj3',where=['index < 5'])
Out[21]:
a
0 1.834601
1 -0.825105
2 0.057644
3 -0.271681
4 -0.284001
如果处理存储在远程服务器上的数据是,比如Amazon S3或HDFS,使用其他专门为分布式存储而设计的二进制格式更为合适,比如Apache Parquet(http://parquet.apache.org)。
如果本地处理大量数据,推荐尝试PyTables和h5py,因为很多数据分析的困难在于I/O密集(而不是CPU密集),使用像HDF5这样的工具可以大大加速你的应用。
HDF5并不是数据库,它是一种适合一次写入多次读取的数据集。尽快数据可以在任何时间添加到文件中,但如果多个写入者持续写入,文件可能会损坏。
读取Excel文件
pandas支持通过ExcelFile类或pandas.read_excel函数来读写Excel2003(或更高版本)文件中的表格型数据。这是使用了xlrd和openpyxl的插件来读取xls和xlsx文件的。
In [3]: xlsx = pd.ExcelFile('examples/ex1.xlsx')
In [5]: pd.read_excel(xlsx,'Sheet1')
Out[5]:
Unnamed: 0 a b c d message
0 0 1 2 3 4 hello
1 1 5 6 7 8 world
2 2 9 10 11 12 foo
可直接将文件名传入pd.read_excel:
In [6]: frame = pd.read_excel('examples/ex1.xlsx','Sheet1')
In [7]: frame
Out[7]:
Unnamed: 0 a b c d message
0 0 1 2 3 4 hello
1 1 5 6 7 8 world
2 2 9 10 11 12 foo
pandas数据写入Excel中,首先生成一个ExcelWriter,然后使用pandas.to_excel方法写入:
In [8]: writer = pd.ExcelWriter('examples/ex2.xlsx')
In [9]: frame.to_excel(writer,'Sheet1')
In [10]: writer.save()
In [11]: frame.to_excel('examples/ex2.xlsx') #也可以直接传入路径。
6.3 与Web API交互
简单易用的使用requests包(http://docs.python-requests.org)
如要获取GitHub上最新的30条关于pandas的问题,可以使用requests发送一个HTTP GET请求:
In [12]: import requests
In [13]: url = 'http://api.github.com/repos/pandas-dev/pandas/issues'
In [14]: resp = requests.get(url)
In [15]: resp
Out[15]: <Response [200]>
Response(响应)对象的json方法将返回一个包含解析为本地python对象的JSON的字典:
In [16]: data = resp.json()
In [17]: data[0]['title']
Out[17]: 'ENH: validate StringArray fillna value arg'
data中每个元素都是包含GitHub问题页面上的所有数据的字典(注释除外)。可以将data直接传给DataFrame,并提取感兴趣的字段:
In [19]: issues = pd.DataFrame(data,columns=['number','title','labels','state'])
In [20]: issues
Out[20]:
number title labels state
0 37987 ENH: validate StringArray fillna value arg [{'id': 42670965, 'node_id': 'MDU6TGFiZWw0MjY3... open
1 37986 REGR: fix inplace operations for EAs with non-... [{'id': 849023693, 'node_id': 'MDU6TGFiZWw4NDk... open
2 37985 CLN: make MultiIndex._shallow_copy signature m... [] open
3 37984 BUG: IntervalArray.astype(categorical_dtype) l... [] open
4 37983 CLN: remove panel compat shim [{'id': 211029535, 'node_id': 'MDU6TGFiZWwyMTE... open
5 37981 Deprecate inplace in Categorical.remove_catego... [] open
6 37979 Should FilePathorBuffer use os.PathLike instea... [{'id': 1280988427, 'node_id': 'MDU6TGFiZWwxMj... open
7 37977 BUG: CategoricalIndex.where nulling out non-ca... [] open
8 37974 BUG: fix astype conversion string -> float [{'id': 1817503692, 'node_id': 'MDU6TGFiZWwxOD... open
9 37972 TST: add nullable array frame constructor dtyp... [{'id': 1465286368, 'node_id': 'MDU6TGFiZWwxND... open
10 37971 PERF: IntervalArray.argsort [] open
11 37967 BUG: Limited available color name list when us... [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ=... open
12 37966 Read csv headers [{'id': 47229171, 'node_id': 'MDU6TGFiZWw0NzIy... open
13 37965 BUG: Make DTI/TDI/PI argsort match their under... [{'id': 1741841389, 'node_id': 'MDU6TGFiZWwxNz... open
14 37964 BUG: Bug in setitem raising ValueError when se... [{'id': 2822098, 'node_id': 'MDU6TGFiZWwyODIyM... open
15 37963 BUG: Some string methods treat "." as regex, o... [{'id': 1741841389, 'node_id': 'MDU6TGFiZWwxNz... open
16 37962 „case-when“ function is missing? [{'id': 76812, 'node_id': 'MDU6TGFiZWw3NjgxMg=... open
17 37957 STYLE: fail on pd.testing direct usage [{'id': 106935113, 'node_id': 'MDU6TGFiZWwxMDY... open
18 37956 ENH: Add argument "multiprocessing" to DataFra... [{'id': 76812, 'node_id': 'MDU6TGFiZWw3NjgxMg=... open
19 37955 ENH: Add argument "multiprocessing" to pd.read... [{'id': 76812, 'node_id': 'MDU6TGFiZWw3NjgxMg=... open
20 37954 BUG: df.__setitem__ can be 10x slower than pd.... [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ=... open
21 37950 ENH: 2D compat for DTA tz_localize, to_period [{'id': 13098779, 'node_id': 'MDU6TGFiZWwxMzA5... open
22 37949 ENH: IntervalIndex as groups in groupby [{'id': 76812, 'node_id': 'MDU6TGFiZWw3NjgxMg=... open
23 37947 Slow autocompletion in python/ipython console ... [] open
24 37941 ENH: An argument for .query() that returns all... [{'id': 76812, 'node_id': 'MDU6TGFiZWw3NjgxMg=... open
25 37939 QST: how to run Pandas tests involving Numba o... [{'id': 1954720290, 'node_id': 'MDU6TGFiZWwxOT... open
26 37937 BUG: Concat automatically sorts index when axi... [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ=... open
27 37935 BUG: json_normalize() upcasts column with miss... [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ=... open
28 37933 ENH: make closed part of IntervalDtype [{'id': 31404521, 'node_id': 'MDU6TGFiZWwzMTQw... open
29 37932 BUG: loc.setitem with expansion expanding rows [{'id': 2822098, 'node_id': 'MDU6TGFiZWwyODIyM... open
通过复杂操作,可以创建更高阶的接口来访问常用的Web API,以返回DataFrame对象用于分析。
6.4 与数据库交互
业务场景中,大部分数据存储在基于SQL的关系型数据库中。如(SQL Server、PostgreSQL、MySQL)等。
从SQL中将数据读取为DataFreme相当简单直接,pandas多个函数可以简化这个过程。例子使用Python内建的sqlite3驱动来生成一个SQLite数据库:
In [21]: import sqlite3
In [22]: query = """
...: CREATE TABLE test
...: (a VARCHAR(20),b VARCHAR(20),
...: c REAL, d INTEGER);"""
In [23]: con = sqlite3.connect('mydata.sqlite')
In [24]: con.execute(query)
Out[24]: <sqlite3.Cursor at 0x13530860>
In [25]: con.commit()
再插入几行数据:
In [26]: data = [('Atlanta','Geogia',1.24,6),
...: ('Tallahassee','Florida',2.6,3),
...: ('Sacramento','California',1.7,5)]
In [27]: stmt = "INSERT INTO test VALUES(?,?,?,?)"
In [29]: con.executemany(stmt,data)
Out[29]: <sqlite3.Cursor at 0x1326e960>
In [30]: con.commit()
当从数据库的表中选择数据时,大部分Python的SQL驱动(PyODBC、psycopg2、MySQLdb、pymssql等)返回的是元组的列表:
In [31]: cursor = con.execute('select * from test')
In [32]: rows = cursor.fetchall()
In [33]: rows
Out[33]:
[('Atlanta', 'Geogia', 1.24, 6),
('Tallahassee', 'Florida', 2.6, 3),
('Sacramento', 'California', 1.7, 5)]
可以将元组的列表传给DataFrame构造函数,但需要包含游标的description属性中的列名:
In [34]: cursor.description
Out[34]:
(('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))
In [35]: pd.DataFrame(rows, columns=[x[0] for x in cursor.description])
Out[35]:
a b c d
0 Atlanta Geogia 1.24 6
1 Tallahassee Florida 2.60 3
2 Sacramento California 1.70 5
SQLAlchemy项目是一个流行的Python SQL工具包,抽象去除了SQL数据库之间的许多常见差异。pandas.read_sql函数允许从通用的SQLAlchemy连接中轻松地读取数据。
In [36]: import sqlalchemy as sqla
In [37]: db = sqla.create_engine('sqlite:///mydata.sqlite')
In [38]: pd.read_sql('select * from test', db)
Out[38]:
a b c d
0 Atlanta Geogia 1.24 6
1 Tallahassee Florida 2.60 3
2 Sacramento California 1.70 5
- [End 2/2] -