R Plotly: Preserving Appearance of Two Legends When Converting Ggplot2 with Ggplotly

R plotly: preserving appearance of TWO legends when converting ggplot2 with ggplotly

The second legend gets lost in during the conversion (or at least I couldn't find in the data). You can look at the result of ggplotly and modify everything from the raw data to the layout, e.g. gp[['x']][['layout']] would contain all the layout variables passed from ggplotly.


Sample Image

A lot more lines of code but you have full control over all aspects of your graph.

library(plotly)
df <- data.frame(cat1 = rep(c("a","b","c"), 3),
cat2 = c(rep("A", 3),
rep("B", 3),
rep("C", 3)),
var1 = 1:9,
var2 = 10:18)

size_multi <- 2 #multiplies your size to avoid pixel sized objects
color_scale <- list(c(0, "#000000"), list(1, "#00BFFF"))
p <- plot_ly(df,
type='scatter',
mode='markers',
x = ~cat1,
y = ~cat2,
marker = list(color = ~var2,
size=~var1 * size_multi,
colorscale = color_scale,
colorbar = list(len = 0.8, y = 0.3),
line = list(color = ~var2,
colorscale = color_scale,
width = 2)
),
showlegend = F)

#adds some dummy traces for the punch card markers
markers = c(min(df$var1), mean(df$var1), max(df$var1))
for (i in 1:3) {
p <- add_trace(p,
df,
type = 'scatter',
mode = 'markers',
showlegend = T,
name = markers[[i]],
x = 'x',
y = 'x',
marker = list(size = markers[[i]] * size_multi,
color='rgba(255,255,255,0)',
showscale = F,
line = list(color = 'rgba(0,0,0,1)',
width = 2))
)
}

#fix the coordinate system
spacer <- 0.2
p <- layout(p, xaxis=list(range=c(-spacer, length(levels(df$cat1)) - 1 + spacer)), yaxis=list(range=c(-spacer, length(levels(df$cat1)) - 1 + spacer)))
p

Duplicated legends when faceting in ggplotly

UPDATE

Issues appear fixed with Plotly 3.6.0 -- 16 May 2016
Sample Image

Due to the ggplotly bug for geom_bar, which distorts the data for the bars, there may not be a good way to do this. For this particular case, facet is not needed. You can use plot_ly() to build an effective plot.

Plot_ly

require(plotly)
require(dplyr)

d <- diamonds[diamonds$cut %in% c("Fair", "Good"),] %>%
count(cut, color)

plot_ly(d, x = color, y = n, type = "bar", group = cut)

Sample Image

Use Plotly subplot()

If this plot type is a must, you can build a facet-like plot using Plotly's subplot. It's not pretty.

d2 <- diamonds[diamonds$cut %in% c("Fair", "Good"),] %>%
count(cut, color) %>%
transform(color = factor(color, levels=rev(levels(color)))) %>%
mutate(id = as.integer(color))

p <- plot_ly(d2, x = cut, y = n, type = "bar", group = color, xaxis = paste0("x", id), marker = list(color = c("#0000FF","#FF0000"))) %>%
layout(yaxis = list(range = range(n), linewidth = 0, showticklabels = F, showgrid = T, title = ""),
xaxis = list(title = ""))

subplot(p) %>%
layout(showlegend = F,
margin = list(r = 100),
yaxis = list(showticklabels = T),
annotations = list(list(text = "Fair", showarrow = F, x = 1.1, y = 1, xref = "paper", yref = "paper"),
list(text = "Good", showarrow = F, x = 1.1, y = 0.96, xref = "paper", yref = "paper")),
shapes = list(list(type = "rect", x0 = 1.1, x1 = 1.13, y0 = 1, y1 = 0.97, line = list(width = 0), fillcolor = "#0000FF", xref = "paper", yref = "paper"),
list(type = "rect", x0 = 1.1, x1 = 1.13, y0 = 0.96, y1 = 0.93, line = list(width = 0), fillcolor = "#FF0000", xref = "paper", yref = "paper")))

Sample Image

R plotly: How to keep width parameter of ggplot2 geom_bar() when using ggplotly()

Any idea hot to tell ggplotly to not stick my bars together, like the
default ggplot2 behavior?

ggplotly sets bargap to 0, you could set to your desired value via layout

ggplotly(p) %>% layout(bargap=0.15)

Sample Image


It also reverses ggplot2s default colors order for some reason
(although keeping the order in the legend...), but I can handle this.

Let's get that fixed as well. You can change the order afterwards by flipping the bars and reversing the legend.

gp <- ggplotly(p) %>% layout(bargap = 0.15, legend = list(traceorder = 'reversed'))

traces <- length(gp[['x']][[1]])
for (i in 1:floor(traces / 2)) {
temp <- gp[['x']][[1]][[i]]
gp[['x']][[1]][[i]] <- gp[['x']][[1]][[traces + 1 - i]]
gp[['x']][[1]][[traces + 1 - i]] <- temp
}

Sample Image

ggplotly() ignores legend and produce different plot with ggplot legend

Try to keep data cleaning/preparation separate from plotting, see cleaned data and plot, now the ggplot and plotly look the same:

library(tidyverse)
library(plotly)

# prepare the data
plotData <- data_a %>%
select(indexlist, datainput, verification) %>%
# remove non-numeric rows before converting
filter(!grepl("^[^0-9.]+$", datainput)) %>%
# prepare data for plotting
mutate(datainput = as.numeric(datainput),
x = seq(n()),
Verification = factor(ifelse(verification == "Yes", "Verified", "Non-Verified"),
levels = c("Verified", "Non-Verified")),
label = ifelse(datainput > quantile(datainput, 0.975, na.rm = TRUE),
indexlist, ""))

# then plot with clean data
p <- ggplot(plotData, aes(x = x, y = datainput,
color = Verification, label = label)) +
scale_colour_manual(values = c("green", "red"))+
geom_point(size = 1.5, alpha = 0.4) +
geom_text(vjust = "inward", hjust = "inward", size = 2, color = "grey50") +
theme_minimal() +
labs(title = "datainput Details", x = "", y = "") +
theme(axis.text.x = element_text(size = 5.5),
axis.text.y = element_text(size = 5.5),
plot.title = element_text(color = "grey40", size = 9, face = "bold"))

# now plotly
ggplotly(p)

ggplot

Sample Image

plotly

Sample Image

R: How to modify legend in ggplotly?

Ordering the origin column by the percentages is straightforward. This is done at the data level, by converting origin to a factor whose levels are determined by the value of Percentage:

df$origin <- factor(df$origin, df$origin[order(df$Percentage)])

The reason that strange things were happening with your customized legend is that you added a layer before some of your existing layers, which throws off the indexing you are using to modify the legend groups at the end. The easiest fix for this is to draw the line after your existing layers:

plt <- ggplot(df, aes(x = rate, y = factor(origin, rev(origin)))) +
geom_segment(aes(x = (min(rate,change)-4), xend = (max(rate,change)+4),
y = origin, yend = origin), color = 'gray') +
geom_vline(xintercept = 0, linetype = 2, color = 'gray') +
geom_point(aes(fill = 'rate'), shape = 21, size = 10, color = NA) +
geom_text(aes(label = rate, color = 'rate')) +
geom_point(aes(x = change, fill = 'change'),
color = NA, shape = 21, size = 10) +
geom_text(aes(label = change, x = change, color = "change")) +
geom_point(aes(x = (max(rate,change)+5.5), fill = "Percentage"),
color = "gray", size = 10, shape = 21) +
geom_text(aes(x = (max(rate,change)+5.5), label = paste0(Percentage, "%")),
size = 3)+
geom_vline(xintercept =17, linetype = 1, color = 'black') +
theme_minimal(base_size = 16) +
scale_x_continuous(labels = ~paste0(.x, '%'), name = NULL) +
scale_fill_manual(values = c('#aac7c4', '#5f9299','black')) +
scale_color_manual(values = c("black", "white")) +
theme(panel.grid = element_blank(),
axis.text.y = element_text(color = 'gray50')) +
labs(color = NULL, y = NULL, fill = NULL)+
theme(axis.title = element_text(size=15), legend.title = element_text(size=2))

plt <- ggplotly(plt)

Now you can customize the legend groups exactly as before:

#customize legend
plt$x$data[[3]]$name <- plt$x$data[[3]]$legendgroup <-
plt$x$data[[4]]$name <- plt$x$data[[4]]$legendgroup <- "rate"
plt$x$data[[5]]$name <- plt$x$data[[5]]$legendgroup <-
plt$x$data[[6]]$name <- plt$x$data[[6]]$legendgroup <- "change"
plt$x$data[[7]]$name <- plt$x$data[[7]]$legendgroup <-
plt$x$data[[8]]$name <- plt$x$data[[8]]$legendgroup <- "Percentage"

plt

Sample Image

If you want the line to be behind all the points and text, then keep your existing plotting code as it was, and increment all the indices in your legend grouping code:

#customize legend
plt$x$data[[4]]$name <- plt$x$data[[4]]$legendgroup <-
plt$x$data[[5]]$name <- plt$x$data[[5]]$legendgroup <- "rate"
plt$x$data[[6]]$name <- plt$x$data[[6]]$legendgroup <-
plt$x$data[[7]]$name <- plt$x$data[[7]]$legendgroup <- "change"
plt$x$data[[8]]$name <- plt$x$data[[8]]$legendgroup <-
plt$x$data[[9]]$name <- plt$x$data[[9]]$legendgroup <- "Percentage"


Related Topics



Leave a reply



Submit