常用函数
pandas提供了一些用于将表格型数据读取为DataFrame对象的函数。下表对它们进行了总结,其中read_csv
和read_table
可能会是你今后用得最多的
这些函数的选项可以划分为以下几个大类
- 索引:将一个或多个列当做返回的DataFrame处理,以及是否从文件、用户获取列名
- 类型推断和数据转换:包括用户定义值的转换、和自定义的缺失值标记列表等
- 日期解析:包括组合功能,比如将分散在多个列中的日期时间信息组合成结果中的单个列
- 迭代:支持对大文件进行逐块迭代。
不规整数据问题:跳过一些行、页脚、注释或其他一些不重要的东西(比如由成千上万个逗号隔开的数值数据)
日期和其他自定义类型的处理需要多花点工夫才行。首先我们来看一个以逗号分隔的(CSV)文本文件In [2]: !type examples\\ex1.csv
a,b,c,d,message
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
这里我是用的是Window Powershell的命令,如果是Linux,就把 [type] 换成cat,然后两个反斜杠’\’ 换成一个普通的斜杠’/‘就行了
由于该文件以逗号分隔,所以我们可以使用read_csv
将其读入一个DataFrame
In [5]: df = pd.read_csv('examples/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 [7]: df = pd.read_table('examples/ex1.csv', sep=',')
In [8]: df
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 examples\\ex2.csv
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
读入该文件的办法有两个。你可以让pandas为其分配默认的列名,也可以自己定义列名
In [10]: pd.read_csv('examples/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 [11]: pd.read_csv('examples/ex2.csv', names=['a', 'b', 'c', 'd', 'message'])
Out[11]:
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的位置上也可以通过inedx_col
参数指定”message“
In [12]: names = ['a', 'b', 'c', 'd', 'message']
In [13]: pd.read_csv('examples/ex2.csv', names=names, index_col='message')
Out[13]:
a b c d
message
hello 1 2 3 4
world 5 6 7 8
foo 9 10 11 12
如果希望将多个列做成一个层次化索引,只需传入由列编号或列名组成的列表即可
In [14]: !type examples\\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 [15]: parsed = pd.read_csv('examples/csv_mindex.csv', index_col=['key1', 'key2'])
In [16]: parsed
Out[16]:
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
有些情况下,有些表格可能不是用固定的分隔符去分隔字段的(比如空白符或其它模式)
In [3]: list(open('examples/ex3.txt'))
Out[3]:
[' 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_table
的分隔符。可以用正则表达式表达为\s+
,于是有
In [4]: result = pd.read_table('examples/ex3.txt', sep='\s+')
In [5]: result
Out[5]:
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_table
推断第一列应该是DataFrame的索引
这些解析器函数还有许多参数可以帮助你处理各种各样的异形文件格式,下面的表格中列出来了一些。
比如说,你可以用 skiprows
跳过文件的第一行、第三行和第四行
In [6]: !type examples\\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 [7]: pd.read_csv('examples/ex4.csv', skiprows=[0,2,3])
Out[7]:
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
In [8]: !type examples\\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 [9]: result = pd.read_csv('examples/ex5.csv')
In [10]: result
Out[10]:
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 [11]: pd.isnull(result)
Out[11]:
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 [12]: result = pd.read_csv('examples/ex5.csv', na_values=['NULL'])
In [13]: result
Out[13]:
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 [16]: pd.read_csv('examples/ex5.csv', na_values=sentinels)
Out[16]:
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
逐块读取文本文件
在处理很大的文件时,或找出大文件中的参数集以便于后续处理时,你可能只想读取文件的一小部分或逐块对文件进行迭代
在看大文件之前, 我们先设置pandas显示地更紧些
In [17]: pd.options.display.max_rows = 10
In [18]: result = pd.read_csv('examples/ex6.csv')
In [19]: result
Out[19]:
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
... ... ... ... ... ..
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 [20]: pd.read_csv('examples/ex6.csv', nrows=5)
Out[20]:
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
(行数)
>>> chunker = pd.read_csv('ch06/ex6.csv', chunksize=1000)
>>> chunker
<pandas.io.parsers.TextParser at 0x8398150>
read_csv
所返回的这个TextParser
对象使你可以根据chunksize
对文件进行逐块迭代。比如说,我们可以迭代处理ex6.csv, 将值计数聚合到”key“列中, 如下所示
In [21]: chunker = pd.read_csv('examples/ex6.csv', chunksize=1000)
In [22]: tot = pd.Series([])
In [24]: for piece in chunker:
...: tot = tot.add(piece['key'].value_counts(), fill_value=0)
In [25]: tot = tot.sort_values(ascending=False)
In [26]: tot[:10]
Out[26]:
E 336.0
X 327.0
L 315.0
M 309.0
K 303.0
Q 301.0
O 299.0
P 299.0
J 298.0
F 295.0
dtype: float64
TextParser
还有一个get_chunk
方法,它使你可以读取任意大小的块
将数据写出到文件格式
数据也可以被输出为分隔符格式的文本。我们再来看看之前读过的一个CSV文件
In [27]: data = pd.read_csv('examples/ex5.csv')
In [28]: data
Out[28]:
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
方法,我们可以将数据写到一个以逗号分隔的文件中
In [29]: data.to_csv('examples/out.csv')
In [30]: !type examples\\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
当然,还可以使用其他分隔符(由于这里直接写出到sys.stdout
,所以仅仅是打印出文本结果而已)
In [31]: import sys
In [32]: 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 [33]: 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
如果没有设置其他选项,则会写出行和列的标签。当然,它们也都可以被禁用
one,1,2,3.0,4,
two,5,6,,8,world
three,9,10,11.0,12,foo
此外,你还可以只写出一部分的列,并以你指定的顺序排列
In [35]: 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 [36]: dates = pd.date_range('1/1/2000', periods=7)
In [37]: ts = pd.Series(np.arange(7), index=dates)
In [39]: ts.to_csv('examples/tseries.csv')
In [40]: !type examples\\tseries.csv
2000-01-01,0
2000-01-02,1
2000-01-03,2
2000-01-04,3
2000-01-05,4
2000-01-06,5
2000-01-07,6
处理分隔符格式
大部分存储在磁盘上的表格型数据都能用 pandas.read_table
进行加载。然而,有时还是需要做一些手工处理。 由于接收到含有畸形行的文件而使read_table
出毛病的情况并不少见。 为了说明这些基本工具, 看看下面这个简单的CSV文件
In [3]: !type examples\\ex7.csv
"a","b","c"
"1","2","3"
"1","2","3"
对于任何单字符分隔符文件, 可以直接使用Python内置的csv模块。将任意已打开的文件或文件型的对象传给 csv.reader
In [4]: import csv
In [5]: f = open('examples/ex7.csv')
In [6]: reader = csv.reader(f)
In [7]: for line in reader:
...: print(line)
['a', 'b', 'c']
['1', '2', '3']
['1', '2', '3']
现在,为了使数据格式合乎要求,你需要对其做一些整理工作。我们一步一步来做。首先,读取文件到一个多行的列表中
In [8]: with open('examples/ex7.csv') as f:
...: lines = list(csv.reader(f))
然后,我们将这些行分为标题行和数据行
In [9]: header, values = lines[0], lines[1:]
然后,我们可以用字典构造式和 zip(*values)
,后者将行转置为列,创建数据列的字典
In [10]: data_dict = {h : v for h, v in zip(header, zip(*values))}
In [11]: data_dict
Out[11]: {'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}
CSV文件的形式有很多。只需定义csv.Dialect
的一个子类即可定义出新格式(如专门的分隔符、字符串引用约定、行结束符等)
class my_dialect(csv.Dialect):
lineterminator = '\n'
delimiter = ';'
quotechar = '"'
quoting = csv.QUOTE_MINIMAL
reader = csv.reader(f, dialect=my_dialect)
各个CSV语支的参数也可以用关键字的形式提供给csv.reader
,而无需定义子类
reader = csv.reader(f, delimiter='|')
下表是 csv.Dialect
的属性
注意:对于那些使用复杂分隔符或多字符分隔符的文件,csv模块就无能为力了。 这种情况下, 你就只能使用字符串的split方法或正则表达式方法re.split进行行拆分和其他整理工作了
要手工输出分隔符文件,你可以使用 csv.writer
。它接受一个已打开且可写的文件对象以及跟csv.reader
相同的那些语支和格式化选项
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'))
JSON数据
除其空值null和一些其他的细微差别(如列表末尾不允许存在多余的逗号)之外,JSON非常接近于有效的Python代码。基本类型有对象(字典)、数组(列表)、字符串、数值、布尔值以及null。对象中所有的键都必须是字符串。许多Python库都可以读写JSON数据。我将使用json, 因为它是构建于Python标准库中的。通过json.load
即可将JSON字符串转换成Python形式
In [12]: ojb = obj = """
...: {"name": "Wes",
...: "places_lived": ["United States", "Spain", "Germany"],
...: "pet": null,
...: "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]},
...: {"name": "Katie", "age": 38,
...: "pets":["Sixes", "Stache", "Cisco"]}]
...: }
...: """
In [13]: import json
In [14]: result = json.loads(obj)
In [15]: result
Out[15]:
{'name': 'Wes',
'places_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格式
In [16]: asjson = json.dumps(result)
如何将(一个或一组)JSON对象转换为DataFrame或其他便于分析的数据结构就由你决定了。 最简单方便的方式是:向DataFrame构造器传入一个字典的列表(就是原先的JSON对象),并选取数据字段的子集
In [17]: siblings = pd.DataFrame(result['siblings'], columns=['name', 'age'])
In [18]: siblings
Out[18]:
name age
0 Scott 30
1 Katie 38
pandas.read_json
可以自动将特别格式的JSON数据集转换为Series或DataFrame
In [20]: !type examples\\example.json
[{"a": 1, "b": 2, "c": 3},
{"a": 4, "b": 5, "c": 6},
{"a": 7, "b": 8, "c": 9}]
pandas.read_json
的默认选项假设JSON数组中的每个对象是表格中的一行
In [21]: data = pd.read_json('examples/example.json')
In [22]: data
Out[22]:
a b c
0 1 2 3
1 4 5 6
2 7 8 9
如果你需要将数据从pandas输出到JSON,可以使用to_json
方法
In [23]: print(data.to_json())
{"a":{"0":1,"1":4,"2":7},"b":{"0":2,"1":5,"2":8},"c":{"0":3,"1":6,"2":9}}
In [24]: print(data.to_json(orient='records'))
[{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]
XML和HTML:WEB信息收集
Python有许多可以读写常见的HTML和XML格式数据的库,包括lxml、Beautiful Soup和html5lib。lxml的速度比较快,但其它的库处理有误的HTML或XML文件更好
conda install lxml
pip install beautifulsoup4 html5lib
pandas.read_html
有一些选项,默认条件下,它会搜索、尝试解析标签内的的表格数据。结果是一个列表的DataFrame对象
In [2]: tables = pd.read_html('examples/fdic_failed_bank_list.html')
In [3]: len(tables)
Out[3]: 1
In [4]: failures = tables[0]
In [5]: failures.head()
Out[5]:
Bank Name City ... Closing Date Updated Date
0 Allied Bank Mulberry ... September 23, 2016 November 17, 2016
1 The Woodbury Banking Company Woodbury ... August 19, 2016 November 17, 2016
2 First CornerStone Bank King of Prussia ... May 6, 2016 September 6, 2016
3 Trust Company Bank Memphis ... April 29, 2016 September 6, 2016
4 North Milwaukee State Bank Milwaukee ... March 11, 2016 June 16, 2016
这里,我们可以做一些数据清洗和分析(后面章节会进一步讲解),比如计算按年份计算倒闭的银行数
In [9]: close_timestamps = pd.to_datetime(failures['Closing Date'])
In [10]: close_timestamps
Out[10]:
0 2016-09-23
1 2016-08-19
2 2016-05-06
3 2016-04-29
4 2016-03-11
...
542 2001-07-27
543 2001-05-03
544 2001-02-02
545 2000-12-14
546 2000-10-13
Name: Closing Date, Length: 547, dtype: datetime64[ns]
In [11]: close_timestamps.value_counts()
Out[11]:
2009-10-30 9
2010-04-16 8
2010-08-20 8
2009-07-24 7
2010-07-23 7
..
2008-09-19 1
2014-10-24 1
2016-08-19 1
2008-08-22 1
2001-05-03 1
Name: Closing Date, Length: 243, dtype: int64
利用lxml.objectify解析XML
我们先用 lxml.objectify
解析该文件,然后通过getroot
得到该XML文件的根节点的引用
In [12]: from lxml import objectify
In [13]: path = 'datasets/mta_perf/Performance_MNR.xml'
In [14]: parsed = objectify.parse(open(path))
In [15]: root = parsed.getroot()
root.INDICATOR
返回一个用于产生各个XML元素的生成器。对于每条记录,我们可以用标记名(YTD_ACTUAL)和数据值填充一个字典(排除几个标记)
In [16]: data = []
In [17]: skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ', 'DESIRED_CHANGE', 'DECIMAL_PLACES']
In [18]: 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)
最后,将这组字典转换为一个DataFrame
In [19]: perf = pd.DataFrame(data)
In [20]: perf.head()
Out[20]:
AGENCY_NAME INDICATOR_NAME ... MONTHLY_TARGET MONTHLY_ACTUAL
0 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 96.9
1 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 95
2 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 96.9
3 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 98.3
4 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 95.8