Python For Data Analysis-六章第一节
《Python For Data Analysis》的第六章主要讨论的是从各类数据源将数据变成dataframe。第六章的第一节主要围绕读取各种格式的文本文件数据的函数展开。
11.1 读取csv文本文件数据
csv文件的特点是数据有规律且多行,每行用特定的符号(一般是逗号,也用用分号和冒号、空格的)间隔各个字段数据。pandas提供了read_csv、readtable等函数可以直接将csv数据文件里的数据读取到dataframe文件里去,就不用python先用readlines读再处理那么麻烦了。 下面以机器学习领域里的一个知名的csv数据集文件iris.data为例使用一下这些函数。首先可以从该文件的官网下载此文件,然后保存到和程序同目录下。
- read_csv读取csv文件到dataframe里
import pandas as pd
import numpy as np
d = pd.read_csv("./iris.data")
print type(d), "# type(d)"
print d.iloc[:10], "# d.iloc[:10]"
执行结果:
<class 'pandas.core.frame.DataFrame'> # type(d)
5.1 3.5 1.4 0.2 Iris-setosa
0 4.9 3.0 1.4 0.2 Iris-setosa
1 4.7 3.2 1.3 0.2 Iris-setosa
2 4.6 3.1 1.5 0.2 Iris-setosa
3 5.0 3.6 1.4 0.2 Iris-setosa
4 5.4 3.9 1.7 0.4 Iris-setosa
5 4.6 3.4 1.4 0.3 Iris-setosa
6 5.0 3.4 1.5 0.2 Iris-setosa
7 4.4 2.9 1.4 0.2 Iris-setosa
8 4.9 3.1 1.5 0.1 Iris-setosa
9 5.4 3.7 1.5 0.2 Iris-setosa # d.iloc[:10]
这里read_csv将iris.data文件里的多行多列数据直接读取到dataframe数据d里了!但是第一行的结果不太满意,是数据而不是字段名,不急等演示完read_table函数后再处理。
- read_table函数读取csv文件。
import pandas as pd
import numpy as np
d = pd.read_table("./iris.data")
print type(d), "# type(d)"
print d.iloc[:10], "# d.iloc[:10]"
程序的执行结果和效果和read_csv函数一样。
- header形参可以说明数据文件是否有文件头,即数据文件的第一行不是数据而是数据的字段名信息。上边的两个程序分别使用了read_csv和read_table函数,都读取了iris.data文件到dataframe数据里了,但第一行(5.1 3.5 1.4 0.2 Iris-setosa)没有行标签,被认为是列(字段)的名字(参看另一个csv文件winequality-red.csv),那么可知两个函数均将第一行当作了非数据了(有很多的csv文件的前几行是文件的说明,有时数据上一行是各个字段名)视为字段名数据了。这时可以使用参数header,告诉函数此csv文件全都是数据,没有字段信息数据,即每行都是数据(如果非要给数据配上字段名可以用names指定字段名)。
import pandas as pd
import numpy as np
d = pd.read_csv("./iris.data", header = None)
print d.iloc[:10], "# d.iloc[:10]"
执行结果:
0 1 2 3 4
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
5 5.4 3.9 1.7 0.4 Iris-setosa
6 4.6 3.4 1.4 0.3 Iris-setosa
7 5.0 3.4 1.5 0.2 Iris-setosa
8 4.4 2.9 1.4 0.2 Iris-setosa
9 4.9 3.1 1.5 0.1 Iris-setosa # d.iloc[:10]
结果好些了,但是还是有些问题, (1). 列名字都是整形数据不太清楚是啥,解决很简单可以指定names来修改。 (2). 如果.csv文件头几行有数据描述和字段名,又如何处理?
- names形参指定字段名,对于上边程序出现的第一个问题,每行就用数字标识还可以,毕竟是一条条的记录,但列名字还是写出来比较好,用names形参为dataframe指定列的字段名字信息col。在iris.data提供的网站有说明文件的各个字段是什么,请自行前往阅读。
import pandas as pd
import numpy as np
col = "sepal_length,sepal_width,petal_length,petal_width,class".split(",")
d = pd.read_csv("./iris.data", header = None, names = col)
print "-" * 40
print d.iloc[:10], "# d.iloc[:10]"
print "-" * 40
print d["sepal_width"][:10], '# d["sepal_width"][:10]'
print "-" * 40
print d.columns, "# d.columns"
print "-" * 40
执行结果:
----------------------------------------
sepal_length sepal_width ... petal_width class
0 5.1 3.5 ... 0.2 Iris-setosa
1 4.9 3.0 ... 0.2 Iris-setosa
2 4.7 3.2 ... 0.2 Iris-setosa
3 4.6 3.1 ... 0.2 Iris-setosa
4 5.0 3.6 ... 0.2 Iris-setosa
5 5.4 3.9 ... 0.4 Iris-setosa
6 4.6 3.4 ... 0.3 Iris-setosa
7 5.0 3.4 ... 0.2 Iris-setosa
8 4.4 2.9 ... 0.2 Iris-setosa
9 4.9 3.1 ... 0.1 Iris-setosa
[10 rows x 5 columns] # d.iloc[:10]
----------------------------------------
0 3.5
1 3.0
2 3.2
3 3.1
4 3.6
5 3.9
6 3.4
7 3.4
8 2.9
9 3.1
Name: sepal_width, dtype: float64 # d["sepal_width"][:10]
----------------------------------------
Index([u'sepal_length', u'sepal_width', u'petal_length', u'petal_width',
u'class'],
dtype='object') # d.columns
----------------------------------------
总之,read_csv和read_table会将数据文件第一行认做数据的字段名集合,如果不是用header=None告诉两个函数。如果数据文件第一行是字段信息那么就不用header好了。使用names可以为纯数据文本文件配以字段名信息。
- seq形参,可以指定数据文本文件不是以逗号间隔的数据行,默认情况下read_csv和read_table读取每行文件时为了区分得到各个字段的值,是以逗号为默认值找到各个字段的值的,但有的数据文件则是以其他分割符号分割数据的各个字段的,下面的这个经典数据集文件winequality-red.csv就是以分号间隔每行各个字段的值,那么在使用read_csv和read_table函数时可以使用sep形参告知函数,字段分割符是什么。
import pandas as pd
import numpy as np
col = "sepal_length,sepal_width,petal_length,petal_width,class".split(",")
d = pd.read_csv("./winequality-red.csv", sep = ";")
print "-" * 40
print d.iloc[:10], "# d.iloc[:10]"
print "-" * 40
print d.columns, "# d.columns"
print "-" * 40
执行结果:
----------------------------------------
fixed acidity volatile acidity ... alcohol quality
0 7.4 0.70 ... 9.4 5
1 7.8 0.88 ... 9.8 5
2 7.8 0.76 ... 9.8 5
3 11.2 0.28 ... 9.8 6
4 7.4 0.70 ... 9.4 5
5 7.4 0.66 ... 9.4 5
6 7.9 0.60 ... 9.4 5
7 7.3 0.65 ... 10.0 7
8 7.8 0.58 ... 9.5 7
9 7.5 0.50 ... 10.5 5
[10 rows x 12 columns] # d.iloc[:10]
----------------------------------------
Index([u'fixed acidity', u'volatile acidity', u'citric acid',
u'residual sugar', u'chlorides', u'free sulfur dioxide',
u'total sulfur dioxide', u'density', u'pH', u'sulphates', u'alcohol',
u'quality'],
dtype='object') # d.columns
----------------------------------------
winequality-red.csv数据文件的第一行是字段信息,所以就不用header形参了,其下各行是数据,行内字段值间用分号(;)间隔而不是逗号(,),所以需要使用sep参数告知函数d = pd.read_csv("./winequality-red.csv", sep = ";")
。
- skiprows形参可以忽略掉数据文件的若干行数据。有点时候,数据文件在有效果数据前会有一些信息来说明、介绍这个数据文件,这些信息是不应当被读入到dataframe数据里去的,应该去掉这些信息,例如下面这个数据文件:
# 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
....
那么第0、2、3行文字,可以视为注释或者说明,不是数据,是没有用的,不需要读取到dataframe里去的,可以使用skiprows参数去掉这几行不读入到dataframe里。假设这个数据文件为“ts.csv”和程序文件在同一个目录下
import pandas as pd
import numpy as np
d = pd.read_csv("./ts.csv", skiprows = [0,2,3])
print "-" * 40
print d.iloc[:10], "# d.iloc[:10]"
print "-" * 40
print d.columns, "# d.columns"
print "-" * 40
执行结果:
----------------------------------------
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo # d.iloc[:10]
----------------------------------------
Index([u'a', u'b', u'c', u'd', u'message'], dtype='object') # d.columns
----------------------------------------
- na_values形参,可以进行数据缺失处理和无效数据预处理。(1).有的时候,csv等数据文件的每行不是都有各个字段值的,即字段缺失数据,不是错是真没有。函数read_csv在读取数据文件时,会自动对列上的缺失数据进行必要的处理设定NaN。(2).有的时候,数据文件某列下出现了某值(不该出现这个值,无效),也可以在读取时被处理成NaN,可以使用na_values参数完成。某列有可能程序比较多的无效数据,可以采用字典给出各个字段在出现某些值时要被视为NaN。以下面的数据文件为例:
# 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
3,31,,the
11,12,,cruel
5,6,7,8,world
9,,11,12,xaxa
数据文件(ts.csv)里有些行的列上是无数据,例如3,31,,the
却c列的数据,函数会自动处理为NaN。
如果b列下出现了6、a列里有3、c列各行有7或者11,如果应用程序认为这些值不该出现在各自的列里,那么可以用na_values来处理读取时也设置为NaN。
import pandas as pd
import numpy as np
deal = {"a": [3], "b":[6], "c" : [7, 11], "d" :[400], "message" : "the" }
d = pd.read_csv("./ts.csv", skiprows = [0,2,3], na_values = deal)
print "-" * 40
print d.iloc[:10], "# d.iloc[:10]"
print "-" * 40
print d.columns, "# d.columns"
print "-" * 40
print d.isnull(), "#d.isnull()"
执行结果:
----------------------------------------
a b c d message
0 1.0 2.0 3.0 4.0 hello
1 NaN 31.0 NaN NaN NaN
2 11.0 12.0 NaN NaN cruel
3 5.0 NaN NaN 8.0 world
4 9.0 NaN NaN 12.0 xaxa # d.iloc[:10]
----------------------------------------
Index([u'a', u'b', u'c', u'd', u'message'], dtype='object') # d.columns
----------------------------------------
a b c d message
0 False False False False False
1 True False True True True
2 False False True True False
3 False True True False False
4 False True True False False #d.isnull()
语句pd.read_csv("./ts.csv", skiprows = [0,2,3], na_values = deal)
里na_values = deal
的意思是当读取数据文件时,
a列字段值如果3,认为数据不合理,处理成NaN,c列下的值如果出现7或者11被处理成NaN。
- nrows可以实现只读数据文件的多少行数据。
import pandas as pd
import numpy as np
col = "sepal_length,sepal_width,petal_length,petal_width,class".split(",")
d0 = pd.read_csv("./iris.data", header = None, names = col, nrows = 5)
print d0, "# d0"
执行结果:
sepal_length sepal_width ... petal_width class
0 5.1 3.5 ... 0.2 Iris-setosa
1 4.9 3.0 ... 0.2 Iris-setosa
2 4.7 3.2 ... 0.2 Iris-setosa
3 4.6 3.1 ... 0.2 Iris-setosa
4 5.0 3.6 ... 0.2 Iris-setosa
[5 rows x 5 columns] # d0
- chunksize可以将一个大的数据文件按chunksize大小分割成若干个小块,一块一块的读数据文件,通过迭代器读取完数据文件。chunksize可以解决内存小、数据文件大的问题,分块读取,小马拉大车。
import pandas as pd
import numpy as np
col = "sepal_length,sepal_width,petal_length,petal_width,class".split(",")
dc = pd.read_csv("./iris.data", chunksize = 50, names = col)
d = pd.DataFrame({}, columns = col)
print "-" * 40
print d, "# d empty!"
print "-" * 40
i = 0
for v in dc:
d = d.append(v,sort=True)
print v.iloc[:5], "# v times is",i, type(v)
i += 1
print "-" * 40
print d.iloc[:10], "# d.iloc[:10]"
print "-" * 40
print d.iloc[-10:], "# d empty?"
print "-" * 40
irir.data文件共150行数据,通过语句pd.read_csv("./iris.data", chunksize = 50, names = col)
里的chunksize = 50
设置每次读取50行,那么读几次呢? $\frac{150}{50} = 3$次。再通过迭代读取每块的数据到dataframe数据d里:
for v in dc:
d = d.append(v,sort=True)
迭代3次把文件全部读完,每次读取都是一个长度为50条记录的dataframe。执行结果如下:
----------------------------------------
Empty DataFrame
Columns: [sepal_length, sepal_width, petal_length, petal_width, class]
Index: [] # d empty!
----------------------------------------
sepal_length sepal_width ... petal_width class
0 5.1 3.5 ... 0.2 Iris-setosa
1 4.9 3.0 ... 0.2 Iris-setosa
2 4.7 3.2 ... 0.2 Iris-setosa
3 4.6 3.1 ... 0.2 Iris-setosa
4 5.0 3.6 ... 0.2 Iris-setosa
[5 rows x 5 columns] # v times is 0 <class 'pandas.core.frame.DataFrame'>
sepal_length sepal_width ... petal_width class
50 7.0 3.2 ... 1.4 Iris-versicolor
51 6.4 3.2 ... 1.5 Iris-versicolor
52 6.9 3.1 ... 1.5 Iris-versicolor
53 5.5 2.3 ... 1.3 Iris-versicolor
54 6.5 2.8 ... 1.5 Iris-versicolor
[5 rows x 5 columns] # v times is 1 <class 'pandas.core.frame.DataFrame'>
sepal_length sepal_width ... petal_width class
100 6.3 3.3 ... 2.5 Iris-virginica
101 5.8 2.7 ... 1.9 Iris-virginica
102 7.1 3.0 ... 2.1 Iris-virginica
103 6.3 2.9 ... 1.8 Iris-virginica
104 6.5 3.0 ... 2.2 Iris-virginica
[5 rows x 5 columns] # v times is 2 <class 'pandas.core.frame.DataFrame'>
----------------------------------------
class petal_length ... sepal_length sepal_width
0 Iris-setosa 1.4 ... 5.1 3.5
1 Iris-setosa 1.4 ... 4.9 3.0
2 Iris-setosa 1.3 ... 4.7 3.2
3 Iris-setosa 1.5 ... 4.6 3.1
4 Iris-setosa 1.4 ... 5.0 3.6
5 Iris-setosa 1.7 ... 5.4 3.9
6 Iris-setosa 1.4 ... 4.6 3.4
7 Iris-setosa 1.5 ... 5.0 3.4
8 Iris-setosa 1.4 ... 4.4 2.9
9 Iris-setosa 1.5 ... 4.9 3.1
[10 rows x 5 columns] # d.iloc[:10]
----------------------------------------
class petal_length ... sepal_length sepal_width
140 Iris-virginica 5.6 ... 6.7 3.1
141 Iris-virginica 5.1 ... 6.9 3.1
142 Iris-virginica 5.1 ... 5.8 2.7
143 Iris-virginica 5.9 ... 6.8 3.2
144 Iris-virginica 5.7 ... 6.7 3.3
145 Iris-virginica 5.2 ... 6.7 3.0
146 Iris-virginica 5.0 ... 6.3 2.5
147 Iris-virginica 5.2 ... 6.5 3.0
148 Iris-virginica 5.4 ... 6.2 3.4
149 Iris-virginica 5.1 ... 5.9 3.0
[10 rows x 5 columns] # d empty?
----------------------------------------
chunksize参数可以实现解决大数据文件的分块读写的功能。
11.2 数据写入文本文件
dataframe数据可以调用to_csv函数将数据输出写入到对于类似于csv的文本文件。
import pandas as pd
import numpy as np
col = "sepal_length,sepal_width,petal_length,petal_width,class".split(",")
d0 = pd.read_csv("./iris.data", header = None, names = col, nrows = 5)
print d0, "# d0"
d0.to_csv("./iris_out.csv", index=False, header=False)
11.3 csv模块读数据文件
本节的内容是书中《Working with Delimited Formats》节次的笔记。内容本身不难,就是用csv读数据文件,然后组成dataframe,但其中有些知识点儿很有参考价值。
In [59]: data_dict = {h: v for h, v in zip(header, zip(*values))}
这句话很有内容。 * zip()可以将长度为m的n个的序列,整合成m个长度为n个的元组的列表。
i = range(1,11)
c = list("abcdefghij")
t = list("klmnopqrst")
print "i->", i
print "c->", c
print "t->", t
m_ict = zip(i, c, t)
这里有3个长度为10的列表,通过zip得到的m_ict是由10个长度为3的元组组成的列表。
i-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
c-> ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
t-> ['k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't']
[(1, 'a', 'k'), (2, 'b', 'l'), (3, 'c', 'm'), (4, 'd', 'n'), (5, 'e', 'o'), (6, 'f', 'p'), (7, 'g', 'q'), (8, 'h', 'r'), (9, 'i', 's'), (10, 'j', 't')] # m_ict
- zip(*)可以将元素为n长度的长度为m的二维列表,展开成n个长度为m元的二维列表,可以理解为是zip()的逆过程。
i = range(1,11)
c = list("abcdefghij")
t = list("klmnopqrst")
#print "i->", i
#print "c->", c
#print "t->", t
m_ict = zip(i, c, t)
print m_ict, "# m_ict = zip(i, c, t)"
print zip(*m_ict), "# zip(*m_ict)"
结果:
[(1, 'a', 'k'), (2, 'b', 'l'), (3, 'c', 'm'), (4, 'd', 'n'), (5, 'e', 'o'), (6, 'f', 'p'), (7, 'g', 'q'), (8, 'h', 'r'), (9, 'i', 's'), (10, 'j', 't')] # m_ict = zip(i, c, t)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), ('k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't')] # zip(*m_ict)
结果的每个元素就是zip()里的每个列表i、c、t。
- 对
{h: v for h, v in zip(header, zip(*values))}
语句的理解,之前见过列表解析:
li = [x for x in range(10)]
和这个很像,h:v类似于列表解析里方括号后的x。那么这个表示实际就是构造了一个字典,用header里的每个值做键,用zip(*values))里的每个数据做值。
k = ["c", "t"]
c = list("abcdefghij")
t = list("klmnopqrst")
v = zip(c, t)
d = {kk : vv for kk, vv in zip(k, zip(*v))}
print "-" * 40
print d, "# d"
print "-" * 40
df = pd.DataFrame(d)
print df, "# df"
print "-" * 40
print zip(*v), "# zip(*v)"
print "-" * 40
执行结果如下:
----------------------------------------
{'c': ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), 't': ('k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't')} # d
----------------------------------------
c t
0 a k
1 b l
2 c m
3 d n
4 e o
5 f p
6 g q
7 h r
8 i s
9 j t # df
----------------------------------------
[('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), ('k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't')]# zip(*v)
----------------------------------------
11.4 JSON数据
JSON数据是http服务器和各类与web相关应用间可传递数据的格式,形式很像字典,但要求key必须是字符串,而值values可以是任意的数据类型。Python有json模块,可以直接读取json数据成Python的字典数据。
import json
j = '''
{"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"]}]
}
'''
ret = json.loads(j)
print "-" * 40
print ret,type(ret)
print "-" * 40
print pd.DataFrame(ret['siblings'])
print "-" * 40
执行结果:
----------------------------------------
{u'pet': None, u'siblings': [{u'age': 30, u'name': u'Scott', u'pets': [u'Zeus', u'Zuko']}, {u'age': 38, u'name': u'Katie', u'pets': [u'Sixes', u'Stache', u'Cisco']}], u'name': u'Wes', u'places_lived': [u'United States', u'Spain', u'Germany']} <type 'dict'>
----------------------------------------
age name pets
0 30 Scott [Zeus, Zuko]
1 38 Katie [Sixes, Stache, Cisco]
----------------------------------------
11.5 在线读表格数据
pandas的read_html函数可以读取网页里的<table>
标签表格数据,一个网页里也许有多个表格,那么函数的返回值就是一个列表,每个元素就是一个表格的数据并以DataFrame形式存在。
import pandas as pd
url = 'http://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html'
df = pd.read_html(url)
print "-" * 40
print type(df), len(df), "# type(df), len(df)"
print type(df[1]), len(df[1]), "# type(df[1]), len(df[1])"
print "-" * 40
print df[1][:5]
print "-" * 40
print df[1].columns,df[1].index
print "-" * 40
执行结果:
----------------------------------------
<type 'list'> 4 # type(df), len(df)
<class 'pandas.core.frame.DataFrame'> 19 # type(df[1]), len(df[1])
----------------------------------------
0 1
0 T Transpose index and columns.
1 at Access a single value for a row/column label p...
2 axes Return a list representing the axes of the Dat...
3 blocks (DEPRECATED) Internal property, property synon...
4 columns The column labels of the DataFrame.
----------------------------------------
Int64Index([0, 1], dtype='int64') RangeIndex(start=0, stop=19, step=1)
----------------------------------------