R语言的plyr包简介

2023-10-29

(转载地址)http://www.jianshu.com/p/bfddfe29aa39

R语言的plyr包简介

字数3040 阅读2875 评论3 喜欢7

R语言中的类SQL操作

plyr包可以进行类似于数据透视表的操作,将数据分割成更小的数据,对分割后的数据进行些操作,最后把操作的结果汇总。

本文主要介绍以下内容:

  • Split-Aapply-Combine 原理介绍
  • baby_names的名字排名
  • 求分段拟合的系数
  • 部分其他函数介绍

在正式开始之前,请确保电脑上已经安装plyr,如果没有,通过install.packages()函数安装。

install.packages(plyr)  # 安装plyr包
require(plyr)  #载入plyr包

假设有美国新生婴儿的取名汇总,每一年,会统计男孩和女孩的取名情况,形成如下的一张表。

year name percent sex
1880 John 0.081541 boy
1880 William 0.080511 boy
1880 James 0.050057 boy
1880 Charles 0.045167 boy
1880 George 0.043292 boy
1880 Frank 0.02738 boy
1880 Joseph 0.022229 boy
1880 Thomas 0.021401 boy

baby_names数据集包含1880 ~ 2008年间的数据, 包含统计的年份(year),新生婴儿的性别、名字、以及改名字的比例。

以提问并解决问题的形式对plyr做介绍。

  1. 想知道数据集中,每年都有多少记录?
  2. 数据集中,男孩和女孩名的各自排名?
  3. 男孩名和女孩名各自排名前100在当年中的比例?

数据集中,每年都有多少记录
先假设我们有某一年的数据,我们会如何统计其中的记录数呢?由于数据集中,每条记录一行,只需要统计对应的行数就可以得到对应的记录数。
写个函数试试

record_count <- function(df) {
  return(data.frame(count = nrow(df)))
}

返回值类型是data.frame类型,是为即将介绍的ddply()函数做铺垫。先来看看2008年,数据集中有多少记录。

baby_names_2008 <- subset(baby_names, year == 2008)
record_count(baby_names_2008)
# 2000

结果显示2000条,貌似我们已经得到答案。下面想想,该如何得到1880 ~ 2008这129年间,每年的记录数呢?

ddply(baby_names,     # 数据集
      .(year),        # 分类的标准
      record_count    # 函数
)

结果比较长,只摘取其中一部分

year count
1880 2000
1881 2000
1882 2000
1883 2000
1884 2000
1885 2000
1886 2000
1887 2000
1888 2000
1889 2000
1890 2000
1891 2000
1892 2000

不错,每年都是2000条记录。再来看看,刚在我们做了什么。

  • 定义了一个负责计数的函数record_count()
  • 调用ddply(),这里出现刚刚定义的函数

ddply()函数是plyr包中用于对data.frame结构的数据做处理的函数,其结果也是data.frameddply的参数列表如下:

ddply(.data, .variables, .fun = NULL, ..., .progress = "none",
  .inform = FALSE, .drop = TRUE, .parallel = FALSE, .paropts = NULL)

各部分解释如下
  1. 第一个参数是要操作的原始数据集,比如baby_name
  2. 第二个参数是按照某个(也可以几个)变量,对数据集分割,比如按照year对数据集分割,可以写成.(year)的形式
  3. 第三个参数是具体执行操作的函数,对分割后的每一个子数据集,调用该函数
  4. 第四个参数可选,表示第三个参数对应函数所需的额外参数

其他参数,可以暂时不用考虑。ddply()函数会自动的将分割后的每一小部分的计算结果汇总,以data.frame的格式保存。<span style="color:red">分割后的数据,是fun的第一个参数。</span>

在上面的描述中,提到的分割、__操作汇总__,在plyr包中是一种处理方式("frame"),即"Split - Apply - Combine"。在plyr包中有很多这种处理方式的函数,在介绍这些函数之前,我们再来看看ddply()的一些更深入的用法。

各年,男孩名与女孩名的各自排名
以2008年的数据为例,男孩名"Jacob"的比例最高,排名应当是第一,"Michael"紧跟其后,排名应当第二,依此类推。对于女孩名,"Emma"排名第一,"Isabella"排名第二,"Emily"排名第三等等。我们希望得到这样的结果。

