简体   繁体   English

使用带有 ggplotly 的 facet_wrap 的第一个和最后一个面大于中间面

[英]First and last facets using facet_wrap with ggplotly are larger than middle facets

Using sample data:使用样本数据:

library(tidyverse)
library(plotly)

myplot <- diamonds %>% ggplot(aes(clarity, price)) +
  geom_boxplot() +
  facet_wrap(~ clarity, ncol = 8, scales = "free", strip.position = "bottom") +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank())

ggplotly(myplot)

Returns something like:返回类似:

在此处输入图像描述

Where the inside facets are horribly scaled compared to the first and last and there is a lot of extra padding.与第一个和最后一个相比,内部刻面的缩放比例非常惊人,并且有很多额外的填充。 I tried to find a solution from these questions:我试图从这些问题中找到解决方案:

ggplotly not working properly when number are facets are more 当数量更多时,ggplotly无法正常工作

R: facet_wrap does not render correctly with ggplotly in Shiny app R:在 Shiny 应用程序中使用 ggplotly 无法正确渲染 facet_wrap

With trial and error I used panel.spacing.x = unit(-0.5, "line") in theme() and it looks a bit better, with a lot of the extra padding gone, but the internal facets are still noticeably smaller.经过反复试验,我在theme()中使用panel.spacing.x = unit(-0.5, "line") ,它看起来好多了,很多额外的填充消失了,但内部方面仍然明显更小。

在此处输入图像描述

Also as an extra question but not as important, the strip labels are the top in the ggplotly() call, when I set them at the bottom.同样作为一个额外的问题但不是那么重要,当我将条形标签设置在底部时,条形标签是ggplotly()调用中的顶部。 Seems like an ongoing issue here , does anyone have a hacky workaround?这里似乎是一个持续存在的问题,有没有人有一个hacky解决方法?

Edit: in my real dataset I need y-axis labels for each of the facets as their scales are quite different so I kept them in the example and is why I need facet_wrap .编辑:在我的真实数据集中,我需要每个方面的 y 轴标签,因为它们的比例非常不同,所以我将它们保留在示例中,这就是我需要facet_wrap的原因。 Screenshot of my real dataset for explanation:我的真实数据集的屏幕截图以供解释:

在此处输入图像描述

Updated answer (2): just use fixfacets()更新答案(2):只需使用fixfacets()

I've put together a function fixfacets(fig, facets, domain_offset) that turns this:我整理了一个 function fixfacets(fig, facets, domain_offset) ,它变成了这样:

在此处输入图像描述

...by using this: ...通过使用这个:

f <- fixfacets(figure = fig, facets <- unique(df$clarity), domain_offset <- 0.06)

...into this: ...进入这个:

在此处输入图像描述

This function should now be pretty flexible with regards to number of facets.这个 function 现在在方面的数量方面应该非常灵活。

Complete code:完整代码:

library(tidyverse)
library(plotly)

# YOUR SETUP:

df <- data.frame(diamonds)

df['price'][df$clarity == 'VS1', ] <- filter(df['price'], df['clarity']=='VS1')*2

myplot <- df %>% ggplot(aes(clarity, price)) +
  geom_boxplot() +
  facet_wrap(~ clarity, scales = 'free', shrink = FALSE, ncol = 8, strip.position = "bottom", dir='h') +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank())
fig <- ggplotly(myplot)

# Custom function that takes a ggplotly figure and its facets as arguments.
# The upper x-values for each domain is set programmatically, but you can adjust
# the look of the figure by adjusting the width of the facet domain and the 
# corresponding annotations labels through the domain_offset variable
fixfacets <- function(figure, facets, domain_offset){

  # split x ranges from 0 to 1 into
  # intervals corresponding to number of facets
  # xHi = highest x for shape
  xHi <- seq(0, 1, len = n_facets+1)
  xHi <- xHi[2:length(xHi)]

  xOs <- domain_offset

  # Shape manipulations, identified by dark grey backround: "rgba(217,217,217,1)"
  # structure: p$x$layout$shapes[[2]]$
  shp <- fig$x$layout$shapes
  j <- 1
  for (i in seq_along(shp)){
    if (shp[[i]]$fillcolor=="rgba(217,217,217,1)" & (!is.na(shp[[i]]$fillcolor))){
       #$x$layout$shapes[[i]]$fillcolor <- 'rgba(0,0,255,0.5)' # optionally change color for each label shape
       fig$x$layout$shapes[[i]]$x1 <- xHi[j]
       fig$x$layout$shapes[[i]]$x0 <- (xHi[j] - xOs)
       #fig$x$layout$shapes[[i]]$y <- -0.05
       j<-j+1
    }
  }

  # annotation manipulations, identified by label name
  # structure: p$x$layout$annotations[[2]]
  ann <- fig$x$layout$annotations
  annos <- facets
  j <- 1
  for (i in seq_along(ann)){
    if (ann[[i]]$text %in% annos){
       # but each annotation between high and low x,
       # and set adjustment to center
       fig$x$layout$annotations[[i]]$x <- (((xHi[j]-xOs)+xHi[j])/2)
       fig$x$layout$annotations[[i]]$xanchor <- 'center'
       #print(fig$x$layout$annotations[[i]]$y)
       #fig$x$layout$annotations[[i]]$y <- -0.05
       j<-j+1
    }
  }

  # domain manipulations
  # set high and low x for each facet domain
  xax <- names(fig$x$layout)
  j <- 1
  for (i in seq_along(xax)){
    if (!is.na(pmatch('xaxis', lot[i]))){
      #print(p[['x']][['layout']][[lot[i]]][['domain']][2])
      fig[['x']][['layout']][[xax[i]]][['domain']][2] <- xHi[j]
      fig[['x']][['layout']][[xax[i]]][['domain']][1] <- xHi[j] - xOs
      j<-j+1
    }
  }

  return(fig)
}

