问题
I'm trying to turn a ggplot into a plotly. The ggplot renders fine, but when I put it through ggplotly, suddenly the legend adds parenthesis and ",1" after the label.
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.)
Here's what I get from this:
Why does the legend show up as "(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?
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. But I really would like to have control over the geom_smooth
's aesthetics, and include it in the legend. So I'm making progress, but still not quite there...
回答1:
Since nothing I did worked, I took the dive and taught myself how to create the chart directly in 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):
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)))
回答2:
Override the legend names using:
scale_color_manual(labels = c("Foo", "Bar"))
Or blank ""
if you're trying to remove them - looks like you may have tried using name =
回答3:
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 :-)
回答4:
Here's an approach that worked for me. It involves diving into the resulting plotly object and cleaning up the legend names.
The first part creates a function to detect if the plotly list element has a legend specified and then cleans it up.
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
来源:https://stackoverflow.com/questions/49133395/strange-formatting-of-legend-in-ggplotly-in-r