对于2008年的数据,可以通过简单的rank即可得到,不过要对男孩和女孩分别排序。

baby_names_2008_boy <- subset(baby_names_2008, sex == "boy") # 获取男孩名
baby_names_2008_boy$rank <- rank(- baby_names_2008_boy$percent) # 排序
head(baby_names_2008_boy) # 查看

对女孩名也执行相同的操作,这里就不写出来了,只需要在subset中,将"boy"替换成"girl"就行。下面来看看2008年,男孩名的排名情况

year name percent sex rank
2008 Jacob 0.010355 boy 1
2008 Michael 0.009437 boy 2
2008 Ethan 0.009301 boy 3
2008 Joshua 0.008799 boy 4
2008 Daniel 0.008702 boy 5
2008 Alexander 0.008566 boy 6

再来看看女孩名的排名结果:

year name percent sex rank
2008 Emma 0.009043 girl 1
2008 Isabella 0.008941 girl 2
2008 Emily 0.008377 girl 3
2008 Madison 0.008199 girl 4
2008 Ava 0.008198 girl 5
2008 Olivia 0.008196 girl 6

如何利用ddply()对原始数据集做相应的操作呢?这里需要介绍R语言中的一个函数transform(),该函数对原始数据集做一些操作,并把结果存储在原始数据中,更详细的用法,参见帮助文档?transform

第一个版本的处理方式是这样的

ddply(baby_names, 
      .(year, sex), 
      transform, 
      rank = rank(-percent, ties.method = "first")
)

第二个参数有点变化,除了year,还有sex,这表示对baby_name数据集,对yearsex分类(类似于SQL中的group by year, sex)。
第四个参数是transform的额外参数,如果查看transform的帮助文档,其函数调用方式如下:

transform(_data, ...)

第一参数为操作的数据,在 ddply()中为按年份和性别分割后的子数据集;后面的 ...参数是 tag = value的形式,这种 tag:value将追加在数据中。

由于rank默认对数据进行升序排序,若要实现逆序排序,常规的做法是将数据的符号取反,这也就是上面的rank函数中出现-percent的原因。在plyr中,有一个类似的函数,实现取反的操作,是desc

x <- 1:10
desc(x)
# -1  -2  -3  -4  -5  -6  -7  -8  -9 -10

所以,上面对percent取反的操作,可以写得更优雅些,就有了第二个版本的函数

baby_names <- ddply(baby_names, 
                    .(year, sex), 
                    transform, 
                    rank = rank(desc(percent), ties.method = "first")
)

注意这里把结果赋给了baby_name,因为后面还会用到排名的信息,就把结果保存下来。

排名前100的男孩名与女孩名在当年中的比例
跟前一问类似,处理方法是:

  • 把每年排名前100的数据筛选出来
  • 把男孩和女孩对应的percent相加
baby_names_top100 <- subset(baby_names, rank <= 100)  # 将前100排名的数据筛选出来
baby_names_top100_trend <- ddply(baby_names_top100, 
                                 .(year, sex), # 按年和性别分割
                                 summarize, # 汇总数据
                                 trend = sum(percent)) # 汇总方式(求和)

这里出现一个新的操作函数summarize(),该函数是对数据做汇总,与transform不一样的是,该函数并不追加结果到原始数据,而是产生新的数据集。比如想知道,2008年的男孩名中,排名最高和最低的名字的百分比之差,可以通过如下方式求得:

summarize(baby_names_2008_boy, trend = max(percent) - min(percent))
# 0.010266

回到刚才的问题,从1880 ~ 2008年间,男孩名与女孩名的前100所占比例(可以衡量名字大众化的程度)到底是什么样的呢?画个图就知道了。


naming_trend.jpeg

还有什么类似函数
上面介绍的ddply()plyr包中处理data.frame的函数,还有处理listarray的函数,汇总起来如下

  arrary data.frame list discarded
arrary aaply adply alply a_ply
data.frame daply ddply dlply d_ply
list laply ldply llply l_ply

