R语言dataframe和pandas的比较
dataframe是统计学研究中经常使用的一种数据格式,最早在R语言上实现。python作为后起之秀,通过pandas这一模块也实现了对dataframe的支持。那么,这两种不同编程语言的dataframe又有什么区别呢?二者之间能否实现数据互通呢?本文将回答这些问题。
从R语言历史说起
S语言,一种用于统计的编程语言,由贝尔实验室的约翰·钱伯斯 、瑞克·贝克尔(Rick Becker)与艾伦·威尔克斯(Allan Wilks)共同研发,在1975年至1976年间在贝尔实验室被开发出来。
在那个年代,最主要的统计运算程序都是直接调用Fortran的子程序。但是S语言采用了高度互动式的方法来实现,因此极其先进,并被统计学工作者广泛采用。
S语言的后继者包括S-PLUS和R,然而S-PLUS是商业软件,因此流行并不广。更为人所熟知的是R语言。
1995年,新西兰Auckland大学的Robert Gentleman 和 Ross Ihaka(名字前缀均为R) 及其他志愿人员基于S语言的源代码开发了R语言系统。R语言增加了Scheme语言中词法作用域这一机制,使程序员得以将代码中某一对象的适用范围限制到一小段代码之中。在统计学家马丁·梅克勒的建议下,R语言成为GNU公共许可证下的一款免费开源软件。1997年4月,R综合文件网(CRAN)正式上线,其作为R语言各种软件包的仓库被广泛使用,地位相当于python的pypi。
因为S语言的血缘,R语言原生拥有一些适合统计学编程的面向对象数据结构,例如data.frame
(数据框)这一数据类型。
原生R语言的语法依然有些晦涩,于是有能人强者开发了tidyverse这一软件包集合,包括ggplot2(绘图)、dplyr(数据过滤和操作)、stringr(字符串处理)、readr(数据导入和导出)等多个著名工具包,它们“共享一个基本的设计理念、语法和数据结构”,也因此被广泛使用,现在已渐渐有了取代了R-base中一些基础函数的趋势。
bioconductor 是一个生信领域开源软件包的分发平台,大量生物学数据的处理工具托管于此。bioconductor上的软件包大都服务于R语言,使用到了许多R的功能,bioconductor包管理器也运行在R语言上,但bioconductor与CRAN没有任何关系,前者使用biocmanager::install
进行包管理,后者使用 install.packages()
进行包管理。
R语言的dataframe
如果是从其他编程语言转到R语言的初学者,对dataframe这一数据类型会感到有些莫名其妙。它既不像python的向量和矩阵那样,可以抽象为一维和二维的数组结构,也不像C++那种支持自定义属性和方法的“对象”。然而,经过一段时间的使用,其实会发现,dataframe是一种强大的数据结构。
如下所示,我们使用head()
函数打印一个dataframe的内容,可以看出这是一种以数据库表或Excel表格形式存储数据的结构。和数组/矩阵不同,dataframe支持按列名访问数据,这很适合处理一些统计表格中的数据。
1 | > data(iris) # 加载iris数据集 |
dataframe也支持按行号或列号进行索引。如下代码是两个例子,分别访问数据框的第一个元素和打印一定范围内的元素。
1 | > iris[1,1] |
与dataframe相辅相成的还有R语言内置的apply函数,它可以对dataframe进行高效的行操作和列操作。R语言还有一系列read
和write
函数,如read.csv
和write.csv
,支持从表格文件中导入数据,或者导出数据到表格文件当中。
Python对dataframe格式的支持
目前我们已经知道,Python的Pandas包也支持dataframe格式,二者在某些细节方面有区别,但设计思路大体上相同。或者说,pandas从一开始就是Python社区为了对标R语言的dataframe而写出来的模块。从R markdown核心开发人员、R语言大佬谢益辉的博客中可以看出这一点:
上回是讲 R 的各种怪癖,经过这五个月,我觉得 Python 有一统江湖的野心。过去我们总说统计是 R 的强项,别的语言要重写 R 的四千个包根本不可能,看 pandas 费了多大劲才实现 R/S 几十年前就实现的数据框结构,蟒蛇社区要重写个 ggplot2 有希望吗?多数人可能不会选择重造轮子,可是我渐渐发现蟒蛇社区真的是鸡血太充足了,真有人愿意把统计的东西一项一项重写出来。上次我在微博上说有人用 IPython notebook 展示了线性模型的设计阵,便是一个一统统计江湖的象征,现在又有人开始用 IPython 写贝叶斯 / MCMC 的书。小众的 R 社区,会不会被蟒蛇吞掉呢?
——《千年等一回》谢益辉 2013-02-27
pandas实现了dataframe的各种操作,包括按列的数据选择器、数据选取和修改、从文件读取数据、导出数据到文件等功能。
如上图,pandas的官方文档甚至贴心的给出了pandas与其他工具的比较,以方便大家从其他工具迁移到pandas。
pandas vs R dataframe
终于到了重点,pandas和R dataframe这两个工具的区别是什么呢?
下面这一段的内容主要参考pandas官方文档 。表格里面列出了dataframe的常见操作在两个工具中的区别:
1. 查询、过滤和抽样
R | pandas | 备注 |
---|---|---|
dim(df) |
df.shape |
输出dataframe的数据维度 |
head(df) |
df.head() |
输出dataframe前几行数据 |
slice(df, 1:10) |
df.iloc[:9] |
按行号或列号索引对数据进行切片。在R中,这一功能需要dplyr 包的支持 |
filter(df, col1 == 1, col2 == 1) |
df.query('col1 == 1 & col2 == 1') |
按条件进行过滤。在R中,这一功能需要dplyr 包的支持 |
df[df$col1 == 1 & df$col2 == 1,] |
df[(df.col1 == 1) & (df.col2 == 1)] |
按条件进行过滤 |
select(df, col1, col2) |
df[['col1', 'col2']] |
按列名进行数据选取。在R中,这一功能需要dplyr 包的支持 |
select(df, col1:col3) |
df.loc[:, 'col1':'col3'] |
按列名进行数据选取(选择多个列)。在R中,这一功能需要dplyr 包的支持 |
select(df, -(col1:col3)) |
df.drop(cols_to_drop, axis=1) |
按列名进行数据过滤(排除掉指定列)。在R中,这一功能需要dplyr 包的支持。在pandas中,需要额外的代码提取出所有要排除的列 |
distinct(select(df, col1)) |
df[['col1']].drop_duplicates() |
选择一列中不同的数据 |
distinct(select(df, col1, col2)) |
df[['col1', 'col2']].drop_duplicates() |
选择两列中不同的数据 |
sample_n(df, 10) |
df.sample(n=10) |
按数量抽样 |
sample_frac(df, 0.01) |
df.sample(frac=0.01) |
按比例抽样 |
2. 排序
R | pandas |
---|---|
arrange(df, col1, col2) |
df.sort_values(['col1', 'col2']) |
arrange(df, desc(col1)) |
df.sort_values('col1', ascending=False) |
3. 变换
R | pandas |
---|---|
select(df, col_one = col1) |
df.rename(columns={'col1': 'col_one'})['col_one'] |
rename(df, col_one = col1) |
df.rename(columns={'col1': 'col_one'}) |
mutate(df, c=a-b) |
df.assign(c=df['a']-df['b']) |
4. 分组与总结
R | pandas |
---|---|
summary(df) |
df.describe() |
gdf <- group_by(df, col1) |
gdf = df.groupby('col1') |
summarise(gdf, avg=mean(col1, na.rm=TRUE)) |
df.groupby('col1').agg({'col1': 'mean'}) |
summarise(gdf, total=sum(col1)) |
df.groupby('col1').sum() |
由于R语言对象系统的特点,上述比较中用到的R语言的函数如select(),mutate(),summary()
等都可以直接调用;但在python中,这些函数属于pandas对象的方法,因此需要用“对象名+方法名”的方法来调用(例如 df.head()
就是调用df
这一pandas对象的head()
方法。但总体上看,两种语言实现的dataframe操作在很多地方都是一致的。
R dataframe和pandas dataframe的相互转换
有些时候我们可能有跨编程语言操作的需要,例如在R语言中使用某个bioconductor上的包进行数据预处理,随后用python上的某些工具进行建模和可视化。因此,将dataframe对象在两种语言之间相互转换也是有必要的。
最简单粗暴的转换方法是以文本文件(.csv或.txt)为中介的。具体方法如下:
R | pandas | |
---|---|---|
导出到文本文件 | write.table() 或write.csv() |
pandas.to_table() 或pandas.to_csv() |
从文本文件导入 | read.table() 或read.csv() |
pandas.read_table() 或pandas.read_csv() |
但是,当数据量过大时,这样的导入导出操作极其耗时,且会产生巨大的文本文件。如果能以一些中间格式(如.RData
)为媒介进行数据交换,则会更方便一点。
.RData
文件是R语言的序列化文件格式。所谓序列化 (Serialization)就是将对象的状态信息转换为可以存储或传输的形式的过程,譬如说将一个dataframe的二进制数据流直接存储到文件当中(而不是转换为csv这种文本文件)。R语言提供了save()
和save.image()
两个函数用于将数据对象存储到 .RData
文件,提供了load()
函数用于从 .RData
文件中读取对象。使用.RData
文件存储速度更快,且更节省空间。
那么python有没有可能去读取一个R语言的.RData
文件呢?答案是可以的。有一个python模块叫做rpy2,可以实现python中对R的调用,以及二者之间的数据结构互转。使用pip install rpy2
指令即可进行安装
关于R dataframe转pandas dataframe,可以参考下面的文档说明
https://rpy2.github.io/doc/v3.5.x/html/generated_rst/pandas.html#from-r-to-pandas
以一个实例来说明。我们将R语言的示例数据集iris
导入到.RData
里面,随后再用python读取这个数据集。首先,使用下面的代码导出iris
:
1 | data(iris) |
然后在同一文件夹下,使用下面的python代码导入iris
:
1 | import numpy as np |
运行结果如下:
如图,通过我们的一番操作,成功实现了将R语言的dataframe导入python中的操作。