常用函数

pandas提供了一些用于将表格型数据读取为DataFrame对象的函数。下表对它们进行了总结,其中read_csvread_table 可能会是你今后用得最多的
image.png
这些函数的选项可以划分为以下几个大类

  • 索引:将一个或多个列当做返回的DataFrame处理,以及是否从文件、用户获取列名
  • 类型推断和数据转换:包括用户定义值的转换、和自定义的缺失值标记列表等
  • 日期解析:包括组合功能,比如将分散在多个列中的日期时间信息组合成结果中的单个列
  • 迭代:支持对大文件进行逐块迭代。
  • 不规整数据问题:跳过一些行、页脚、注释或其他一些不重要的东西(比如由成千上万个逗号隔开的数值数据)


    日期和其他自定义类型的处理需要多花点工夫才行。首先我们来看一个以逗号分隔的(CSV)文本文件

    1. In [2]: !type examples\\ex1.csv
    2. a,b,c,d,message
    3. 1,2,3,4,hello
    4. 5,6,7,8,world
    5. 9,10,11,12,foo

    这里我是用的是Window Powershell的命令,如果是Linux,就把 [type] 换成cat,然后两个反斜杠’\’ 换成一个普通的斜杠’/‘就行了

由于该文件以逗号分隔,所以我们可以使用read_csv 将其读入一个DataFrame

  1. In [5]: df = pd.read_csv('examples/ex1.csv')
  2. In [6]: df
  3. Out[6]:
  4. a b c d message
  5. 0 1 2 3 4 hello
  6. 1 5 6 7 8 world
  7. 2 9 10 11 12 foo

我们还可以使用read_table ,并指定分隔符

  1. In [7]: df = pd.read_table('examples/ex1.csv', sep=',')
  2. In [8]: df
  3. Out[8]:
  4. a b c d message
  5. 0 1 2 3 4 hello
  6. 1 5 6 7 8 world
  7. 2 9 10 11 12 foo

并不是所有文件都有标题行。看看下面这个文件

  1. In [9]: !type examples\\ex2.csv
  2. 1,2,3,4,hello
  3. 5,6,7,8,world
  4. 9,10,11,12,foo

读入该文件的办法有两个。你可以让pandas为其分配默认的列名,也可以自己定义列名

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

假设你希望将message 列做成DataFrame的索引。你可以明确表示要将该列放到索引4的位置上也可以通过inedx_col 参数指定”message

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

如果希望将多个列做成一个层次化索引,只需传入由列编号或列名组成的列表即可

  1. In [14]: !type examples\\csv_mindex.csv
  2. key1,key2,value1,value2
  3. one,a,1,2
  4. one,b,3,4
  5. one,c,5,6
  6. one,d,7,8
  7. two,a,9,10
  8. two,b,11,12
  9. two,c,13,14
  10. two,d,15,16
  11. In [15]: parsed = pd.read_csv('examples/csv_mindex.csv', index_col=['key1', 'key2'])
  12. In [16]: parsed
  13. Out[16]:
  14. value1 value2
  15. key1 key2
  16. one a 1 2
  17. b 3 4
  18. c 5 6
  19. d 7 8
  20. two a 9 10
  21. b 11 12
  22. c 13 14
  23. d 15 16

有些情况下,有些表格可能不是用固定的分隔符去分隔字段的(比如空白符或其它模式)

  1. In [3]: list(open('examples/ex3.txt'))
  2. Out[3]:
  3. [' A B C\n',
  4. 'aaa -0.264438 -1.026059 -0.619500\n',
  5. 'bbb 0.927272 0.302904 -0.032399\n',
  6. 'ccc -0.264273 -0.386314 -0.217601\n',
  7. 'ddd -0.871858 -0.348382 1.100491\n']

虽然可以手动对数据进行规整,这里的字段是被数量不同的空白字符间隔开的。这种情况下,你可以传递一个正则表达式作为read_table 的分隔符。可以用正则表达式表达为\s+ ,于是有

  1. In [4]: result = pd.read_table('examples/ex3.txt', sep='\s+')
  2. In [5]: result
  3. Out[5]:
  4. A B C
  5. aaa -0.264438 -1.026059 -0.619500
  6. bbb 0.927272 0.302904 -0.032399
  7. ccc -0.264273 -0.386314 -0.217601
  8. ddd -0.871858 -0.348382 1.100491