所有的函数具有xyply的形式,其中x表示数据数据类型,y表示输出数据类型,而_表示丢弃。

应用举例

R语言基础数据集中,有mtcars数据,其中记录了车重"weight"、"miles per galon"、"cylinder"等参数。由图可知,不同气缸下,车重与行驶里程有着不同的关系,如果以线性函数来刻画,是三条有着明显区别的函数。


mtcars.jpeg

该如何求着三条直线的参数呢(截距与斜率)?
将问题简化下,对于数据集df,有自变量x,因变量y,如何求y = a x + b的参数ab?写个函数试试

linear_fit <- function(df) {
  model <- lm(mpg ~ wt, df)
  linear_coef <- coef(model)
  linear_coef <- data.frame(intercept = linear_coef[1], 
                           slope = linear_coef[-1])
  row.names(linear_coef) <- NULL
  linear_coef
}

下面再应用split - apply - combine的思想求出每一种cyl对应数据的截距和斜率

mtcars_coef <- ddply(mtcars, .(cyl), linear_fit)
names(mtcars_coef)[2:3] <- c("intercept", "slope")

所得拟合直线的截距和斜率为

  cyl intercept slope
1 4 39.57120 -5.647025
2 6 28.40884 -2.780106
3 8 23.86803 -2.192438

再结合这原图,把这些直线画出来,与原图做个比较。


mtcars_with_linear_model.jpeg

黑色的线为拟合的曲线,而彩色短线为系统所绘制的拟合曲线,说明我们的方法正确。

再来看看上面的拟合过程,将对每个子数据集的拟合封装成一个函数linear_fit,这样做没有问题,但是使得代码的可读性比较差,一种比价优雅的方式是在dlply的第三个参数处,直接放上lm函数,将额外的参数赋给第四个参数。

mtcars_model <- dlply(mtcars, .(cyl), lm, 
                      formula = mpg ~ wt)
mtcars_coef <- ldply(mtcars_model, coef)
names(mtcars_coef)[2:3] <- c("intercept", "slope")

注意,这里通过dlply()函数调用拟合函数lm,而把具体的拟合形式formula = mpg ~ wt赋值给第四个参数。dlply()函数返回的是listlist的每个元素是一个lm的返回结果,通过ldply()调用coef获得每个模型对应的系数,记得到上述结果。

读入多个文件中的数据,并合并
下面来看看一个实际生活中的问题:

假设文件夹下有若干.csv文件, 每个文件的数据格式相同,且含有表头,如何将多个文件合并成一个文件呢?

如果没有表头的话,操作起来比较容易,可以直接用命令行工具实现,比如在linux下可以cat *.csv > total.csv实现文件合并。 此处给出一种使用plyr包中提供的ldaply的函数,实现上出操作,效率不一定是最高的,但可以进一步掌握plyr包的特性。

可以继续使用上述使用的baby_names数据集,使用如下命令, 将baby_names按年份写到不同的csv文件中。

d_ply(baby_names, .(year), 
      function(baby) write.csv(baby, paste0(baby$year[1], ".csv"), row.names = FALSE)
)

上述命令将在当前文件夹下,产生129个csv文件,从1880 ~ 2008, 每年一个文件,以年份命名。

使用如下的命令将

files <- list.files(pattern = "^\\d+\\.csv")
baby_names_recovered <- ldply(files, read.csv, stringsAsFactors = FALSE)

上述命令将129个文件名存储在files变量中,通过ldply,读取每个文件,并最后通过ldply合并成一个data.frame。需要说明的是ldply的第一个参数要求list,但是files变量却是vector,这个没有影响,函数内部会将第一个参数通过as.list()转换成list

现在需要验证读入的baby_names_recovered与原始的baby_names一致,使用如下参数可以做相应的比较。

identical(arrange(baby_names, year, name, sex), arrange(baby_names_recovered, year, name, sex))
# TRUE

返回的结果是TRUE,即二者其实是一致的。至于为什么要用arrange函数对数据做一下排列,是因重新生成的baby_names_recovered,其读入数据的顺序并没有严格按照年份进行。

