6

I have a problem when transforming a ggplot2 graph to plotly.

Here it is a reproducible code:

library(ggplot2)
library(plotly)

# Build the data

dgraf<-data.frame(num=seq(0,2*pi,length.out=100), 
                  name=sample(apply(expand.grid(letters,letters),1,function(x) paste(x,collapse="\n")),100),stringsAsFactors = F)
dgraf$y<-sin(dgraf$num)
rpoint<-sample(25:75,1)
dgraf$ypoint<-NA
dgraf$ypoint[rpoint]<-dgraf$y[rpoint]
dgraf$ymin<-NA
dgraf$ymin[1:rpoint]<-runif(1,0.25,0.75)
dgraf$ymax<-NA
dgraf$ymax[rpoint:100]<-runif(1,-0.75,-0.25)

# ggplot

labels<-c("Data y","Breaking point","Lower line","Higher line")
shapes<-c(21,21,NA,NA)
colors<-c("grey","white","cyan","green")
fills<-c("black","red","black","black")
sizes<-c(3,4,1,1)
linetypes<-c("solid","blank", "solid", "dashed")

dgrafgg<-reshape2::melt(dgraf,id.var="num", measure.var=c("y", "ypoint", "ymin", "ymax"))

gplot<-ggplot(dgrafgg) +
  geom_line(aes(x=num,y=value,group=variable, color=variable, linetype=variable),size=1.2) +
  geom_point(aes(x=num,y=value,group=variable, color=variable, size=variable, fill=variable, shape=variable), color="white", stroke = 0.1) +
  scale_shape_manual(values=shapes, name="Legend", labels=labels) + 
  scale_color_manual(values=colors, name="Legend", labels=labels) +
  scale_fill_manual(values=fills, name="Legend", labels=labels) +
  scale_size_manual(values=sizes, name="Legend", labels=labels) +
  scale_linetype_manual(values=linetypes, name="Legend", labels=labels) + 
  scale_x_continuous(breaks = dgraf$num[seq(1,100,by=10)], labels = dgraf$name[seq(1,100,by=10)]) +
  labs(title = "Main title", x = "x", y = "y") + 
  ggthemes::theme_few()
gplot

ggplot2 graph

# Plotly

gplotly <- plotly_build(gplot)
gplotly

plotly graph

About the ggplot2 graph:

How to completely remove the border (color="white", stroke = 0.1) from the points?

About the plotly version:

Why legend is modified?

Why x.axis labels are modified?

Why geom_lines are modified and now white points are shown in the graph?

lmo
  • 37,904
  • 9
  • 56
  • 69
Jose Lozano
  • 147
  • 9
  • It would have been reproducible if you had set a seed. Each picture is different now. – Mike Wise Apr 07 '17 at 19:12
  • I think there are too many things interacting here. ggplot2 is relatively old and complex, and plotly is young and buggy. You need to start with something simpler and build up to it. But yeah, some weird stuff happening here. – Mike Wise Apr 07 '17 at 19:23
  • Finally I was forced to create a function to fix the legend in the Plotly object, changing labels and text manually. – Jose Lozano Apr 18 '17 at 20:21
  • Can you post what you tried @lozanoje and how did you fix it – Prasanna Nandakumar Jun 23 '17 at 05:17

3 Answers3

1

To change legend label after plotly_bluid(gplot) you can change gplot information with that ggplo$x$data[[i]]$name and ggplo$x$data[[i]]$legendgroup.

B.Gees
  • 1,125
  • 2
  • 11
  • 28
  • Appreciate this response. Looping through the legend names solved my issue with dynamic lines/points on ggplots. – dmanuge Oct 12 '18 at 23:37
1

As requested, I post here my solution. It is very specific of my particular problem, but probably it could help someone to deal with the structure of a plotly object:

fixplotly<-function(i.plotly,i.labels,i.lines,i.points,i.xname,i.yname,i.weeklabels){

  nlabels<-length(i.labels)
  nlists<-length(i.plotly$x$data)
  if (nlists!=2*nlabels) return(i.plotly)
  # Show all labels
  for (i in 1:nlists) i.plotly$x$data[[i]]$showlegend<-T
  # Fix x.axis labels
  a<-strsplit(as.character(i.plotly$x$layout$xaxis$ticktext),"\\\n")
  a.len <- max(sapply(a, length))
  a.corrected <- lapply(a, function(x) {c(x, rep("", a.len - length(x)))})
  divideit<-matrix(unlist(a.corrected),nrow=length(i.plotly$x$layout$xaxis$ticktext), byrow=T)
  i.plotly$x$layout$margin$b<-(NCOL(divideit))*i.plotly$x$layout$margin$b
  i.plotly$x$layout$xaxis$ticktext<-apply(divideit,1,paste,collapse="<br />")
  # Fix labels names
  sequ<-1:nlists-nlabels*(floor((1:nlists-1)/nlabels))
  for (i in 1:nlists) i.plotly$x$data[[i]]$name<-i.labels[sequ[i]]
  # Fix text to showup
  for (i in 1:nlists){
    if (length(grep(i.yname,i.plotly$x$data[[i]]$text))>0){
      #i.plotly$x$data[[i]]$text
      dividetext<-matrix(unlist(strsplit(i.plotly$x$data[[i]]$text,"<br>|<br />")),nrow=length(i.plotly$x$data[[i]]$text), byrow=T)
      i.plotly$x$data[[i]]$text<-paste("Week: ",i.weeklabels,"<br />",sub(i.yname,i.labels[sequ[i]],dividetext[,2]),sep="")
    }
  }
  # For those with points and labels, i modify the mode and add the marker
  pandl<-i.points & i.lines
  index.pandl<-(1:nlabels)[pandl]
  if (length(index.pandl)>0){
    for (i in 1:length(index.pandl)){
      i.plotly$x$data[[index.pandl[i]]]$mode<-"lines+markers"
      i.plotly$x$data[[index.pandl[i]]]$marker<-i.plotly$x$data[[index.pandl[i]+nlabels]]$marker
    }
  }
  # Remove unnecesary legend entries
  panol<-i.points & !i.lines
  index.panol<-(1:nlabels)[panol]
  nopal<-!i.points & i.lines
  index.nopal<-(1:nlabels)[nopal]
  toremove<-c(index.pandl+nlabels,index.panol,index.nopal+nlabels)
  toremove<-toremove[order(toremove, decreasing = T)]
  # in reverse order, since removing changes order
  for (i in 1:length(toremove)) i.plotly$x$data[[toremove[i]]]<-NULL
  return(i.plotly)
}

gplotly <- ggplotly(gplot)
zfix<-fixplotly(gplotly,labels,c(T,F,T,T),c(T,T,F,F),"num","value",dgraf$name)
zfix

The arguments of the function are: 1 ggplotly output 2 names of the diferent series of the plot 3 does the series have lines? 4 does the series have points? 5 name of xaxis var 6 name of yaxis var 7 labels of x-axis values

Jose Lozano
  • 147
  • 9
0

Thanks, Jose Lozano, for sharing your concepts and code.

I had a similar problem where ggplotly was duplicating the legend entries when the ggplot object was facetted.

Here's a thread with multiple solutions, including my solution that uses tidyverse.

Avoid legend duplication in plotly conversion from ggplot with facet_wrap

James N
  • 315
  • 2
  • 9