python数据载入、存储及文件格式

2020-04-21  本文已影响0人  弦好想断

将表格型数据读取为DataFrame对象是pandas的重要特征,read_csv和read_table可能是后期我们使用最多的函数。


从一个小型的逗号分隔文本文件(CSV)开始:

!type ex1.csv
a,b,c,d,message
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
with open('ex1.csv') as f:
    lines = [x.rstrip() for x in f]#删除 string 字符串末尾的指定字符(默认为空格)
lines
['a,b,c,d,message', '1,2,3,4,hello', '5,6,7,8,world', '9,10,11,12,foo']
df = pd.read_csv('ex1.csv')#读取逗号分隔文件
df
    a   b   c   d   message
0   1   2   3   4   hello
1   5   6   7   8   world
2   9   10  11  12  foo

header = None参数允许自动分配默认列名,也可以自己指定列名:

pd.read_csv('ex2.csv',names = ['a','b','c','d','message'])
    a   b   c   d   message
0   1   2   3   4   hello
1   5   6   7   8   world
2   9   10  11  12  foo

假设你想要message列成为返回DataFrame的索引,你可以指定位置4的列为索引,或将‘message’传给参数index_col:

names = ['a','b','c','d','message']
pd.read_csv('ex2.csv',names = names,index_col = 'message')
        a   b   c   d
message                 
hello   1   2   3   4
world   5   6   7   8
foo     9   10  11  12

如果你想从多个类中形成一个分层索引,需要传入一个包含序列号或列明名的列表:

!type csv_mindex.csv
key1,key2,value1,value2
one,a,1,2
one,b,3,4
one,c,5,6
one,d,7,8
two,a,9,10
two,b,11,12
two,c,13,14
two,d,15,16
parsed = pd.read_csv('csv_mindex.csv',index_col = ['key1','key2'])
parsed
        value1  value2
key1 key2       
one   a     1   2
      b     3   4
      c     5   6
      d     7   8
two   a     9   10
      b     11  12
      c     13  14
      d     15  16

在某些情况下,一张表的分隔符并不是固定的,使用空白或其他方式来分隔字段。考虑如下文本文件:

list(open('ex3.txt'))
['            A         B         C\n',
 'aaa -0.264438 -1.026059 -0.619500\n',
 'bbb  0.927272  0.302904 -0.032399\n',
 'ccc -0.264273 -0.386314 -0.217601\n',
 'ddd -0.871858 -0.348382  1.100491\n']

当字段是以多种不同数量的空格分开时,可以向read_csv传递一个正则表达式作为分隔符。本例中,正则表达式为\s+,因此我们可得

pd.read_csv('ex3.txt',sep = '\s+')#由于列名的数量比数据的列数少一列,因此read_csv推断第一列应当作为DataFrame的索引
                A           B          C
aaa     -0.264438   -1.026059   -0.619500
bbb     0.927272    0.302904    -0.032399
ccc     -0.264273   -0.386314   -0.217601
ddd     -0.871858   -0.348382   1.100491

可以在read_csv中使用skiprows来跳过第一行、第三行和第四行,传入参数skiprows = [0,2,3]。


在尝试大文件之前,我们可以先对pandas的显示设置进行调整,使之更为紧凑。

pd.options.display.max_rows = 10#最大显示行数为10行

如果你只想读取一小部分行,可以指明要读取的行数nrows:
为了分块读入文件,可以指定chunksize作为每一块的行数,read_csv返回的TextParser对象允许你根据chunksize遍历文件。

#例如,我们可以遍历ex6.csv,并对'key'列聚合获得计数值:
tot = pd.Series([])
for piece in chunker:
    tot = tot.add(piece['key'].value_counts(),fill_value = 0)
tot = tot.sort_values(ascending = False)
tot[:10]
X    210.0
E    207.0
O    197.0
L    196.0
Q    193.0
F    187.0
H    182.0
K    180.0
V    178.0
J    175.0
dtype: float64

TextParser对象还具有get_chunk方法,允许你按照任意大小读取数据块

a = pd.read_csv('ex6.csv',chunksize=1000)
a.get_chunk(size=4000)#省去文件中前400行,只显示4000行以后的内容,并按1000行分块显示
for x in a:
    print(x)

将数据写入文本格式

使用DataFrame的to_csv方法,可以将数据导出为逗号分隔的文件。
当然,其他的分隔符也是可以的,(写入到sys.stdout时,控制台中打印的中文结果)
sys模块可以实现程序与python解释器交互,提供一系列变量或函数在python运行时的环境。sys.stdout指的是标准输出。
缺失值在输出时以空字符串出现,也可以使用na_rep参数对缺失值进行标注