这里,由于列名比数据行的数量少,所以read_table 推断第一列应该是DataFrame的索引
这些解析器函数还有许多参数可以帮助你处理各种各样的异形文件格式,下面的表格中列出来了一些。
比如说,你可以用 skiprows 跳过文件的第一行、第三行和第四行

  1. In [6]: !type examples\\ex4.csv
  2. # hey!
  3. a,b,c,d,message
  4. # just wanted to make things more difficult for you
  5. # who reads CSV files with computers, anyway?
  6. 1,2,3,4,hello
  7. 5,6,7,8,world
  8. 9,10,11,12,foo
  9. In [7]: pd.read_csv('examples/ex4.csv', skiprows=[0,2,3])
  10. Out[7]:
  11. a b c d message
  12. 0 1 2 3 4 hello
  13. 1 5 6 7 8 world
  14. 2 9 10 11 12 foo

缺失的数据经常是要么没有(空字符串),要么用某个标记值表示。默认情况下,pandas会用一组经常出现的标记值进行识别,比如NA以及NULL

  1. In [8]: !type examples\\ex5.csv
  2. something,a,b,c,d,message
  3. one,1,2,3,4,NA
  4. two,5,6,,8,world
  5. three,9,10,11,12,foo
  6. In [9]: result = pd.read_csv('examples/ex5.csv')
  7. In [10]: result
  8. Out[10]:
  9. something a b c d message
  10. 0 one 1 2 3.0 4 NaN
  11. 1 two 5 6 NaN 8 world
  12. 2 three 9 10 11.0 12 foo
  13. In [11]: pd.isnull(result)
  14. Out[11]:
  15. something a b c d message
  16. 0 False False False False False True
  17. 1 False False False True False False
  18. 2 False False False False False False

na_values 可以用一个列表集合的字符串表示缺失值

  1. In [12]: result = pd.read_csv('examples/ex5.csv', na_values=['NULL'])
  2. In [13]: result
  3. Out[13]:
  4. something a b c d message
  5. 0 one 1 2 3.0 4 NaN
  6. 1 two 5 6 NaN 8 world
  7. 2 three 9 10 11.0 12 foo

字典的各列可以使用不同的NA标记值

  1. In [16]: pd.read_csv('examples/ex5.csv', na_values=sentinels)
  2. Out[16]:
  3. something a b c d message
  4. 0 one 1 2 3.0 4 NaN
  5. 1 NaN 5 6 NaN 8 world
  6. 2 three 9 10 11.0 12 NaN

下表是 read_csv read_table 函数的参数
image.png
image.png

逐块读取文本文件

在处理很大的文件时,或找出大文件中的参数集以便于后续处理时,你可能只想读取文件的一小部分或逐块对文件进行迭代
在看大文件之前, 我们先设置pandas显示地更紧些

  1. In [17]: pd.options.display.max_rows = 10
  2. In [18]: result = pd.read_csv('examples/ex6.csv')
  3. In [19]: result
  4. Out[19]:
  5. one two three four key
  6. 0 0.467976 -0.038649 -0.295344 -1.824726 L
  7. 1 -0.358893 1.404453 0.704965 -0.200638 B
  8. 2 -0.501840 0.659254 -0.421691 -0.057688 G
  9. 3 0.204886 1.074134 1.388361 -0.982404 R
  10. 4 0.354628 -0.133116 0.283763 -0.837063 Q
  11. ... ... ... ... ... ..
  12. 9995 2.311896 -0.417070 -1.409599 -0.515821 L
  13. 9996 -0.479893 -0.650419 0.745152 -0.646038 E
  14. 9997 0.523331 0.787112 0.486066 1.093156 K
  15. 9998 -0.362559 0.598894 -1.843201 0.887292 G
  16. 9999 -0.096376 -1.012999 -0.657431 -0.573315 0
  17. [10000 rows x 5 columns]

如果只想读取几行(避免读取整个文件),通过nrows 进行指定即可

  1. In [20]: pd.read_csv('examples/ex6.csv', nrows=5)
  2. Out[20]:
  3. one two three four key
  4. 0 0.467976 -0.038649 -0.295344 -1.824726 L
  5. 1 -0.358893 1.404453 0.704965 -0.200638 B
  6. 2 -0.501840 0.659254 -0.421691 -0.057688 G
  7. 3 0.204886 1.074134 1.388361 -0.982404 R
  8. 4 0.354628 -0.133116 0.283763 -0.837063 Q

