读写文本格式的数据
pandas提供了一些用于将表格型数据读取为DataFrame对象的函数。
pandas中的解析函数
函数 | 说明 |
---|---|
read_csv | 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为逗号 |
read_table | 从文件、URL、文件型对象中加载带分隔符的数据。默认分隔符为制表符 |
read_fwf | 读取定宽列格式数据(即没有分隔符) |
read_clipboard | 读取剪贴板中的数据,可以看做read_table 的剪贴板版。在网页转换为表格时很有用 |
大致介绍一下这些函数在将文本数据转换为DataFrame时所用到的一些技术。这些函数的选项可以划分为以下几个大类:
- 索引:将一个或多个列当作返回的DataFrame处理,以及是否从文件,用户获取列名。
- 类型推断和数据转换:包括用户定义值的转换、缺失值标记列表等。
- 日期解析:包括组合功能,比如将分散在多个列中的日期时间信息组合成结果中的单个列。
- 迭代:支持对大文件进行逐块迭代。
- 不规整数据问题:跳过一些行、页脚、注释或其他一些不重要的东西(比如由成千上万个逗号隔开的数值数据)。
类型推断是这些函数中最重要的功能之一,我们不需要指定列的类型到底是数值、整数、布尔值,还是字符串。首先看一个以逗号分隔(CSV)文本文件:
In [4]: !type ex1.csv
a,b,c,d,message
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
使用read_csv将其读入一个DataFrame:
In [5]: df = pd.read_csv('ex1.csv')
In [6]: df
Out[6]:
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
,只不过要指定分隔符而已:
In [8]: pd.read_table('ex1.csv',sep=',')
Out[8]:
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
并不是所有文件都有标题行,看下面这个文件:
In [9]: !type ex2.csv
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
读入该文件的办法有两个。可以让pandas为其分配默认的列名,也可以自己定义列名:
In [10]: pd.read_csv('ex2.csv',header=None)
Out[10]:
0 1 2 3 4
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
In [15]: pd.read_csv('ex2.csv',names=['a','b',
...: 'c','d','message'])
Out[15]:
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的索引,可以如下设置:
In [19]: pd.read_csv('ex2.csv',names=names,index_col=4)
Out[19]:
a b c d
message
hello 1 2 3 4
world 5 6 7 8
foo 9 10 11 12
In [20]: pd.read_csv('ex2.csv',names=names,index_col='message')
Out[20]:
a b c d
message
hello 1 2 3 4
world 5 6 7 8
foo 9 10 11 12
如果希望将多个列做成一个层次化索引,只需传入有列编号或列名组成的列表即可:
In [27]: !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
In [28]: parsed = pd.read_csv('csv_mindex.csv',index_col=['key1','key2'])
In [29]: parsed
Out[29]:
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的分隔符。
In [31]: list(open('ex3.txt'))
Out[31]:
[' 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']
In [32]: result = pd.read_table('ex3.txt',sep='\s+')
In [33]: result
Out[33]:
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
这些解析器函数还有许多参数可以帮助你处理各种各样的异形文件格式。比如,可以用skiprows跳过文件的第一行、第三行和第四行:
In [39]: !type ex4.csv
# hey!
a,b,c,d,message
# just wanted to make things more difficult for you
# who reads CSV files with computers, anyway?
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
In [40]: pd.read_csv('ex4.csv',skiprows=[0,2,3])
Out[40]:
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、-1.#IND以及NULL等:
In [41]: !type ex5.csv
something,a,b,c,d,message
one,1,2,3,4,NA
two,5,6,,8,world
three,9,10,11,12,foo
In [42]: result = pd.read_csv('ex5.csv')
In [43]: result
Out[43]:
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
In [44]: pd.isnull(result)
Out[44]:
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可以接受一组用于表示缺失值的字符串:
In [45]: result = pd.read_csv('ex5.csv',na_values=['NULL'])
In [46]: result
Out[46]:
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
可以用一个字典为各列指定不同的NA标记值:
In [48]: pd.read_csv('ex5.csv',na_values=sentinels)
Out[48]:
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
In [49]: pd.read_csv('ex5.csv')
Out[49]:
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
read_csv
/read_table
函数的参数
参数 | 说明 |
---|---|
path | 表示文件系统位置,URL,文件型对象的字符串 |
sep或delimiter | 用于对行中各字段进行拆分的字符序列或正则表达式 |
header | 用作列名的行号。默认为0(第一行),如果没有header行就应该设置为None |
index_col | 用作行索引的列编号或列名。可以是单个名称/数字或由多个名称/数字组成的列表(层次化索引) |
names | 用于结果的列名列表,结合header=None |
skiprows | 需要忽略的行号,或需要跳过的行号列表 |
na_values | 一组用于替换NA的值 |
comment | 用于将注释信息从行尾拆分出去的字符(一个或多个) |
parse_dates | 尝试将数据解析为日期,默认为False |
keep_data_col |
如果连接多列解析日期,则保持参与连接的列。默认为False |
converters | 由列号/列名跟函数之间的映射关系组成的字典。例如,{'foo':f}会对foo列的所有值应用函数f |
dayfirst | 当解析由歧义的日期时,将其看作国际格式。默认为False |
data_parser | 用于解析日期的函数 |
nrows | 需要读取的行数(从文件开始处算起) |
iterator | 返回一个TextParser以便逐块读取文件 |
chunksize | 文件块的大小(用于迭代) |
skip_footer | 需要忽略的行数(从文件末尾处算起) |
verbose | 打印各种解释器输出的信息,比如“非数值列中缺失值的数量”等 |
encoding | 用于unicode的文本编码格式 |
squeeze | 如果数据经解析后仅含一行,则返回Series |
thousands | 千分位分隔符,如“,”或“.” |
逐块读取文本文件
在处理很大的文件时,或找出大文件中的参数集以便于后续处理时,你可能只想读取文件中的一小部分或逐块对文件进行迭代。
In [53]: result = pd.read_csv('ex6.csv')
In [54]: result
Out[54]:
one two three four key
0 0.467976 -0.038649 -0.295344 -1.824726 L
1 -0.358893 1.404453 0.704965 -0.200638 B
2 -0.501840 0.659254 -0.421691 -0.057688 G
3 0.204886 1.074134 1.388361 -0.982404 R
4 0.354628 -0.133116 0.283763 -0.837063 Q
5 1.817480 0.742273 0.419395 -2.251035 Q
6 -0.776764 0.935518 -0.332872 -1.875641 U
7 -0.913135 1.530624 -0.572657 0.477252 K
8 0.358480 -0.497572 -0.367016 0.507702 S
9 -1.740877 -1.160417 -1.637830 2.172201 G
10 0.240564 -0.328249 1.252155 1.072796 8
11 0.764018 1.165476 -0.639544 1.495258 R
12 0.571035 -0.310537 0.582437 -0.298765 1
13 2.317658 0.430710 -1.334216 0.199679 P
14 1.547771 -1.119753 -2.277634 0.329586 J
15 -1.310608 0.401719 -1.000987 1.156708 E
16 -0.088496 0.634712 0.153324 0.415335 B
17 -0.018663 -0.247487 -1.446522 0.750938 A
18 -0.070127 -1.579097 0.120892 0.671432 F
19 -0.194678 -0.492039 2.359605 0.319810 H
20 -0.248618 0.868707 -0.492226 -0.717959 W
21 -1.091549 -0.867110 -0.647760 -0.832562 C
22 0.641404 -0.138822 -0.621963 -0.284839 C
23 1.216408 0.992687 0.165162 -0.069619 V
24 -0.564474 0.792832 0.747053 0.571675 I
25 1.759879 -0.515666 -0.230481 1.362317 S
26 0.126266 0.309281 0.382820 -0.239199 L
27 1.334360 -0.100152 -0.840731 -0.643967 6
28 -0.737620 0.278087 -0.053235 -0.950972 J
29 -1.148486 -0.986292 -0.144963 0.124362 Y
... ... ... ... ... ..
9970 0.633495 -0.186524 0.927627 0.143164 4
9971 0.308636 -0.112857 0.762842 -1.072977 1
9972 -1.627051 -0.978151 0.154745 -1.229037 Z
9973 0.314847 0.097989 0.199608 0.955193 P
9974 1.666907 0.992005 0.496128 -0.686391 S
9975 0.010603 0.708540 -1.258711 0.226541 K
9976 0.118693 -0.714455 -0.501342 -0.254764 K
9977 0.302616 -2.011527 -0.628085 0.768827 H
9978 -0.098572 1.769086 -0.215027 -0.053076 A
9979 -0.019058 1.964994 0.738538 -0.883776 F
9980 -0.595349 0.001781 -1.423355 -1.458477 M
9981 1.392170 -1.396560 -1.425306 -0.847535 H
9982 -0.896029 -0.152287 1.924483 0.365184 6
9983 -2.274642 -0.901874 1.500352 0.996541 N
9984 -0.301898 1.019906 1.102160 2.624526 I
9985 -2.548389 -0.585374 1.496201 -0.718815 D
9986 -0.064588 0.759292 -1.568415 -0.420933 E
9987 -0.143365 -1.111760 -1.815581 0.435274 2
9988 -0.070412 -1.055921 0.338017 -0.440763 X
9989 0.649148 0.994273 -1.384227 0.485120 Q
9990 -0.370769 0.404356 -1.051628 -1.050899 8
9991 -0.409980 0.155627 -0.818990 1.277350 W
9992 0.301214 -1.111203 0.668258 0.671922 A
9993 1.821117 0.416445 0.173874 0.505118 X
9994 0.068804 1.322759 0.802346 0.223618 H
9995 2.311896 -0.417070 -1.409599 -0.515821 L
9996 -0.479893 -0.650419 0.745152 -0.646038 E
9997 0.523331 0.787112 0.486066 1.093156 K
9998 -0.362559 0.598894 -1.843201 0.887292 G
9999 -0.096376 -1.012999 -0.657431 -0.573315 0
[10000 rows x 5 columns]
如果只想读取几行(避免读取整个文件),可以用nrows指定:
In [55]: pd.read_csv('ex6.csv',nrows=5)
Out[55]:
one two three four key
0 0.467976 -0.038649 -0.295344 -1.824726 L
1 -0.358893 1.404453 0.704965 -0.200638 B
2 -0.501840 0.659254 -0.421691 -0.057688 G
3 0.204886 1.074134 1.388361 -0.982404 R
4 0.354628 -0.133116 0.283763 -0.837063 Q
需逐块读取文件,需要设置chunksize(行数):
In [56]: chunker = pd.read_csv('ex6.csv',chunksize=1000)
In [57]: chunker
Out[57]: <pandas.io.parsers.TextFileReader at 0x289f2f3a438>
read_csv所返回的这个TextParser对象使你可以根据chunksize对文件进行逐块迭代。比如说,我们可以迭代处理ex6.csv,将值计数聚合到“key”列中,如下:
In [58]: chunker = pd.read_csv('ex6.csv',chunksize=1000)
In [59]: tot = pd.Series([])
In [60]: for piece in chunker:
...: tot = tot.add(piece['key'].value_counts(),fill_value=0)
...:
In [61]: tot = tot.sort_values(ascending=False)
In [63]: tot[:10]
Out[63]:
E 368.0
X 364.0
L 346.0
O 343.0
Q 340.0
M 338.0
J 337.0
F 335.0
K 334.0
H 330.0
dtype: float64
TextParser还有一个get_chunk方法,它使你可以读取任何大小的块。
将数据写出到文本格式
利用DataFrame的to_csv方法,我们可以将数据写到一个以逗号分隔的文件中:
In [73]: data = pd.read_csv('ex5.csv')
In [74]: data
Out[74]:
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
In [75]: !type ex5.csv
something,a,b,c,d,message
one,1,2,3,4,NA
two,5,6,,8,world
three,9,10,11,12,foo
In [76]: data = pd.read_csv('ex5.csv')
In [77]: data
Out[77]:
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
In [78]: data.to_csv('out.csv')
In [79]: !type out.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
当然,还可以使用其他分隔符:
In [80]: import sys
In [81]: data.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
缺失值在输出结果中会被表示为空字符串。你可能希望将其表示为别的标记值:
In [82]: data.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
如果没有设置其他选项,则会写出行和列的标签。也可以被禁用:
In [83]: data.to_csv(sys.stdout, index=False,header=False)
one,1,2,3.0,4,
two,5,6,,8,world
three,9,10,11.0,12,foo
此外,你还可以只写出一部分的列,并以你指定的顺序排列:
In [84]: data.to_csv(sys.stdout, index=False,columns=['a','b','c'])
a,b,c
1,2,3.0
5,6,
9,10,11.0
Series也有一个to_csv方法:
In [85]: dates = pd.date_range('1/16/2018',periods=7)
In [86]: ts = pd.Series(np.arange(7),index=dates)
In [87]: ts.to_csv('tseries.csv')
In [88]: !type tseries.csv
2018-01-16,0
2018-01-17,1
2018-01-18,2
2018-01-19,3
2018-01-20,4
2018-01-21,5
2018-01-22,6
虽然只需一点整理工作(无header行,第一列作索引)就能用read_csv将CSV文件读取为Series,但还有一个更为方便的from_csv
方法:
In [93]: pd.read_csv('tseries.csv',header=None,index_col=0)
Out[93]:
1
0
2018-01-16 0
2018-01-17 1
2018-01-18 2
2018-01-19 3
2018-01-20 4
2018-01-21 5
2018-01-22 6
In [94]: pd.Series.from_csv('tseries.csv',parse_dates=True)
Out[94]:
2018-01-16 0
2018-01-17 1
2018-01-18 2
2018-01-19 3
2018-01-20 4
2018-01-21 5
2018-01-22 6
dtype: int64
手工处理分隔符格式
大部分表格型数据都能用pandas.read_table进行加载。然而,有时还是需要做一些手工处理,因为接收到含有畸形行的文件而使read_table
出问题的情况并不少见。如下:
In [96]: !type ex7.csv
"a","b","c"
"1","2","3"
"1","2","3","4"
对于任何单字符分隔符文件,可以直接使用Python内置的csv模块。将任意已打开的文件或文件型的对象传给csv.reader:
In [97]: import csv
In [98]: f = open('ex7.csv')
In [99]: reader = csv.reader(f)
# 为每行产生一个列表(并移除了所有的引号)
In [100]: for line in reader:
...: print(line)
...:
['a', 'b', 'c']
['1', '2', '3']
['1', '2', '3', '4']
为了使格式合乎要求,要对其做些整理工作:
In [103]: lines = list(csv.reader(open('ex7.csv')))
In [104]: header,values = lines[0],lines[1:]
In [105]: data_dict = {h:v for h,v in zip(header,zip(*values))}
In [106]: data_dict
Out[106]: {'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}
JSON数据
JSON已经成为通过HTTP请求在Web浏览器和其他应用程序之间发送数据的标准格式之一。
使用Python标准库中的json,通过json.loads即可将JSON字符串转换成Python形式,用json.dumps则将Python对象转换成JSON格式。
使用数据库
在许多应用中,数据很少取自文本文件,因为这种方式存储大量数据很低效,基于SQL的关系型数据库和一些非SQL型数据库使用非常广泛。
将数据从SQL加载到DataFrame的过程很简单,pandas还有一些能简化该过程的函数。我们使用嵌入式的SQLite数据库:
import sqlite3
query = "create table test (a VARCHAR(2),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驱动器都会返回一个元组列表:
In [124]: cursor = con.execute('select * from test')
In [125]: rows = cursor.fetchall()
In [126]: rows
Out[126]:
[('Atlanta', 'Georgia', 1.25, 6),
('Tallahassee', 'Florida', 2.6, 3),
('Sacramento', 'California', 1.7, 5)]
你可以将这个元组列表传给DataFrame构造器,但还需要列名(位于光标的description属性中):
In [132]: cursor.description
Out[132]:
(('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 [133]: pd.DataFrame(rows,columns=[x[0] for x in cursor.description])
Out[133]:
a b c d
0 Atlanta Georgia 1.25 6
1 Tallahassee Florida 2.60 3
2 Sacramento California 1.70 5
这种数据规整操作相当多,你肯定不想每查一次数据库就重写一次。SQLAlchemy项目是一个流行的Python SQL工具,它抽象出了SQL数据库中的许多常见差异。pandas有一个read_sql函数,可以让你轻松的从SQLAlchemy连接读取数据。这里,我们用SQLAlchemy连接SQLite数据库,并从之前创建的表读取数据:(引自:简书作者SeanCheney 链接:https://www.jianshu.com/p/047d8c1c7e14)
In [134]: import sqlalchemy as sqla
In [135]: db = sqla.create_engine('sqlite:///memory')
In [136]: pd.read_sql('select * from test', db)
Out[136]:
a b c d
0 Atlanta Georgia 1.25 6
1 Tallahassee Florida 2.60 3
2 Sacramento California 1.70 5
以上。
网友评论