pandas 与 R 对照指南:dplyr、plyr、reshape2 到 pandas 的完整迁移速查
【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas
本文基于 pandas 官方文档中的 "Comparison with R / R libraries" 页面整理而成,面向熟悉 R 生态(尤其是dplyr、plyr、reshape/reshape2)的数据分析师,系统梳理 R 常用数据操作在 pandas 中的等价实现:从 dplyr 动词(filter/select/mutate/group_by/summarise)的快速对照表,到 Base R 函数(aggregate、match、tapply、subset、with、factor)的逐一映射,再到 reshape 系列(melt/acast/dcast)的等价写法。读完本文,你可以把日常 R 数据分析代码平移到 Python/pandas,并知道每个操作在 pandas 源码中的实现位置与更进一步的官方文档入口。
比较的三大维度
pandas 的目标是提供 R 生态中大量被用于数据操作与分析的功能,因此官方文档将 R 语言及其众多第三方库与 pandas 的对照列为专门的对比维度。在与 R 及 CRAN 库的对比中,官方文档关注以下三点:
- 功能 / 灵活性(Functionality / flexibility):每个工具能做什么、不能做什么;
- 性能(Performance):操作的执行速度,官方更倾向于硬性的基准测试数据;
- 易用性(Ease-of-use):孰易孰难(这往往需要读者通过并排代码对比自行判断)。
该页面同时也是一份给 R 用户的"翻译指南"。下文所有内容均出自 comparison_with_r.rst。
dplyr 快速对照表
以下四组对照表覆盖了 dplyr 最常用的动词。建议直接作为速查卡片使用。
查询、过滤与采样
| R (dplyr) | pandas |
|---|---|
dim(df) | df.shape |
head(df) | df.head() |
slice(df, 1:10) | df.iloc[:9] |
filter(df, col1 == 1, col2 == 1) | df.query('col1 == 1 & col2 == 1') |
df[df$col1 == 1 & df$col2 == 1,] | df[(df.col1 == 1) & (df.col2 == 1)] |
select(df, col1, col2) | df[['col1', 'col2']] |
select(df, col1:col3) | df.loc[:, 'col1':'col3'] |
select(df, -(col1:col3)) | df.drop(cols_to_drop, axis=1)(见下方脚注) |
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) |
脚注(官方原文):R 中表示列区间的简写
select(df, col1:col3)在 pandas 中若已有列名列表,可以很干净地实现,例如df[cols[1:3]]或df.drop(cols[1:3]);但如果仅凭列名做这件事会稍显繁琐。
排序
| R (dplyr) | pandas |
|---|---|
arrange(df, col1, col2) | df.sort_values(['col1', 'col2']) |
arrange(df, desc(col1)) | df.sort_values('col1', ascending=False) |
sort_values定义于 frame.py 中,支持多列排序与ascending逐列指定升降序,对应 R 中desc()包裹单列降序的写法。
变换(Transforming)
| R (dplyr) | 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']) |
注意 dplyr 中select(df, col_one = col1)是"选择并重命名"的组合操作,pandas 需要先rename再按新列名取出。
分组与汇总
| R (dplyr) | 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 的
na.rm=TRUE是显式跳过 NA 后计算;pandas 的mean/sum等聚合默认跳过 NaN(skipna=True是默认值),即 pandas 侧默认行为已包含na.rm=TRUE的语义,无需额外参数。 - 惰性 vs 即时。
group_by在 R 中只是标记分组对象(gdf尚未计算),pandas 的df.groupby('col1')同样返回DataFrameGroupBy惰性对象,真正触发计算的是mean()/sum()/agg()等终端方法。
更完整的用法见 groupby 用户指南,其中 "Split" 与 "Aggregate" 小节分别对应原文档引用的groupby.split与groupby.aggregate锚点。
Base R 函数对照
用c切片
R 中可以很方便地按名称访问data.frame的列:
df <- data.frame(a=rnorm(5), b=rnorm(5), c=rnorm(5), d=rnorm(5), e=rnorm(5)) df[, c("a", "c", "e")]也可以按整数位置访问:
df <- data.frame(matrix(rnorm(1000), ncol=100)) df[, c(1:10, 25:30, 40, 50:100)]pandas 中按名称选取多列非常直接:
df = pd.DataFrame(np.random.randn(10, 3), columns=list("abc")) df[["a", "c"]] df.loc[:, ["a", "c"]]按整数位置选取不连续的多列,可以用iloc索引器配合numpy.r_实现——np.r_会把多个切片拼接成一个索引数组,正好对应 Rc()中并列多个区间的写法:
named = list("abcdefg") n = 30 columns = named + np.arange(len(named), n).tolist() df = pd.DataFrame(np.random.randn(n, n), columns=columns) df.iloc[:, np.r_[:10, 24:30]]aggregate对应groupby
R 中常见需求是把数据拆分成分组再各组求均值。给定df,按by1、by2分组:
df <- data.frame( v1 = c(1,3,5,7,8,3,5,NA,4,5,7,9), v2 = c(11,33,55,77,88,33,55,NA,44,55,77,99), by1 = c("red", "blue", 1, 2, NA, "big", 1, 2, "red", 1, NA, 12), by2 = c("wet", "dry", 99, 95, NA, "damp", 95, 99, "red", 99, NA, NA)) aggregate(x=df[, c("v1", "v2")], by=list(mydf2$by1, mydf2$by2), FUN = mean)pandas 的groupby方法与 Base R 的aggregate类似,官方文档中给出的等价写法(by1/by2混合字符串与数值、含 NA 的场景在两侧都能处理):
df = pd.DataFrame( { "v1": [1, 3, 5, 7, 8, 3, 5, np.nan, 4, 5, 7, 9], "v2": [11, 33, 55, 77, 88, 33, 55, np.nan, 44, 55, 77, 99], "by1": ["red", "blue", 1, 2, np.nan, "big", 1, 2, "red", 1, np.nan, 12], "by2": [ "wet", "dry", 99, 95, np.nan, "damp", 95, 99, "red", 99, np.nan, np.nan, ], } ) g = df.groupby(["by1", "by2"]) g[["v1", "v2"]].mean()细节上,R 的aggregate一步完成"分组 + 函数";pandas 先构建g(惰性DataFrameGroupBy),再选中要聚合的列调用mean()。更多细节与示例见 groupby 用户指南。
match/%in%对应isin
R 中用%in%选取数据很常见,而%in%底层就是用match函数定义的。%in%返回一个逻辑向量,指示每个元素是否匹配:
s <- 0:4 s %in% c(2,4)pandas 中isin方法与 R 的%in%操作符类似:
s = pd.Series(np.arange(5), dtype=np.float32) s.isin([2, 4])而 R 的match函数返回第一个参数在第二个参数中匹配位置的向量:
s <- 0:4 match(s, c(2,4))从源码结构看,isin的核心实现位于 algorithms.py(def isin),Series.isin/DataFrame.isin最终都委托到这一函数;它基于哈希表实现成员判断,对大规模去重后的成员查找是 O(n) 级别的。更多用法见 indexing 用户指南 中indexing_isin小节。
tapply对应pivot_table
aggregate要求子集大小规整,而tapply允许数据是"参差不齐的"数组(各组大小可以不规则)。用名为baseball的 data.frame,按team数组检索信息:
baseball <- data.frame(team = gl(5, 5, labels = paste("Team", LETTERS[1:5])), player = sample(letters, 25), batting.average = runif(25, .200, .400)) tapply(baseball$batting.average, baseball.example$team, max)在 pandas 中可以用pivot_table方法处理这类问题:
import random import string baseball = pd.DataFrame( { "team": ["team %d" % (x + 1) for x in range(5)] * 5, "player": random.sample(list(string.ascii_lowercase), 25), "batting avg": np.random.uniform(0.200, 0.400, 25), } ) baseball.pivot_table(values="batting avg", columns="team", aggfunc="max")pivot_table的函数签名定义在 pivot.py,values、columns、aggfunc三个参数与上例一一对应:值列、列方向展开键、聚合函数。更多细节见 reshaping 用户指南 的reshaping.pivot小节。
subset对应query
DataFrame.query方法与 Base R 的subset函数类似。在 R 中你可能想取出某一列值小于另一列值的行:
df <- data.frame(a=rnorm(10), b=rnorm(10)) subset(df, a <= b) df[df$a <= df$b,] # 注意逗号在 pandas 中有多种方式完成子集筛选:可以用query,也可以把表达式当作索引/切片传入,或使用标准布尔索引:
df = pd.DataFrame({"a": np.random.randn(10), "b": np.random.randn(10)}) df.query("a <= b") df[df["a"] <= df["b"]] df.loc[df["a"] <= df["b"]]query定义于 frame.py。从源码结构看,query内部基于pandas.eval的表达式引擎解析字符串条件,因此列名可以直接以裸标识符书写(无需df["a"]),这一点与 R 的subset(df, a <= b)语法体验最接近。更多细节见 indexing 用户指南 的indexing.query小节。
with对应eval
R 中利用with可以在 data.frame 的"环境"里求值表达式:
df <- data.frame(a=rnorm(10), b=rnorm(10)) with(df, a + b) df$a + df$b # 与上一表达式等价pandas 中利用eval方法得到等价表达式:
df = pd.DataFrame({"a": np.random.randn(10), "b": np.random.randn(10)}) df.eval("a + b") df["a"] + df["b"] # 与上一表达式等价官方文档特别指出:在特定情况下eval会比纯 Python 求值快很多(典型场景是对多个已有列做逐元素算术,避免创建大量中间 Series)。eval方法定义于 frame.py。更多细节与示例见 enhancingperf 用户指南 的enhancingperf.eval小节。
plyr:split-apply-combine
plyr是 R 中实现"拆分-应用-合并"(split-apply-combine)策略的库。其函数围绕 R 的三种数据结构设计:a代表arrays(数组),l代表lists(列表),d代表data.frame。下表给出这些结构在 Python 中的映射:
| R | Python |
|---|---|
| array | list |
| lists | dictionary or list of objects |
| data.frame | dataframe |
ddply对应groupby.agg
对名为df的 data.frame,按month、week分组汇总x:
require(plyr) df <- data.frame( x = runif(120, 1, 168), y = runif(120, 7, 334), z = runif(120, 1.7, 20.7), month = rep(c(5,6,7,8),30), week = sample(1:4, 120, TRUE) ) ddply(df, .(month, week), summarize, mean = round(mean(x), 2), sd = round(sd(x), 2))pandas 中利用groupby得到等价结果:
df = pd.DataFrame( { "x": np.random.uniform(1.0, 168.0, 120), "y": np.random.uniform(7.0, 334.0, 120), "z": np.random.uniform(1.7, 20.7, 120), "month": [5, 6, 7, 8] * 30, "week": np.random.randint(1, 4, 120), } ) grouped = df.groupby(["month", "week"]) grouped["x"].agg(["mean", "std"])细节差异:R 用sd(x)计算总体标准差(除以 n),pandas 的std默认是样本标准差(ddof=1,除以 n-1);如需对齐 R 的sd,可写grouped["x"].std(ddof=0)。更多细节见 groupby 用户指南 的groupby.aggregate小节。
reshape / reshape2
meltarray:三维数组熔化
在 R 中,把三维数组a熔化成 data.frame:
a <- array(c(1:23, NA), c(2,3,4)) data.frame(melt(a))在 Python 中,a是 numpy 数组(对应 R 的 array),可以直接用列表推导式展开:
a = np.array(list(range(1, 24)) + [np.nan]).reshape(2, 3, 4) pd.DataFrame([tuple(list(x) + [val]) for x, val in np.ndenumerate(a)])np.ndenumerate逐个产出 (多维索引, 值) 对,把索引拼上值即得到与 Rmelt输出同构的长表。
meltlist:列表熔化
R 中把列表a熔化成 data.frame:
a <- as.list(c(1:4, NA)) data.frame(melt(a))在 Python 中,这个列表等价于"带索引的元组列表",直接交给DataFrame构造器即可:
a = list(enumerate(list(range(1, 5)) + [np.nan])) pd.DataFrame(a)更多细节与示例见 数据结构的入门文档(原文档引用的dsintro锚点)。
meltdf对应pd.melt
R 中对cheese数据框做 reshape(melt 宽表为长表):
cheese <- data.frame( first = c('John', 'Mary'), last = c('Doe', 'Bo'), height = c(5.5, 6.0), weight = c(130, 150) ) melt(cheese, id=c("first", "last"))pandas 的pd.melt就是 R 中melt的等价物:
cheese = pd.DataFrame( { "first": ["John", "Mary"], "last": ["Doe", "Bo"], "height": [5.5, 6.0], "weight": [130, 150], } ) pd.melt(cheese, id_vars=["first", "last"]) cheese.set_index(["first", "last"]).stack() # 另一种等价写法从源码结构看,pd.melt的完整签名(melt.py)为melt(frame, id_vars=None, value_vars=None, var_name=None, value_name="value", col_level=None, ignore_index=True):id_vars对应 R 的id(保留为标识列),value_vars指定要展开的度量列(缺省时展开所有非id_vars列),var_name/value_name分别控制输出中"变量名"列(默认"variable")与"值"列(默认"value")的列名。此外DataFrame.set_index(...).stack()也提供了一种不引入 reshape 模块的替代路径。更多细节见 reshaping 用户指南 的reshaping.melt小节。
cast(acast/dcast)对应pivot_table
R 中acast把 melted 数据框"铸造"回高维数组:
df <- data.frame( x = runif(12, 1, 168), y = runif(12, 7, 334), z = runif(12, 1.7, 20.7), month = rep(c(5,6,7),4), week = rep(c(1,2), 6) ) mdf <- melt(df, id=c("month", "week")) acast(mdf, week ~ month ~ variable, mean)在 Python 中最直接的方式是组合pd.melt+pivot_table:
df = pd.DataFrame( { "x": np.random.uniform(1.0, 168.0, 12), "y": np.random.uniform(7.0, 334.0, 12), "z": np.random.uniform(1.7, 20.7, 12), "month": [5, 6, 7] * 4, "week": [1, 2] * 6, } ) mdf = pd.melt(df, id_vars=["month", "week"]) pd.pivot_table( mdf, values="value", index=["variable", "week"], columns=["month"], aggfunc="mean", )R 公式week ~ month ~ variable的三层结构(行 ~ 列 ~ 深度)在这里被拆解为pivot_table的index(多键行)与columns(列键)。
对dcast——用df按Animal与FeedType聚合信息:
df <- data.frame( Animal = c('Animal1', 'Animal2', 'Animal3', 'Animal2', 'Animal1', 'Animal2', 'Animal3'), FeedType = c('A', 'B', 'A', 'A', 'B', 'B', 'A'), Amount = c(10, 7, 4, 2, 5, 6, 2) ) dcast(df, Animal ~ FeedType, sum, fill=NaN) # 使用 Base R 的替代写法 with(df, tapply(Amount, list(Animal, FeedType), sum))Python 可以用两种方式逼近。第一种,同上利用pivot_table:
df = pd.DataFrame( { "Animal": [ "Animal1", "Animal2", "Animal3", "Animal2", "Animal1", "Animal2", "Animal3", ], "FeedType": ["A", "B", "A", "A", "B", "B", "A"], "Amount": [10, 7, 4, 2, 5, 6, 2], } ) df.pivot_table(values="Amount", index="Animal", columns="FeedType", aggfunc="sum")第二种,利用groupby:
df.groupby(["Animal", "FeedType"])["Amount"].sum()注意dcast的fill=NaN参数用于填充无观测组合;pivot_table默认行为就是在缺失组合处产生 NaN,语义天然对齐。更多细节见 reshaping 用户指南 的reshaping.pivot小节,或 groupby 用户指南 的groupby.split小节。
factor对应 Categorical
pandas 拥有专门针对分类数据的类型。R 中的示例:
cut(c(1,2,3,4,5,6), 3) factor(c(1,2,3,2,2,3))在 pandas 中由pd.cut与astype("category")完成:
pd.cut(pd.Series([1, 2, 3, 4, 5, 6]), 3) pd.Series([1, 2, 3, 2, 2, 3]).astype("category")两个函数的源码实现分别是:
pd.cut:定义于 tile.py,签名为cut(x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False, duplicates="raise", ordered=True)。bins传整数时表示"等宽分箱数"(会对 x 的范围两端各外扩 0.1% 以包住最小/最大值);传标量序列或IntervalIndex时按给定边界分箱(不扩展范围)。返回类型取决于labels:默认返回Intervaldtype 的 Series/Categorical;labels=False时返回各值所属箱的整数码——这与 Rfactor内部"codes + levels"的表达方式非常接近。- Categorical:定义于 categorical.py(
class Categorical),是 pandas 的扩展数组(ExtensionArray),内部即"codes(整数码)+ categories(取值表)"结构。
官方文档进一步指出 pandas Categorical 与 Rfactor的差异,详见 categorical 用户指南 的 "Differences to R's factor" 小节(categorical.rfactor锚点),要点包括:
- R 的
levels在 pandas 中叫categories; - R 的
levels恒为字符串类型,而 pandas 的categories可以是任意 dtype; - pandas 不允许在创建时指定 labels,需要事后用
s.cat.rename_categories(new_labels)改; - 与 R 的
factor不同,用已有序列化的数据单独构造新的分类序列不会删掉未使用的类别(pandas 保留全部 categories); - R 允许缺失值出现在
levels中,pandas 不允许NaN出现在categories中,但允许缺失值出现在values中。
更多细节与示例见 categorical 入门 与 Categorical 数组 API 文档(原文档引用的api.arrays.categorical锚点)。
小结
| R 概念 | pandas 等价物 | 源码位置 |
|---|---|---|
dplyrfilter/select/slice | query/ 列切片 /iloc | frame.py |
dplyrarrange | sort_values | frame.py |
dplyrgroup_by+summarise | groupby+agg/终端方法 | frame.py |
aggregate | groupby(...).mean() | 同上 |
%in%/match | isin | algorithms.py |
tapply | pivot_table | pivot.py |
subset | query(布尔索引等价) | frame.py |
with | eval | frame.py |
ddply | groupby.agg | frame.py |
reshape2::melt | pd.melt | melt.py |
acast/dcast | pivot_table/groupby.sum | pivot.py |
factor/cut | Categorical /pd.cut | categorical.py、tile.py |
需要强调的前提:本文所有对照关系均来自 pandas 官方文档的 R 对比页,代码示例与官方示例保持一致;其中涉及随机数生成的示例(np.random.*系列)在每次运行时输出不同,仅用于展示操作语法而非验证具体数值。R 的sample(1:4, 120, TRUE)为有放回均匀采样,pandas 侧示例改用np.random.randint(1, 4, 120)生成整数列,两者在"生成模拟数据"的目的下等价但分布细节不同。
【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考