分享

利用Python进行数据分析

 imelee 2017-02-18

1、二进制数据格式

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

In [1]: frame = pd.read_csv('ch06/ex1.csv')
In [2]: frame
Out[2]: 
   a  b  c  d  message
0  1  2  3  4    hello
1  5  6  7  8    world
2  9 10 11 12      foo

In [3]: frame.save('ch06/frame_pickle')

你可以通过另一个也很好用的pickle函数pandas.load将数据读回到Python:

In [4]: pd.load('ch06/frame_pickle')
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


2、使用HDF5格式

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

Python中的HDF5库有两个接口(即PyTables和h5py),它们各自采取了不同的问题解决方式。h5py提供了一种直接而高级的HDF5 API访问接口,而PyTables则抽象了HDF5的许多细节以提供多种灵活的数据容器、表索引、查询功能以及对核外计算技术(out-of-core computation)的某些支持。

pandas有一个最小化的类似于字典的HDFStore类,它通过PyTables存储pandas对象:

In [5]: store = pd.HDFStore('mydata.h5')

In [6]: store['obj1'] = frame

In [7]: store['obj1_col'] = frame['a']

In [8]: store
Out[8]: 
<class 'pandas.io.pytables.HDFStore'>
File path: mydata.h5
obj1       DataFrame
obj1_col   Series

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

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

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

注意:

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


3、读取Microsoft Excel文件

pandas的ExcelFile类支持读取存储在Excel 2003(或更高版本)中的表格型数据。由于ExcelFile用到了xlrd和openpyxl包,所以你先得安装它们才行。通过传入一个xls或xlsx文件的路径即可创建一个ExcelFile实例:

xls_file = pd.ExcelFile('data.xls')

存放在某个工作表中的数据可以通过parse读取到DataFrame中:

table = xls_file.parse('Sheet1')


4、使用HTML和Web API

许多网站都有一些通过JSON或其他格式提供数据的公共API。通过Python访问这些API的办法有不少。一个简单易用的办法(推荐)是requests包(http://docs.)。为了在Twitter上搜索“python pandas”,我们可以发送一个HTTP
GET请求,如下所示:

In [1]: import requests

In [2]: url = 'http://search.twitter.com/search.json?q=python%20pandas'

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

In [4]: resp
Out[4]: <Response [200]>

Response对象的text属性含有GET请求的内容。许多Web API返回的都是JSON字符串,我们必须将其加载到一个Python对象中:

In [5]: import json

In [6]: data = json.loads(resp.text)

In [7]: data.keys()
Out[7]: 
[u'next_page',
u'completed_in',
u'max_id_str',
u'since_id_str',
u'refresh_url',
u'results',
u'since_id',
u'results_per_page',
u'query',
u'max_id',
u'page']

响应结果中的results字段含有一组tweet,每条tweet被表示为一个Python字典,如下所示:

{u'created_at': u'Mon, 25 Jun 2012 17:50:33 +0000',
u'from_user': u'wesmckinn',
u'from_user_id': 115494880,
u'from_user_id_str': u'115494880',
u'from_user_name': u'Wes McKinney',
u'geo': None,
u'id': 217313849177686018,
u'id_str': u'217313849177686018',
u'iso_language_code': u'pt',
u'metadata': {u'result_type': u'recent'},
u'source': u'<a href="http://twitter.com/">web</a>',
u'text': u'Lunchtime pandas-fu http:///SI70xZZQ #pydata',
u'to_user': None,
u'to_user_id': 0,
u'to_user_id_str': u'0',
u'to_user_name': None}

我们用一个列表定义出感兴趣的tweet字段,然后将results列表传给DataFrame:

In [8]: tweet_fields = ['created_at', 'from_user', 'id', 'text']

In [9]: tweets = DataFrame(data['results'], columns=tweet_fields)

In [10]: tweets
Out[10]: 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 15 entries, 0 to 14
Data columns:
created_at 15 non-null values
from_user 15 non-null values
id 15 non-null values
text 15 non-null values
dtypes: int64(1), object(3)

现在,DataFrame中的每一行就有了来自一条tweet的数据:

In [11]: tweets.ix[7]
Out[11]:
created_at Thu, 23 Jul 2012 09:54:00 +0000
from_user deblike
id 227419585803059201
text pandas: powerful Python data analysis toolkit
Name: 7

要想能够直接得到便于分析的DataFrame对象,只需再多费些精力创建出对常见Web API的更高级接口即可。


5、使用数据库

在许多应用中,数据很少取自文本文件,因为用这种方式存储大量数据很低效。基于SQL的关系型数据库(如SQL Server、PostgreSQL和MySQL等)使用非常广泛,此外还有一些非SQL(即所谓的NoSQL)型数据库也变得非常流行。数据库的选择通过取决于性能、数据完整性以及应用程序的伸缩性需求。

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

import sqlite3

query = """
CREATE TABLE test
(a VARCHAR(20), b VARCHAR(20),
c REAL, d INTEGER
);"""
con = sqlite3.connect(':memory:')
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等)都会返回一个元组列表:

In [1]: cursor = con.execute('select * from test')

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

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

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

In [4]: cursor.description
Out[4]: 
(('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 [5]: DataFrame(rows, columns=zip(*cursor.description)[0])
Out[5]: 
            a          b    c d
0     Atlanta    Georgia 1.25 6
1 Tallahassee    Florida 2.60 3
2  Sacramento California 1.70 5

这种数据规整操作相当多,你肯定不想每查一次数据库就重写一次。pandas有一个可以简化该过程的read_frame函数(位于pandas.io.sql模块)。只需传入select语句和连接对象即可:

In [6]: import pandas.io.sql as sql

In [7]: sql.read_frame('select * from test', con)
Out[7]: 
            a          b    c d
0     Atlanta    Georgia 1.25 6
1 Tallahassee    Florida 2.60 3
2  Sacramento California 1.70 5

6、存取MongoDB中的数据

NoSQL数据库有许多不同的形式。有些是简单的字典式键值对存储(如BerkeleyDB和Tokyo Cabinet),另一些则是基于文档的(其中的基本单元是字典型的对象)。本例选用的是MongoDB(http://)。我先在自己的电脑上启动一个MongoDB实例,然后用pymongo(MongoDB的官方驱动器)通过默认端口进行连接:

import pymongo
con = pymongo.Connection('localhost', port=27017)

存储在MongoDB中的文档被组织在数据库的集合(collection)中。MongoDB服务器的每个运行实例可以有多个数据库,而每个数据库又可以有多个集合。假设你想保存之前通过Twitter API获取的数据。首先,我可以访问tweets集合(暂时还是空的):

tweets = con.db.tweets

然后,我将那组tweet加载进来并通过tweets.save(用于将Python字典写入MongoDB)逐个存入集合中:

import requests, json
url = 'http://search.twitter.com/search.json?q=python%20pandas'
data = json.loads(requests.get(url).text)

for tweet in data['results']:
    tweets.save(tweet)

现在,如果我想从该集合中取出我自己发的tweet(如果有的话),可以用下面的代码对集合进行查询:

cursor = tweets.find({'from_user': 'wesmckinn'})

返回的游标是一个迭代器,它可以为每个文档产生一个字典。跟之前一样,我可以将其转换为一个DataFrame。此外,还可以只获取各tweet的部分字段:

tweet_fields = ['created_at', 'from_user', 'id', 'text']
result = DataFrame(list(cursor), columns=tweet_fields)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多