【Chapter 6.2】二进制数据格式

6.2 Binary Data Formats (二进制数据格式)

实现数据的高效二进制格式存储最简单的办法之一是使用Python内置的pickle序列化。pandas对象都有一个用于将数据以pickle格式保存到磁盘上的to_pickle方法:

frame=pd.read_csv(basic_address + 'ex1.csv')

frame
Out[349]: 
   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:

In [90]: pd.read_pickle('examples/frame_pickle')
Out[90]: 
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

注意:pickle只推荐用于短期存储。因为这种格式无法保证长期稳定;比如今天pickled的一个文件,可能在库文件更新后无法读取。

pandas内置支持两个二进制数据格式:HDF5和MessagePack。下一节,我会给出几个HDF5的例子,但我建议你尝试下不同的文件格式,看看它们的速度以及是否适合你的分析工作。pandas或NumPy数据的其它存储格式有:

  • bcolz:一种可压缩的列存储二进制格式,基于Blosc压缩库。
  • Feather:我与R语言社区的Hadley Wickham设计的一种跨语言的列存储文件格式。Feather使用了Apache Arrow的列式内存格式。

1 Using HDF5 Format

HDF5格式是用来存储大量的科学数组数据的。这种格式还能用于其他一些语言。其中HDF表示hierarchical data format。每一个HDF5格式能存储 多个数据集,并支持metadata。

元数据(meta data)——“data about data” 关于数据的数据,一般是结构化数据(如存储在数据库里的数据,规定了字段的长度、类型等)。元数据是指从信息资源中抽取出来的用于说明其特征、内容的结构化的数据(如题名,版本、出版数据、相关说明,包括检索点等),用于组织、描述、检索、保存、管理信息和知识资源。

HDF5 支持多种压缩模式的on-the-fly compression(即时压缩),能让数据中一些重复的部分存储地更有效。HDF5对于处理大数据集是一个很好的选择,因为他不会把所有数据一次性读取到内存里,我们可以从很大的数组中有效率地读取一小部分。

能用PyTables或h5py来访问HDF5数据,pandas也有提供一个high-level的交互界面。HDFStore类像dict一样能用来处理low-level细节:

frame = pd.DataFrame({'a':np.random.randn(100)})

store = pd.HDFStore(basic_address + 'mydata.h5')

frame.head()
Out[359]: 
          a
0  0.249467
1  0.941035
2 -0.234888
3  1.558333
4  0.917696

HDF5中的object能用像dict一样的API来提取:

store['obj1']
Out[367]: 
           a
0   0.249467
1   0.941035
2  -0.234888
3   1.558333
4   0.917696
..       ...
95  0.940475
96 -0.442066
97 -0.421799
98 -1.357791
99 -1.123226

[100 rows x 1 columns]

HDFStore支持两种存储架构,fixed和table。后者通常更慢一些,但支持查询操作:

store.put('obj2', frame, format='table')

store.select('obj2',where=['index >= 10 and index < 15'])
Out[369]: 
           a
10  0.671290
11  0.015502
12 -0.687449
13 -1.117944
14  0.991097

put是存储的另一种写法,类似于之前的store['obj2'] = frame,但这种协防能让我们设置存储格式。

pandas.read_hdf函数也很方便:

In [101]: frame.to_hdf('mydata.h5', 'obj3', format='table')

In [102]: pd.read_hdf('mydata.h5', 'obj3', where=['index < 5'])
Out[102]: 
          a
0 -0.204708
1  0.478943
2 -0.519439
3 -0.555730
4  1.965781

笔记:如果你要处理的数据位于远程服务器,比如Amazon S3或HDFS,使用专门为分布式存储(比如Apache Parquet)的二进制格式也许更加合适。Python的Parquet和其它存储格式还在不断的发展之中,所以这本书中没有涉及。

如果是在本地处理很大数据量的话,推荐尝试PyTables和h5py看是否符合你的要求。因为很多数据分析问题都受限于I/O,所以用HDF5这样的工具能加快应用。

注意:HDF5不是数据库(database)。它最适合一次写入,多次读取的数据库。尽管数据可以在任何时间多次写入一个文件,如果多个使用者同时写入的话,文件会被破坏。

2 读取Microsoft Excel文件

pandas的ExcelFile类或pandas.read_excel函数支持读取存储在Excel 2003(或更高版本)中的表格型数据。这两个工具分别使用扩展包xlrd和openpyxl读取XLS和XLSX文件。你可以用pip或conda安装它们。

要使用ExcelFile,通过传递xls或xlsx路径创建一个实例:

xlsx = pd.ExcelFile(basic_address + 'ex1.xlsx')

存储在表单中的数据可以read_excel读取到DataFrame(原书这里写的是用parse解析,但代码中用的是read_excel,是个笔误:只换了代码,没有改文字):

pd.read_excel(xlsx,'Sheet1')
Out[379]: 
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

如果要读取一个文件中的多个sheet,用ExcelFile会更快。但让,你也能把文件名直接传递给pandas.read_excel:

frame = pd.read_excel(basic_address + 'ex1.xlsx', 'Sheet1')
frame

Out[381]: 
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

如果要把pandas数据写为Excel格式,你必须先创建一个ExcelWrite,然后用to_excel方法:


writer = pd.ExcelWriter(basic_address + 'ex2.xlsx')

frame.to_excel(writer,'Sheet1')

writer.save()
#~如果不适用ExcelWriter的话,可以直接传给to_excel一个path:
frame.to_excel(basic_address + "ex2.xlsx")

6.3 Interacting with Web APIs (网络相关的API交互)

很多网站都有公开的API,通过JSON等格式提供数据流。有很多方法可以访问这些API,这里推荐一个易用的requests包。

找到github里pandas最新的30个issues,制作一个GET HTTP request, 通过使用requests包:


import pandas as pd
import numpy as np


import requests

url = 'https://api.github.com/repos/pandas-dev/pandas/issues'

resp = requests.get(url)

resp
Out[391]: <Response [200]>

response的json方法能返回一个dict,包含可以解析为python object的JSON:


import pandas as pd
import numpy as np


import requests

url = 'https://api.github.com/repos/pandas-dev/pandas/issues'

resp = requests.get(url)

resp
Out[391]: <Response [200]>

data = resp.json()
data[0]['title']

Out[392]: 'BUG: make dense ranks results scale to 100 percent (#20731)' #就是这个数据,不是错误

data中的每一个元素都是一个dict,这个dict就是在github上找到的issue页面上的信息。我们可以把data传给DataFrame并提取感兴趣的部分:

issues = pd.DataFrame(data, columns=['number', 'title', 
                                    'labels', 'state'])
issues


issues = pd.DataFrame(data, columns=['number', 'title', 
                                    'labels', 'state'])
issues
                                    
Out[394]: 
    number                                              title  \
0    21203  BUG: make dense ranks results scale to 100 per...   
1    21201  #21128 DOC: Add documentation for freq='infer'...   
2    21200  ``groupby`` followed by ``pct_change`` does no...   
3    21199  ENH: 'to_sql()' add param 'method' to control ...   
4    21198  BUG: Fix inconsistency between the shape prope...   
..     ...                                                ...   
25   21163                            Cleanup clipboard tests   
26   21160                   ENH: Integer NA Extension Array    
27   21158              json_normalize gives KeyError in 0.23   
28   21157  Rolling correlation for DataFrame with MultiIn...   
29   21152  datetime.date no longer coerced to datetime64 ...   

                                               labels state  
0                                                  []  open  
1                                                  []  open  
2                                                  []  open  
3                                                  []  open  
4                                                  []  open  
..                                                ...   ...  
25  [{'id': 211029535, 'url': 'https://api.github....  open  
26  [{'id': 31404521, 'url': 'https://api.github.c...  open  
27  [{'id': 49379259, 'url': 'https://api.github.c...  open  
28  [{'id': 71268330, 'url': 'https://api.github.c...  open  
29  [{'id': 47223669, 'url': 'https://api.github.c...  open  

[30 rows x 4 columns]

通过一些体力活,我们可以构建一些高层级的界面,让web API直接返回DataFrame格式,以便于分析。

6.4 数据库交互

如果在工作中,大部分数据并不会以text或excel的格式存储。最广泛使用的是SQL-based的关系型数据库(SQL Server,PostgreSQL,MySQL)。选择数据库通常取决于性能,数据整合性,实际应用的可扩展性。

读取SQL到DataFrame非常直观,pandas中有一些函数能简化这个过程。举个例子,这里创建一个SQLite数据库,通过使用python内建的sqlite3 driver:

import sqlite3
import pandas as pd


query = """
CREATE TABLE test
(a VARCHAR(20), b VARCHAR(20),
 c REAL,        d INTEGER
);"""

con = sqlite3.connect(basic_address + 'mydata.sqlite')

con.execute(query)
Out[399]: <sqlite3.Cursor at 0x2018ad78e30>

然后我们插入几行数据:


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)
Out[403]: <sqlite3.Cursor at 0x2018a0c0260>

con.commit()

大部分python的SQL驱动(PyODBC, psycopg2, MySQLdb, pymssql, 等)返回a list of tuple,当从一个表格选择数据的时候:


cursor = con.execute('select * from test')

rows = cursor.fetchall()

rows
Out[408]: 
[('Atlanta', 'Georgia', 1.25, 6),
 ('Tallahassee', 'Florida', 2.6, 3),
 ('Sacramento', 'California', 1.7, 5)]

我们可以把list of tuples传递给DataFrame,但是我们也需要column names,包含cursor的description属性:

cursor = con.execute('select * from test')

rows = cursor.fetchall()

rows
Out[408]: 
[('Atlanta', 'Georgia', 1.25, 6),
 ('Tallahassee', 'Florida', 2.6, 3),
 ('Sacramento', 'California', 1.7, 5)]

你可以将这个元组列表传给DataFrame构造器,但还需要列名(包含cursor的description属性 ):

cursor.description
Out[409]: 
(('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[410]: 
             a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5

这种数据规整操作相当多,你肯定不想每查一次数据库就重写一次。SQLAlchemy项目是一个流行的Python SQL工具,它抽象出了SQL数据库中的许多常见差异。pandas有一个read_sql函数,可以让你轻松的从SQLAlchemy连接读取数据。这里,我们用SQLAlchemy连接SQLite数据库,并从之前创建的表读取数据:

import sqlalchemy as sqla

db = sqla.create_engine('sqlite:///'+basic_address+'mydata.sqlite')

pd.read_sql ('select * from test',db)
Out[413]: 
             a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5

6.5 总结

访问数据通常是数据分析的第一步。在本章中,我们已经学了一些有用的工具。在接下来的章节中,我们将深入研究数据规整、数据可视化、时间序列分析和其它主题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容