news 2026/9/18 6:25:58

pandas 与 R 对照指南:dplyr、plyr、reshape2 到 pandas 的完整迁移速查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
pandas 与 R 对照指南:dplyr、plyr、reshape2 到 pandas 的完整迁移速查

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 生态(尤其是dplyrplyrreshape/reshape2)的数据分析师,系统梳理 R 常用数据操作在 pandas 中的等价实现:从 dplyr 动词(filter/select/mutate/group_by/summarise)的快速对照表,到 Base R 函数(aggregatematchtapplysubsetwithfactor)的逐一映射,再到 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()

两点差异值得注意:

  1. 缺失值处理语义不同。R 的na.rm=TRUE是显式跳过 NA 后计算;pandas 的mean/sum等聚合默认跳过 NaN(skipna=True是默认值),即 pandas 侧默认行为已包含na.rm=TRUE的语义,无需额外参数。
  2. 惰性 vs 即时group_by在 R 中只是标记分组对象(gdf尚未计算),pandas 的df.groupby('col1')同样返回DataFrameGroupBy惰性对象,真正触发计算的是mean()/sum()/agg()等终端方法。

更完整的用法见 groupby 用户指南,其中 "Split" 与 "Aggregate" 小节分别对应原文档引用的groupby.splitgroupby.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,按by1by2分组:

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,valuescolumnsaggfunc三个参数与上例一一对应:值列、列方向展开键、聚合函数。更多细节见 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 中的映射:

RPython
arraylist
listsdictionary or list of objects
data.framedataframe

ddply对应groupby.agg

对名为df的 data.frame,按monthweek分组汇总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小节。

castacast/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_tableindex(多键行)与columns(列键)。

dcast——用dfAnimalFeedType聚合信息:

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()

注意dcastfill=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.cutastype("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/slicequery/ 列切片 /ilocframe.py
dplyrarrangesort_valuesframe.py
dplyrgroup_by+summarisegroupby+agg/终端方法frame.py
aggregategroupby(...).mean()同上
%in%/matchisinalgorithms.py
tapplypivot_tablepivot.py
subsetquery(布尔索引等价)frame.py
withevalframe.py
ddplygroupby.aggframe.py
reshape2::meltpd.meltmelt.py
acast/dcastpivot_table/groupby.sumpivot.py
factor/cutCategorical /pd.cutcategorical.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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 6:20:53

Bid2X框架:基于基础模型的实时广告竞价优化方案

1. 项目背景与核心价值在数字广告生态中&#xff0c;竞价环境建模一直是个"黑箱难题"。传统方法依赖人工特征工程和静态规则&#xff0c;难以应对实时竞价&#xff08;RTB&#xff09;中毫秒级决策的复杂性。我们团队提出的Bid2X框架&#xff0c;首次将基础模型&…

作者头像 李华
网站建设 2026/9/18 6:20:50

uC/OS-II 事件控制块与信号量源码解析:RTOS 同步公共地基

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 6:20:41

JSP流浪动物救助系统:从源码部署到避坑实践全解析

先说个真实场景&#xff1a;前阵子有个学弟拿着这套JSP流浪动物救助系统源码来找我&#xff0c;说代码能打开、数据库也导进去了&#xff0c;可首页就是白屏&#xff0c;他自己调了一晚上也没头绪。我扫了几眼就发现是数据库连接串里的时区参数没加&#xff0c;MySQL 8.0版本下…

作者头像 李华
网站建设 2026/9/18 6:20:25

Security-101 应用安全关键概念(AppSec Key Concepts)入门指南

Security-101 应用安全关键概念&#xff08;AppSec Key Concepts&#xff09;入门指南 【免费下载链接】Security-101 8 Lessons, Kick-start Your Cybersecurity Learning. 项目地址: https://gitcode.com/GitHub_Trending/se/Security-101 应用安全&#xff08;Applic…

作者头像 李华
网站建设 2026/9/18 6:18:51

嵌入式开发面试新趋势:从Linux内核到AI部署的全栈技能盘点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 6:18:10

oh-my-hermes:为消息队列开发体验而生的命令行工具集

先说我自己的感受&#xff1a;消息队列这个东西&#xff0c;项目一多、环境一杂&#xff0c;真的会把人逼疯。每个服务都得配连接参数&#xff0c;本地测一套、测试环境一套、线上又是另一套&#xff0c;稍不注意配置文件就飘了&#xff1b;上了生产之后&#xff0c;日常查堆积…

作者头像 李华