import sys
data.to_csv(sys.stdout,sep='|',na_rep = 'NULL')
|something|a|b|c|d|message
0|one|1|2|3.0|4|NULL
1|two|5|6||8|world
2|three|9|10|11.0|12|foo

默认行和列的标签都会被写入,不过传入index = False,header = False二者也都可以禁止写入。

json数据

JSON已经成为Web浏览器和其他应用间通过HTTP请求发送数据的标准格式它是一种比CSV等表格文本形式更为自由的数据形式。
json非常接近与有效的Python代码,处理它的空值null合一起其他细小差异(例如不允许列表末尾的逗号)之外。json对象中所有的键都必须是字符串,基本类型是字典,列表、字符串、数字、布尔值和空值。
将JSON字符串转换为Python形式,使用Python的内置模块json的json.loads方法:

import json
result = json.loads(obj)
result
{'name': 'Wes',
 'place_lived': ['United States', 'Spain', 'Germany'],
 'pet': None,
 'siblings': [{'name': 'Scott', 'age': 30, 'pets': ['Zeus', 'Zuko']},
  {'name': 'Katie', 'age': 38, 'pets': ['Sixes', 'Stache', 'Cisco']}]}

另一方面,json.dumps可以将Python对象转换为JSON

asjson = json.dumps(result)
asjson
'{"name": "Wes", "place_lived": ["United States", "Spain", "Germany"], "pet": null, "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]}, {"name": "Katie", "age": 38, "pets": ["Sixes", "Stache", "Cisco"]}]}'

将JSON对象或对象列表转换为DataFrame或其他数据结构。
比较方便的方式是将字典构成的列表(之前是json对象)传入DataFrame构造函数,并选出数据字段的子集。

siblings = pd.DataFrame(result['siblings'],columns = ['name','age'])
siblings
    name    age
0   Scott   30
1   Katie   38

pandas.read_json 的默认选项是假设JSON数组中的每个对象是表里的一行,将数据导出为json,一种办法是对Series或DataFrame使用to_json方法。

XML和HTML:网络抓取

pandas的内建函数read_html函数可以使用lxml和Beautiful Soul等库将HTML中的表自动解析为DataFrame对象。
pandas.read_html函数有很多选项,默认情况下,它会搜索并尝试解析所有包含在<table>标签中的表格型数据,返回的结果是DataFrame对象的列表。
XML是另一种常用的结构化数据格式,它使用元数据支持分层,嵌套数据。XML和XTML结构类似,但是XML更通用。
使用lxml从更为通用的XML格式解析数据:

#使用lxml.objectify,我们可以解析这个文件,并用getroot来获得对XML文件的根节点的引用
from lxml import objectify
path = 'mta_perf/Performance_MNR.xml
parsed = objectify.parse(open(path))
root = parsed.getroot()
data = []
skip_field = ['PARENT_SEQ','INDICATOR_SEQ','DESIRED_CHANGE','DECIMAL_PLACES']
#root.INDICATOR返回一个用于产生各个XML元素的生成器。
for elt in root.INDICATOR:
    el_data = {}
    for child in elt.getchildren():
        if child.tag in skip_field:
            continue
        el_data['child.tag'] = child.pyval
    data.append(el_data)
#最后将包含字典的列表转换为DataFrame
perf = pd.DataFrame(data)
perf.head()
    child.tag
0   96.9
1   95
2   96.9
3   98.3
4   95.8

child.tag返回的是xml文件每行中的第一个值,child.pyval返回的是第二个(也就是中间的)值。
XML 数据还可以更复杂,每个标签也可以包含元数据。考虑一个HTML连接标签,也是有效的XML:

from io import StringIO
tag = '<a href = "http://www.google.com">Google</a>'
root = objectify.parse(StringIO(tag)).getroot()
root
<Element a at 0x2a8b112bd48>
root.get('href')
'http://www.google.com'
root.text
'Google'

这里StringIO的概念:在内存中读写str,参考:https://blog.csdn.net/lucyxu107/article/details/82728266

二进制格式

使用Python内建的pickle序列化模块进行二进制操作是存储数据(也成为序列化)最高效、最方便的方式之一。pandas拥有一个to_pickle方法可以将数据以pickle格式写入硬盘

frame = pd.read_csv('ex1.csv')
frame
    a   b   c   d   message
0   1   2   3   4   hello
1   5   6   7   8   world
2   9   10  11  12  foo
rame.to_pickle('frame_pickle')
#可以直接使用内建的pickle读取文件中pickle化的对象,或更方便地使用pandas.read_pickle做上述操作
pd.read_pickle('frame_pickle')
    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很难确保格式的长期有效性。