这里抛出一个问题,如果不使用plyr包,如何实现上述操作。
提示:查阅lapplydo.call函数,剩下的函数,已经在上面的示例中讲解。
部分其他函数
这一部分将简略介绍plyr 包中未提及的函数,以及其用法。

未完待续

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

R语言的plyr包简介 的相关文章

  • 融化R中的下半矩阵

    如何融化下半三角形加对角矩阵 11 NA NA NA NA 12 22 NA NA NA 13 23 33 NA NA 14 24 34 44 NA 15 25 35 45 55 A lt t matrix c 11 NA NA NA NA
  • R 将多个值与向量进行比较并返回向量[重复]

    这个问题在这里已经有答案了 我有一个向量 A 对于 A 的每个元素 我想检查它是否等于第二个向量 Targets 中的任何元素 我想要一个逻辑值向量 其长度为 A 作为返回 也提到了同样的问题here http r 789695 n4 na
  • 如何在ubuntu的conda环境中更改Rstudio中的R版本

    我在基本系统中安装了 R 4 3 和 Rstudio 在 conda 环境中安装了旧版本的 R 4 2 3 命令which R返回环境中安装的 R 的目录 home 用户 miniconda3 envs anndata2ri pip bin
  • 将第 N 行上的 NA 行插入 data.frames 列表,其中 N 来自列表

    经过几个小时后 我发现自己无法解决以下问题 我有一个数据框列表 我想分别向每个 DF 插入 而不是替换 一行或多行 NA 始终至少一行 要插入的 NA 数量存储在单独的列表中 为了说明这一点 我有以下两个列表 list of datafra
  • 获取函数的命名空间

    我正在开发一个包 我希望在其中向对象添加编辑历史记录 该包允许其他包注册用于编辑对象的函数 我正在寻找一种方法来记录注册用于编辑的函数的包的版本 问题是 给定一个函数 如何从导出的位置获取包 我的想法是调查它的搜索路径 但是search 仅
  • 无法更新/编辑从 R 中的包(`gratia`)导出的 ggplot2 对象

    我希望我在这里遗漏了一些令人痛苦的明显的东西 我希望更新 例如 修复标题 实验室等 由 生成的 ggplot 对象gratia draw 不太确定为什么我无法更新该对象 有一个简单的解决方案吗 devtools install github
  • 如何对数字进行四舍五入并使其显示零?

    R 中将数字四舍五入到小数点后 2 位的常用代码是 gt a 14 1234 gt round a digits 2 gt a gt 14 12 但是 如果该数字的前两位小数位为零 则 R 会在显示中抑制零 gt a 14 0034 gt
  • 将字符串列拆分为多个虚拟变量

    作为 R 中 data table 包的相对缺乏经验的用户 我一直在尝试将一个文本列处理为大量指示符列 虚拟变量 每列中的 1 表示特定的子字符串是在字符串列中找到 例如我想处理这个 ID String 1 a b 2 b c 3 c 进入
  • 为什么数据帧上的 is.vector 不返回 TRUE?

    tl dr R 中的向量到底是什么 长版 R 中很多东西都是向量 例如 数字是长度为 1 的数值向量 is vector 1 1 TRUE 列表也是一个向量 is vector list 1 1 TRUE 好的 所以列表是一个向量 显然 数
  • 尝试使用 JRI 将 R 与我的 Java 应用程序集成,但出现错误。谁能解释一下原因和解决办法吗?

    我需要将 Java 与 R 集成来运行一些数学命令并使用 R 的功能进行绘图 以下部分代码给出了错误 public static void main String args HelloRWorld r new HelloRWorld r h
  • R - 计算 bin 中特定值的数量

    我有一个如下所示的数据框 df Value lt c 1 1 0 2 1 3 4 0 0 1 2 0 3 0 4 5 2 3 0 6 Sl lt c 1 20 df lt data frame Sl Value gt df Sl Value
  • 列出 R 数据文件的内容而不加载

    我有时用print load myDataFile RData 当我加载数据文件时列出它的内容 有没有办法列出内容而不加载数据文件中包含的对象 我认为如果不加载对象就无法做到这一点 解决方案可能是使用包装器将 R 对象保存到save 该函数
  • R 闪亮仪表板中的动态重复条件面板

    我正在尝试创建一个动态条件面板 所以我的条件如下 在用户界面中输入 selectInput inpt Input Number seq 1 50 1 selectize FALSE 我的条件面板 UI 输入是 conditionalPane
  • 警告消息 - 来自 dummies 包的 dummy

    我正在使用 dummies 包为分类变量生成虚拟变量 其中一些变量具有两个以上类别 testdf lt data frame A as factor c 1 2 2 3 3 1 B c A B A B C C C c D D E D D E
  • 如何在 data.table 中分组后使用条件计算行数

    我有以下数据框 dat lt read csv s1 s2 v1 v2 a b 10 20 a b 22 NA a b 13 33 c d 3 NA c d 4 5 NA c d 10 20 dat gt A tibble 6 x 4 gt
  • 在R中循环子文件夹

    我正在 R 环境中包含多个子文件夹的文件夹中工作 我想要循环遍历多个子文件夹 然后在每个子文件夹中调用 R 脚本来执行 我想出了下面的代码 但我的代码似乎添加了 到子文件夹列表 我收到错误 文件中的错误 文件名 r 编码 编码 无效的 描述
  • 如何在 R 中的 for 循环内将值存储在向量中

    我正在开始使用 R 但我对以下问题感到非常沮丧 我试图将 for 循环内完成的某些计算的值存储到我之前定义的向量中 问题是如何进行索引 因为for循环迭代代码的次数取决于用户的输入 所以变量i不一定要从1开始 它可以从80开始 for举个例
  • 在 RMarkdown 输出到 PDF 时缩进而不添加项目符号点或编号

    之前有人问过如何在没有项目符号的情况下缩进文本 RMarkdown 中的点 但这是针对 HTML 输出的 在 RMarkdown 中缩进而不添加项目符号点或数字 https stackoverflow com questions 47087
  • R:按组,测试一个变量的每个值是否存在于另一个变量中

    我有一个数据框架 结构如下 a lt c 1 1 1 2 2 2 3 3 3 3 4 4 b lt c 1 2 3 1 2 3 1 2 3 4 1 2 c lt c NA NA 2 NA 1 1 NA NA 1 1 NA NA df lt
  • 要在子集中显示的非数字条目的维恩图

    我有以下数据框 SET1 SET2 SET3 par1 par2 par1 par2 par3 par2 par3 par4 par5 我想制作一个维恩图 其中所有这些 parX 元素都显示在各自的子集中 即作为标签 而不仅仅是重叠元素的数