要逐块读取文件,可以指定chunksize (行数)

  1. >>> chunker = pd.read_csv('ch06/ex6.csv', chunksize=1000)
  2. >>> chunker
  3. <pandas.io.parsers.TextParser at 0x8398150>

read_csv 所返回的这个TextParser 对象使你可以根据chunksize 对文件进行逐块迭代。比如说,我们可以迭代处理ex6.csv, 将值计数聚合到”key“列中, 如下所示

  1. In [21]: chunker = pd.read_csv('examples/ex6.csv', chunksize=1000)
  2. In [22]: tot = pd.Series([])
  3. In [24]: for piece in chunker:
  4. ...: tot = tot.add(piece['key'].value_counts(), fill_value=0)
  5. In [25]: tot = tot.sort_values(ascending=False)
  6. In [26]: tot[:10]
  7. Out[26]:
  8. E 336.0
  9. X 327.0
  10. L 315.0
  11. M 309.0
  12. K 303.0
  13. Q 301.0
  14. O 299.0
  15. P 299.0
  16. J 298.0
  17. F 295.0
  18. dtype: float64

TextParser 还有一个get_chunk 方法,它使你可以读取任意大小的块

将数据写出到文件格式

数据也可以被输出为分隔符格式的文本。我们再来看看之前读过的一个CSV文件

  1. In [27]: data = pd.read_csv('examples/ex5.csv')
  2. In [28]: data
  3. Out[28]:
  4. something a b c d message
  5. 0 one 1 2 3.0 4 NaN
  6. 1 two 5 6 NaN 8 world
  7. 2 three 9 10 11.0 12 foo

利用DataFrameto_csv 方法,我们可以将数据写到一个以逗号分隔的文件中

  1. In [29]: data.to_csv('examples/out.csv')
  2. In [30]: !type examples\\out.csv
  3. ,something,a,b,c,d,message
  4. 0,one,1,2,3.0,4,
  5. 1,two,5,6,,8,world
  6. 2,three,9,10,11.0,12,foo

当然,还可以使用其他分隔符(由于这里直接写出到sys.stdout ,所以仅仅是打印出文本结果而已)

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

缺失值在输出结果中会被表示为空字符串。 你可能希望将其表示为别的标记值

  1. In [33]: data.to_csv(sys.stdout, na_rep='NULL')
  2. ,something,a,b,c,d,message
  3. 0,one,1,2,3.0,4,NULL
  4. 1,two,5,6,NULL,8,world
  5. 2,three,9,10,11.0,12,foo

如果没有设置其他选项,则会写出行和列的标签。当然,它们也都可以被禁用

  1. one,1,2,3.0,4,
  2. two,5,6,,8,world
  3. three,9,10,11.0,12,foo

此外,你还可以只写出一部分的列,并以你指定的顺序排列

  1. In [35]: data.to_csv(sys.stdout, index=False, columns=['a', 'b', 'c'])
  2. a,b,c
  3. 1,2,3.0
  4. 5,6,
  5. 9,10,11.0

Series也有一个to_csv 方法

  1. In [36]: dates = pd.date_range('1/1/2000', periods=7)
  2. In [37]: ts = pd.Series(np.arange(7), index=dates)
  3. In [39]: ts.to_csv('examples/tseries.csv')
  4. In [40]: !type examples\\tseries.csv
  5. 2000-01-01,0
  6. 2000-01-02,1
  7. 2000-01-03,2
  8. 2000-01-04,3
  9. 2000-01-05,4
  10. 2000-01-06,5
  11. 2000-01-07,6

处理分隔符格式

大部分存储在磁盘上的表格型数据都能用 pandas.read_table 进行加载。然而,有时还是需要做一些手工处理。 由于接收到含有畸形行的文件而使read_table 出毛病的情况并不少见。 为了说明这些基本工具, 看看下面这个简单的CSV文件

  1. In [3]: !type examples\\ex7.csv
  2. "a","b","c"
  3. "1","2","3"
  4. "1","2","3"

对于任何单字符分隔符文件, 可以直接使用Python内置的csv模块。将任意已打开的文件或文件型的对象传给 csv.reader

  1. In [4]: import csv
  2. In [5]: f = open('examples/ex7.csv')
  3. In [6]: reader = csv.reader(f)
  4. In [7]: for line in reader:
  5. ...: print(line)
  6. ['a', 'b', 'c']
  7. ['1', '2', '3']
  8. ['1', '2', '3']

