[英]First and last facets using facet_wrap with ggplotly are larger than middle facets
使用样本数据:
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)
返回类似:
与第一个和最后一个相比,内部刻面的缩放比例非常惊人,并且有很多额外的填充。 我试图从这些问题中找到解决方案:
R:在 Shiny 应用程序中使用 ggplotly 无法正确渲染 facet_wrap
经过反复试验,我在theme()
中使用panel.spacing.x = unit(-0.5, "line")
,它看起来好多了,很多额外的填充消失了,但内部方面仍然明显更小。
同样作为一个额外的问题但不是那么重要,当我将条形标签设置在底部时,条形标签是ggplotly()
调用中的顶部。 这里似乎是一个持续存在的问题,有没有人有一个hacky解决方法?
编辑:在我的真实数据集中,我需要每个方面的 y 轴标签,因为它们的比例非常不同,所以我将它们保留在示例中,这就是我需要facet_wrap
的原因。 我的真实数据集的屏幕截图以供解释:
fixfacets()
我整理了一个 function fixfacets(fig, facets, domain_offset)
,它变成了这样:
...通过使用这个:
f <- fixfacets(figure = fig, facets <- unique(df$clarity), domain_offset <- 0.06)
...进入这个:
这个 function 现在在方面的数量方面应该非常灵活。
完整代码:
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
需要进行一些编辑以满足您在维护每个方面的缩放和修复奇怪的布局方面的需求的图形元素是:
fig$x$layout$annotations
,fig$x$layout$shapes
,和fig$x$layout$xaxis$domain
沿 x 轴开始和停止例如,唯一真正的挑战是在许多其他形状和注释中引用正确的形状和注释。 下面的代码片段将执行此操作以生成以下 plot:
代码片段可能需要对每个案例的方面名称和名称数量进行一些仔细的调整,但代码本身是非常基本的,所以你不应该有任何问题。 当我有时间时,我会自己多打磨一下。
完整代码:
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
由于许多变量的值非常不同,看起来无论如何你都会得到一个具有挑战性的格式,这意味着要么
因此,我建议为每个独特的清晰度重新调整您的price
列并设置scale='free_x
。 我仍然希望有人能提出更好的答案。 但这是我要做的:
Plot 1:重新缩放的值和scale='free_x
代码 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
这当然只会让您深入了解每个类别的内部分布,因为值已重新调整。 如果您想显示原始价格数据并保持可读性,我建议通过将width
设置得足够大来为滚动条腾出空间。
Plot 2: scales='free'
和足够大的宽度:
代码 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
而且,当然,如果您的值在各个类别中变化不大, scales='free_x'
就可以正常工作。
Plot 3: scales='free_x
代码 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
有时,如果您对所选的 plot 感到困惑,那么考虑完全不同的 plot 会有所帮助。 这一切都取决于您希望可视化的是什么。 有时箱形图有效,有时直方图有效,有时密度有效。 这是密度 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.