R中ggplotly中图例的奇怪格式
Strange formatting of legend in ggplotly in R
我正在尝试将 ggplot 变成 plotly。 ggplot 渲染得很好,但是当我通过 ggplotly 进行处理时,图例突然在标签后添加了括号和“,1”。
这是一个虚假数据示例:
sorted1<-data.frame(CommDate=c(as.Date("2017-09-12"), as.Date("2017-10-15")), CommName=c("Foo", "Bar"), PubB4=c(2,3))
这是我尝试 运行 的代码:
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"))
(geom_smooth
不会在此处呈现,但会呈现完整数据。)
这是我从中得到的:
为什么图例显示为“(foo,1)”?
我尝试删除实际上解决了问题的 geom_smooth
,但我需要它 - 如何保留它但修复图例?
谢谢!
更新: 好的,我开始注释掉一些东西看看会发生什么。如果我从 geom_smooth
中删除 aes()
,这也可以解决问题,只要我也将 scale_colour_manual
注释掉即可。但我真的很想控制 geom_smooth
的美学,并将其包含在图例中。所以我正在取得进步,但还不够...
因为我没有做任何工作,所以我开始潜水并自学如何直接在 plotly
中创建图表。为了未来可能的观众的利益,以下是在 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)))
这是一个实际有效的解决方法:
# 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, 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 :-)
这是一种对我有用的方法。它涉及深入研究生成的 plotly 对象并清理图例名称。
第一部分创建一个函数来检测 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
}
第二部分将其应用于您的示例,但有一个中间步骤。
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
我欢迎任何使此代码更高效的建议,但至少它有效
这是另一个优雅的解决方案。在引擎盖下,它检测是否有 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
如果有人仍然遇到这个问题:检查你是否没有设置 scale_fill_viridis_d
或类似的东西,当你应该使用 scale_color_viridis_d
时使用 fill
(即检查你 aes()
看看你是否使用 color
或 fill
或两者。
另一个要检查的是数据集是否分组,如果分组,你应该在做图之前ungroup()
我正在尝试将 ggplot 变成 plotly。 ggplot 渲染得很好,但是当我通过 ggplotly 进行处理时,图例突然在标签后添加了括号和“,1”。
这是一个虚假数据示例:
sorted1<-data.frame(CommDate=c(as.Date("2017-09-12"), as.Date("2017-10-15")), CommName=c("Foo", "Bar"), PubB4=c(2,3))
这是我尝试 运行 的代码:
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"))
(geom_smooth
不会在此处呈现,但会呈现完整数据。)
这是我从中得到的:
为什么图例显示为“(foo,1)”?
我尝试删除实际上解决了问题的 geom_smooth
,但我需要它 - 如何保留它但修复图例?
谢谢!
更新: 好的,我开始注释掉一些东西看看会发生什么。如果我从 geom_smooth
中删除 aes()
,这也可以解决问题,只要我也将 scale_colour_manual
注释掉即可。但我真的很想控制 geom_smooth
的美学,并将其包含在图例中。所以我正在取得进步,但还不够...
因为我没有做任何工作,所以我开始潜水并自学如何直接在 plotly
中创建图表。为了未来可能的观众的利益,以下是在 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)))
这是一个实际有效的解决方法:
# 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, 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 :-)
这是一种对我有用的方法。它涉及深入研究生成的 plotly 对象并清理图例名称。
第一部分创建一个函数来检测 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
}
第二部分将其应用于您的示例,但有一个中间步骤。
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
我欢迎任何使此代码更高效的建议,但至少它有效
这是另一个优雅的解决方案。在引擎盖下,它检测是否有 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
如果有人仍然遇到这个问题:检查你是否没有设置 scale_fill_viridis_d
或类似的东西,当你应该使用 scale_color_viridis_d
时使用 fill
(即检查你 aes()
看看你是否使用 color
或 fill
或两者。
另一个要检查的是数据集是否分组,如果分组,你应该在做图之前ungroup()