现在,为了使数据格式合乎要求,你需要对其做一些整理工作。我们一步一步来做。首先,读取文件到一个多行的列表中

  1. In [8]: with open('examples/ex7.csv') as f:
  2. ...: lines = list(csv.reader(f))

然后,我们将这些行分为标题行和数据行

  1. In [9]: header, values = lines[0], lines[1:]

然后,我们可以用字典构造式zip(*values) ,后者将行转置为列,创建数据列的字典

  1. In [10]: data_dict = {h : v for h, v in zip(header, zip(*values))}
  2. In [11]: data_dict
  3. Out[11]: {'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}

CSV文件的形式有很多。只需定义csv.Dialect 的一个子类即可定义出新格式(如专门的分隔符、字符串引用约定、行结束符等)

  1. class my_dialect(csv.Dialect):
  2. lineterminator = '\n'
  3. delimiter = ';'
  4. quotechar = '"'
  5. quoting = csv.QUOTE_MINIMAL
  6. reader = csv.reader(f, dialect=my_dialect)

各个CSV语支的参数也可以用关键字的形式提供给csv.reader ,而无需定义子类

  1. reader = csv.reader(f, delimiter='|')

下表是 csv.Dialect 的属性
image.png

注意:对于那些使用复杂分隔符或多字符分隔符的文件,csv模块就无能为力了。 这种情况下, 你就只能使用字符串的split方法或正则表达式方法re.split进行行拆分和其他整理工作了

要手工输出分隔符文件,你可以使用 csv.writer 。它接受一个已打开且可写的文件对象以及跟csv.reader 相同的那些语支和格式化选项

  1. with open('mydata.csv', 'w') as f:
  2. writer = csv.writer(f, dialect=my_dialect)
  3. writer.writerow(('one', 'two', 'three'))
  4. writer.writerow(('1', '2', '3'))
  5. writer.writerow(('4', '5', '6'))
  6. writer.writerow(('7', '8', '9'))

JSON数据

除其空值null和一些其他的细微差别(如列表末尾不允许存在多余的逗号)之外,JSON非常接近于有效的Python代码。基本类型有对象(字典)、数组(列表)、字符串、数值、布尔值以及null。对象中所有的键都必须是字符串。许多Python库都可以读写JSON数据。我将使用json, 因为它是构建于Python标准库中的。通过json.load 即可将JSON字符串转换成Python形式

  1. In [12]: ojb = obj = """
  2. ...: {"name": "Wes",
  3. ...: "places_lived": ["United States", "Spain", "Germany"],
  4. ...: "pet": null,
  5. ...: "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]},
  6. ...: {"name": "Katie", "age": 38,
  7. ...: "pets":["Sixes", "Stache", "Cisco"]}]
  8. ...: }
  9. ...: """
  10. In [13]: import json
  11. In [14]: result = json.loads(obj)
  12. In [15]: result
  13. Out[15]:
  14. {'name': 'Wes',
  15. 'places_lived': ['United States', 'Spain', 'Germany'],
  16. 'pet': None,
  17. 'siblings': [{'name': 'Scott', 'age': 30, 'pets': ['Zeus', 'Zuko']},
  18. {'name': 'Katie', 'age': 38, 'pets': ['Sixes', 'Stache', 'Cisco']}]}

json.dumps 则将Python对象转换成JSON格式

  1. In [16]: asjson = json.dumps(result)

如何将(一个或一组)JSON对象转换为DataFrame或其他便于分析的数据结构就由你决定了。 最简单方便的方式是:向DataFrame构造器传入一个字典的列表(就是原先的JSON对象),并选取数据字段的子集

  1. In [17]: siblings = pd.DataFrame(result['siblings'], columns=['name', 'age'])
  2. In [18]: siblings
  3. Out[18]:
  4. name age
  5. 0 Scott 30
  6. 1 Katie 38

pandas.read_json 可以自动将特别格式的JSON数据集转换为SeriesDataFrame

  1. In [20]: !type examples\\example.json
  2. [{"a": 1, "b": 2, "c": 3},
  3. {"a": 4, "b": 5, "c": 6},
  4. {"a": 7, "b": 8, "c": 9}]

pandas.read_json 的默认选项假设JSON数组中的每个对象是表格中的一行

  1. In [21]: data = pd.read_json('examples/example.json')
  2. In [22]: data
  3. Out[22]:
  4. a b c
  5. 0 1 2 3
  6. 1 4 5 6
  7. 2 7 8 9

如果你需要将数据从pandas输出到JSON,可以使用to_json 方法

  1. In [23]: print(data.to_json())
  2. {"a":{"0":1,"1":4,"2":7},"b":{"0":2,"1":5,"2":8},"c":{"0":3,"1":6,"2":9}}
  3. In [24]: print(data.to_json(orient='records'))
  4. [{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]

XML和HTML:WEB信息收集

Python有许多可以读写常见的HTMLXML格式数据的库,包括lxmlBeautiful Souphtml5liblxml的速度比较快,但其它的库处理有误的HTMLXML文件更好

  1. conda install lxml
  2. pip install beautifulsoup4 html5lib

pandas.read_html 有一些选项,默认条件下,它会搜索、尝试解析标签内的的表格数据。结果是一个列表的DataFrame对象

  1. In [2]: tables = pd.read_html('examples/fdic_failed_bank_list.html')
  2. In [3]: len(tables)
  3. Out[3]: 1
  4. In [4]: failures = tables[0]
  5. In [5]: failures.head()
  6. Out[5]:
  7. Bank Name City ... Closing Date Updated Date
  8. 0 Allied Bank Mulberry ... September 23, 2016 November 17, 2016
  9. 1 The Woodbury Banking Company Woodbury ... August 19, 2016 November 17, 2016
  10. 2 First CornerStone Bank King of Prussia ... May 6, 2016 September 6, 2016
  11. 3 Trust Company Bank Memphis ... April 29, 2016 September 6, 2016
  12. 4 North Milwaukee State Bank Milwaukee ... March 11, 2016 June 16, 2016

这里,我们可以做一些数据清洗和分析(后面章节会进一步讲解),比如计算按年份计算倒闭的银行数

  1. In [9]: close_timestamps = pd.to_datetime(failures['Closing Date'])
  2. In [10]: close_timestamps
  3. Out[10]:
  4. 0 2016-09-23
  5. 1 2016-08-19
  6. 2 2016-05-06
  7. 3 2016-04-29
  8. 4 2016-03-11
  9. ...
  10. 542 2001-07-27
  11. 543 2001-05-03
  12. 544 2001-02-02
  13. 545 2000-12-14
  14. 546 2000-10-13
  15. Name: Closing Date, Length: 547, dtype: datetime64[ns]
  16. In [11]: close_timestamps.value_counts()
  17. Out[11]:
  18. 2009-10-30 9
  19. 2010-04-16 8
  20. 2010-08-20 8
  21. 2009-07-24 7
  22. 2010-07-23 7
  23. ..
  24. 2008-09-19 1
  25. 2014-10-24 1
  26. 2016-08-19 1
  27. 2008-08-22 1
  28. 2001-05-03 1
  29. Name: Closing Date, Length: 243, dtype: int64

利用lxml.objectify解析XML

我们先用 lxml.objectify 解析该文件,然后通过getroot 得到该XML文件的根节点的引用

  1. In [12]: from lxml import objectify
  2. In [13]: path = 'datasets/mta_perf/Performance_MNR.xml'
  3. In [14]: parsed = objectify.parse(open(path))
  4. In [15]: root = parsed.getroot()

root.INDICATOR 返回一个用于产生各个XML元素的生成器。对于每条记录,我们可以用标记名(YTD_ACTUAL)和数据值填充一个字典(排除几个标记)

  1. In [16]: data = []
  2. In [17]: skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ', 'DESIRED_CHANGE', 'DECIMAL_PLACES']
  3. In [18]: for elt in root.INDICATOR:
  4. ...: el_data = {}
  5. ...: for child in elt.getchildren():
  6. ...: if child.tag in skip_fields:
  7. ...: continue
  8. ...: el_data[child.tag] = child.pyval
  9. ...: data.append(el_data)

最后,将这组字典转换为一个DataFrame

  1. In [19]: perf = pd.DataFrame(data)
  2. In [20]: perf.head()
  3. Out[20]:
  4. AGENCY_NAME INDICATOR_NAME ... MONTHLY_TARGET MONTHLY_ACTUAL
  5. 0 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 96.9
  6. 1 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 95
  7. 2 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 96.9
  8. 3 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 98.3
  9. 4 Metro-North Railroad On-Time Performance (West of Hudson) ... 95 95.8