简体   繁体   English

R中ggplotly中图例的奇怪格式

[英]Strange formatting of legend in ggplotly in R

I'm trying to turn a ggplot into a plotly.我正在尝试将 ggplot 变成 plotly。 The ggplot renders fine, but when I put it through ggplotly, suddenly the legend adds parenthesis and ",1" after the label. ggplot 渲染得很好,但是当我通过 ggplotly 放置它时,图例突然在标签后添加了括号和“,1”。

Here's a sample fake data:这是一个示例假数据:

sorted1<-data.frame(CommDate=c(as.Date("2017-09-12"), as.Date("2017-10-15")), CommName=c("Foo", "Bar"), PubB4=c(2,3))

And here's the code I'm trying to run on it:这是我试图在其上运行的代码:

ggplotly(ggplot(sorted1, aes(x=as.Date(CommDate), y=PubB4))+
           geom_smooth(level=0.0, aes(colour="Moving average"), se=FALSE)+
           geom_point(aes(fill=CommName), size=4)+
           expand_limits(y=c(0,4.5))+
           geom_line(mapping=aes(y=4),colour="orangered3",size=1)+
           geom_text(mapping=aes(y=4.2, x=min(sorted1$CommDate)+4), label="Target", size=3)+
           xlab("Committee Date")+
           guides(fill=guide_legend(title="Committee Names"), colour=guide_legend(title.theme=element_blank(),title=NULL))+
           scale_x_date(labels = date_format("%b-%y"))+
           theme_light()+
           theme(plot.title=element_text(hjust=0.5, size=12),panel.grid.major.x = (element_blank()), 
                 panel.grid.minor.x = (element_blank()), 
                 axis.title = element_text(size=8), legend.title = element_text(size=10),
                 legend.text = element_text(size=8), legend.box = 'vertical', legend.spacing.y = unit(-2,"mm"))+
           scale_colour_manual(name="",values="#0072B2"))

(the geom_smooth doesn't render here, but it does with the full data.) geom_smooth不会在此处呈现,但会处理完整数据。)

Here's what I get from this:这是我从中得到的:

图例格式错误的 ggplotly 结果

Why does the legend show up as "(foo,1)"?为什么图例显示为“(foo,1)”?

I tried removing the geom_smooth which actually solved the problem, but I need it there - how can I keep it but fix the legend?我尝试删除实际上解决了问题的geom_smooth ,但我在那里需要它 - 如何保留它但修复图例?

Thanks!谢谢!

Update: OK, I started commenting out stuff to see what happens.更新:好的,我开始注释掉一些东西看看会发生什么。 If I remove the aes() from the geom_smooth , that also fixes the problem, as long as I keep the scale_colour_manual commented off as well.如果我从geom_smooth中删除aes() ,这也可以解决问题,只要我也将scale_colour_manual注释掉即可。 But I really would like to have control over the geom_smooth 's aesthetics, and include it in the legend.但我真的很想控制geom_smooth的美学,并将其包含在图例中。 So I'm making progress, but still not quite there...所以我正在取得进步,但还没有完全达到目标......

Here's yet another elegant solution.这是另一个优雅的解决方案。 Under the hood it detects if a plotly legend name option is available and if so, removes the "(" and ",1)".在引擎盖下,它检测是否有 plotly legend name 选项可用,如果可用,则删除“(”和“,1)”。

library(ggplot2)
library(plotly)
library(stringr)
library(dplyr)

data = data.frame(Date=as.Date(c("2017-09-12","2017-10-15")), PubB4=c(2,3), category=c("Foo", "Bar"))

myplot = ggplotly(ggplot(data, aes(x=Date, y=PubB4))+
    geom_hline(aes(yintercept=2.5, color="my line label"))+
    geom_point(aes(fill=category), size=4))

for (i in 1:length(myplot$x$data)){
    if (!is.null(myplot$x$data[[i]]$name)){
        myplot$x$data[[i]]$name =  gsub("\\(","",str_split(myplot$x$data[[i]]$name,",")[[1]][1])
    }
}

myplot

绘图结果

Here's an approach that worked for me.这是一种对我有用的方法。 It involves diving into the resulting plotly object and cleaning up the legend names.它涉及深入研究生成的 plotly 对象并清理图例名称。

The first part creates a function to detect if the plotly list element has a legend specified and then cleans it up.第一部分创建一个函数来检测 plotly 列表元素是否指定了图例,然后将其清理。

clean_plotly_leg <- function(.plotly_x, .extract_str) {
  # Inpects an x$data list in a plotly object, cleans up legend values where appropriate
  if ("legendgroup" %in% names(.plotly_x)) {
    # The list includes a legend group

    .plotly_x$legendgroup <- stringr::str_extract(.plotly_x$legendgroup, .extract_str)
    .plotly_x$name <- stringr::str_extract(.plotly_x$name, .extract_str)

  }
  .plotly_x


}

The second part applies it to your example, but with an intermediate step.第二部分将其应用于您的示例,但有一个中间步骤。

sorted_plotly <-
  ggplotly(ggplot(sorted1, aes(x=as.Date(CommDate), y=PubB4))+
           geom_smooth(level=0.0, aes(colour="Moving average"), se=FALSE)+
           geom_point(aes(fill=CommName), size=4)+
           expand_limits(y=c(0,4.5))+
           geom_line(mapping=aes(y=4),colour="orangered3",size=1)+
           geom_text(mapping=aes(y=4.2, x=min(sorted1$CommDate)+4), label="Target", size=3)+
           xlab("Committee Date")+
           guides(fill=guide_legend(title="Committee Names"), colour=guide_legend(title.theme=element_blank(),title=NULL))+
           scale_x_date(labels = date_format("%b-%y"))+
           theme_light()+
           theme(plot.title=element_text(hjust=0.5, size=12),panel.grid.major.x = (element_blank()), 
                 panel.grid.minor.x = (element_blank()), 
                 axis.title = element_text(size=8), legend.title = element_text(size=10),
                 legend.text = element_text(size=8), legend.box = 'vertical', legend.spacing.y = unit(-2,"mm"))+
           scale_colour_manual(name="",values="#0072B2"))

sorted_plotly$x$data <-
  sorted_plotly$x$data %>% 
  map(clean_plotly_leg, "[^\\(][^,]*") # ie remove the opening bracket, if one exists, and extract each character up to the first comma

sorted_plotly 

I'd welcome any suggestions for making this code more efficient, but at least it works我欢迎任何使这段代码更有效率的建议,但至少它是有效的

Since nothing I did worked, I took the dive and taught myself how to create the chart directly in plotly .由于我没有做任何工作,我开始潜水并自学如何直接在plotly中创建图表。 For the benefit of possible future viewers, here's how the chart is recreated in plotly (sans some beautification that I'll get around to another time):为了未来可能的观众的利益,以下是图表是如何以plotly方式重新创建的(没有一些美化,我将在其他时间进行):