随机推荐

  • 剑指Offer - 面试题12:矩阵中的路径

    题目 请设计一个函数 用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径 路径可以从矩阵中的任意一格开始 每一步可以在矩阵总向左 右 上 下移动一格 如果一条路径经过了矩阵的某一格 那么该路径不能再次进入该格子 如 在下面的3 4的
  • 增量式PID vs 位置式PID: 选择最佳的PID控制策略

    增量式PID和位置式PID是两种常见的PID控制器实现方法 它们之间的区别主要在于反馈信号的处理方式和输出计算方式 反馈信号处理方式 增量式PID 增量式PID控制器使用系统输出值与前一时刻的输出值之差作为反馈信号 它将当前的误差值与前一时
  • 第五篇 如何调用驱动接口,完成互操作

    1 如何调用驱动功能 1 编写调用驱动的应用程序 1 1 定义驱动设备接口 以及调用接口的控制码 例如 DEFINE GUID GUID DEVINTERFACE SampleDriver 0xc459a9ec 0xb463 0x4838
  • Keras标准的模型训练、验证以及评估测试

    from keras models import Model Sequential from keras layers import Input Conv2D GlobalAveragePooling2D Dropout Dense Fla
  • 车祸现场~线上突然宕机,一条订单消息丢失了...

    V xin ruyuanhadeng获得600 页原创精品文章汇总PDF 一 写在前面 之前写过一篇文章 RabbitMQ是如何收发消息的 通俗易懂 我们用一个简单易懂的电商场景给大家引入说明了一个消息中间件的使用场景 同时 我们还基于Ra
  • idea项目中的idea文件如何隐藏,以及不小心隐藏之后如何显示idea文件夹

    由于我们写项目时 发现了idea文件夹有时需要隐藏 或者隐藏后无法显示 接下来我就告诉你们 打开file settings editor gt file types 选择 gitignore 再那一栏的后面输入 idea 点击应用 点击ok
  • NoSQL 概念

    http www infoq com cn news 2011 01 nosql why 写道
  • shell中的变量

    变量 shell中预设的变量都是大写的 变量就是使用一个较简单的字符串来替代某些具有特殊意义的设定以及数据 可以使用echo命令显示变量的值 echo PWD echo PATH 另外可以使用env命令列出全部系统预设的系统环境变量 不同登
  • 华为云云耀云服务器L实例评测|深度体验云耀云服务器购买和Ubuntu环境性能压力评测

    文章目录 步骤一 云耀云服务器L实例配置选型 步骤二 购买云耀云服务器L实例 购买关键须知 购买流程体验 步骤三 轻松上手远程登录云耀云服务器L实例 步骤四 对云耀云服务器L实例进行压力测试 第一项测试 CPU压力测试 第三项测试 云耀云服
  • redis根据指定端口号方式访问redis服务端!

    以默认的方式进入 端口号 6379 指定端口号进入客户端 端口号 6378 此服务器安装了两个redis服务端 害老子去一个没存数据的redis服务器里刷缓存
  • PandoraBox版本及已安装软件包

    主机名 PandoraBox 7FBB 型号 Netgear R6220 固件版本 PandoraBox 19 02 2019 02 01 git 93f2639a7 LuCI Master git 19 026 77036 498ca21
  • ThinkPHP实现凯撒密码加密和解密

    ThinkPHP实现凯撒密码加密和解密 在密码学中 恺撒密码 英语 Caesar cipher 或称恺撒加密 恺撒变换 变换加密 是一种最简单且最广为人知的加密技术 它是一种替换加密的技术 明文中的所有字母都在字母表上向后 或向前 按照一个
  • CSS 类选择器的特殊使用-多类名

    title CSS 类选择器的特殊使用 多类名 CSS 类选择器的特殊使用 多类名概念 一个标签有多个名字 多类名的使用方式 示例代码
  • 在NW.js里面使用node-printer

    0 写在前面的一些提示 不要使用太高版本的 NW js 以及 Node js 因为 node printer 会有兼容性问题 目前笔者测试到 node printer 最高只能在 0 43 x 版本以及低于这个版本的 NW js 里面正常运
  • 代码走查与代码审查区别

    代码走查 程序员和测试员组成审查小组 通过逻辑运行程序 第一步 小组成员提前阅读设计规格书 程序文本等相关文档 第二步 利用测试用例 使程序逻辑运行 记录程序的踪迹 发现 讨论 解决问题 代码审查 程序员和测试员组成审查小组 第一步 小组成
  • robot通过一个元素定位另一个元素

    xpath定位 xpath span text 文章评为精华 div div span text 去完成
  • 一些keil编程错误总结

    一 error c267 delay 10us requires ANSI style prototype问题 解决方法 1 未进行 模块化程序设计时 delay 10us该子函数要在主函数前 2 在进行 模块化程序设计时 这是由于 c文件
  • 区块链在非金融领域有哪些值得期待的应用

    引言 自己关注区块链好多年了 感觉大部分人都在炒币 或者所谓的搞金融 但是现在各国的审查都越来越严格 针对金融领域的审查尤其严格 在国内 基本上是禁止区块链应用有任何的发币行为的 我个人也是觉得金融领域的监管太多 反而不容易落地 这篇文章来
  • Redis中的String类型与List类型个人理解

    String类型 String类型是Redis的最基础类型 最大能存储512MB 其中存储值可以是最简单的字符串 复杂的 xml json的字符串 二进制图像或者音频的字符串 以及是数字的字符串 String数据类型与结构 基本操作 set
  • R语言的plyr包简介

    转载地址 http www jianshu com p bfddfe29aa39 R语言的plyr包简介 字数3040 阅读2875 评论3 喜欢7 R语言中的类SQL操作 plyr包可以进行类似于数据透视表的操作 将数据分割成更小的数据