【问题标题】:Interactive point labels with gridSVG and ggplot2 v.0.9.0带有 gridSVG 和 ggplot2 v.0.9.0 的交互式点标签
【发布时间】:2012-06-21 08:33:44
【问题描述】:

我想以交互方式在 ggplot 中标记点,以便将鼠标悬停在一个点上会显示一个标签。

我正在尝试调整this question 中给出的答案,以便它可以在最新版本的 ggplot2 中运行。受 ggplot google 群 here 的 cmets 的影响,我使用了最新版本的 geom-point-.r 作为模板,在各个地方的 gp 参数中添加了“标签”字段。然后我从 kohske 的答案中复制了剩余的代码。但它不起作用——生成的 svg 中没有任何标签,我不知道为什么。

我确实注意到point_grobs_labels 中的所有内容都是空的,当我查看grid.get(point_grob_names[1])$gp 时,没有标签字段...

library(ggplot2)
library(gridSVG)
library(proto)
library(rjson)

geom_point2 <- function (mapping = NULL, data = NULL, stat = "identity", 
                         position = "identity",
                         na.rm = FALSE, ...) {
  ggplot2:::GeomPoint$new(mapping = mapping, data = data, stat = stat, 
                          position = position, 
                          na.rm = na.rm, ...)
}

GeomPoint2 <- proto(ggplot2:::Geom, {
  objname <- "point"

  draw_groups <- function(., ...) .$draw(...)
  draw <- function(., data, scales, coordinates, na.rm = FALSE, ...) {    
    data <- remove_missing(data, na.rm, 
                           c("x", "y", "size", "shape"), name = "geom_point")
    if (empty(data)) return(zeroGrob())

    with(coord_transform(coordinates, data, scales), 
         ggname(.$my_name(), pointsGrob(x, y, size=unit(size, "mm"), pch=shape, 
                                        gp=gpar(
                                          col=alpha(colour, alpha),
                                          fill = alpha(fill, alpha),  
                                          label = label, 
                                          fontsize = size * .pt)))
    )
  }

  draw_legend <- function(., data, ...) {
    data <- aesdefaults(data, .$default_aes(), list(...))

    with(data,
         pointsGrob(0.5, 0.5, size=unit(size, "mm"), pch=shape, 
                    gp=gpar(
                      col = alpha(colour, alpha), 
                      fill = alpha(fill, alpha), 
                      label = label,
                      fontsize = size * .pt)
         )
    )
  }

  default_stat <- function(.) StatIdentity
  required_aes <- c("x", "y")
  default_aes <- function(.) aes(shape=16, colour="black", size=2, 
                                 fill = NA, alpha = NA, label = NA)

})

p <- ggplot(mtcars, aes(mpg, wt, label = rownames(mtcars))) + geom_point2() + facet_wrap(~ gear)
print(p)

grob_names <- grid.ls(print = FALSE)$name
point_grob_names <- sort(grob_names[grepl("point", grob_names)])
point_grobs_labels <- lapply(point_grob_names, function(x) grid.get(x)$gp$label)  

jlabel <- toJSON(point_grobs_labels)

grid.text("value", 0.05, 0.05, just = c(0, 0), name = "text_place", gp = gpar(col = "red"))

script <- '
var txt = null;
function f() {
var id = this.id.match(/geom_point2.([0-9]+)\\.points.*\\.([0-9]+)$/);
txt.textContent = label[id[1]-1][id[2]-1];
}

window.addEventListener("load",function(){
var es = document.getElementsByTagName("circle");
for (i=0; i<es.length; ++i) es[i].addEventListener("mouseover", f, false);

txt = (document.getElementById("text_place").getElementsByTagName("tspan"))[0];

},false);
'

grid.script(script = script)
grid.script(script = paste("var label = ", jlabel))

gridToSVG()