HDF5格式

HDF5是一个广受好评的文件格式,用于存储大量的科学数组数据。HDF5中的“HDF”代表分层数据格式。每个HDF5文件可以存储多个数据集并支持元数据。
与更简单的格式相比,HDF5支持多种压缩模式的即时压缩,使得重复模式的数据可以更高效的存储。HDF5适用于处理不适合在内存中存储的超大型数据,可以使你高效的读写大型数组的一小块。
尽管你可以通过PyTables和h5py等库直接访问HDF5文件,但pandas提供了一个高阶的接口,可以简化Series和DataFrame的存储。
HDFStore类像字典一样工作并处理低级别细节:

frame = pd.DataFrame({'a':np.random.randn(100)})
store = pd.HDFStore('mydata.h5')
store['obj1'] = frame
store['obj1_col'] = frame['a']
store
<class 'pandas.io.pytables.HDFStore'>
File path: mydata.h5
#包含在HDF5文件中的对象可以使用相同的字典型API进行检索:
store['obj1'].shape()
(100,1)

HDFStore支持两种存储模式,‘fixed’和‘table’。后者速度更慢,但支持一种特殊语法的查询操作

store.put('obj2',frame,format = 'table')
store.select('obj2',where = ['index>=10 and index <= 15'])
    a
10  -0.518309
11  0.198280
12  0.488141
13  -0.711851
14  -0.709542
15  0.754240
store.close()

put是store['obj2']=frame 方法的显示版本,但允许我们设置其他选项,如存储格式。

#pandas.read_hdf函数是这些工具的快捷方法
frame.to_hdf('my_data.h5','obj3',format = 'table')
pd.read_hdf('my_data.h5','obj3',where =['index<5'] )
    a
0   -1.260942
1   1.297276
2   -0.125102
3   0.211125
4   0.731398

!!HDF5并不是数据库,它是一种适合一次写入多次读取的数据集。尽管数据可以在嗯和实践添加到文件中,但如果多个写入者持续写入,文件可能会损坏。

读取Microdoft Excel文件

pandas也支持通过ExcelFile类或pandas.read_excel函数来读取存储在Excel文件中的表格型数据。
使用ExcelFile时,通过将xls或xlsx的路径传入,生成一个实例

xlsx = pd.ExcelFile('ex1.xlsx')
#存储在表中的数据可以通过pandas.read_excel读取到DataFrame中
pd.read_excel(xlsx,'Sheet1')
   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
#如果你读取的是含有多个表的文件,生成ExcelFile更快,但你也可以更简洁地将文件名传入pandas.read_excel:
frame = pd.read_excel('ex1.xlsx','Sheet1')
frame
   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方法将数据写入  
writer = pd.ExcelWriter('ex2.xlsx')
frame.to_excel(writer,'Sheet1')
writer.save()
#你也可以直接将文件路径传入to_excel,避免直接调用ExcelWriter:
frame.to_excel('ex2.xlsx')

与Web API交互

有多种方式可以利用Python来访问API,我推荐简单易用的方式是使用requests包

要获取GitHub上最新的30条关于pandas的问题,我们使用附加库requests发送一个HTTP GET请求

import numpy as np
import pandas as pd
from pandas import Series,DataFrame
import requests
url = 'https://api.github.com/repos/pandas-dev/pandas/issues'
resp = requests.get(url)
resp
输出#<Response [200]>
#Response(响应)对象的json方法将返回一个包含解析为本地Python对象的json字典
data = resp.json()
data[0]['title']
"`The default of the 'keep_tz' keyword in DatetimeIndex.to_series will change to True in a future release.`"
#data中的每个元素都是一个包含DitHub问题页面上的所有数据的字典(除注释外)
#我们可以直接将data传给DataFrame,并提取感兴趣的字段
issues = pd.DataFrame(data,columns=['number','title','labels','state'])
issues.head()
    number  title                                             labels    state
0   28083   `The default of the 'keep_tz' keyword in Datet...   []  open
1   28082   Change `randn` to `np.random.randn` in v0.10.0...   [{'id': 134699, 'node_id': 'MDU6TGFiZWwxMzQ2OT...   open
2   28081   REF: separate bloated test  []  open
3   28080   BUG: Series[int] + datetime64 should raise  []  open
4   28079   API: flex comparisons DataFrame vs Series inco...   [{'id': 35818298, 'node_id': 'MDU6TGFiZWwzNTgx...   open
5   28078   Auto backport of pr 28074 on 0.25.x     []  open

与数据库交互

...

上一篇下一篇

猜你喜欢

热点阅读