从头开始创建 geom / stat

2024-01-11

我不久前刚刚开始使用 R,目前正在努力加强我的可视化技能。我想做的是创建箱线图平均钻石作为顶部的一层(参见下面链接中的图片)。我还没有找到任何可以执行此操作的函数,所以我想我必须自己创建它。

我希望做的是创建一个几何或统计数据,让这样的事情能够工作:

ggplot(data, aes(...))) + 
   geom_boxplot(...) +
   geom_meanDiamonds(...)

我不知道从哪里开始来构建这个新功能。我知道平均钻石需要哪些值(平均值和置信区间),但我不知道如何构建从中获取数据的几何/统计数据ggplot(),计算每个组的平均值和 CI,并在每个箱线图顶部绘制平均菱形。

我已经搜索了有关如何从头开始构建这些类型的函数的详细描述,但是,我还没有找到任何真正从底层开始的内容。如果有人能给我指出一些有用的指南,我将非常感激。

谢谢你!


我目前正在学习自己编写几何图形,因此当我经历我的思维过程时,这将是一篇相当长且杂乱的文章,从统计方面(计算这些多边形的位置)理清几何方面(创建多边形和线段) & 段应该是)geom 的。

免责声明:我对这种情节并不熟悉,谷歌也没有给出很多权威指南。我对这里如何计算/使用置信区间的理解可能不正确。

步骤 0. 了解 geom / stat 和图层函数之间的关系。

geom_boxplot and stat_boxplot是层函数的示例。如果将它们输入 R 控制台,您会发现它们(相对)较短,并且不包含用于计算箱线图的箱线/须线的实际代码。反而,geom_boxplot包含一行内容:geom = GeomBoxplot, while stat_boxplot包含一行内容:stat = StatBoxplot(转载如下)。

