将 ggplot2 图形转换为 plotly - 图例、标签和值

Convert ggplot2 graph to plotly - legend, labels and values

我用 ggmap()geom_sf() 创建了一个 ggplot 图,但是当我尝试用 ggplotly() 可视化它时,有些元素没有翻译。

  1. 似乎使用scale_fill_manual()时的标签没有被plotly解释。

  2. 此外,标签必须格式化,如ggplot生成的图表所示,并按照ggplot中建立的标签参数scale_fill_manual()进行修改。

  3. 标签应位于彩色框上。正如您在 ggplot 生成的图表中所见。

  4. 情节加框我觉得不美观,希望不要显示。

  5. 用ggplot2显示的副标题在plotly生成的图中找不到。

  6. 是否可以从图形中隐藏菜单(下载、缩放...)?

  7. 最后,以不同的方式,当鼠标悬停时,我尝试向图表的每个区域添加信息,但它没有显示任何内容,仅显示变量 geosmunicipios$ 的值Renta.media.por.hogar.2016.quantile,已经默认显示,在ggplotly().

    中使用hoverinfo()
  8. 它只显示我在第 7 点中通过将鼠标悬停在多边形线上而不是多边形区域中指示的默认文本。

简而言之,ggplotly 图表应该尽可能接近 ggplot2 生成的图表。

下载文件位于: https://www.dropbox.com/s/9nmy0uj00jhc1y4/geosmunicipios.R?dl=0


> str(geosmunicipios)
Classes ‘sf’ and 'data.frame':  45 obs. of  21 variables:
 $ CODIGOINE                          : chr  "30001" "30002" "30003" "30004" ...
 $ OBJECTID                           : num  577 578 579 580 581 582 583 584 585 586 ...
 $ INSPIREID                          : chr  "ES.IGN.SIGLIM34143030001" "ES.IGN.SIGLIM34143030002" "ES.IGN.SIGLIM34143030003" "ES.IGN.SIGLIM34143030004" ...
 $ NATCODE                            : chr  "34143030001" "34143030002" "34143030003" "34143030004" ...
 $ NAMEUNIT                           : chr  "Abanilla" "Abarán" "Águilas" "Albudeite" ...
 $ CODNUT1                            : chr  "ES6" "ES6" "ES6" "ES6" ...
 $ CODNUT2                            : chr  "ES62" "ES62" "ES62" "ES62" ...
 $ CODNUT3                            : chr  "ES620" "ES620" "ES620" "ES620" ...
 $ Shape__Are                         : num  0 0 0 0 0 0 0 0 0 0 ...
 $ Shape__Len                         : num  0 0 0 0 0 0 0 0 0 0 ...
 $ Unidades.territoriales             : chr  "30001 Abanilla" "30002 Abarán" "30003 Águilas" "30004 Albudeite" ...
 $ Renta.media.por.persona.2016       : num  8444 8401 8269 7680 8465 ...
 $ Renta.media.por.persona.2015       : num  8274 8338 7982 7458 8305 ...
 $ Renta.media.por.hogar.2016         : num  21569 23597 23222 19855 24567 ...
 $ Renta.media.por.hogar.2015         : num  21017 23522 22368 19713 23875 ...
 $ CPRO                               : int  30 30 30 30 30 30 30 30 30 30 ...
 $ CMUN                               : int  1 2 3 4 5 6 7 8 9 10 ...
 $ DC                                 : int  1 6 2 7 0 3 9 5 8 2 ...
 $ NOMBRE                             : chr  "Abanilla" "Abarán" "Águilas" "Albudeite" ...
 $ geometry                           :sfc_MULTIPOLYGON of length 45; first list element: List of 1
  ..$ :List of 1
  .. ..$ : num [1:27, 1:2] -1.14 -1.12 -1.1 -1.08 -1.06 ...
  ..- attr(*, "class")= chr [1:3] "XY" "MULTIPOLYGON" "sfg"
 $ Renta.media.por.hogar.2016.quantile: Factor w/ 4 levels "[1.98e+04,2.19e+04)",..: 1 3 2 1 4 4 3 4 3 2 ...
 - attr(*, "sf_column")= chr "geometry"
 - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
  ..- attr(*, "names")= chr [1:20] "CODIGOINE" "OBJECTID" "INSPIREID" "NATCODE" ...

图表的R代码


library(ggplot2)
library(plotly)

dget("geosmunicipios.R")

quantile.interval = quantile(geosmunicipios$Renta.media.por.hogar.2016, probs = c(0.00, 0.25, 0.50, 0.75, 1.00))
geosmunicipios$Renta.media.por.hogar.2016.quantile = cut(geosmunicipios$Renta.media.por.hogar.2016, breaks=quantile.interval, right = FALSE, include.lowest = TRUE)
colors = c("#fee5d9","#fcae91","#fb6a4a","#de2d26")
cuartiles <- quantile(geosmunicipios$Renta.media.por.hogar.2016, probs = c(0.00, 0.25, 0.50, 0.75, 1.00))

rmurcia <- ggplot(data = geosmunicipios) + 
 geom_sf(
  aes(
   fill=Renta.media.por.hogar.2016.quantile
  ),
  color="#FFFFFF",
  size=0.5
 ) +
 theme_void() +
 scale_fill_manual(
  values = colors,
  labels = c(
   paste("[1Q)\n", format(cuartiles[2], big.mark=".", decimal.mark=","), "€", sep=""),
   paste("[2Q)\n", format(cuartiles[3], big.mark=".", decimal.mark=","), "€", sep=""),
   paste("[3Q)\n", format(cuartiles[4], big.mark=".", decimal.mark=","), "€", sep=""),
   paste("[4Q]\n", format(cuartiles[5], big.mark=".", decimal.mark=","), "€", sep="")
  ),
  guide = guide_legend(
   direction = "horizontal",
   nrow = 1,
   #title.position = "top",
   label.position = "top",
   label.hjust = 1,
   keyheight = 0.75
  )
 ) +
 labs(
  title = "Región de Murcia",
  subtitle = "Renta media por hogar (2016)",
  caption = "",
  fill = "" # Etiqueta para la Leyenda
 ) +
 theme(
  text = element_text(color = "#22211d"),
  plot.background = element_rect(fill = "#ffffff", color = NA),
  panel.background = element_rect(fill = "#ffffff", color = NA),
  legend.background = element_rect(fill = "#ffffff", color = NA),
  plot.title = element_text(size= 22, hjust=0.5, color = "#4e4d47", margin = margin(b = -0.1, t = 0.4, l = 2, unit = "cm")),
  plot.subtitle = element_text(size= 17, hjust=0.5, color = "#4e4d47", margin = margin(b = -0.1, t = 0.43, l = 2, unit = "cm")),
  plot.caption = element_text(size=12, color = "#4e4d47", margin = margin(b = 0.3, r=-99, unit = "cm") ),
  #legend.position = c(0.85, 0.08)
  legend.position = "bottom"
 )
rmurcia


ggplotly(
 rmurcia,
 hoverinfo = 'text',
 text = ~paste(
  '</br> Municipio: ', NOMBRE,
  '</br> Renta Hogar 2016: ', Renta.media.por.hogar.2016,
  '</br> Renta Hogar 2015: ', Renta.media.por.hogar.2015
 )
) %>%
layout(
 legend = list(
  orientation = "h",
  xanchor = "center",
  x = 0.5,
  y = -0.01
 )
)

这可行,但您可能不喜欢它的工作方式。工具提示的问题只出现在行上...最重要的是,这是 Github 中的一张票。

这会从 ggplot object 中获取图例、标题和副标题,并将它们作为图像添加到 plotly object 中。如果调整图像大小,则必须刷新,才能重新对齐。

library(tidyverse)
library(plotly)
library(magick) # for the legend and title
library(ggpubr) # for extracting the ggplot legend

# load data
# didn't include it--you obviously have your data
load("./_rdata/geosData.Rdata")

我从这里创建了名为 rmurciaggplot object。我改变的唯一元素是 ggplot(),其余的与您最初设计的完全一样。

修改后ggplot()

rmurcia <- ggplot(data = geosmunicipios,
                  aes(text = paste('\nMunicipio: ', NOMBRE,
                                   '\nRenta Hogar 2016: ',
                                   format(Renta.media.por.hogar.2016, 
                                          big.mark=".", 
                                          decimal.mark=","), 
                                   "€",
                                   '\nRenta Hogar 2015: ', 
                                   format(Renta.media.por.hogar.2015, 
                                          big.mark = ".", 
                                          decimal.mark=","),
                                   "€",
                                   sep = ""),
                       color = Renta.media.por.hogar.2016.quantile)) +

以及这部分的其余代码。

接下来是图例:

#----------- the legend for plotly -------------
# instead of recreating your legend or title (matching font blocks, etc)

ggLegend = get_legend(rmurcia, position = "bottom")

as_ggplot(ggLegend)

