12-16、17第06章 数据加载、存储与文件格式

访问数据是使用本书所介绍的这些工具的第一步。我会着重介绍pandas的数据输入与输出,虽然别的库中也有不少以此为目的的工具。

输入输出通常可以划分为几个大类:读取文本文件和其他更高效的磁盘存储格式,加载数据库中的数据,利用Web API操作网络资源。

6.1 读写文本格式的数据

pandas提供了一些用于将表格型数据读取为DataFrame对象的函数。表6-1对它们进行了总结,其中read_csvread_table可能会是你今后用得最多的。

读取的函数

这里我们给出这些函数的大致功能,就是把test data变为dataframe。这些函数的一些可选参数有以下几类:

  • Indexing(索引)
    能把返回的一列或多列作为一个dataframe。另外也可以选择从文件中获取列名或完全不获取列名

  • Type inference and data conversion(类型推测和数据转换)
    这个包括用户自己定义的转换类型和缺失值转换

  • Datetime parsing(日期解析)
    包含整合能力,可以把多列中的时间信息整合为一列

  • Iterating(迭代)
    支持对比较大的文件进行迭代

  • Unclean data issues(未清洗的数据问题)
    跳过行或柱脚,评论,或其他一些小东西,比如csv中的逗号

因为现实中的数据非常messy(杂乱),所以有一些数据加载函数(特别是read_csv)的选项也变得越来越多。对于众多参数感觉不知所措是正常的(read_csv有超过50个参数)。具体的可以去看pandas官网给出的例子。

一些函数,比如pandas.read_csv实现type inference,因为column data type不是数据类型的一种。这意味着我们没有必要指定哪些columns是数值,哪些是整数,哪些是字符串。其他一些数据格式,比如HDF5,数据类型是在格式里的。
先来一个CSV文件热热身(CSV文件指的是用逗号隔开数据的文件):

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

笔记:这里,我用的是Unix的cat shell命令将文件的原始内容打印到屏幕上。如果你用的是Windows,你可以使用type达到同样的效果。

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

In [9]: df = pd.read_csv('examples/ex1.csv')

In [10]: df
Out[10]: 
   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 [11]: pd.read_table('examples/ex1.csv', sep=',')
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

一个文件不会总是有header row(页首行),考虑下面的文件:

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

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

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

In [14]: pd.read_csv('examples/ex2.csv', names=['a', 'b', 'c', 'd', 'message'])
Out[14]: 
   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的索引。你可以明确表示要将该列放到索引的位置上,也可以通过index_col参数指定"message":

In [15]: names = ['a', 'b', 'c', 'd', 'message']

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

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

In [17]: !cat 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 [18]: parsed = pd.read_csv('examples/csv_mindex.csv',
   ....:                      index_col=['key1', 'key2'])

In [19]: parsed
Out[19]: 
           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 [20]: list(open('examples/ex3.txt'))