plot_ly(sorted, x=~CommDate) %>%
  add_markers(y=~PubB4, color=~factor(CommName), size=I(15)) %>% 
  add_lines(x=loess.smooth(sorted$CommDate,sorted$PubB4)$x,         
    y=loess.smooth(sorted$CommDate,sorted$PubB4)$y, name = "Rolling Average", 
    showlegend = TRUE, size=I(3)) %>%
  add_lines(x=c(min(sorted$CommDate),max(sorted$CommDate)),y=4, 
    color=I("red"), name="target", size=I(3)) %>%
  layout(yaxis=(list(range=c(0,max(c(4.5,sorted$PubB4))))),xaxis=list(range=c(min(sorted$CommDate)-10, max(sorted$CommDate)+5))) 

Here's a workaround that actually works:这是一个实际有效的解决方法:

# First, repeating your code, noting the plot as p1
# --------------------------------------------------
sorted1<-data.frame(CommDate=c(as.Date("2017-09-12"), as.Date("2017-10-15")), CommName=c("Foo", "Bar"), PubB4=c(2,3))
p1 <- ggplotly(ggplot(sorted1, aes(x=as.Date(CommDate), y=PubB4))+
           geom_smooth(level=0.0, aes(colour="Moving average"), se=FALSE)+
           geom_point(aes(fill=CommName), size=4)+
           expand_limits(y=c(0,4.5))+
           geom_line(mapping=aes(y=4),colour="orangered3",size=1)+
           geom_text(mapping=aes(y=4.2, x=min(sorted1$CommDate)+4), label="Target", size=3)+
           xlab("Committee Date")+
           guides(fill=guide_legend(title="Committee Names"), colour=guide_legend(title.theme=element_blank(),title=NULL))+
           scale_x_date(labels = date_format("%b-%y"))+
           theme_light()+
           theme(plot.title=element_text(hjust=0.5, size=12),panel.grid.major.x = (element_blank()), 
                 panel.grid.minor.x = (element_blank()), 
                 axis.title = element_text(size=8), legend.title = element_text(size=10),
                 legend.text = element_text(size=8), legend.box = 'vertical', legend.spacing.y = unit(-2,"mm"))+
           scale_colour_manual(name="",values="#0072B2"))

Now we can proceed to the workaround:现在我们可以继续解决方法:

# Now, the workaround:
# ------------------------------------------------------
p1Names <- unique(sorted1$CommName) # we need to know the "true" legend values
for (i in 1:length(p1$x$data)) { # this goes over all places where legend values are stored
  n1 <- p1$x$data[[i]]$name # and this is how the value is stored in plotly
  n2 <- " "
  for (j in 1:length(p1Names)) {
    if (grepl(x = n1, pattern = p1Names[j])) {n2 = p1Names[j]} # if the plotly legend name contains the original value, replace it with the original value
  }
  p1$x$data[[i]]$name <- n2 # now is the time for actual replacement
  if (n2 == " ") {p1$x$data[[i]]$showlegend = FALSE}  # sometimes plotly adds to the legend values that we don't want, this is how to get rid of them, too
}
p1   # now we can see the result :-)

In case someone is out there still having this problem: check that you aren't setting scale_fill_viridis_d or something similar, with fill when you should be using scale_color_viridis_d (ie check you aes() to see if you are using color of fill or both).如果有人仍然有这个问题:检查你没有设置scale_fill_viridis_d或类似的东西,当你应该使用scale_color_viridis_d时使用fill (即检查你aes()看看你是否使用fill color或两者) .

Another check to make is if the dataset is grouped or not, if it is grouped, you should ungroup() before doing the graph另一个要检查的是数据集是否分组,如果分组,你应该在做图之前ungroup()

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

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