f <- fixfacets(figure = fig, facets <- unique(df$clarity), domain_offset <- 0.06)
f

Updated answer (1): How to handle each element programmatically!更新答案 (1):如何以编程方式处理每个元素!

The elements of your figure that require some editing to meet your needs with regards to maintaining the scaling of each facet and fix the weird layout, are:需要进行一些编辑以满足您在维护每个方面的缩放和修复奇怪的布局方面的需求的图形元素是:

  1. x label annotations through fig$x$layout$annotations , x label 注释通过fig$x$layout$annotations
  2. x label shapes through fig$x$layout$shapes , and x label 形状通过fig$x$layout$shapes ,和
  3. the position where each facet starts and stops along the x axis through fig$x$layout$xaxis$domain position,其中每个方面通过fig$x$layout$xaxis$domain沿 x 轴开始和停止

The only real challenge was referincing, for example, the correct shapes and annotations among many other shapes and annotations.例如,唯一真正的挑战是在许多其他形状和注释中引用正确的形状和注释。 The code snippet below will do exatly this to produce the following plot:下面的代码片段将执行此操作以生成以下 plot:

在此处输入图像描述

The code snippet might need some careful tweaking for each case with regards to facet names, and number of names, but the code in itself is pretty basic so you shouldn't have any problem with that.代码片段可能需要对每个案例的方面名称和名称数量进行一些仔细的调整,但代码本身是非常基本的,所以你不应该有任何问题。 I'll polish it a bit more myself when I find the time.当我有时间时,我会自己多打磨一下。

Complete code:完整代码:

ibrary(tidyverse)
library(plotly)

# YOUR SETUP:

df <- data.frame(diamonds)

df['price'][df$clarity == 'VS1', ] <- filter(df['price'], df['clarity']=='VS1')*2

myplot <- df %>% ggplot(aes(clarity, price)) +
  geom_boxplot() +
  facet_wrap(~ clarity, scales = 'free', shrink = FALSE, ncol = 8, strip.position = "bottom", dir='h') +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank())
#fig <- ggplotly(myplot)

# MY SUGGESTED SOLUTION:

# get info about facets
# through unique levels of clarity
facets <- unique(df$clarity)
n_facets <- length(facets)

# split x ranges from 0 to 1 into
# intervals corresponding to number of facets
# xHi = highest x for shape
xHi <- seq(0, 1, len = n_facets+1)
xHi <- xHi[2:length(xHi)]

# specify an offset from highest to lowest x for shapes
xOs <- 0.06

# Shape manipulations, identified by dark grey backround: "rgba(217,217,217,1)"
# structure: p$x$layout$shapes[[2]]$
shp <- fig$x$layout$shapes
j <- 1
for (i in seq_along(shp)){
  if (shp[[i]]$fillcolor=="rgba(217,217,217,1)" & (!is.na(shp[[i]]$fillcolor))){
     #fig$x$layout$shapes[[i]]$fillcolor <- 'rgba(0,0,255,0.5)' # optionally change color for each label shape
     fig$x$layout$shapes[[i]]$x1 <- xHi[j]
     fig$x$layout$shapes[[i]]$x0 <- (xHi[j] - xOs)
     j<-j+1
  }
}

# annotation manipulations, identified by label name
# structure: p$x$layout$annotations[[2]]
ann <- fig$x$layout$annotations
annos <- facets
j <- 1
for (i in seq_along(ann)){
  if (ann[[i]]$text %in% annos){
     # but each annotation between high and low x,
     # and set adjustment to center
     fig$x$layout$annotations[[i]]$x <- (((xHi[j]-xOs)+xHi[j])/2)
     fig$x$layout$annotations[[i]]$xanchor <- 'center'

     j<-j+1
  }
}