Out[20]: 
['            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 [21]: result = pd.read_table('examples/ex3.txt', sep='\s+')

In [22]: result
Out[22]: 
            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的index。

这个解析器功能有很多其他参数能帮你解决遇到文件格式异常的问题(可以见之后的表格)。比如,我们要跳过第一、三、四行,使用skiprows:

In [23]: !cat 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 [24]: pd.read_csv('examples/ex4.csv', skiprows=[0, 2, 3])
Out[24]: 
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

对于缺失值,pandas使用一些sentinel value(标记值)来代表,比如NA和NULL:

In [25]: !cat 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 [26]: result = pd.read_csv('examples/ex5.csv')

In [27]: result
Out[27]: 
  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 [28]: pd.isnull(result)
Out[28]: 
   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 [29]: result = pd.read_csv('examples/ex5.csv', na_values=['NULL'])

In [30]: result
Out[30]: 
  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

我们还可以给不同的column设定不同的缺失值标记符,这样的话需要用到dict:

In [31]: sentinels = {'message': ['foo', 'NA'], 'something': ['two']}

In [32]: pd.read_csv('examples/ex5.csv', na_values=sentinels)
Out[32]:
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

表6-2列出了pandas.read_csv和pandas.read_table常用的选项。


pandas.read_csv和pandas.read_table中常用的选项

pandas.read_csv和pandas.read_table中常用的选项

1.逐块读取文本文件

在处理很大的文件时,或找出大文件中的参数集以便于后续处理时,你可能只想读取文件的一小部分或逐块对文件进行迭代。
对于一些比较大的文件,我们想要一次读取一小部分,或者每次迭代一小部分。在我们看一个比较大的文件前,先设置一下pandas中显示的数量:

In [33]: pd.options.display.max_rows = 10
#然后有
In [34]: result = pd.read_csv('examples/ex6.csv')

In [35]: result
Out[35]: 
           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 [36]: pd.read_csv('examples/ex6.csv', nrows=5)
Out[36]: 
        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 [874]: chunker = pd.read_csv('ch06/ex6.csv', chunksize=1000)

In [875]: chunker
Out[875]: <pandas.io.parsers.TextParser at 0x8398150>

pandas返回的TextParser object能让我们根据chunksize每次迭代文件的一部分。比如,我们想要迭代ex6.csv, 计算key列的值的综合:

chunker = pd.read_csv('examples/ex6.csv', chunksize=1000)

tot = pd.Series([])
for piece in chunker:
    tot = tot.add(piece['key'].value_counts(), fill_value=0)

tot = tot.sort_values(ascending=False)

然后有:

In [40]: tot[:10]
Out[40]: 
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方法,能返回任意大小的数据片段:

chunker = pd.read_csv('../examples/ex6.csv', chunksize=1000)

chunker.get_chunk(10)

2 Writing Data to Text Format (写入数据到文本格式)

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

In [41]: data = pd.read_csv('examples/ex5.csv')

In [42]: data
Out[42]: 
  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 [43]: data.to_csv('examples/out.csv')

In [44]: !cat 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 [45]: import sys

In [46]: 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 [47]: 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 [48]: 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 [49]: 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 [50]: dates = pd.date_range('1/1/2000', periods=7)

In [51]: ts = pd.Series(np.arange(7), index=dates)

In [52]: ts.to_csv('examples/tseries.csv')

In [53]: !cat 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

3. 处理分隔符格式

对于大部分磁盘中的表格型数据,用pandas.read_table就能解决。不过,有时候一些人工的处理也是需要的。

当然,有时候,一些格式不正确的行能会把read_table绊倒。为了展示一些基本用法,这里先考虑一个小的CSV文件:

In [54]: !cat examples/ex7.csv
"a","b","c"
"1","2","3"
"1","2","3"

对于单个字符的分隔符,可以使用python内建的csv方法。只要给csv.reader一个打开的文件即可:

import csv
f = open('examples/ex7.csv')

reader = csv.reader(f)

对这个reader进行迭代将会为每行产生一个元组(并移除了所有的引号):对这个reader进行迭代将会为每行产生一个元组(并移除了所有的引号):

In [56]: for line in reader:
   ....:     print(line)
['a', 'b', 'c']
['1', '2', '3']
['1', '2', '3']

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

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

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

In [58]: header, values = lines[0], lines[1:]

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

In [59]: data_dict = {h: v for h, v in zip(header, zip(*values))}

In [60]: data_dict
Out[60]: {'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的属性)及其功能如表6-3所示。


CSV可用的选项

4.JSON数据

JSON (short for JavaScript Object Notation)已经是发送HTTP请求的标准数据格式了。
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率

5 XML and HTML: Web Scraping (网络爬取)

python有很多包用来读取和写入HTML和XML格式。比如:lxml, Beautiful Soup, html5lib。其中lxml比较快,其他一些包则能更好的处理一些复杂的HTML和XML文件。

pandas有一个内建的函数,叫read_html, 这个函数利用lxml和Beautiful Soup这样的包来自动解析HTML,变为DataFrame。这里我们必须要先下载这些包才能使用read_html:

conda install lxml
pip install beautifulsoup4 html5lib

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

In [73]: tables = pd.read_html('examples/fdic_failed_bank_list.html')

In [74]: len(tables)
Out[74]: 1

In [75]: failures = tables[0]

In [76]: failures.head()
Out[76]: 
                      Bank Name             City  ST   CERT  \
0                   Allied Bank         Mulberry  AR     91   
1  The Woodbury Banking Company         Woodbury  GA  11297   
2        First CornerStone Bank  King of Prussia  PA  35312   
3            Trust Company Bank          Memphis  TN   9956   
4    North Milwaukee State Bank        Milwaukee  WI  20364   
                 Acquiring Institution        Closing Date       Updated Date  
0                         Today's Bank  September 23, 2016  November 17, 2016  
1                          United Bank     August 19, 2016  November 17, 2016  
2  First-Citizens Bank & Trust Company         May 6, 2016  September 6, 2016  
3           The Bank of Fayette County      April 29, 2016  September 6, 2016  
4  First-Citizens Bank & Trust Company      March 11, 2016      June 16, 2016

因为failures有许多列,pandas插入了一个换行符\。

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

In [77]: close_timestamps = pd.to_datetime(failures['Closing Date'])

In [78]: close_timestamps.dt.year.value_counts()
Out[78]: 
2010    157
2009    140
2011     92
2012     51
2008     25
       ... 
2004      4
2001      4
2007      3
2003      3
2000      2
Name: Closing Date, Length: 15, dtype: int64

利用lxml.objectify解析XML

6.2 二进制数据格式

实现数据的高效二进制格式存储最简单的办法之一是使用Python内置的pickle序列化。pandas对象都有一个用于将数据以pickle格式保存到磁盘上的to_pickle方法:

In [87]: frame = pd.read_csv('examples/ex1.csv')

In [88]: frame
Out[88]: 
   a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

In [89]: frame.to_pickle('examples/frame_pickle')

你可以通过pickle直接读取被pickle化的数据,或是使用更为方便的pandas.read_pickle:

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

注意:pickle只推荐用于短期存储。因为这种格式无法保证长期稳定;比如今天pickled的一个文件,可能在库文件更新后无法读取。

python还支持另外两种二进制数据格式:HDF5和MessagePack。下面会介绍一个HDF5,但是我们鼓励你多尝试一个不同的文件格式,看看他们能有多快,是否符合你数据分析的要求。另外一些可用的存储格式有:bcolz 和 Feather。

1.使用HDF5格式

HDF5格式是用来存储大量的科学数组数据的。这种格式还能用于其他一些语言。其中HDF表示hierarchical data format。每一个HDF5格式能春初多个数据集,并支持metadata。

元数据(meta data)——“data about data” 关于数据的数据,一般是结构化数据(如存储在数据库里的数据,规定了字段的长度、类型等)。元数据是指从信息资源中抽取出来的用于说明其特征、内容的结构化的数据(如题名,版本、出版数据、相关说明,包括检索点等),用于组织、描述、检索、保存、管理信息和知识资源。

HDF5 支持多种压缩模式的on-the-fly compression(即时压缩),能让数据中一些重复的部分存储地更有效。HDF5对于处理大数据集是一个很好的选择,因为他不会把所有数据一次性读取到内存里,我们可以从很大的数组中有效率地读取一小部分。
能用PyTables或h5py来访问HDF5数据,pandas也有提供一个high-level的交互界面。HDFStore类像dict一样能用来处理low-level细节:

In [92]: frame = pd.DataFrame({'a': np.random.randn(100)})

In [93]: store = pd.HDFStore('mydata.h5')

In [94]: store['obj1'] = frame

In [95]: store['obj1_col'] = frame['a']

In [96]: store
Out[96]: 
<class 'pandas.io.pytables.HDFStore'>
File path: mydata.h5
/obj1                frame        (shape->[100,1])                               
        
/obj1_col            series       (shape->[100])                                 
        
/obj2                frame_table  (typ->appendable,nrows->100,ncols->1,indexers->
[index])
/obj3                frame_table  (typ->appendable,nrows->100,ncols->1,indexers->
[index])

HDF5文件中的对象可以通过与字典一样的API进行获取:

In [97]: store['obj1']
Out[97]: 
           a
0  -0.204708
1   0.478943
2  -0.519439
3  -0.555730
4   1.965781
..       ...
95  0.795253
96  0.118110
97 -0.748532
98  0.584970
99  0.152677
[100 rows x 1 columns]

HDFStore支持两种存储模式,'fixed'和'table'。后者通常会更慢,但是支持使用特殊语法进行查询操作:

In [98]: store.put('obj2', frame, format='table')

In [99]: store.select('obj2', where=['index >= 10 and index <= 15'])
Out[99]: 
           a
10  1.007189
11 -1.296221
12  0.274992
13  0.228913
14  1.352917
15  0.886429

In [100]: store.close()

pandas.read_hdf函数可以快捷使用这些工具:

In [101]: frame.to_hdf('mydata.h5', 'obj3', format='table')

In [102]: pd.read_hdf('mydata.h5', 'obj3', where=['index < 5'])
Out[102]: 
          a
0 -0.204708
1  0.478943
2 -0.519439
3 -0.555730
4  1.965781

2 Reading Microsoft Excel Files(读取微软的excel文件)

pandas支持读取表格型数据(excel 2003或更高)文件,使用ExcelFile class或pandas.read_excel函数。这些工具需要一些附加的包xlrd和openpyxl来分别读取XLS和XLSX文件。你可以通过pip或conda来安装。

使用ExcelFile,创建一个instance,通过给xls或xlsx一个路径:

In [7]:  xlsx = pd.ExcelFile(r'C:\Users\yujiawen\Desktop\新建 Microsoft Excel 工作表.xlsx')

In [8]: pd.read_excel(xlsx, 'Sheet1')
Out[8]:
         J       JL         JK  Unnamed: 3     固定     斜率      转移电子
0  4.41204  4.78128  57.131401         NaN  28.15  11.55  2.437229

如果要读取一个文件中的多个表单,创建ExcelFile会更快,但你也可以将文件名传递到pandas.read_excel:

In [9]: frame = pd.read_excel(r'C:\Users\yujiawen\Desktop\新建 Microsoft Excel 工作表.xlsx', 'Sheet1')

In [10]: frame
Out[10]:
         J       JL         JK  Unnamed: 3     固定     斜率      转移电子
0  4.41204  4.78128  57.131401         NaN  28.15  11.55  2.437229

如果要将pandas数据写入为Excel格式,你必须首先创建一个ExcelWriter,然后使用pandas对象的to_excel方法将数据写入到其中:

In [108]: writer = pd.ExcelWriter('examples/ex2.xlsx')

In [109]: frame.to_excel(writer, 'Sheet1')

In [110]: writer.save()

你还可以不使用ExcelWriter,而是传递文件的路径到to_excel:

In [111]: frame.to_excel('examples/ex2.xlsx')

6.3 Interacting with Web APIs (网络相关的API交互)

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
许多网站都有一些通过JSON或其他格式提供数据的公共API。通过Python访问这些API的办法有不少。一个简单易用的办法(推荐)是requests包(http://docs.python-requests.org)。
找到github里pandas最新的30个issues,制作一个GET HTTP request, 通过使用requests包:

In [11]: import pandas as pd
    ...: import numpy as np
    ...:
In [12]: import requests

In [13]:

In [13]:  url = 'https://api.github.com/repos/pandas-dev/pandas/issues'

In [14]: resp = requests.get(url)

In [15]: resp
Out[15]: <Response [200]>

response的json方法能返回一个dict,包含可以解析为python object的JSON:

In [16]: data = resp.json()

In [17]:  data[0]['title']
Out[17]: 'API: Remove CalendarDay'
In [18]: data[0]
Out[18]:
{'url': 'https://api.github.com/repos/pandas-dev/pandas/issues/24330',
 'repository_url': 'https://api.github.com/repos/pandas-dev/pandas',
 'labels_url': 'https://api.github.com/repos/pandas-dev/pandas/issues/24330/labels{/name}',
 'comments_url': 'https://api.github.com/repos/pandas-dev/pandas/issues/24330/comments',
 'events_url': 'https://api.github.com/repos/pandas-dev/pandas/issues/24330/events',
 'html_url': 'https://github.com/pandas-dev/pandas/pull/24330',
 'id': 392011696,
 'node_id': 'MDExOlB1bGxSZXF1ZXN0MjM5Mzc2MDk4',
 'number': 24330,
 'title': 'API: Remove CalendarDay',
 'user': {'login': 'mroeschke',
  'id': 10647082,
  'node_id': 'MDQ6VXNlcjEwNjQ3MDgy',
  'avatar_url': 'https://avatars0.githubusercontent.com/u/10647082?v=4',
  'gravatar_id': '',
  'url': 'https://api.github.com/users/mroeschke',
  'html_url': 'https://github.com/mroeschke',
  'followers_url': 'https://api.github.com/users/mroeschke/followers',
  'following_url': 'https://api.github.com/users/mroeschke/following{/other_user}',
  'gists_url': 'https://api.github.com/users/mroeschke/gists{/gist_id}',
  'starred_url': 'https://api.github.com/users/mroeschke/starred{/owner}{/repo}',
  'subscriptions_url': 'https://api.github.com/users/mroeschke/subscriptions',
  'organizations_url': 'https://api.github.com/users/mroeschke/orgs',
  'repos_url': 'https://api.github.com/users/mroeschke/repos',
  'events_url': 'https://api.github.com/users/mroeschke/events{/privacy}',
  'received_events_url': 'https://api.github.com/users/mroeschke/received_events',
  'type': 'User',
  'site_admin': False},
 'labels': [],
 'state': 'open',
 'locked': False,
 'assignee': None,
 'assignees': [],
 'milestone': {'url': 'https://api.github.com/repos/pandas-dev/pandas/milestones/55',
  'html_url': 'https://github.com/pandas-dev/pandas/milestone/55',
  'labels_url': 'https://api.github.com/repos/pandas-dev/pandas/milestones/55/labels',
  'id': 3228419,
  'node_id': 'MDk6TWlsZXN0b25lMzIyODQxOQ==',
  'number': 55,
  'title': '0.24.0',
  'description': '',
  'creator': {'login': 'jorisvandenbossche',
   'id': 1020496,
   'node_id': 'MDQ6VXNlcjEwMjA0OTY=',
   'avatar_url': 'https://avatars2.githubusercontent.com/u/1020496?v=4',
   'gravatar_id': '',
   'url': 'https://api.github.com/users/jorisvandenbossche',
   'html_url': 'https://github.com/jorisvandenbossche',
   'followers_url': 'https://api.github.com/users/jorisvandenbossche/followers',
   'following_url': 'https://api.github.com/users/jorisvandenbossche/following{/other_user}',
   'gists_url': 'https://api.github.com/users/jorisvandenbossche/gists{/gist_id}',
   'starred_url': 'https://api.github.com/users/jorisvandenbossche/starred{/owner}{/repo}',
   'subscriptions_url': 'https://api.github.com/users/jorisvandenbossche/subscriptions',
   'organizations_url': 'https://api.github.com/users/jorisvandenbossche/orgs',
   'repos_url': 'https://api.github.com/users/jorisvandenbossche/repos',
   'events_url': 'https://api.github.com/users/jorisvandenbossche/events{/privacy}',
   'received_events_url': 'https://api.github.com/users/jorisvandenbossche/received_events',
   'type': 'User',
   'site_admin': False},
  'open_issues': 33,
  'closed_issues': 1611,
  'state': 'open',
  'created_at': '2018-03-29T12:00:12Z',
  'updated_at': '2018-12-18T06:22:35Z',
  'due_on': '2018-12-31T08:00:00Z',
  'closed_at': None},
 'comments': 3,
 'created_at': '2018-12-18T06:16:42Z',
 'updated_at': '2018-12-18T06:56:49Z',
 'closed_at': None,
 'author_association': 'MEMBER',
 'pull_request': {'url': 'https://api.github.com/repos/pandas-dev/pandas/pulls/24330',
  'html_url': 'https://github.com/pandas-dev/pandas/pull/24330',
  'diff_url': 'https://github.com/pandas-dev/pandas/pull/24330.diff',
  'patch_url': 'https://github.com/pandas-dev/pandas/pull/24330.patch'},
 'body': '- [x] tests added / passed\r\n- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`\r\n\r\nGiven that we want to ship 0.24.0 soon and that converting `\'D\'` and `Day` to always act as calendar day warrants more game-planning, this PR just simply removes `CalendarDay` and reverts `\'D\'` and `Day` to their prior behavior. '}

data中的每一个元素都是一个dict,这个dict就是在github上找到的issue页面上的信息。我们可以把data传给DataFrame并提取感兴趣的部分:

In [19]: issues = pd.DataFrame(data, columns=['number', 'title',
    ...:                                     'labels', 'state'])
    ...: issues
    ...:
Out[19]:
    number                                              title                                             labels state
0    24330                            API: Remove CalendarDay                                                 []  open
1    24329  BUG: Timestamp(Timestamp(Ambiguous time)) modi...  [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ=...  open
2    24328   Fixed PEP8 errors in doc/source/whatsnew/v0.15.*                                                 []  open
3    24327  ERR: Improve error message for cut with infini...  [{'id': 42670965, 'node_id': 'MDU6TGFiZWw0MjY3...  open
4    24326  Fixtures making IntNA tests difficult to run i...                                                 []  open
5    24325         pandas.Series docstrings dtype information  [{'id': 134699, 'node_id': 'MDU6TGFiZWwxMzQ2OT...  open
6    24324  REF/TST: Add more pytest idiom to resample/tes...                                                 []  open
7    24323  API: Remove 'codes' parameter from MultiIndex ...                                                 []  open
8    24322  DOC: Fixes flake8 issues in whatsnew v0.13.* #...  [{'id': 211029535, 'node_id': 'MDU6TGFiZWwyMTE...  open
9    24321  Doc on pandas.read_parquet says path is a stri...  [{'id': 134699, 'node_id': 'MDU6TGFiZWwxMzQ2OT...  open
10   24319     BUG: Ensure incomplete stata files are deleted                                                 []  open
11   24318  hash_pandas_object fails on empty dataframe wi...  [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ=...  open
12   24314        Numpy 'inf' values cause pandas.cut to fail  [{'id': 42670965, 'node_id': 'MDU6TGFiZWw0MjY3...  open
13   24312  Segmentation fault while loading csv into data...  [{'id': 31932467, 'node_id': 'MDU6TGFiZWwzMTkz...  open
14   24310       BUG: Series.__repr__ crashing with tzlocal()                                                 []  open
15   24305  DOC/API: PeriodIndex sub/add with Integers: up...  [{'id': 35818298, 'node_id': 'MDU6TGFiZWwzNTgx...  open
16   24303       DOC: Fix flake8 issues with whatsnew v0.18.*  [{'id': 211029535, 'node_id': 'MDU6TGFiZWwyMTE...  open
17   24302    Series.value_counts: Preserve original ordering                                                 []  open
18   24301                    EA ops alignment with DataFrame                                                 []  open
19   24295  Elegant way to generate indexable sliding wind...                                                 []  open
20   24294  BUG: Fix index bug due to parse_time_string GH...  [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ=...  open
21   24293  standardize signature for Index reductions, im...  [{'id': 35818298, 'node_id': 'MDU6TGFiZWwzNTgx...  open
22   24288  DOC: Fix docstrings with the sections in the w...  [{'id': 134699, 'node_id': 'MDU6TGFiZWwxMzQ2OT...  open
23   24282                 WIP: decorator for ops boilerplate  [{'id': 211029535, 'node_id': 'MDU6TGFiZWwyMTE...  open
24   24280  DOC: Fix docstrings with the sections in the w...  [{'id': 211029535, 'node_id': 'MDU6TGFiZWwyMTE...  open
25   24279                Added extra info section to EA repr  [{'id': 849023693, 'node_id': 'MDU6TGFiZWw4NDk...  open
26   24278  Add an "extra info" section to the base Extens...  [{'id': 849023693, 'node_id': 'MDU6TGFiZWw4NDk...  open
27   24275        GH24241 make Categorical.map transform nans  [{'id': 78527356, 'node_id': 'MDU6TGFiZWw3ODUy...  open
28   24274  BLD: for C extension builds on mac, target mac...  [{'id': 129350, 'node_id': 'MDU6TGFiZWwxMjkzNT...  open
29   24271  sort_index not sorting when multi-index made b...                                                 []  open

通过一些体力活,我们可以构建一些高层级的界面,让web API直接返回DataFrame格式,以便于分析。

6.4 Interacting with Databases(与数据库的交互)

如果在工作中,大部分数据并不会以text或excel的格式存储。最广泛使用的是SQL-based的关系型数据库(SQL Server,PostgreSQL,MySQL)。选择数据库通常取决于性能,数据整合性,实际应用的可扩展性。

读取SQL到DataFrame非常直观,pandas中有一些函数能简化这个过程。举个例子,这里创建一个SQLite数据库,通过使用python内建的sqlite3 driver:

In [20]: import sqlite3
    ...: import pandas as pd
    ...:
    ...:

In [21]: query = """
    ...: CREATE TABLE test
    ...: (a VARCHAR(20), b VARCHAR(20),
    ...:  c REAL,        d INTEGER
    ...: );"""

In [22]: con = sqlite3.connect('mydata.sqlite')

In [23]: con.execute(query)
Out[23]: <sqlite3.Cursor at 0x1aa05e626c0>

In [24]: con.commit()

然后插入几行数据:

In [25]: data = [('Atlanta', 'Georgia', 1.25, 6),
    ...:         ('Tallahassee', 'Florida', 2.6, 3),
    ...:         ('Sacramento', 'California', 1.7, 5)]
    ...:

In [26]: stmt = "INSERT INTO test VALUES(?, ?, ?, ?)"

In [27]: con.executemany(stmt, data)
Out[27]: <sqlite3.Cursor at 0x1aa02d1f340>

In [28]: con.commit()

从表中选取数据时,大部分Python SQL驱动器(PyODBC、psycopg2、MySQLdb、pymssql等)都会返回一个元组列表:

In [29]: cursor = con.execute('select * from test')

In [30]: rows = cursor.fetchall()

In [31]: rows
Out[31]:
[('Atlanta', 'Georgia', 1.25, 6),
 ('Tallahassee', 'Florida', 2.6, 3),
 ('Sacramento', 'California', 1.7, 5)]

你可以将这个元组列表传给DataFrame构造器,但还需要列名(位于光标的description属性中):

In [32]: cursor.description
Out[32]:
(('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 [33]: pd.DataFrame(rows, columns=[x[0] for x in cursor.description])
Out[33]:
             a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5

我们不希望每次询问数据库的时候都重复以上步骤,这样对计算机很不好(mung,【计】逐步对计算机系统或文件做小改动导致大的损害)。SQLAlchemy计划是一个六星的Python SQL工具箱,它能抽象出不同SQL数据库之间的不同。pandas有一个read_sql函数,能让我们从SQLAlchemy connection从读取数据。这里我们用SQLAlchemy连接到同一个SQLite数据库,并从之前创建的表格读取数据:

In [135]: import sqlalchemy as sqla

In [136]: db = sqla.create_engine('sqlite:///mydata.sqlite')

In [137]: pd.read_sql('select * from test', db)
Out[137]: 
             a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容