# create a temp file to hold the image
temp <- tempfile(fileext = "png") 

# save plot legend as an image
ggsave(filename = temp, 
       plot = as_ggplot(ggLegend),
       device = "png",
       scale = 1,
       bg = "transparent")

# now read the png and convert to raster for plotly
imgr <- image_read(temp)

# take a look at the excessive whitespace that ggplot added 
image_border(image_background(imgr,
                              "hotpink"), 
             "#000080",
             ".1x.1") # you will have to scroll A LOT in the viewer pane

# remove this excess
(imgr <- image_trim(imgr))

# and convert to raster for plotly
imgrR <- as.raster(imgr)

# take a look to make sure it looks as expected
plot(imgrR)

# remove the tempfile
unlink(temp) 

现在的标题和副标题:

#----------- the title for plotly -------------
# literally an empty graph with the title you originally specified
forTitle <- ggplot(data = geosmunicipios) + 
  theme_void() + 
  labs(title = "Región de Murcia",
       subtitle = "Renta media por hogar (2016)") + 
  theme(text = element_text(color = "#22211d"),
        plot.title = element_text(size= 22,
                                  hjust=0.5, 
                                  color = "#4e4d47", 
                                  margin = margin(b = -0.1,
                                                  t = 0.4,
                                                  l = 2, 
                                                  unit = "cm")),
        plot.subtitle = element_text(size= 17,
                                     hjust=0.5, 
                                     color = "#4e4d47", 
                                     margin = margin(b = -0.1,
                                                     t = 0.43,
                                                     l = 2, 
                                                     unit = "cm"))
  )

# create a temp file to hold the image
tmp <- tempfile(fileext = "png")

# save title plot as an image
ggsave(filename = tmp, 
       plot = forTitle,
       device = "png",
       scale = 1,
       bg = "transparent")

# now read the png 
imgr2 <- image_read(tmp)

# take a look at the excessive whitespace that ggplot added 
image_border(image_background(imgr2,
                              "hotpink"), 
             "#000080",
             ".1x.1") # you will have to scroll to the right

# remove this excess
(imgr2 <- image_trim(imgr2))

# and convert to raster for plotly
imgr2r <- as.raster(imgr2)

# take a look to make sure it looks as expected
plot(imgr2r)

# remove the tempfile
unlink(tmp) 

I do want to caveat to say that you could use "\n" and keep the title and subtitle together as the title. However, that keeps them the same font size. I thought that was ugly, so I came up with this approach. (Where there is a will, there is a way.)

现在 plotly object...

#------------- now for plotly ---------------
ggplotly(rmurcia, 
         tooltip = "text") %>% 
  style(hoveron = "points+fills", # this isn't working,
                                  # currently a ticket in Github
        # this traces makes it so there is only one type of tooltip
        traces = seq.int(2,  
                         length(rmurcia$x$data))) %>%   
  hide_legend() %>%          # we aren't going to use the plotly legend
  layout(                    # remove the legend layout code
    title = list(text = ""), # remove the title
    images = list(
      list(source = raster2uri(imgrR), # the legend
           xref = "paper",
           yref = "paper",
           x = 0.32,       # x placement on the grid
           y = 0.03,       # y placement on the grid
           sizex = .4,     # scale to 40% of size width
           sizey = .4,     # scale to 40% of size height
           opacity = 1,
           layer = "above"),
      list(source = raster2uri(imgr2r), # the title/subtitle
           xref = "paper",
           yref = "paper",
           x = .23,       # x placement on the grid
           y = 1.09,      # y placement on the grid
           sizex = .55,   # scale to 55% of size width
           sizey = .55,   # scale to 55% of size height
           opacity = 1,
           layer = "above")
      # for controlling the plot size
    ), margin = list(l = 4, 
                     r = 40, 
                     b = 65, 
                     t = 65, 
                     pad = 4)
  )

虽然我尝试过,但我无法找到摆脱黑色的方法 'L'。我想你可以添加一个白色块的图像来覆盖它。

布局中的大小可能会有很大变化,具体取决于 plots/viewer 窗格的大小。我建议在 ggplot 中选择你最喜欢的,然后从那里开始调整 plotly。如果您的理想情节在维度上有很大不同,您将不得不调整图像的布局。

ggplot 一样,导出像素为 608 宽 x 661 高:

plotly 使用相同大小的窗格和代码中的尺寸:

差点忘了 -- 工具提示:

我可以告诉你 plotly object 中的黑色 'L' 是放大和平移等所有操作的基础。