【问题讨论】:

    标签: r ggplot2


    【解决方案1】:

    试试这个:

    library(ggplot2)
    library(gridSVG)
    library(proto)
    library(rjson)
    mtcars2 <- data.frame(mtcars, names = rownames(mtcars))
    
    geom_point2 <- function (...) {
      GeomPoint2$new(...)
    }
    
    GeomPoint2 <- proto(ggplot2:::Geom, {
      objname <- "point"
    
      draw_groups <- function(., ...) .$draw(...)
      draw <- function(., data, scales, coordinates, na.rm = FALSE, ...) {    
        data <- remove_missing(data, na.rm, 
                               c("x", "y", "size", "shape"), name = "geom_point")
        if (empty(data)) return(zeroGrob())
        name <- paste(.$my_name(), data$PANEL[1], sep = ".")
        with(coord_transform(coordinates, data, scales), 
             ggname(name, pointsGrob(x, y, size=unit(size, "mm"), pch=shape, 
                                            gp=gpar(
                                              col=alpha(colour, alpha),
                                              fill = alpha(fill, alpha),  
                                              label = label, 
                                              fontsize = size * .pt)))
        )
      }
    
      draw_legend <- function(., data, ...) {
        data <- aesdefaults(data, .$default_aes(), list(...))
    
        with(data,
             pointsGrob(0.5, 0.5, size=unit(size, "mm"), pch=shape, 
                        gp=gpar(
                          col = alpha(colour, alpha), 
                          fill = alpha(fill, alpha), 
                          label = label,
                          fontsize = size * .pt)
             )
        )
      }
    
      default_stat <- function(.) StatIdentity
      required_aes <- c("x", "y")
      default_aes <- function(.) aes(shape=16, colour="black", size=2, 
                                     fill = NA, alpha = NA, label = NA)
    
    })
    
    p <- ggplot(mtcars2, aes(mpg, wt, label = names)) + geom_point2() +facet_wrap(~ gear)
    print(p)
    
    grob_names <- grid.ls(print = FALSE)$name
    point_grob_names <- sort(grob_names[grepl("point", grob_names)])
    point_grobs_labels <- lapply(point_grob_names, function(x) grid.get(x)$gp$label)
    
    jlabel <- toJSON(point_grobs_labels)
    
    grid.text("value", 0.05, 0.05, just = c(0, 0), name = "text_place", gp = gpar(col = "red"))
    
    script <- '
    var txt = null;
    function f() {
        var id = this.id.match(/geom_point.([0-9]+)\\.points.*\\.([0-9]+)$/);
        txt.textContent = label[id[1]-1][id[2]-1];
    }
    
    window.addEventListener("load",function(){
        var es = document.getElementsByTagName("circle");
        for (i=0; i<es.length; ++i) es[i].addEventListener("mouseover", f, false);
    
        txt = (document.getElementById("text_place").getElementsByTagName("tspan"))[0];
    
    },false);
    '
    grid.script(script = paste("var label = ", jlabel))
    grid.script(script = script)
    
    gridToSVG()
    

    没有大的变化,但我不得不添加

    mtcars2 <- data.frame(mtcars, names = rownames(mtcars))
    

    然后

    p <- ggplot(mtcars, aes(mpg, wt, label = rownames(mtcars)))
         + geom_point2() + facet_wrap(~ gear)
    

    也改为

    p <- ggplot(mtcars2, aes(mpg, wt, label = names)) 
         + geom_point2() +facet_wrap(~ gear)
    

    因为我们有rownames(mtcars)

    rownames(mtcars)
     [1] "Mazda RX4"           "Mazda RX4 Wag"       "Datsun 710"          "Hornet 4 Drive"     
     [5] "Hornet Sportabout"   "Valiant"             "Duster 360"          "Merc 240D"          
     [9] "Merc 230"            "Merc 280"            "Merc 280C"           "Merc 450SE"
    .....
    

    然后标签(我们设法通过其他修改获得)保持不变,即不被gears重新排列,只被它分割:

    point_grobs_labels
    [[1]]
     [1] "Mazda RX4"          "Mazda RX4 Wag"      "Datsun 710"         "Hornet 4 Drive"    
     [5] "Hornet Sportabout"  "Valiant"            "Duster 360"         "Merc 240D"         
     [9] "Merc 230"           "Merc 280"           "Merc 280C"          "Merc 450SE"        
    [13] "Merc 450SL"         "Merc 450SLC"        "Cadillac Fleetwood"
    [[2]]
    ....
    

    但是将这些标签名称作为一列可以解决问题。

    point_grobs_labels 
    [[1]]
     [1] "Hornet 4 Drive"      "Hornet Sportabout"   "Valiant"             "Duster 360"         
     [5] "Merc 450SE"          "Merc 450SL"          "Merc 450SLC"         "Cadillac Fleetwood" 
     [9] "Lincoln Continental" "Chrysler Imperial"   "Toyota Corona"       "Dodge Challenger"   
    [13] "AMC Javelin"         "Camaro Z28"          "Pontiac Firebird"   
    
    [[2]]
    ....
    

    【讨论】:

    • 不错!谢谢!我一直在寻找一个完整的工作示例,就像这个一样。不过,我有几个问题:“proto”在脚本中扮演什么角色?你用它来重新定义geom_point,对吧?我有兴趣从我制作的 ggplot2 图表中创建交互式图表。你能推荐任何关于让 R 使用带有 RJSON 的 Javascript 和创建 SVG 的教程/资源吗?
    • @MatteoS,不幸的是,我在这里帮不了你太多,主要是我只是想找到解决这个特殊问题的方法。但你可能是对的,可能重新定义geom_point 允许我们使用这些标签,至少在这种情况下我会称之为基础。所以,我建议你探索这个例子,尤其是 Javascript,grid.get()grid.ls() 部分。
    • 非常感谢 Julius(以及 tracy 提出这个问题)。您对如何将这种方法应用于多边形几何有任何想法吗?
    • @ferrouswheel,不是真的,因为至少对我来说这个版本不再适用于积分。
    【解决方案2】:

    感谢 tracy 提出了一个很好的问题,感谢 Julius 提供了非常有帮助的答案。

    为了让 Julius 的 javascript 在 Chrome 和 Safari 中为我工作,我不得不将 this.id 替换为 this.correspondingUseElement.id。这是有道理的,因为单个 &lt;circle&gt; SVG 元素没有每个 geom_point 的 id,我们想要的 id 附加到 &lt;use&gt; 元素。

    即使这在 Firefox 中对我不起作用,所以我将其更改为将事件侦听器附加到 &lt;use&gt; 元素本身。请注意,如果 SVG 更复杂,它可能有 &lt;use&gt;s 而不是 geom_points,所以我添加了一个 if 以仅将事件附加到 geom_point.XX &lt;use&gt; 元素。这适用于我的 Chrome、Safari 和 Firefox:

    window.addEventListener("load",function(){
      var es = document.getElementsByTagName("use"); 
      for (i=0; i<es.length; ++i) {
        if(es[i].id.search(/geom_point.([0-9]+)\.points.*\.([0-9]+)$/) >= 0) es[i].addEventListener("mouseover", f, false);
      }
      txt = (document.getElementById("text_place").getElementsByTagName("tspan"))[0];
    },false);
    

    (所有其他代码与朱利叶斯的相同)

    【讨论】:

      【解决方案3】:

      我们通过检测生成的 .svg 中的颜色属性并使用 css 检测鼠标悬停来解决此问题。结果在此演示的第 4、5、6 步中可见:

      Showing svg highlighting using css

      这是我的第一个 stackoverflow 回复 - 希望我的礼仪正确

      【讨论】:

        【解决方案4】:

        这是一个笼统的答案。我只是在这里帮助您使情节具有互动性。你可以试试这个——

        library(ggplot2)
        library(plotly)
        
        # Plot how you would normally code for ggplot2
        p <- ggplot(data,... 'add your variables and subsequent plots')
        ggplotly(p)
        

        享受吧!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-24
          • 1970-01-01
          • 2021-01-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多