数据存储格式有哪些(免费解说常见的6种格式)

利用lxml.objectify解析XML

XML(Extensible Markup Language)是另一种常见的支持分层、嵌套数据以及元数据的结构化数据格式。本书所使用的这些文件实际上来自于一个很大的XML文档。
数据存储格式有哪些(免费解说常见的6种格式)

前面,我介绍了pandas.read_html函数,它可以使用lxml或BeautifulSoup从HTML解析数据。XML和HTML的结构很相似,但XML更为通用。这里,我会用一个例子演示如何利用lxml从XML格式解析数据。

纽约大都会运输署发布了一些有关其公交和列车服务的数据资料(http://www.mta.info/developers/download.html)。这里,我们将看看包含在一组XML文件中的运行情况数据。每项列车或公交服务都有各自的文件(如Metro-NorthRailroad的文件是Performance_MNR.xml),其中每条XML记录就是一条月度数据,如下所示:

<INDICATOR>

<INDICATOR_SEQ>373889</INDICATOR_SEQ>

<PARENT_SEQ></PARENT_SEQ>

<AGENCY_NAME>Metro-NorthRailroad</AGENCY_NAME>

<INDICATOR_NAME>EscalatorAvailability</INDICATOR_NAME>

<DESCRIPTION>Percent of the time thatescalators are operational

systemwide. The availability rate is based onphysical observations performed

the morning of regular business days only.This is a new indicator the agency

began reporting in 2009.</DESCRIPTION>

<PERIOD_YEAR>2011</PERIOD_YEAR>

<PERIOD_MONTH>12</PERIOD_MONTH>

<CATEGORY>ServiceIndicators</CATEGORY>

<FREQUENCY>M</FREQUENCY>

<DESIRED_CHANGE>U</DESIRED_CHANGE>

<INDICATOR_UNIT>%</INDICATOR_UNIT>

<DECIMAL_PLACES>1</DECIMAL_PLACES>

<YTD_TARGET>97.00</YTD_TARGET>

<YTD_ACTUAL></YTD_ACTUAL>

<MONTHLY_TARGET>97.00</MONTHLY_TARGET>

<MONTHLY_ACTUAL></MONTHLY_ACTUAL>

</INDICATOR>

我们先用lxml.objectify解析该文件,然后通过getroot得到该XML文件的根节点的引用:

from lxml importobjectify

path =’datasets/mta_perf/Performance_MNR.xml’

parsed =objectify.parse(open(path))

root = parsed.getroot()

root.INDICATOR返回一个用于产生各个<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:

In [81]: perf =pd.DataFrame(data)

In [82]: perf.head()

Out[82]:

Empty DataFrame

Columns: []

Index: []

XML数据可以比本例复杂得多。每个标记都可以有元数据。看看下面这个HTML的链接标签(它也算是一段有效的XML):

from io import StringIO

tag = ‘<ahref=”http://www.google.com”>Google</a>’

root =objectify.parse(StringIO(tag)).getroot()

现在就可以访问标签或链接文本中的任何字段了(如href):

In [84]: root

Out[84]: <Element aat 0x7f6b15817748>

In [85]:root.get(‘href’)

Out[85]:’http://www.google.com’

In [86]: root.text

Out[86]: ‘Google’

6.2 二进制数据格式

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

In [87]: frame =pd.read_csv(‘examples/ex1.csv’)

In [88]: frame

Out[88]:

ab c d message

0 12 3 4hello

1 56 7 8world

2 910 11 12foo

In [89]:frame.to_pickle(‘examples/frame_pickle’)

你可以通过pickle直接读取被pickle化的数据,或是使用更为方便的pandas.read_pickle:

In [90]:pd.read_pickle(‘examples/frame_pickle’)

Out[90]:

ab c d message

0 12 3 4hello

1 56 7 8world

2 910 11 12foo

注意:pickle仅建议用于短期存储格式。其原因是很难保证该格式永远是稳定的;今天pickle的对象可能无法被后续版本的库unpickle出来。虽然我尽力保证这种事情不会发生在pandas中,但是今后的某个时候说不定还是得“打破”该pickle格式。

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

bcolz:一种可压缩的列存储二进制格式,基于Blosc压缩库。

Feather:我与R语言社区的HadleyWickham设计的一种跨语言的列存储文件格式。Feather使用了Apache Arrow的列式内存格式。

使用HDF5格式

HDF5是一种存储大规模科学数组数据的非常好的文件格式。它可以被作为C标准库,带有许多语言的接口,如Java、Python和MATLAB等。HDF5中的HDF指的是层次型数据格式(hierarchicaldata format)。每个HDF5文件都含有一个文件系统式的节点结构,它使你能够存储多个数据集并支持元数据。与其他简单格式相比,HDF5支持多种压缩器的即时压缩,还能更高效地存储重复模式数据。对于那些非常大的无法直接放入内存的数据集,HDF5就是不错的选择,因为它可以高效地分块读写。

虽然可以用PyTables或h5py库直接访问HDF5文件,pandas提供了更为高级的接口,可以简化存储Series和DataFrame对象。HDFStore类可以像字典一样,处理低级的细节:

In [92]: frame =pd.DataFrame({‘a’: np.random.randn(100)})

In [93]: store =pd.HDFStore(‘mydata.h5’)

In [94]: store[‘obj1’]= frame

In [95]:store[‘obj1_col’] = frame[‘a’]

In [96]: store

Out[96]:

<class’pandas.io.pytables.HDFStore’>

File path: mydata.h5

/obj1 frame (shape->[100,1])

/obj1_col series (shape->[100])

/obj2 frame_table(typ->appendable,nrows->100,ncols->1,indexers->

[index])

/obj3 frame_table(typ->appendable,nrows->100,ncols->1,indexers->

[index])

HDF5文件中的对象可以通过与字典一样的API进行获取:

In [97]: store[‘obj1’]

Out[97]:

a

0 -0.204708

1 0.478943

2 -0.519439

3 -0.555730

4 1.965781

.. …

95 0.795253

96 0.118110

97 -0.748532

98 0.584970

99 0.152677

[100 rows x 1 columns]

HDFStore支持两种存储模式,’fixed’和’table’。后者通常会更慢,但是支持使用特殊语法进行查询操作:

In [98]:store.put(‘obj2′, frame, format=’table’)

In [99]:store.select(‘obj2’, where=[‘index >= 10 and index <= 15’])

Out[99]:

a

10 1.007189

11 -1.296221

12 0.274992

13 0.228913

14 1.352917

15 0.886429

In [100]: store.close()

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,使用专门为分布式存储(比如ApacheParquet)的二进制格式也许更加合适。Python的Parquet和其它存储格式还在不断的发展之中,所以这本书中没有涉及。

如果需要本地处理海量数据,我建议你好好研究一下PyTables和h5py,看看它们能满足你的哪些需求。。由于许多数据分析问题都是IO密集型(而不是CPU密集型),利用HDF5这样的工具能显著提升应用程序的效率。

注意:HDF5不是数据库。它最适合用作“一次写多次读”的数据集。虽然数据可以在任何时候被添加到文件中,但如果同时发生多个写操作,文件就可能会被破坏。

读取MicrosoftExcel文件

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

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

In [104]: xlsx =pd.ExcelFile(‘examples/ex1.xlsx’)

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

In [105]:pd.read_excel(xlsx, ‘Sheet1’)

Out[105]:

ab c d message

0 12 3 4hello

1 56 7 8world

2 910 11 12foo

如果要读取一个文件中的多个表单,创建ExcelFile会更快,但你也可以将文件名传递到pandas.read_excel:

In [106]: frame =pd.read_excel(‘examples/ex1.xlsx’, ‘Sheet1’)

In [107]: frame

Out[107]:

ab c d message

0 12 3 4hello

1 56 7 8world

2 910 11 12foo

如果要将pandas数据写入为Excel格式,你必须首先创建一个ExcelWriter,然后使用pandas对象的to_excel方法将数据写入到其中:

In [108]: writer =pd.ExcelWriter(‘examples/ex2.xlsx’)

In [109]:frame.to_excel(writer, ‘Sheet1’)

In [110]: writer.save()

你还可以不使用ExcelWriter,而是传递文件的路径到to_excel:

In [111]:frame.to_excel(‘examples/ex2.xlsx’)

6.3 Web API交互

许多网站都有一些通过JSON或其他格式提供数据的公共API。通过Python访问这些API的办法有不少。一个简单易用的办法(推荐)是requests包(http://docs.python-requests.org)。

为了搜索最新的30个GitHub上的pandas主题,我们可以发一个HTTP GET请求,使用requests扩展库:

In [113]: importrequests

In [114]: url =’https://api.github.com/repos/pandas-dev/pandas/issues’

In [115]: resp =requests.get(url)

In [116]: resp

Out[116]: <Response[200]>

响应对象的json方法会返回一个包含被解析过的JSON字典,加载到一个Python对象中:

In [117]: data =resp.json()

In [118]:data[0][‘title’]

Out[118]: ‘Period doesnot round down for frequencies less that 1 hour’

data中的每个元素都是一个包含所有GitHub主题页数据(不包含评论)的字典。我们可以直接传递数据到DataFrame,并提取感兴趣的字段:

In [119]: issues =pd.DataFrame(data, columns=[‘number’, ‘title’,

…..: ‘labels’,’state’])

In [120]: issues

Out[120]:

numbertitle \

0 17666Period does not round down for frequencies les…

1 17665 DOC: improve docstring of functionwhere

2 17664COMPAT: skip 32-bit teston int repr

3 17662 implement Delegatorclass

4 17654BUG: Fix series rename called with str alterin…

.. ……

25 17603BUG: Correctly localize naive datetime strings…

26 17599 core.dtypes.generic –>cython

27 17596Merge cdate_range functionality into bdate_range

28 17587Time Grouper bug fix when applied for list gro…

29 17583 BUG: fix tz-aware DatetimeIndex TimedeltaInd…

labels state

0[] open

1 [{‘id’: 134699, ‘url’:’https://api.github.com… open

2 [{‘id’: 563047854, ‘url’:’https://api.github…. open

3[] open

4 [{‘id’: 76811, ‘url’:’https://api.github.com/… open

.. … …

25 [{‘id’: 76811, ‘url’:’https://api.github.com/… open

26 [{‘id’: 49094459, ‘url’:’https://api.github.c… open

27 [{‘id’: 35818298, ‘url’:’https://api.github.c… open

28 [{‘id’: 233160, ‘url’: ‘https://api.github.com… open

29 [{‘id’: 76811, ‘url’:’https://api.github.com/… open

[30 rows x 4 columns]

花费一些精力,你就可以创建一些更高级的常见的Web API的接口,返回DataFrame对象,方便进行分析。

6.4 数据库交互

在商业场景下,大多数数据可能不是存储在文本或Excel文件中。基于SQL的关系型数据库(如SQL Server、PostgreSQL和MySQL等)使用非常广泛,其它一些数据库也很流行。数据库的选择通常取决于性能、数据完整性以及应用程序的伸缩性需求。

将数据从SQL加载到DataFrame的过程很简单,此外pandas还有一些能够简化该过程的函数。例如,我将使用SQLite数据库(通过Python内置的sqlite3驱动器):

In [121]: importsqlite3

In [122]: query =”””

…..: CREATE TABLE test

…..: (a VARCHAR(20), b VARCHAR(20),

…..:c REAL, d INTEGER

…..: );”””

In [123]: con =sqlite3.connect(‘mydata.sqlite’)

In [124]:con.execute(query)

Out[124]:<sqlite3.Cursor at 0x7f6b12a50f10>

In [125]: con.commit()

然后插入几行数据:

In [126]: data =[(‘Atlanta’, ‘Georgia’, 1.25, 6),

…..: (‘Tallahassee’, ‘Florida’, 2.6, 3),

…..: (‘Sacramento’, ‘California’, 1.7, 5)]

In [127]: stmt =”INSERT INTO test VALUES(?, ?, ?, ?)”

In [128]:con.executemany(stmt, data)

Out[128]:<sqlite3.Cursor at 0x7f6b15c66ce0>

从表中选取数据时,大部分Python SQL驱动器(PyODBC、psycopg2、MySQLdb、pymssql等)都会返回一个元组列表:

In [130]: cursor =con.execute(‘select * from test’)

In [131]: rows =cursor.fetchall()

In [132]: rows

Out[132]:

[(‘Atlanta’, ‘Georgia’,1.25, 6),

(‘Tallahassee’, ‘Florida’, 2.6, 3),

(‘Sacramento’, ‘California’, 1.7, 5)]

你可以将这个元组列表传给DataFrame构造器,但还需要列名(位于光标的description属性中):

In [133]:cursor.description

Out[133]:

((‘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 [134]:pd.DataFrame(rows, columns=[x[0] for x in cursor.description])

Out[134]:

a bc d

0 Atlanta Georgia1.25 6

1 Tallahassee Florida2.60 3

2 SacramentoCalifornia 1.70 5

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

In [135]: importsqlalchemy as sqla

In [136]: db =sqla.create_engine(‘sqlite:///mydata.sqlite’)

In [137]:pd.read_sql(‘select * from test’, db)

Out[137]:

a bc d

0 Atlanta Georgia1.25 6

1 Tallahassee Florida2.60 3

2 SacramentoCalifornia 1.70 5

发表评论

登录后才能评论