访问数据是使用各类工具所必需的第一步。我们将重点关注使用pandas进行数据输入和输出,尽管其他库中有许多工具可帮助读取和写入各种格式的数据。
输入和输出通常有以下几种类型:读取文本文件及硬盘上其他更高效的格式文件、从数据库载入数据、与网络资源进行交互(比如Web API)。
1.1文本格式数据的读写
将表格型数据读取为DataFrame对象是pandas的重要特性。
表1-1:Pandas的解析函数
函数 | 描述 |
---|---|
read_csv | 从文件、URL或文件型对象读取分隔好的数据,逗号是默认分隔符 |
read_table | 从文件、URL或文件型对象读取分隔好的数据,制表符(‘\t’)是默认分隔符 |
read_excel | 从Excel的XLS或XLSX文件中读取表格数据 |
read_html | 从HTML文件中读取所有表格数据 |
read_json | 从JSON字符串中读取数据 |
read_sas | 读取存储在SAS系统中定制存储格式的SAS数据集 |
read_sql | 将SQL查询的结果读取为pandas的DataFrame |
一些数据载入函数,比如pandas.read_csv,会进行类型判断,因为列的数据类型并不是数据格式的一部分。那就意味着我们不必指定哪一列是数值、整数、布尔值或字符串。
让我们从一个小型的逗号分隔文本文件(CSV)开始:
examples.csv
a,b,c,d,message
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
由于这个文件是逗号分隔的,我们可以使用read_csv将它读入一个DataFrame:
import pandas as pd
df = pd.read_csv('examples.csv')
print(df)
------------------------
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
我们也可以使用read_table,并指定分隔符:
df = pd.read_table('examples.csv',sep = ',')
print(df)
------------------------
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
有的文件不包含表投行。考虑以下文件:
examples1.csv
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
我们可以允许pandas自动分配默认列名,也可以自己指定列名:
df = pd.read_csv('examples1.csv',header = None)
print(df)
-----------------------
0 1 2 3 4
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
df = pd.read_csv('examples1.csv',names = ['a','b','c','d','message'])
print(df)
------------------------
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的索引,我们可以这样操作:
names = ['a','b','c','d','message']
df = pd.read_csv('examples1.csv',names = names,index_col = 'message')
print(df)
----------------------
a b c d
message
hello 1 2 3 4
world 5 6 7 8
foo 9 10 11 12
当你想要从多个列中形成一个分层索引,需要传入一个包含列序号或列名的列表:
examples2.csv
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('examples2.csv',index_col = ['key1','key2'])
print(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
在某些情况下,一张表的分隔符并不是固定的,使用空白或其他方法来分隔字段,在这些情况下也可以向read_table传入一个正则表达式作为分隔符。
解析函数有很多附加参数帮助我们来处理发生异常的文件格式,例如,我们可以使用skiprows来跳过第一行、第三行和第四行:
examples3.csv
嘿!
a,b,c,d,message
只是为了让你觉得更难
谁用计算机读取CSV文件?
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
df = pd.read_csv('examples3.csv',skiprows = [0,2,3])
print(df)
------------------------
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
缺失值处理是文件解析过程中一个重要的部分。通常情况下,缺失值要么不显示(空字符串),要么用一些标识值。默认情况下,pandas使用一些常见的标识,例如NA和NULL:
examples4.csv
something,a,b,c,d,message
one,1,2,3,4,NA
two,5,6,8,world
three,9,10,11,12,foo
result = pd.read_csv('examples4.csv')
print(result)
------------------------------------
something a b c d message
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 world
2 three 9 10 11.0 12 foo
print(pd.isnull(result))
-------------------------------------------------
something a b c d message
0 False False False False False True
1 False False False True False False
2 False False False False False False
na_values选项可以传入一个列表或一组字符串来处理缺失值:
sentinels = {'message':['foo'],'something':['two']}
print(pd.read_csv('examples4.csv',na_values = sentinels))
------------------------------------
something a b c d message
0 one 1 2 3.0 4 NaN
1 NaN 5 6 NaN 8 world
2 three 9 10 11.0 12 NaN
表1-1:一些read_csv/read_table函数参数
参数 | 描述 |
---|---|
path | 表明文件系统位置的字符串、URL或文件型对象 |
sep或delimeter | 用于分隔每行字段的字符串序列或正则表达式 |
header | 用作列名的行号,默认是0(第一行),如果没有列名的话,应该是None |
index_col | 用作结果中行索引的列号或者列名,可以是单一的名称/数字,也可以是一个分层索引 |
names | 结果的列名列表,和header=None一起用 |
skiprows | 从文件开头处起,需要跳过的行数或行号列表 |
na_values | 需要用NA替换的值序列 |
nrows | 从文件开头处读入的行数 |
encoding | Unicode文本编码 |
chunksize | 用于迭代的块大小 |
1.1.1将数据写入文本格式
数据可以导出为分隔的形式。让我们看下之前读取的CSV文件:
result = pd.read_csv('examples4.csv')
print(result)
------------------------------------
something a b c d message
0 one 1 2 3.0 4 NaN
1 two 5 6 NaN 8 world
2 three 9 10 11.0 12 foo
使用DataFrame的to_csv方法,我们可以将数据导出为逗号分隔的文件:
result.to_csv('examples5.csv')
examples5.csv
,something,a,b,c,d,message
0,one,1,2,3.0,4,
1,two,5,6,8,world
2,three,9,10,11.0,12,foo
当然,其他的分隔符也是可以的(写入到sys.stdout时,控制台中打印的文本结果):
import sys
result.to_csv('sys.stdout',sep = '|')
|something|a|b|c|d|message
0|one|1|2|3.0|4|
1|two|5|6||8|world
2|three|9|10|11.0|12|foo
缺失值在输出时以空字符串出现。我们也可以用其他标识值对缺失值进行标注:
result.to_csv('sys.stdout',na_rep = 'NULL')
,something,a,b,c,d,message
0,one,1,2,3.0,4,NULL
1,two,5,6,NULL,8,world
2,three,9,10,11.0,12,foo
如果没有其他选项被指定的话,行和列标签都会被写入。不过二者也都可以禁止写入:
result.to_csv('sys.stdout',header = False,index = False)
one,1,2,3.0,4,
two,5,6,8,world
three,9,10,11.0,12,foo
我们也可以仅仅写入列的子集,并且按照我们所选择的顺序写入:
result.to_csv('sys.stdout',columns = ['a','b','c'],index = False)
a,b,c
1,2,3.0
5,6,
9,10,11.0
1.1.2使用分割形式
绝大多数的表型数据都可以使用函数pandas.read_table从硬盘中读取。然而,某些情况下,一些手动操作可能是必不可少的。接收一个带有一行或多行错误的文件并不少见,read_table也无法解决这种情况。为了介绍一些基础工具,考虑如下的小型CSV文件:
examples6.csv
“a”,“b”,“c”
“1”,“2”,“3”
“1”,“2”,“3”
对于任何带有单字符分隔符的文件,我们可以使用python的内建csv模块。要使用它,需要将任一打开的文件或文件型对象传给csv.reader:
import csv
f = open('examples6.csv')
reader = csv.reader(f)
像遍历文件那样遍历reader会产生元祖,元祖的值为删除了引号的字符:
for line in reader:
print(line)
---------------
['a', 'b', 'c']
['1', '2', '3']
['1', '2', '3']
随后我们按部就班,首先将文件读取为行的列表:
with open('examples6.csv') as f:
lines = list(csv.reader(f))
然后,我们将数据拆分为列名行和数据行:
header,values = lines[0],lines[1:]
再然后,我们使用字典推导式和表达式zip(*values)生成一个包含数据列的字典,字典中行转置成列:
data_dict = {h:v for h,v in zip(header,zip(*values))}
print(data_dict)
---------------------------------------------------
{'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}