zyhhelen 2018-07-16
在我看来,对于Numpy以及Matplotlib,Pandas可以帮助创建一个非常牢固的用于数据挖掘与分析的基础。而Scipy(会在接下来的帖子中提及)当然是另一个主要的也十分出色的科学计算库,但是我认为前三者才是真正的Python科学计算的支柱。
所以,不需要太多精力,让我们马上开始Python科学计算系列的第三帖——Pandas。如果你还没有查看其他帖子,不要忘了去看一下哦!
导入Pandas
我们首先要导入我们的演出明星——Pandas。
这是导入Pandas的标准方式。显然,我们不希望每时每刻都在程序中写’pandas’,但是保持代码简洁、避免命名冲突还是相当重要的。因而我们折衷一下,用‘pd’代替“pandas’。如果你仔细查看其他人使用Pandas的代码,你会发现这条导入语句。
Pandas的数据类型
Pandas基于两种数据类型:series与dataframe。
一个series是一个一维的数据类型,其中每一个元素都有一个标签。如果你阅读过这个系列的关于Numpy的文章,你就可以发现series类似于Numpy中元素带标签的数组。其中,标签可以是数字或者字符串。
一个dataframe是一个二维的表结构。Pandas的dataframe可以存储许多种不同的数据类型,并且每一个坐标轴都有自己的标签。你可以把它想象成一个series的字典项。
将数据导入Pandas
在我们开始挖掘与分析之前,我们首先需要导入能够处理的数据。幸好,Pandas在这一点要比Numpy更方便。
在这里我推荐你使用自己所感兴趣的数据集来使用。你的或其他国家的政府网站上会有一些好的数据源。例如,你可以搜索英国政府数据或美国政府数据来获取数据源。当然,Kaggle是另一个好用的数据源。
在此,我将采用英国政府数据中关于降雨量数据,因为他们十分易于下载。此外,我还下载了一些日本降雨量的数据来使用。
这里我们从csv文件中读取到了数据,并将他们存入了dataframe中。我们只需要调用read_csv函数并将csv文件的路径作为函数参数即可。header关键字告诉Pandas这些数据是否有列名,在哪里。如果没有列名,你可以将其置为None。Pandas非常智能,所以你可以省略这一关键字。
将你的数据准备好以进行挖掘和分析
现在我们已经将数据导入了Pandas。在我们开始深入探究这些数据之前,我们一定迫切地想大致浏览一下它们,并从中获得一些有用信息,帮助我们确立探究的方向。
想要快速查看前x行数据:
我们仅仅需要使用head()函数并传入我们期望获得的行数。
你将获得一个类似下图一样的表:
另一方面,你可能想要获得最后x行的数据:
类似于head,我们只需要调用tail函数并传入我们想获取的行数。需要注意的是,Pandas不是从dataframe的结尾处开始倒着输出数据,而是按照它们在dataframe中固有的顺序输出给你。
你将获得类似下图的表
当你在Pandas中查找列时,你通常需要使用列名。这样虽然非常便于使用,但有时候,数据可能会有特别长的列名,例如,有些列名可能是问卷表中的某整个问题。把这些列名变短会让你的工作更加轻松:
有一点需要注意的是,在这里我故意让所有列的标签都没有空格和横线。后面你将会看到,如果我们这样命名变量,Pandas会将它们存成什么类型。
你将获得同之前一样的数据,但是列名已经变了:
另一件你很想知道的关于你的数据的重要的事情是数据一共有多少条目。在Pandas中,一个条目等同于一行,所以我们可以通过len方法获取数据的行数,即条目数。
这将给你一个整数告诉你数据的行数。在我的数据集中,我有33行。
此外,你可能需要知道你数据的一些基本的统计信息。Pandas让这件事变得非常简单。
这将返回一个包含多种统计信息的表格,例如,计数,均值,标准方差等。它看起来像这样:
过滤
当你查看你的数据集时,你可能希望获得一个特殊的样本数据。例如,如果你有一个关于工作满意度的问卷调查数据,你可能想要获得所有在同一行业或同一年龄段的人的数据。
Pandas为我们提供了多种方法来过滤我们的数据并提取出我们想要的信息。有时候你想要提取一整列。可以直接使用列标签,非常容易。
注意到当我们提取了一列,Pandas将返回一个series,而不是一个dataframe。是否还记得,你可以将dataframe视作series的字典。所以,如果我们取出了某一列,我们获得的自然是一个series。
还记得我所说的命名列标签的注意事项吗?不使用空格和横线等可以让我们以访问类属性相同的方法来访问列,即使用点运算符。
这里返回的结果和之前的一模一样,即一个包含我们所选列的数据的series。
如果你读过这一系列中Numpy那一篇帖子,你可能会记得一项技术叫做‘boolean masking’,即我们可以在数组上运行一个条件语句来获得对应的布尔值数组。好,我们也可以在Pandas中做同样的事。
上述代码将范围一个布尔值的dataframe,其中,如果9、10月的降雨量低于1000毫米,则对应的布尔值为‘True’,反之,则为’False’。
我们也可以使用这些条件表达式来过滤一个已知的dataframe。
这将返回一个仅仅包含9、10月降雨量低于1000mm的条目的dataframe。
你也可以使用多条条件表达式来进行过滤:
这将返回rain_octsep小于1000并且outflow_octsep小于4000的那些条目。
值得注意的是,由于操作符优先级的问题,在这里你不可以使用关键字‘and’,而只能使用’&’与括号
好消息是,如果在你的数据中有字符串,你也可以使用字符串方法来过滤数据。
注意到你必须使用.str.[string method],你不能直接在字符串上直接调用字符串方法。这一语句返回1990年代的所有条目。
索引
前几部分为我们展示了如何通过列操作来获得数据。实际上,Pandas同样有标签化的行操作。这些行标签可以是数字或是其他标签。获取行数据的方法也取决于这些标签的类型。
如果你的行有数字索引,你可以使用iloc引用他们:
iloc仅仅作用于数字索引。它将会返回该行的一个series。在返回的series中,这一行的每一列都是一个独立的元素。
可能在你的数据集里有年份的列,或者年代的列,并且你希望可以用这些年份或年代来索引某些行。这样,我们可以设置一个(或多个)新的索引。
这将会给’water_year’一个新的索引值。注意到列名虽然只有一个元素,却实际上需要包含于一个列表中。如果你想要多个索引,你可以简单地在列表中增加另一个列名。
在上面这个例子中,我们把我们的索引值全部设置为了字符串。这意味着我们不可以使用iloc索引这些列了。这种情况该如何?我们使用loc。
这里,loc和iloc一样会返回你所索引的行数据的一个series。唯一的不同是此时你使用的是字符串标签进行引用,而不是数字标签。
ix是另一个常用的引用一行的方法。那么,如果loc是字符串标签的索引方法,iloc是数字标签的索引方法,那什么是ix呢?事实上,ix是一个字符串标签的索引方法,但是它同样支持数字标签索引作为它的备选。
正如loc和iloc,上述代码将返回一个series包含你所索引的行的数据。
既然ix可以完成loc和iloc二者的工作,为什么还需要它们呢?最主要的原因是ix有一些轻微的不可预测性。还记得我说数字标签索引是ix的备选吗?数字标签可能会让ix做出一些奇怪的事情,例如将一个数字解释成一个位置。而loc和iloc则为你带来了安全的、可预测的、内心的宁静。然而必须指出的是,ix要比loc和iloc更快。
通常我们都希望索引是整齐有序地。我们可以在Pandas中通过调用sort_index来对dataframe实现排序。
由于我的所以已经是有序的了,所以为了演示,我设置了关键字参数’ascending’为False。这样,我的数据会以降序排列。
当你为一列数据设置了一个索引时,它们将不再是数据本身了。如果你想把索引设置为原始数据的形式,你可以使用和set_index相反的操作——reset_index。
这将返回数据原始的索引形式。
对数据集应用函数
有时候你会想以某些方式改变或是操作你数据集中的数据。例如,如果你有一列年份的数据而你希望创建一个新的列显示这些年份所对应的年代。Pandas对此给出了两个非常有用的函数,apply和applymap。
这会创建一个名为‘year‘的新列。这一列是由’water_year’列所导出的。它获取的是主年份。这便是使用apply的方法,即如何对一列应用一个函数。如果你想对整个数据集应用某个函数,你可以使用dataset.applymap()。
操作一个数据集结构
另一件经常会对dataframe所做的操作是为了让它们呈现出一种更便于使用的形式而对它们进行的重构。
首先,groupby:
grouby所做的是将你所选择的列组成一组。上述代码首先将年代组成一组。虽然这样做没有给我们带来任何便利,但我们可以紧接着在这个基础上调用其它方法,例如max, min, mean等。例子中,我们可以得到90年代的均值。
你也可以对多行进行分组操作:
接下来的unstack操作可能起初有一些困惑。它的功能是将某一列前置成为列标签。我们最好如下看看它的实际效果。
这个操作会将我们在上面小节创建的dataframe转变成如下形式。它将标识‘year’索引的第0列推起来,变为了列标签。
我们再附加一个unstack操作。这次我们对’rain_octsep’索引的第1列操作:
现在,在我们下一个操作前,我们首先创造一个新的dataframe。
上述代码为我们创建了如下的dataframe,我们将对它进行pivot操作。
pivot实际上是在本文中我们已经见过的操作的组合。首先,它设置了一个新的索引(set_index()),然后它对这个索引排序(sort_index()),最后它会进行unstack操作。组合起来就是一个pivot操作。看看你能不能想想会发生什么:
注意到最后有一个.fillna(‘’)。这个pivot创造了许多空的或值为NaN的条目。我个人觉得我的dataframe被乱七八糟的NaN分散了注意力,所以使用了fillna(‘’)将他们变成了空字符串。你也可以输入任何你喜欢的东西,例如一个0。我们也可以使用函数dropna(how=’any’)来删除所有的带有NaN的行。然而在这个例子里,它可能会把所有东西都删了,所以我们没有这样做。
上述dataframe为我们展现了所有降雨量大于1250的年份中的总雨量。不可否认的是,这个并不是一个pivot的最好的示范,但是希望你能get到它的核心。看看你能在你自己的数据集中想出什么点子。
合并数据集
有时候你有两个单独的数据集,它们直接互相关联,而你想要比较它们的差异或者合并它们。没问题,Pandas可以很容易实现:
开始时你需要通过’on’关键字参数指定你想要合并的列。你也可以忽略这个参数,这样Pandas会自动确定合并哪列。
如下你可以看到,两个数据集在年份这一类上已经合并了。rain_jpn数据集仅仅包含年份以及降雨量。当我们以年份这一列进行合并时,仅仅’jpn_rainfall’这一列和我们UK雨量数据集的对应列进行了合并。
采用Pandas快速绘制图表
Matplotlib很好用,但是想要画出一个中途下降的图表还是需要费一番功夫的。而有的时候你仅仅想要快速画出一个数据的大致走势来帮助你发掘搞清这些数据的意义。Pandas提供了plot函数满足你的需求:
这里非常轻松快速地利用plot画出了一个你的数据的图表。利用这个图表,你可以紧接着直观地发现深入挖掘的方向。例如,如果你看我画出的我数据的图表,你可以看到1995年英国可能发生了干旱。
你也能发现英国的降雨量明显低于日本,然而人们却说英国雨下得很多!
存储你的数据集
在清理、重构以及挖掘完你的数据后,你通常会剩下一些非常重要有用的东西。你不仅应当保留下你的原始数据,也同样需要保存下你最新处理过的数据集。
上述代码会将你的数据存入一个csv文件以备下次使用。
到此为止,我们简单介绍了Pandas。正如我之前说的,Pandas是非常好用的库,而我们仅仅是接触了一点皮毛。但是我希望通过我的介绍,你可以开始进行真正的数据清理与挖掘工作了。
像往常一样,我非常希望你能尽快开始尝试Pandas。找一两个你喜欢的数据集,开一瓶啤酒,坐下来,然后开始探索你的数据吧。这确实是唯一的熟悉Pandas以及其他这一系列文章中提到的库的方式。再加上你永远不知道的,你会找到一些你感兴趣的东西的。
计算的时候总共分3步,1到2是第二组......lower: i. 这组数据中的小值 higher: j. 这组数据中的大值,fraction 是第三步中的小数部分,意思是当前这组数据的0到1的分位数
Series是一种类似于一维数组的对象,由一组数据以及一组与之对应的索引组成。 index: 索引序列,必须是唯一的,且与数据的长度相同. 如果没有传入索引参数,则默认会自动创建一个从0~N的整数索引