# domain manipulations
# set high and low x for each facet domain
lot <- names(fig$x$layout)
j <- 1
for (i in seq_along(lot)){
  if (!is.na(pmatch('xaxis', lot[i]))){
    #print(p[['x']][['layout']][[lot[i]]][['domain']][2])
    fig[['x']][['layout']][[lot[i]]][['domain']][2] <- xHi[j]
    fig[['x']][['layout']][[lot[i]]][['domain']][1] <- xHi[j] - xOs
    j<-j+1
  }
}

fig

Initial answers based on built-in functionalities基于内置功能的初步答案


With many variables of very different values, it seems that you're going to end up with a challenging format no matter what, meaning either由于许多变量的值非常不同,看起来无论如何你都会得到一个具有挑战性的格式,这意味着要么

  1. facets will have varying width, or刻面将具有不同的宽度,或
  2. labels will cover facets or be too small to be readable, or标签将覆盖各个方面或太小而无法阅读,或者
  3. the figure will be too wide to display without a scrollbar.如果没有滚动条,该图将太宽而无法显示。

So what I'd suggest is rescaling your price column for each unique clarity and set scale='free_x .因此,我建议为每个独特的清晰度重新调整您的price列并设置scale='free_x I still hope someone will come up with a better answer.我仍然希望有人能提出更好的答案。 But here's what I would do:但这是我要做的:

Plot 1: Rescaled values and scale='free_x Plot 1:重新缩放的值和scale='free_x

在此处输入图像描述

Code 1:代码 1:

#install.packages("scales")
library(tidyverse)
library(plotly)
library(scales)

library(data.table)
setDT(df)

df <- data.frame(diamonds)

df['price'][df$clarity == 'VS1', ] <- filter(df['price'], df['clarity']=='VS1')*2

# rescale price for each clarity
setDT(df)
clarities <- unique(df$clarity)
for (c in clarities){
  df[clarity == c, price := rescale(price)]
}

df$price <- rescale(df$price)

myplot <- df %>% ggplot(aes(clarity, price)) +
  geom_boxplot() +
  facet_wrap(~ clarity, scales = 'free_x', shrink = FALSE, ncol = 8, strip.position = "bottom") +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank())

p <- ggplotly(myplot)
p

This will of course only give insight into the internal distribution of each category since the values have been rescaled.这当然只会让您深入了解每个类别的内部分布,因为值已重新调整。 If you want to show the raw price data, and maintain readability, I'd suggest making room for a scrollbar by setting the width large enough.如果您想显示原始价格数据并保持可读性,我建议通过将width设置得足够大来为滚动条腾出空间。

Plot 2: scales='free' and big enough width: Plot 2: scales='free'和足够大的宽度:

在此处输入图像描述

Code 2:代码 2:

library(tidyverse)
library(plotly)

df <- data.frame(diamonds)

df['price'][df$clarity == 'VS1', ] <- filter(df['price'], df['clarity']=='VS1')*2

myplot <- df %>% ggplot(aes(clarity, price)) +
  geom_boxplot() +
  facet_wrap(~ clarity, scales = 'free', shrink = FALSE, ncol = 8, strip.position = "bottom") +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank())

p <- ggplotly(myplot, width = 1400)
p

And, of course, if your values don't vary too much accross categories, scales='free_x' will work just fine.而且,当然,如果您的值在各个类别中变化不大, scales='free_x'就可以正常工作。

Plot 3: scales='free_x Plot 3: scales='free_x

在此处输入图像描述

Code 3:代码 3:

library(tidyverse)
library(plotly)

df <- data.frame(diamonds)

df['price'][df$clarity == 'VS1', ] <- filter(df['price'], df['clarity']=='VS1')*2

myplot <- df %>% ggplot(aes(clarity, price)) +
  geom_boxplot() +
  facet_wrap(~ clarity, scales = 'free_x', shrink = FALSE, ncol = 8, strip.position = "bottom") +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank())

p <- ggplotly(myplot)
p

sometimes it is helpful to consider a different plot altogether if you struggle with the selected plot.有时,如果您对所选的 plot 感到困惑,那么考虑完全不同的 plot 会有所帮助。 It all depends on what it is that you wish to visualise.这一切都取决于您希望可视化的是什么。 Sometimes box plots work, sometimes histograms work and sometime densities works.有时箱形图有效,有时直方图有效,有时密度有效。 Here is an example of how a density plot can give you a quick idea of data distribution for many parameters.这是密度 plot 如何让您快速了解许多参数的数据分布的示例。

library(tidyverse)
library(plotly)
myplot <- diamonds %>% ggplot(aes(price, colour = clarity)) +
  geom_density(aes(fill = clarity), alpha = 0.25) +
  theme(axis.ticks.x = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank())

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM