《利用Python进行数据分析》读书笔记。
第6章:数据加载、存储与文件格式。
%pylab inline
import pandas as pd
from pandas import Series, DataFrame
from __future__ import division
from numpy.random import randn
import numpy as np
import os
import sys
import matplotlib.pyplot as plt
np.random.seed(12345)
plt.rc('figure', figsize=(10, 6))
np.set_printoptions(precision=4)
# 标准格式
!cat data/ch06/ex1.csv
df = pd.read_csv('data/ch06/ex1.csv')
df
Out[5]:
# 指定分隔符
pd.read_table('data/ch06/ex1.csv', sep=',')
Out[20]:
# 无标题行数据
!cat data/ch06/ex2.csv
pd.read_csv('data/ch06/ex2.csv', header=None)
Out[22]:
# 可以指定标题
pd.read_csv('data/ch06/ex2.csv', names=['a', 'b', 'c', 'd', 'message'])
Out[23]:
# 指定标题和索引
names = ['a', 'b', 'c', 'd', 'message']
pd.read_csv('data/ch06/ex2.csv', names=names, index_col='message')
Out[24]:
# 指定层次化索引
!cat data/ch06/csv_mindex.csv
parsed = pd.read_csv('data/ch06/csv_mindex.csv', index_col=['key1', 'key2'])
parsed
Out[25]:
# 非标准分隔符
list(open('data/ch06/ex3.txt'))
Out[26]:
# 可以用正则表达式作为分隔符
result = pd.read_table('data/ch06/ex3.txt', sep='\s+')
result
Out[27]:
# 跳过一些行
!cat data/ch06/ex4.csv
pd.read_csv('data/ch06/ex4.csv', skiprows=[0, 2, 3])
Out[28]:
# pandas 能自动识别缺失值
!cat data/ch06/ex5.csv
result = pd.read_csv('data/ch06/ex5.csv')
result
pd.isnull(result)
Out[29]:
# 指定缺失标志(如何判断缺失)
result = pd.read_csv('data/ch06/ex5.csv', na_values=['NULL'])
result
Out[30]:
# 为每列指定不同的判断缺失标志
sentinels = {'message': ['foo', 'NA'], 'something': ['two']}
pd.read_csv('data/ch06/ex5.csv', na_values=sentinels)
Out[31]:
逐块读取¶
result = pd.read_csv('data/ch06/ex6.csv')
result.head()
Out[34]:
#读取5行
pd.read_csv('data/ch06/ex6.csv', nrows=5)
Out[35]:
# chunksize: 设置逐块读取的行数
chunker = pd.read_csv('data/ch06/ex6.csv', chunksize=1000)
# 返回一个 reader
chunker
Out[37]:
# 对chunker 进行遍历 , 逐块计算
chunker = pd.read_csv('data/ch06/ex6.csv', chunksize=1000)
tot = Series([])
for piece in chunker:
tot = tot.add(piece['key'].value_counts(), fill_value=0)
#tot = tot.sort_values(ascending=False)
tot[:10]
Out[48]:
输出文本文件¶
data = pd.read_csv('data/ch06/ex5.csv')
data
Out[49]:
# 输出csv文件
data.to_csv(sys.stdout)
# 指定分隔符
data.to_csv(sys.stdout, 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
dates = pd.date_range('1/1/2000', periods=7)
ts = Series(np.arange(7), index=dates)
ts.to_csv(sys.stdout)
# 读取 Series
Series.from_csv('data/ch06/tseries.csv', parse_dates=True)
Out[58]:
手工处理分隔符¶
对于不规范的文件,经常需要进行预处理。
Python内置的csv模块可以读取任何单字符分隔符文件。 通过定义csv.Dialect参数,可定义适应很多格式(如专门的分隔符、字符串引用约定、行结束符等)。
csv.Dialect 的主要属性包括:
- delimiter: 分隔符,默认为“,”
- lineterminator: 行结束符,默认为“\r\n” 。 只作用于写操作,读的时候自动识别。
- quotechar: 特殊字符的引用符,默认为双引号
- quoting: 引用约定?
- skipinitialspace: 是否忽略分隔符后的空白符,默认为 False
- doublequote: 是否双写字段内的引用符号。默认为 True
- escapechar: 对分隔符进行转义的字符串?
!cat data/ch06/ex7.csv
import csv
f = open('data/ch06/ex7.csv')
reader = csv.reader(f)
for line in reader:
print(line)
lines = list(csv.reader(open('data/ch06/ex7.csv')))
header, values = lines[0], lines[1:]
data_dict = {h: v for h, v in zip(header, zip(*values))}
data_dict
Out[12]:
class my_dialect(csv.Dialect):
lineterminator = '\n'
delimiter = ';'
quotechar = '"'
quoting = csv.QUOTE_MINIMAL
# 用 csv 模块输出到文件
with open('mydata.csv', 'w') as f:
writer = csv.writer(f, dialect=my_dialect)
writer.writerow(('one', 'two', 'three'))
writer.writerow(('1', '2', '3'))
writer.writerow(('4', '5', '6'))
writer.writerow(('7', '8', '9'))
%cat mydata.csv
JSON 数据¶
obj = """
{"name": "Wes",
"places_lived": ["United States", "Spain", "Germany"],
"pet": null,
"siblings": [{"name": "Scott", "age": 25, "pet": "Zuko"},
{"name": "Katie", "age": 33, "pet": "Cisco"}]
}
"""
import json
result = json.loads(obj)
result
Out[19]:
asjson = json.dumps(result)
siblings = DataFrame(result['siblings'], columns=['name', 'age'])
siblings
Out[21]:
XML 和 HTML: Web 信息收集¶
NB. The Yahoo! Finance API has changed and this example no longer works
from lxml.html import parse
from urllib2 import urlopen
parsed = parse(urlopen('http://finance.yahoo.com/q/op?s=AAPL+Options'))
doc = parsed.getroot()
links = doc.findall('.//a')
links[15:20]
lnk = links[28]
lnk
lnk.get('href')
lnk.text_content()
urls = [lnk.get('href') for lnk in doc.findall('.//a')]
urls[-10:]
tables = doc.findall('.//table')
calls = tables[9]
puts = tables[13]
rows = calls.findall('.//tr')
def _unpack(row, kind='td'):
elts = row.findall('.//%s' % kind)
return [val.text_content() for val in elts]
_unpack(rows[0], kind='th')
_unpack(rows[1], kind='td')
from pandas.io.parsers import TextParser
def parse_options_data(table):
rows = table.findall('.//tr')
header = _unpack(rows[0], kind='th')
data = [_unpack(r) for r in rows[1:]]
return TextParser(data, names=header).get_chunk()
call_data = parse_options_data(calls)
put_data = parse_options_data(puts)
call_data[:10]
使用 lxml.objectify 解析 XML¶
path = './data/ch06/mta_perf/Performance_MNR.xml'
from lxml import objectify
parsed = objectify.parse(open(path))
root = parsed.getroot()
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)
perf = DataFrame(data)
perf
Out[12]:
root
Out[13]:
root.get('href')
root.text
frame = pd.read_csv('data/ch06/ex1.csv')
frame
Out[17]:
# 实现数据的二进制格式最简单的方法之一是使用Python内置的pickle序列化
# pickle,作者建议用作短期存储,因为会遇到解析版本问题
# 写入二进制文件
frame.to_pickle('frame_pickle')
pd.read_pickle('frame_pickle')
Out[22]:
使用 HDF5 格式¶
很多工具都能实现高效读写磁盘上以二进制格式存储的科学数据。HDF5就是一个流行工业级库,是一个C库,有Java、Python、MATLAB等多种接口。
HDF 是指层次型数据格式(hierarchical data format),非常适合存储时间序列数据。
HDF5 支持多种格式的即时压缩,能高效存储重复数据。
HDF5 读写速度极快。
python 中提供了两个 HDF5格式的工具:PyTables
和 h5py
。h5py 更高级,而 PyTables 提供了更多的细节。
pandas 使用了 PyTables 作为 HDF5 的底层支持。
store = pd.HDFStore('mydata.h5')
store['obj1'] = frame
store['obj1_col'] = frame['a']
store
Out[23]:
store['obj1']
Out[24]:
store.close()
os.remove('mydata.h5')
读取Excel数据¶
底层使用 xlrd
和 openpyxl
。
xls_file = pd.ExcelFile('data/ch06/ex1.xlsx')
xls_file
Out[36]:
xls_file.parse()
Out[37]:
通过 HTML 和 Web APIs 获取数据¶
import requests
url = 'https://api.github.com/repos/pydata/pandas/milestones/28/labels'
resp = requests.get(url)
resp
Out[30]:
# data[:5]
issue_labels = DataFrame(data)
issue_labels.head()
Out[32]:
使用数据库¶
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()
cursor = con.execute('select * from test')
rows = cursor.fetchall()
rows
Out[40]:
cursor.description
Out[41]:
DataFrame(rows, columns=zip(*cursor.description)[0])
import pandas.io.sql as sql
sql.read_sql('select * from test', con)
Out[43]:
参考资料¶