本篇文章是python数据探索系列扫盲文章的第二篇,主要和各位朋友探讨一下pandas,后续文章会陆续介绍Matplotlib。如有疏漏不当的地方,请留言或私信我。共同进步!
第一篇的链接:
python数据探索(1)—numpy
本文参考了十分钟快速入门Pandas,建议有兴趣的朋友先去看看原文。
一、简述
在上篇讲述numpy的教程中,我们知道使用numpy也可以进行切片操作。但我们使用pandas可以更加方便地实现上文中的操作。
下面我们以一个数据集为例,简单说明一下pandas的用法。数据集为英国降雨数据。
我们现在假设对该数据集一无所知,我们从头开始探索这个数据集。
二、Pandas基本使用
2.1 导入数据
第一步当然是导入数据。和numpy类似,Pandas也只需要一句话导入csv文件。
import pandas as pd
df = pd.read_csv('uk_rain_2014.csv', header=0,encoding='gbk')
其中header参数指明哪些是数据的列名。如果没有列名的话就将它设定为 None 。Pandas一般能自动识别,所以这个经常可以省略。
当我们处理含中文数据的集合时,最好加上encoding='gbk',可以避免乱码。
2.2 Pandas数据类型
Pandas 基于两种数据类型,series 和 dataframe。series 和nump数组差不多,是一种一维的数据类型,其中的每个元素都有各自的标签,标签可以是数字或者字符。dataframe 是一个二维的、表格型的数据结构。Pandas 的 dataframe 可以储存许多不同类型的数据,并且每个轴都有标签。你可以把它当作一个 series 的字典。
2.3 信息搜集
之前数据已经导入到 Pandas ,我们想看一眼数据来得到一些基本信息,以便在真正开始探索之前找到一些方向。
查看前 x 行的数据:
# Getting first x rows.
df.head(5)
我们得到的结果如下:
Water Year Rain (mm) Oct-Sep Outflow (m3/s) Oct-Sep Rain (mm) Dec-Feb \
0 1980/81 1182 5408 292
1 1981/82 1098 5112 257
2 1982/83 1156 5701 330
3 1983/84 993 4265 391
4 1984/85 1182 5364 217
Outflow (m3/s) Dec-Feb Rain (mm) Jun-Aug Outflow (m3/s) Jun-Aug
0 7248 174 2212
1 7316 242 1936
2 8567 124 1802
3 8905 141 1078
4 5813 343 4313
一共是7列数据,我们再看看后面几行。
# Getting last x rows.
df.tail(5)
结果如下:
Water Year Rain (mm) Oct-Sep Outflow (m3/s) Oct-Sep Rain (mm) Dec-Feb \
28 2008/09 1139 4941 268
29 2009/10 1103 4738 255
30 2010/11 1053 4521 265
31 2011/12 1285 5500 339
32 2012/13 1090 5329 350
Outflow (m3/s) Dec-Feb Rain (mm) Jun-Aug Outflow (m3/s) Jun-Aug
28 6690 323 3189
29 6435 244 1958
30 6593 267 2885
31 7630 379 5261
32 9615 187 1797
跟 head 一样,我们只需要调用 tail 并且传入想要查看的行数即可。注意,它并不是从最后一行倒着显示的,而是按照数据原来的顺序显示。
我们可以发现列名很长,使用起来不太方便。所以我们可以修改一下列名。
# Changing column labels.
df.columns = ['water_year','rain_octsep', 'outflow_octsep',
'rain_decfeb', 'outflow_decfeb', 'rain_junaug', 'outflow_junaug']
df.head(5)
我们发现列名改变了。
water_year rain_octsep outflow_octsep rain_decfeb outflow_decfeb \
0 1980/81 1182 5408 292 7248
1 1981/82 1098 5112 257 7316
2 1982/83 1156 5701 330 8567
3 1983/84 993 4265 391 8905
4 1984/85 1182 5364 217 5813
rain_junaug outflow_junaug
0 174 2212
1 242 1936
2 124 1802
3 141 1078
4 343 4313
我们通常会想知道数据的另一个特征——它有多少条记录。在 Pandas 中,一条记录对应着一行,所以我们可以对数据集调用 len 方法,它将返回数据集的总行数:
# Finding out how many rows dataset has.
len(df)
本例中结果是33。
数据集的一些基本的统计数据,在 Pandas 中我们可以用describe()获得
df.describe()
rain_octsep outflow_octsep rain_decfeb outflow_decfeb rain_junaug \
count 33.000000 33.000000 33.000000 33.000000 33.000000
mean 1129.000000 5019.181818 325.363636 7926.545455 237.484848
std 101.900074 658.587762 69.995008 1692.800049 66.167931
min 856.000000 3479.000000 206.000000 4578.000000 103.000000
25% 1053.000000 4506.000000 268.000000 6690.000000 193.000000
50% 1139.000000 5112.000000 309.000000 7630.000000 229.000000
75% 1182.000000 5497.000000 360.000000 8905.000000 280.000000
max 1387.000000 6391.000000 484.000000 11486.000000 379.000000
outflow_junaug
count 33.000000
mean 2439.757576
std 1025.914106
min 1078.000000
25% 1797.000000
50% 2142.000000
75% 2959.000000
max 5261.000000
2.4 数据筛选
下面我们讨论如何获取我们想要的数据。比如提取一整列,使用列的标签可以非常简单地做到。
# Getting a column by label
df['rain_octsep']
结果如下。当我们提取列的时候,会得到一个 series ,而不是 dataframe 。记得我们前面提到过,你可以把 dataframe 看作是一个 series 的字典,所以在抽取列的时候,我们就会得到一个 series。
0 1182
1 1098
2 1156
3 993
4 1182
5 1027
6 1151
7 1210
8 976
9 1130
10 1022
……
我们就可以像访问对象属性一样访问数据集的列——只用一个点号。如下所示,而结果不变。
# Getting a column by label using .
df.rain_octsep
之前numpy系列的文档中我们提到可以使用“布尔过滤”,在Pandas中,我们依然可以这么使用。
# Creating a series of booleans based on a conditional
df.rain_octsep < 1000
或者这么写
# Using a series of booleans to filter
df[df.rain_octsep < 1000]
结果如下:
3 1983/84 993 4265 391 8905
8 1988/89 976 4330 309 6465
15 1995/96 856 3479 245 5515
rain_junaug outflow_junaug
3 141 1078
8 200 1440
15 172 1439
可以通过复合条件表达式来进行过滤:
# Filtering by multiple conditionals
df[(df.rain_octsep < 1000) & (df.outflow_octsep < 4000)] # Can't use the keyword 'and'
这条代码只会返回 rain_octsep 中小于 1000 的和 outflow_octsep 中小于 4000 的记录:
注意重要的一点:这里不能用 and 关键字,因为会引发操作顺序的问题。必须用 & 和圆括号。
如果你的数据中字符串,也可以使用字符串方法来进行过滤:
# Filtering by string methods
df[df.water_year.str.startswith('199')]
必须用 .str.[string method] ,而不能直接在字符串上调用字符方法。上面的代码返回所有 90 年代的记录:
water_year rain_octsep outflow_octsep rain_decfeb outflow_decfeb \
10 1990/91 1022 4418 305 7120
11 1991/92 1151 4506 246 5493
12 1992/93 1130 5246 308 8751
13 1993/94 1162 5583 422 10109
14 1994/95 1110 5370 484 11486
15 1995/96 856 3479 245 5515
16 1996/97 1047 4019 258 5770
17 1997/98 1169 4953 341 7747
18 1998/99 1268 5824 360 8771
19 1999/00 1204 5665 417 10021
rain_junaug outflow_junaug
10 216 1923
11 280 2118
12 219 2551
13 193 1638
14 103 1231
15 172 1439
16 256 2102
17 285 3206
18 225 2240
19 197 2166
2.5 使用索引
Pandas 的行也有标签。行标签可以是基于数字的或者是标签,而且获取行数据的方法也根据标签的类型各有不同。
如果你的行标签是数字型的,你可以通过 iloc 来引用:
# Getting a row via a numerical index
df.iloc[30]
iloc 只对数字型的标签有用。它会返回给定行的 series,行中的每一列都是返回 series 的一个元素。
也许你的数据集中有年份或者年龄的列,你可能想通过这些年份或者年龄来引用行,这个时候我们就可以设置一个(或者多个)新的索引:
# Setting a new index from an existing column
df = df.set_index(['water_year'])
df.head(5)
上面的代码将 water_year 列设置为索引。注意,列的名字实际上是一个列表,虽然上面的例子中只有一个元素。如果你想设置多个索引,只需要在列表中加入列的名字即可。
上例中我们设置的索引列中都是字符型数据,这意味着我们不能继续使用 iloc 来引用,那我们用什么呢?用 loc 。
# Getting a row via a label-based index
df.loc['2000/01']
和 iloc 一样,loc 会返回你引用的列,唯一一点不同就是此时你使用的是基于字符串的引用,而不是基于数字的。
还有一个引用列的常用常用方法—— ix 。如果 loc 是基于标签的,而 iloc 是基于数字的,那 ix 是基于什么的?事实上,ix 是基于标签的查询方法,但它同时也支持数字型索引作为备选。
# Getting a row via a label-based or numerical index
df.ix['1999/00'] # Label based with numerical index fallback *Not recommended
将索引排序通常会很有用,在 Pandas 中,我们可以对 dataframe 调用 sort_index 方法进行排序。
df.sort_index(ascending=False).head(5) #inplace=True to apple the sorting in place
我的索引本来就是有序的,为了演示,我将参数 ascending 设置为 false,这样我的数据就会呈降序排列。
当你将一列设置为索引的时候,它就不再是数据的一部分了。如果你想将索引恢复为数据,调用 set_index 相反的方法 reset_index 即可:
# Returning an index to data
df = df.reset_index('water_year')
df.head(5)
2.5 操作数据集的结构
重新建立数据结构,使得数据集呈现出一种更方便并且(或者)有用的形式。
首先,是 groupby :
#Manipulating structure (groupby, unstack, pivot)
# Grouby
df.groupby(df.year // 10 *10).max()
groupby 会按照你选择的列对数据集进行分组。上例是按照年代分组。不过仅仅这样做并没有什么用,我们必须对其调用函数,比如 max 、 min 、mean 等等。例中,我们可以得到 90 年代的均值。
也可以按照多列进行分组:
# Grouping by multiple columns
decade_rain = df.groupby([df.year // 10 * 10, df.rain_octsep // 1000 * 1000])[['outflow_octsep', 'outflow_decfeb', 'outflow_junaug']].mean()
decade_rain
接下来是 unstack ,最开始可能有一些困惑,它可以将一列数据设置为列标签。最好还是看看实际的操作:
# Unstacking
decade_rain.unstack(0)
这条语句将上例中的 dataframe 转换为下面的形式。它将第 0 列,也就是 year 列设置为列的标签。
让我们再操作一次。这次使用第 1 列,也就是 rain_octsep 列:
# More unstacking
decade_rain.unstack(1)
有一个 .fillna('') 。pivot 产生了很多空的记录,也就是值为 NaN 的记录。数据集里面有很多 NaN 会很烦,所以使用了 fillna('') 。你也可以用别的别的东西,比方说 0 。我们也可以使用 dropna(how = 'any') 来删除有 NaN 的行,不过这样就把所有的数据都删掉了,所以不这样做。
2.6 合并数据集
有两个相关联的数据集,你想将它们放在一起比较或者合并它们。好的,没问题,在 Pandas 里很简单:
# Merging two datasets together
rain_jpn = pd.read_csv('jpn_rain.csv')
rain_jpn.columns = ['year', 'jpn_rainfall']
uk_jpn_rain = df.merge(rain_jpn, on='year')
uk_jpn_rain.head(5)
首先你需要通过 on 关键字来指定需要合并的列。通常你可以省略这个参数,Pandas 将会自动选择要合并的列。
如下图所示,两个数据集在年份这一类上合并了。jpn_rain 数据集只有年份和降雨量两列,通过年份列合并之后,jpn_rain 中只有降雨量那一列合并到了 UK_rain 数据集中。