> stat_boxplot
function (mapping = NULL, data = NULL, geom = "boxplot", position = "dodge2", 
    ..., coef = 1.5, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
{
    layer(data = data, mapping = mapping, stat = StatBoxplot, 
        geom = geom, position = position, show.legend = show.legend, 
        inherit.aes = inherit.aes, params = list(na.rm = na.rm, 
            coef = coef, ...))
}

GeomBoxplot and StatBoxplot是 ggproto 对象。它们是魔法发生的地方。

第 1 步:认识到这一点ggproto()'s _inherit参数是你的朋友。

不要重新发明轮子。由于我们想要创建与箱线图很好地重叠的东西,因此我们可以参考Geom https://github.com/cran/ggplot2/blob/master/R/geom-boxplot.r / Stat https://github.com/cran/ggplot2/blob/master/R/stat-boxplot.r用于此目的,并且仅更改必要的内容。

StatMeanDiamonds <- ggproto(
  `_class` = "StatMeanDiamonds",
  `_inherit` = StatBoxplot,
  ... # add functions here to override those defined in StatBoxplot
)

GeomMeanDiamonds <- ggproto(
  `_class` = "GeomMeanDiamonds",
  `_inherit` = GeomBoxplot,
  ... # as above
)

步骤 2. 修改统计数据。

StatBoxplot 中定义了 3 个函数:setup_data, setup_params, and compute_group。您可以参考Github上的代码(上面的链接)了解详细信息,或者输入例如StatBoxplot$compute_group.

The compute_group函数计算与每个组关联的所有 y 值(即每个唯一的 x 值)的 ymin/lower/middle/upper/ymax 值,这些值用于绘制箱线图。我们可以用计算置信区间和平均值的方法来覆盖它:

# ci is added as a parameter, to allow the user to specify different confidence intervals
compute_group_new <- function(data, scales, width = NULL, 
                              ci = 0.95, na.rm = FALSE){
  a <- mean(data$y)
  s <- sd(data$y)
  n <- sum(!is.na(data$y))
  error <- qt(ci + (1-ci)/2, df = n-1) * s / sqrt(n)
  stats <- c("lower" = a - error, "mean" = a, "upper" = a + error)

  if(length(unique(data$x)) > 1) width <- diff(range(data$x)) * 0.9

  df <- as.data.frame(as.list(stats))

  df$x <- if(is.factor(data$x)) data$x[1] else mean(range(data$x))
  df$width <- width

  df
}

(可选)StatBoxplot 允许用户包含weight作为一种美学映射。我们也可以通过替换来实现这一点:

  a <- mean(data$y)
  s <- sd(data$y)
  n <- sum(!is.na(data$y))

with:

  if(!is.null(data$weight)) {
    a <- Hmisc::wtd.mean(data$y, weights = data$weight)
    s <- sqrt(Hmisc::wtd.var(data$y, weights = data$weight))
    n <- sum(data$weight[!is.na(data$y) & !is.na(data$weight)])
  } else {
    a <- mean(data$y)
    s <- sd(data$y)
    n <- sum(!is.na(data$y))
  }

无需更改 StatBoxplot 中的其他函数。所以我们可以定义 StatMeanDiamonds 如下:

StatMeanDiamonds <- ggproto(
  `_class` = "StatMeanDiamonds",
  `_inherit` = StatBoxplot,
  compute_group = compute_group_new
)

步骤 3. 修改 Geom。

Geom_Boxplot 有 3 个函数:setup_data, draw_group, and draw_key。它还包括以下定义default_aes() and required_aes().

由于我们更改了上游数据源(StatMeanDiamonds 生成的数据包含计算列“lower”/“mean”/“upper”,而 StatBoxplot 生成的数据将包含计算列“ymin”/“lower” /“middle”/“upper”/“ymax”),检查下游是否setup_data功能也受到影响。 (在这种情况下,GeomBoxplot$setup_data没有引用受影响的列,因此此处不需要进行任何更改。)

The draw_group函数采用 StatMeanDiamonds 生成的数据并通过以下方式设置setup_data,并产生多个数据帧。 “common”包含所有几何图形共有的美学映射。 “diamond.df”用于对菱形多边形做出贡献的映射,“segment.df”用于对平均值处的水平线段做出贡献的映射。然后数据帧被传递到draw_panel分别使用 GeomPolygon 和 GeomSegment 函数来生成实际的多边形/线段。

draw_group_new = function(data, panel_params, coord,
                      varwidth = FALSE){
  common <- data.frame(colour = data$colour, 
                       size = data$size,
                       linetype = data$linetype, 
                       fill = alpha(data$fill, data$alpha),
                       group = data$group, 
                       stringsAsFactors = FALSE)
  diamond.df <- data.frame(x = c(data$x, data$xmax, data$x, data$xmin),
                           y = c(data$upper, data$mean, data$lower, data$mean),
                           alpha = data$alpha,
                           common,
                           stringsAsFactors = FALSE)
  segment.df <- data.frame(x = data$xmin, xend = data$xmax,
                           y = data$mean, yend = data$mean,
                           alpha = NA,
                           common,
                           stringsAsFactors = FALSE)
  ggplot2:::ggname("geom_meanDiamonds",
                   grid::grobTree(
                     GeomPolygon$draw_panel(diamond.df, panel_params, coord),
                     GeomSegment$draw_panel(segment.df, panel_params, coord)
                   ))
}

The draw_key函数用于在需要时创建该层的图例。由于GeomMeanDiamonds继承自GeomBoxplot,因此默认为draw_key = draw_key_boxplot,而我们不have改变它。保持不变不会破坏代码。然而,我认为更简单的传说,例如draw_key_polygon提供一个不那么混乱的外观。

GeomBoxplot 的default_aes规格看起来不错。但我们需要改变required_aes因为我们期望从 StatMeanDiamonds 获得的数据是不同的(“lower”/“mean”/“upper”而不是“ymin”/“lower”/“middle”/“upper”/“ymax”)。

我们现在准备定义 GeomMeanDiamonds:

GeomMeanDiamonds <- ggproto(
  "GeomMeanDiamonds",
  GeomBoxplot,
  draw_group = draw_group_new,
  draw_key = draw_key_polygon,
  required_aes = c("x", "lower", "upper", "mean")
)

步骤 4. 定义图层功能。

这是无聊的部分。我复制自geom_boxplot / stat_boxplot直接删除所有对异常值的引用geom_meanDiamonds,改为geom = GeomMeanDiamonds / stat = StatMeanDiamonds,并添加ci = 0.95 to stat_meanDiamonds.

geom_meanDiamonds <- function(mapping = NULL, data = NULL,
                              stat = "meanDiamonds", position = "dodge2",
                              ..., varwidth = FALSE, na.rm = FALSE, show.legend = NA,
                              inherit.aes = TRUE){
  if (is.character(position)) {
    if (varwidth == TRUE) position <- position_dodge2(preserve = "single")
  } else {
    if (identical(position$preserve, "total") & varwidth == TRUE) {
      warning("Can't preserve total widths when varwidth = TRUE.", call. = FALSE)
      position$preserve <- "single"
    }
  }
  layer(data = data, mapping = mapping, stat = stat,
        geom = GeomMeanDiamonds, position = position,
        show.legend = show.legend, inherit.aes = inherit.aes,
        params = list(varwidth = varwidth, na.rm = na.rm, ...))
}

stat_meanDiamonds <- function(mapping = NULL, data = NULL,
                         geom = "meanDiamonds", position = "dodge2",
                         ..., ci = 0.95,
                         na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) {
  layer(data = data, mapping = mapping, stat = StatMeanDiamonds,
        geom = geom, position = position, show.legend = show.legend,
        inherit.aes = inherit.aes,
        params = list(na.rm = na.rm, ci = ci, ...))
}

步骤 5. 检查输出。

# basic
ggplot(iris, 
       aes(Species, Sepal.Length)) +
  geom_boxplot() +
  geom_meanDiamonds()

# with additional parameters, to see if they break anything
ggplot(iris, 
       aes(Species, Sepal.Length)) +
  geom_boxplot(width = 0.8) +
  geom_meanDiamonds(aes(fill = Species),
                    color = "red", alpha = 0.5, size = 1, 
                    ci = 0.99, width = 0.3)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从头开始创建 geom / stat 的相关文章

  • 返回带有参数的函数的函数

    创建一个应返回包含原始函数参数的函数时 我应该如何处理 例如考虑这个函数 a lt function value function x x value 我希望它返回我在结果函数的参数中指定的值 如下所示 b lt a 3 gt b gt f
  • 在 for 循环中绘制的多个 ggplot2 绘图的网格

    作为一个新的 ggplot2 用户 我对可能性的数量感到有点迷失 并且很难在网上找到我认为简单问题的简单答案 我想在同一张纸上显示 ggplot2 的多个图 但知道这些图来自 for 循环 以下示例无法编译 仅用于说明 for i in c
  • R比例置信区间因子

    我正在尝试总结家庭调查的数据 因此我的大部分数据都是分类 因子 数据 我想用对某些问题的回答频率图来总结它 例如 回答某些问题的家庭百分比的条形图 误差线显示置信区间 我发现了这个很棒的教程 我认为它是我祈祷的答案 http www coo
  • R 中 write.table 文件名中的变量

    请帮助我解决一个幼稚的问题 已经用谷歌搜索 并尝试了很多变体 但失败了 如何使用 R 中 write table 的文件名中的变量保存文件 脚本循环遍历 dir 中的文件 应用一些函数 然后将结果保存到具有相同名称但附加结尾的文件中 谢谢
  • 如何判断某个软件包是否已经安装?

    当我安装 yaml 包时 如果之前已经安装过 RStudio 则会弹出一条烦人的错误消息 如何判断该软件包是否已安装 以便我可以在代码中决定是否安装该软件包 该消息位于弹出窗口中 内容如下 此安装将更新的一个或多个软件包 当前已加载 在更新
  • mclapply 用户时间大于已用时间

    我正在尝试使用mclapply的功能parallel封装在R 该函数通过计算对数似然距离将值分配给序列矩阵 这是一个 CPU 密集型操作 所结果的system time价值观令人困惑 gt system time mclapply work
  • 如何在 R 地图库中绘制正确的颜色

    我正在尝试使用 R 地图库为特定国家绘制特定颜色 我可以填写颜色 但它们与各自的国家 地区没有正确关联 我想知道是否有人能知道为什么 我的数据框是 filld 有 3 列 第一列是国家名称 第二列只是一些数字数据 第三列是颜色 countr
  • 使用 SparkR 1.5 从 RStudio 中的 hdfs 读取大文件(纯文本、xml、json、csv)的选项

    我是 Spark 新手 想知道除了下面的选项之外是否还有其他选项可以使用 SparkR 从 RStudio 读取存储在 hdfs 中的数据 或者我是否正确使用它们 数据可以是任何类型 纯文本 csv json xml 或任何包含关系表的数据
  • R:使用管道将单个参数传递到函数中的多个位置

    我试图专门使用管道来重写以下代码 使用babynames包中的babynames数据 library babynames library dplyr myDF lt babynames gt group by year gt summari
  • 带有嵌套分组变量的多行轴标签,用于 - R 中的堆积条形图

    我想使用 ggplot 制作一个包含多个类别的堆叠条形图 并带有嵌套的标记 X 轴 类似于我使用 Excel 制作的条形图 如此处所示 我尝试使用给出的例子here https stackoverflow com questions 181
  • 当子集长度为零时,如何简洁地处理子集?

    从向量中排除元素x x lt c 1 4 3 2 我们可以减去位置向量 excl lt c 2 3 x excl 1 1 2 这也是动态工作的 excl lt which x which max x gt quantile x 25 1 2
  • 分割单个 SpatialPolygons 对象的多边形部分

    在 R 中 我有一个SpatialPolygons包含数百个多边形的对象 即多个多边形 我想分割这个SpatialPolygons对象放入列表中Polygons 即孔应保持连接到父多边形 知道如何做到这一点吗 EDITED 使用以下提供的示
  • 如何使用r中的dplyr在特定位置插入空白行

    我想在数据框中的特定位置插入空白行 我的数据框是这样的 dat lt data frame group c rep A 1 rep B 4 rep C 2 rep D 2 group 1 A 2 B 3 B 4 B 5 B 6 C 7 C
  • 使用亚毫秒日期时间从字符->POSIXct->字符准确转换

    我的文件中有一个字符日期时间列 我加载文件 到data table 并执行需要将列转换为的操作POSIXct 然后我需要写POSIXct值返回文件 但日期时间不会相同 因为打印不正确 这个打印 格式问题是众所周知的 并且已经被讨论过多次 我
  • for 循环与 cor.test 在许多类别上

    我正在尝试在 R 中编写一个循环 它将循环遍历 3 个不同的物种 以计算两个连续变量 Redness 和 VarNormAbund 之间的相关性 我的循环正在运行 但 3 个物种中每一个的输出都是相同的 这让我认为循环卡在第一个物种上 co
  • 如何制作一连串的ggplots并在它们之间绘制箭头?

    对于一个项目 我需要绘制一些图并在它们之间放置箭头作为序列的指示 我想知道我是否可以用 ggplot 来做到这一点 是否可以使用 ggplot2 绘制一个干净的大箭头并将其添加到最终的多重图中 作为示例 我使用此代码来绘制绘图 librar
  • 通过 RCpp 返回 NA

    新手 RCpp 问题在这里 How can I make a NumericVector returnNA到R 例如 假设我有一个 RCpp 代码 它分配NA到向量的第一个元素 RCpp export NumericVector myFun
  • 将 csv 文件上传到shinyApps.io

    我的应用程序在本地运行良好 并且我能够成功地将应用程序部署到shinyapps io 服务器 但是当我尝试使用shinyapps URL 在浏览器中加载应用程序时 收到以下错误消息 错误对象 数据 不是成立 我认为这是因为 data 变量从
  • 求解非线性方程组

    我正在尝试求解以下四个方程组 我尝试过使用 rootSolve 包 但似乎我无法通过这种方式找到解决方案 我正在使用的代码如下 model lt function x F1 lt sqrt x 1 2 x 3 2 1 F2 lt sqrt
  • R - 如何为数据范围内的缺失值绘制条形图零点?

    假设我对 1 到 10 之间的整数的 200 个点有 10 个观察值 mysample sample rep seq 1 10 20 10 我想用条形图绘制它 barplot table mysample barplot https i s

随机推荐

  • 在函数中使用关键字 - PHP [重复]

    这个问题在这里已经有答案了 可能的重复 在 PHP 5 3 0 中 函数 Use 标识符是什么 一个理智的程序员应该使用它吗 https stackoverflow com questions 1065188 in php 5 3 0 wh
  • Ruby 未定义方法 `+' for nil:NilClass (NoMethodError)

    红宝石新手 接收错误 nil NilClass 的未定义方法 NoMethodError 我不明白为什么我会收到这样一个简单的递增值任务的错误 但是 该错误可能是由其他原因引起的 原因是什么 class LinkedList class N
  • IP地址和端口号一起可以唯一标识进程ID吗?

    IP地址和端口号一起可以唯一标识进程ID吗 我正在寻找一种方法来获取相应的进程ID 给定IP地址和端口号 但我不确定这样的ip 端口对是否可以唯一标识一个pid 不必要 如果在进程中打开 接受套接字 然后分叉 则子进程也打开套接字 因此 I
  • 管道与重定向到进程

    寻找一些bash专家解释 下一个之间的确切区别是什么 command1 command2 例如经典管道 其中 command1 的 stdout 被重定向到 command2 的 stdin 例如 bash fork 自己两次 更改文件描述
  • 让私有方法返回 String 而不是传递 StringBuilder 的优点和缺点是什么[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 在多台机器和硬盘上使用 nginx 提供静态文件

    我有一个项目 它将在部署后存储大量媒体内容 项目使用Python Django 也通过Gunicorn和Supervisor运行 对于静态文件 我将使用 nginx 我通过以下方式进行了基本设置本文 http michal karzynsk
  • 如何从其他类访问jFrame的组件?

    我有两个类 第一个是 NewJFrame java 它的代码是 package javaapplication10 import java awt Label public class NewJFrame extends javax swi
  • 在 Flask 中创建不返回响应的视图函数

    我对网络编程和 Flask 相当陌生 最近我尝试创建的网站遇到了问题 我目前有一个 jquery 过程 它将 post 请求发送到 Flask 中的视图函数 该函数只是增加我的数据库中的一个值 并且我实际上没有必要在增加该值后返回响应 然而
  • 如果开关盒掉落,如何使 MSVC 发出警告或失败?

    如果我的 switch 语句之一有一个不会中断的情况 我希望收到警告 错误 这可能吗 switch i case 1 cout lt lt one forgot to break here I want to be warned about
  • .pem、.cer 和 .der 之间有什么区别?

    两者有什么区别 pem cer and der 据我所知 cer包含公钥 是否有任何开放框架可用于使用此公钥加密我的数据 pem cer and der都是可能包含 X 509 v3 证书的文件的文件扩展名 The der扩大 DER 是对
  • iPhone Flash 中的不同屏幕尺寸? (出现黑条)

    我是整个编码世界的新手 而 ActionScript 3 是我第一次真正的体验 如果我不能立即理解您的答案 很抱歉 我在 AIR for iOS 中使用 Adob e Flash CC 构建了一个 iPhone 应用程序 所有代码都位于时间
  • Angular 6:如何使用 Angular Material 隐藏无线电圆圈并使用 NgStyle 来检查答案?

    我在两件事上遇到了麻烦 隐藏 mat radio group 的圆圈 如果选中 请将 p 标签背景更改为蓝色 我尝试使用 ng deep 覆盖 css 属性并将颜色更改为白色 尝试配置 invisibility hidden 但没有成功 另
  • 导航属性“SenderId”不是类型“Conversation”上声明的属性

    当我尝试更新数据库时 出现以下错误 导航属性 SenderId 不是类型 Conversation 上声明的属性 验证它是否未从模型中显式排除并且它是有效的导航属性 Edit 我认为问题在于对话和用户之间的映射关系 因为对话和用户以两个一对
  • DTLS 和 TLS 之间的区别

    TLS 和 DTLS 之间的功能区别是什么 使用 TLS 与 DTLS 时应用程序流程 协商有何不同 基本上 DTLS 是在数据报 UDP DCCP 等 上构建 TLS DTLS 有意与 TLS 类似 只不过 DTLS 必须解决两个问题 数
  • (异步)NSURLConnection:下面发生了什么?

    我知道它必须处理启动线程来发出网络请求的丑陋 然后可能调用performSelectorOnMainThread 用我的委托方法 我知道怎么做use在进行 iOS 编程时 它效果很好 但是 我想知道如何使其在 例如 命令行实用程序的上下文中
  • 在keycloak登录页面显示应用程序名称

    我有两个应用程序App1 and App2与相互作用keycloak用于用户身份验证 我想在以下位置显示应用程序名称keycloak登录页面 例如 如果用户正在登录App1 the keycloak登录页面应显示 登录到 App1 同样的情
  • 如何在 Travis 中显示 HTML 格式的输出文件?

    我正在尝试使用 Travis CI 在 Bintray 上部署 Android 库 但是当我上传我的仓库时 我得到了这个 Ran lint 变体发布 发现 6 个问题 Ran lint on variant debug 6 issues f
  • 存储轮播图像的最佳方式是什么?

    我正在使用 Ruby on Rails 我的目标网页上有两个轮播 我想知道存储和显示这些图像的最佳方式是什么 选项一 将图像放入assets images并在轮播中显示它们 如果这是一种好方法 我可以循环浏览文件夹中的图像还是必须指定每个文
  • 我可以在 WinRT 应用程序中使用 Sqlite (javascript) 吗?

    是否可以在 Windows 8 winRT javascript 应用程序中使用 Sqlite 数据库 我想要实现的是下载一个 Sqlite 数据库并在使用前将其存储在本地存储中 我相信某种形式的本地存储可用于基于 javascript 的
  • 从头开始创建 geom / stat

    我不久前刚刚开始使用 R 目前正在努力加强我的可视化技能 我想做的是创建箱线图平均钻石作为顶部的一层 参见下面链接中的图片 我还没有找到任何可以执行此操作的函数 所以我想我必须自己创建它 我希望做的是创建一个几何或统计数据 让这样的事情能够