Second x axis in ggplotly with invisible second trace - r

I am trying to add a second x axis on a ggplotly plot, not to accommodate a second trace, but for better visualisation.
I have worked out that I do need to add a trace for it, but the question is how. The examples I have found to add simple, transparent traces are not working for my plot which has factors on the y-axis.
Please take it as given that for my purposes I need to use ggplotly and need the second axis. The example I am about to provide is just minimal, the real application has other requirements accommodated by ggplotly (as opposed to straight plotly or ggplot2). Imagine if there were 100 different iris species that people were scrolling through, and that the top axis provides a good guide at first. Using ggplot2, here is the example of what I would like to achieve with ggplotly:
library(tidyverse)
library(plotly)
dat <- iris %>%
group_by(Species) %>%
summarise(meanSL = mean(Sepal.Length, na.rm = TRUE),
count = n())
labels_dup = c("low", "medium", "high")
labels = c("low", "medium\n\nmeans to the right\nof this line are\nso cool", "high")
breaks = c(5,6,7)
limits = c(4,8)
p <- ggplot(dat, aes(x = reorder(as.character(Species),meanSL), y = meanSL)) +
geom_point() +
geom_hline(yintercept = 6, lty = 2) +
coord_flip() +
ggtitle("Means of sepal length by species") +
theme_classic()+
theme(axis.title.x=element_blank(),
axis.title.y=element_blank(),
axis.line.y = element_blank(),
plot.title = element_text(size = 10, hjust = 0.5))
p + scale_y_continuous(breaks = breaks, labels = labels, limits = limits, sec.axis = dup_axis(labels = labels_dup)) +
geom_text(aes(y = 4,label = paste0("n=",count)), size = 3)
and here is the output:
Here is a start to the ggplotly solution:
ax <- list(
side = "bottom",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels)
ax2 <- list(
overlaying = "x",
side = "top",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels_dup)
ggplotly(p) %>%
#<need a trace here e.g. add_lines, add_segment. It could either be transparent, or use the vertical line or count text in the plot as shown in the example> %>%
layout(
xaxis = ax,
xaxis2 = ax2)
Edit: Here is less minimal code that produces the warning when I use the suggested fix. I use geom_pointrange instead of stat_summary for reasons related to the hover text:
library(boot)
library(tidyverse)
library(plotly)
boot_sd <- function(x, fun=mean, R=1001) {
fun <- match.fun(fun)
bfoo <- function(data, idx) {
fun(data[idx])
}
b <- boot(x, bfoo, R=R)
sd(b$t)
}
#Summarise the data for use with geom_pointrange and add some hover text for use with plotly:
dat <- iris %>%
mutate(flower_colour = c(rep(c("blue", "purple"), 25), rep(c("blue", "white"), 25), rep(c("white", "purple"), 25))) %>%
group_by(Species) %>%
summarise(meanSL = mean(Sepal.Length, na.rm = TRUE),
countSL = n(),
meSL = qt(0.975, countSL-1) * boot_sd(Sepal.Length, mean, 1001),
lowerCI_SL = meanSL - meSL,
upperCI_SL = meanSL + meSL,
group = "Mean &\nConfidence Interval",
colours_in_species = paste0(sort(unique(flower_colour)), collapse = ",")) %>%
as.data.frame() %>%
mutate(colours_in_species = paste0("colours: ", colours_in_species))
#Some plotting variables
purple <- "#8f11e7"
plot_title_colour <- "#35373b"
axis_text_colour <- "#3c4042"
legend_text_colour <- "#3c4042"
annotation_colour <- "#3c4042"
labels_dup = c("low", "medium", "high")
labels = c("low", "medium\n\nmeans to the right\nof this line are\nso cool", "high")
breaks = c(5,6,7)
limits = c(4,8)
p <- ggplot(dat, aes(x = reorder(as.character(Species),meanSL), text = colours_in_species)) +
geom_text(aes(y = 4.2,label = paste0("n=",countSL)), color = annotation_colour, size = 3) +
geom_pointrange(aes(y = meanSL, ymin=lowerCI_SL, ymax=upperCI_SL,color = group, fill = group), size = 1) +
scale_fill_manual(values = "#f4a01f", name = "Mean &\nConfidence Interval") +
scale_color_manual(values = "#f4a01f", name = "Mean &\nConfidence Interval") +
geom_hline(yintercept = 5, colour = "dark grey", linetype = "dashed") +
geom_hline(yintercept = 6, colour = purple, linetype = "dashed") +
coord_flip() +
ggtitle("Means of sepal length by species") +
theme_classic()+
theme(axis.text.y=element_text(size=10, colour = axis_text_colour),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
axis.line.y = element_blank(),
axis.ticks.y = element_blank(),
plot.title = element_text(size = 12, hjust = 0, colour = plot_title_colour),
legend.justification=c("right", "top"),
legend.box.just = "center",
legend.position ="top",
legend.title.align = "left",
legend.text=element_text(size = 8, hjust = 0.5, colour = legend_text_colour),
legend.title=element_blank())
ax <- list(
side = "top",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels_dup)
ay <- list(
side = "right")
ax2 <- list(
overlaying = "x",
side = "bottom",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels_dup,
tickfont = list(size = 11))
ggplotly(p, tooltip = 'text') %>%
add_markers(data = NULL, inherit = TRUE, xaxis = "x2") %>%
layout(
xaxis = ax,
xaxis2 = ax2,
yaxis = ay,
legend = list(orientation = "v", itemclick = FALSE, x = 1.2, y = 1.04),
margin = list(t = 120, l = 60)
)
and the warning is this:
Warning message:
'scatter' objects don't have these attributes: 'label'
Valid attributes include:
'type', 'visible', 'showlegend', 'legendgroup', 'opacity', 'name', 'uid', 'ids', 'customdata', 'meta', 'selectedpoints', 'hoverinfo', 'hoverlabel', 'stream', 'transforms', 'uirevision', 'x', 'x0', 'dx', 'y', 'y0', 'dy', 'stackgroup', 'orientation', 'groupnorm', 'stackgaps', 'text', 'texttemplate', 'hovertext', 'mode', 'hoveron', 'hovertemplate', 'line', 'connectgaps', 'cliponaxis', 'fill', 'fillcolor', 'marker', 'selected', 'unselected', 'textposition', 'textfont', 'r', 't', 'error_x', 'error_y', 'xcalendar', 'ycalendar', 'xaxis', 'yaxis', 'idssrc', 'customdatasrc', 'metasrc', 'hoverinfosrc', 'xsrc', 'ysrc', 'textsrc', 'texttemplatesrc', 'hovertextsrc', 'hovertemplatesrc', 'textpositionsrc', 'rsrc', 'tsrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

I get it working by just adding:
add_markers(data = NULL, inherit = TRUE, xaxis = "x2")
And I did also set the tickfont size of your second axis to 11 to match the font size of your original axis.
Although it is working, sometimes changing the zoom (especially when clicking "autoscale") will mess up the scales of the x axes so that they are not in sync anymore. Probably the best option is to limit the available options in the icon bar.
Here is your edited code put into a running shiny app:
library(tidyverse)
library(plotly)
library(shiny)
dat <- iris %>%
group_by(Species) %>%
summarise(meanSL = mean(Sepal.Length, na.rm = TRUE),
count = n())
labels_dup = c("low", "medium", "high")
labels = c("low", "medium\n\nmeans to the right\nof this line are\nso cool", "high")
breaks = c(5,6,7)
limits = c(4,8)
p <- ggplot(dat, aes(x = reorder(as.character(Species),meanSL), y = meanSL)) +
geom_point() +
geom_hline(yintercept = 6, lty = 2) +
coord_flip() +
ggtitle("Means of sepal length by species") +
theme_classic() +
theme(axis.title.x=element_blank(),
axis.title.y=element_blank(),
axis.line.y = element_blank(),
plot.title = element_text(size = 10, hjust = 0.5))
p + scale_y_continuous(breaks = breaks, labels = labels, limits = limits, sec.axis = dup_axis(labels = labels_dup)) +
geom_text(aes(y = 4,label = paste0("n=",count)), size = 3)
ax <- list(
side = "bottom",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels)
ax2 <- list(
overlaying = "x",
side = "top",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels_dup,
tickfont = list(size = 11)) # I added this line
shinyApp(
ui = fluidPage(
plotlyOutput("plot")
),
server = function(input, output) {
output$plot <- renderPlotly({
ggplotly(p) %>%
add_markers(data = NULL, inherit = TRUE, xaxis = "x2") %>% # new line
layout(
xaxis = ax,
xaxis2 = ax2)
})
}
)
Update
Below is a running shiny app with the additional example code. Although it is showing a warning that
Warning: 'scatter' objects don't have these attributes: 'label'
the plot is displayed correctly with both x axes.
I assume that the plot not showing correctly is unrelated to the warning above.
library(boot)
library(tidyverse)
library(plotly)
library(shiny)
boot_sd <- function(x, fun=mean, R=1001) {
fun <- match.fun(fun)
bfoo <- function(data, idx) {
fun(data[idx])
}
b <- boot(x, bfoo, R=R)
sd(b$t)
}
#Summarise the data for use with geom_pointrange and add some hover text for use with plotly:
dat <- iris %>%
mutate(flower_colour = c(rep(c("blue", "purple"), 25), rep(c("blue", "white"), 25), rep(c("white", "purple"), 25))) %>%
group_by(Species) %>%
summarise(meanSL = mean(Sepal.Length, na.rm = TRUE),
countSL = n(),
meSL = qt(0.975, countSL-1) * boot_sd(Sepal.Length, mean, 1001),
lowerCI_SL = meanSL - meSL,
upperCI_SL = meanSL + meSL,
group = "Mean &\nConfidence Interval",
colours_in_species = paste0(sort(unique(flower_colour)), collapse = ",")) %>%
as.data.frame() %>%
mutate(colours_in_species = paste0("colours: ", colours_in_species))
#Some plotting variables
purple <- "#8f11e7"
plot_title_colour <- "#35373b"
axis_text_colour <- "#3c4042"
legend_text_colour <- "#3c4042"
annotation_colour <- "#3c4042"
labels_dup = c("low", "medium", "high")
labels = c("low", "medium\n\nmeans to the right\nof this line are\nso cool", "high")
breaks = c(5,6,7)
limits = c(4,8)
p <- ggplot(dat, aes(x = reorder(as.character(Species),meanSL), text = colours_in_species)) +
geom_text(aes(y = 4.2,label = paste0("n=",countSL)), color = annotation_colour, size = 3) +
geom_pointrange(aes(y = meanSL, ymin=lowerCI_SL, ymax=upperCI_SL,color = group, fill = group), size = 1) +
scale_fill_manual(values = "#f4a01f", name = "Mean &\nConfidence Interval") +
scale_color_manual(values = "#f4a01f", name = "Mean &\nConfidence Interval") +
geom_hline(yintercept = 5, colour = "dark grey", linetype = "dashed") +
geom_hline(yintercept = 6, colour = purple, linetype = "dashed") +
coord_flip() +
ggtitle("Means of sepal length by species") +
theme_classic()+
theme(axis.text.y=element_text(size=10, colour = axis_text_colour),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
axis.line.y = element_blank(),
axis.ticks.y = element_blank(),
plot.title = element_text(size = 12, hjust = 0, colour = plot_title_colour),
legend.justification=c("right", "top"),
legend.box.just = "center",
legend.position ="top",
legend.title.align = "left",
legend.text=element_text(size = 8, hjust = 0.5, colour = legend_text_colour),
legend.title=element_blank())
ax <- list(
side = "top",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels_dup)
ay <- list(
side = "right")
ax2 <- list(
overlaying = "x",
side = "bottom",
showticklabels = TRUE,
range = limits,
tickmode = "array",
tickvals = breaks,
ticktext = labels_dup,
tickfont = list(size = 11))
shinyApp(
ui = fluidPage(
plotlyOutput("plot")
),
server = function(input, output) {
output$plot <- renderPlotly({
ggplotly(p, tooltip = 'text') %>%
add_markers(data = NULL, inherit = TRUE, xaxis = "x2") %>%
layout(
xaxis = ax,
xaxis2 = ax2,
yaxis = ay,
legend = list(orientation = "v", itemclick = FALSE, x = 1.2, y = 1.04),
margin = list(t = 120, l = 60)
)
})
}
)

Related

In RStudio, when using flexdashboard, shiny and plotly for a visualization, how can I use tryCatch?

I have some visualizations plotted in a flexdashboard, but, sometimes when I choose a parameter (for instance "customer group") and it's empty (no records), the visualization will throw an error. I need something like the trycatch() function in order to show a message when the code returns an error, or at least something similar.
Following is the piece of code that corresponds to the graph that throws the error, where should my trycatch() go and with what other parameters?
### {data-width=498}
```{r}
## Serie de tiempo
timeseries88 <- reactive(selected_80() %>%
dplyr::group_by(Year, FECHAFACTURA, dia_año)%>%
dplyr::summarise(VALORNETO = sum(VALORNETO)) %>%
mutate(text = paste("Año: ", Year,
"\nVenta Neta: $", round(VALORNETO/1000000, 4), " M",
"\nFecha: ", format(FECHAFACTURA, format = "%d/%m/%Y"), sep = "") ,
fechames = format(FECHAFACTURA, "%d-%m"))%>%
arrange(FECHAFACTURA)
)
renderPlotly({
ggplotly(ggplot(timeseries88(), aes(x = FECHAFACTURA,
y = VALORNETO,
group = Year,
text = text))+
geom_point(size = 0.75, color = color_periodo_unico)+
geom_line(size = 0.3, color = color_periodo_unico)+
labs(
x = "", y = "")+
theme(
axis.text.x = element_text(angle = 60, hjust = 1),
legend.text = element_text(size = 8.5),
text = element_text(size = 8.5),
axis.ticks.x = element_blank(),
panel.background = element_blank() ) +
scale_y_continuous(labels = unit_format(unit = "M", scale = 1e-6)),
tooltip = "text") %>%
layout(xaxis = list(autorange = TRUE),
yaxis = list(autorange = TRUE),
showlegend = F,
title = list(text = 'Venta de Período', x = 0.05)
) %>% config(displayModeBar = F)
})
```

How to pass column name as an arguments in the function

I am trying to convert ggplot code which is repetitive into function; I have done almost but only concern is I need to pass the application as a variable.
Here is the plot code where I used column called Application inside the summarise and mutate. The challenge is same column has been used for y axis
ggplot_common_function <- function(data,x,y,z) {
Removestring(ggplotly(
data %>%
group_by(m_year,status) %>%
summarise(Applications = sum(Applications)) %>%
mutate(total_sum = sum(Applications)) %>%
ggplot(mapping = aes({{x}},{{y}},text = paste(total_sum))) +
geom_col(aes(fill = {{z}})) +
theme_classic() +
theme(axis.line.y = element_blank(),
axis.ticks = element_blank(),
legend.position = "bottom") +
labs(x = "", y = "Agreements Values (In Lakhs)", fill = "") +
theme(axis.title.y = element_text(size = 8)) +
scale_fill_manual(values = c("#1F7A3F", "#70B821")) +
scale_y_continuous(labels = function(x) format(x, scientific = FALSE),
expand = expansion(mult = c(0,.3)),
breaks = integer_breaks()),
tooltip = c("text")) %>%
layout(legend = list(orientation = "h", x = 0.1, y = -0.2,
font = list( family = 'Arial', size = 10, color = 'black')),
xaxis = x_labels, yaxis = y_labels) %>%
config(displaylogo = FALSE, modeBarButtonsToRemove = list(
'sendDataToCloud', 'autoScale2d', 'resetScale2d',
'toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian',
'zoom2d', 'pan2d', 'select2d',
'lasso2d', 'zoomIn2d', 'zoomOut2d'))
)
}
ggplot_common_function(data,m_year,Applications,status)
To run the above code there is some pre function
integer_breaks <- function(n = 5, ...) {
fxn <- function(x) {
breaks <- floor(pretty(x, n, ...))
names(breaks) <- attr(breaks, "labels")
breaks
}
return(fxn)
}
Removestring = function(d){
for (i in 1:length(d$x$data)){
if (!is.null(d$x$data[[i]]$name)){
d$x$data[[i]]$name = gsub("\\(","",str_split(d$x$data[[i]]$name,",")[[1]][1])
}
}
return(d)
}
Plan <- "#288D55"
multicolor = c("#135391","#0098DB","#828388","#231F20","#C41330","#698714","#162FBF","#F36717","#BD00FF")
x_labels = list(tickangle = -45,tickfont = list(family = "Arial",size = 10,color = "black",face="bold"))
y_labels = list(tickfont = list(family = "Arial",size = 10,color = "black",face="bold"))
Any suggestions would be appreciated
This is basically a question related to programming in dplyr. To achieve your desired result and get rid of hardcoding the column names in your function and use x, y, z instead you could make use of the {{ curly-curly operator as you did in the ggplot code and the special assignment operator :=. Additionally instead of wrapping all your code inside ggplotly you proceed in steps. Do the data wrangling, make your ggplot and finally pass it to ggplotly:
library(plotly)
library(dplyr)
library(stringr)
ggplot_common_function <- function(data, x, y, z) {
data <- data %>%
group_by({{ x }}, {{ z }}) %>%
summarise({{ y }} := sum({{ y }})) %>%
mutate(total_sum = sum({{ y }}))
p <- ggplot(data, mapping = aes({{ x }}, {{ y }}, text = paste(total_sum))) +
geom_col(aes(fill = {{ z }})) +
theme_classic() +
theme(axis.line.y = element_blank(), axis.ticks = element_blank(), legend.position = "bottom") +
labs(x = "", y = "Agreements Values (In Lakhs)", fill = "") +
theme(axis.title.y = element_text(size = 8)) +
scale_fill_manual(values = c("#1F7A3F", "#70B821")) +
scale_y_continuous(labels = function(x) format(x, scientific = FALSE), expand = expansion(mult = c(0, .3)), breaks = integer_breaks())
ggp <- ggplotly(p, tooltip = c("text")) %>%
layout(legend = list(orientation = "h", x = 0.1, y = -0.2, font = list(family = "Arial", size = 10, color = "black")), xaxis = x_labels, yaxis = y_labels) %>%
config(displaylogo = FALSE, modeBarButtonsToRemove = list("sendDataToCloud", "autoScale2d", "resetScale2d", "toggleSpikelines", "hoverClosestCartesian", "hoverCompareCartesian", "zoom2d", "pan2d", "select2d", "lasso2d", "zoomIn2d", "zoomOut2d"))
Removestring(ggp)
}
ggplot_common_function(data, m_year, Applications, status)
#> `summarise()` has grouped output by 'm_year'. You can override using the
#> `.groups` argument.

Letters appearing outside plots when using multiple facet plotting with ggplot and ggplotly

I want to create a facet plot using both ggplot and plotly (ggplotly to be precise). Almost everything works fine. The following code :
library(dplyr)
library(plotly)
library(ggplot2)
Year <- c(2000:2008)
Name <- c('A', 'B')
Size <- rep(c('Small', 'Medium', 'Large'), each=6)
City <- c('NY', 'PARIS', 'BERLIN')
Frequency <- sample(x = c(100:1000), size = 144)
Rel_Freq <- sample(x = c(1:100), size = 144, replace = TRUE)
StackData <- data.frame(Year, Name, Size, City, Frequency, Rel_Freq)
StackData$Size <- factor(StackData$Size, levels = c("Small", "Medium", "Large"))
ggplotly(ggplot(StackData, aes(x= Year, y= Frequency, shape = Name, col = Name)) +
geom_point(size = 3)+
scale_shape_manual(values= c(17, 6))+
scale_color_manual(values = c("#37D9E1", "#3D3D3F")) +
facet_grid(City ~ Size, scales="free_y")+
theme_bw()+
theme(legend.position = "bottom",
panel.background = element_rect(fill = "transparent"),
axis.text.x = element_text(angle = 30, hjust = 1),
strip.text.x = element_text( size = 12, face = "bold" ),
strip.text.y = element_text( size = 12, face = "bold" ))+
scale_fill_manual(values = c("#D3D3D3", "#A9A9A9", "#696969"), guide=FALSE)+
scale_y_continuous(trans = "log10",
labels = scales::unit_format(
unit = "",
scale = 1))+
labs(y= "",
x= ""),
tooltip = c("x","y","colour"),
autosize = T, width = 680, height = 530) %>%
layout(showlegend = FALSE,
margin = list(l = 0, r = 25, t = 50, b = 130),
annotations = list(x = .5, y = -0.25, #position of text adjust as needed
text = "Super cool Plot",
showarrow = F,
xref='paper',
yref='paper',
xanchor='auto',
yanchor='bottom',
xshift=0,
yshift=0,
font=list(size=9, color="black")))
Results in this
Like shown in the image, there is a letter showing up in the upper right corner. After some changes, I realized it is the first letter of the variable to which I redirect the color and the shapes in ggplot (in this case 'name').
How can I do get the same plot without this letter appearing there? And perhaps more interesting, why is this occuring?
Thanks in advance,
That weird "N" is coming from the legend portion of your theme in ggplot:
theme(legend.position = "bottom")
In fact, this is quite a thorny problem. ggplotly actually does not transfer everything form ggplot correctly. There is a github issue on this topic, but I beleive that the problem persists.
See:
(legend.position always 'right' in ggplotly, except when legend.position = 'none'
) https://github.com/ropensci/plotly/issues/1049
In your case, the legend.position = "bottom" argument is being ignored by ggplotly.
Option 1:
It looks like you may not actually want the legend in the chart. In that case, you might be better off synchronizing the legend calls across ggplot and ggplotly:
# ggplot portion
theme(legend.position = "none")
# plotly portion:
layout(showlegend = FALSE)
Option 2:
Format the legend only in plotly. From the github issue link above, this was one of the suggested ideas:
ggplotly(
ggplot(df, aes(year, freq, color = clas)) +
geom_line() +
theme(legend.position = 'top')
) %>%
layout(legend = list(
orientation = "h"
)
)
I modified your code using option 1 and came up with the below. The weird "N" is now gone!
library(dplyr)
library(plotly)
library(ggplot2)
Year <- c(2000:2008)
Name <- c('A', 'B')
Size <- rep(c('Small', 'Medium', 'Large'), each=6)
City <- c('NY', 'PARIS', 'BERLIN')
Frequency <- sample(x = c(100:1000), size = 144)
Rel_Freq <- sample(x = c(1:100), size = 144, replace = TRUE)
StackData <- data.frame(Year, Name, Size, City, Frequency, Rel_Freq)
StackData$Size <- factor(StackData$Size, levels = c("Small", "Medium", "Large"))
StackData
ggplotly(ggplot(StackData, aes(x= Year, y= Frequency, shape = Name, col = Name)) +
geom_point(size = 3)+
scale_shape_manual(values= c(17, 6))+
scale_color_manual(values = c("#37D9E1", "#3D3D3F")) +
facet_grid(City ~ Size, scales="free_y")+
theme_bw()+
theme(legend.position = "none", ## this is the only change to your code
panel.background = element_rect(fill = "transparent"),
axis.text.x = element_text(angle = 30, hjust = 1),
strip.text.x = element_text( size = 12, face = "bold" ),
strip.text.y = element_text( size = 12, face = "bold" ))+
scale_fill_manual(values = c("#D3D3D3", "#A9A9A9", "#696969"), guide=FALSE)+
scale_y_continuous(trans = "log10",
labels = scales::unit_format(
unit = "",
scale = 1))+
labs(y= "",
x= ""),
tooltip = c("x","y","colour"),
autosize = T, width = 680, height = 530) %>%
layout(showlegend = FALSE,
margin = list(l = 0, r = 25, t = 50, b = 130),
annotations = list(x = .5, y = -0.25, #position of text adjust as needed
text = "Super cool Plot",
showarrow = F,
xref='paper',
yref='paper',
xanchor='auto',
yanchor='bottom',
xshift=0,
yshift=0,
font=list(size=9, color="black")))

Pie Chart labels overlapping

No matter what I do, I can not seem to find code to ensure the labels of my Pie chart, do not overlap the Pie chart OR other labels.
I've entered geom_text_repel and adjusted vjust size force x in various ways and nothing works. It works on some charts, and other charts it does not.
---
title: "Untitled"
date: "August 14, 2019"
output: html_document
---
```{r eval = TRUE, echo = FALSE, results = "asis", warning = FALSE, message = FALSE, fig.height = 6.25, fig.width = 12}
library(plyr)
library(dplyr)
library(kableExtra)
library(scales)
library(ggplot2)
library(RODBC)
library(data.table)
library(DT)
library(treemapify)
library(devtools)
library(digest)
library(plotly)
library(shiny)
library(ggrepel)
library(expss)
rptyear <- 2018
colours <- c("A" = "royalblue3", "B" = "red", "C" = "gold", "D" = "green4")
Category <- c("A", "B", "C", "D")
premiumtable <- cbind(rep(c("A","B","C","D"),11), c(rep(2009,4),rep(2010,4),rep(2011,4),rep(2012,4),rep(2013,4),rep(2014,4),rep(2015, 4), rep(2016,4), rep(2017,4), rep(2018,4),rep(2019,4)), as.numeric(c(13223284, 3379574,721217, 2272843,14946074,4274769, 753797,2655032, 15997384, 4952687, 722556,3035566,16244348,5541543,887109,3299966,15841630,6303443,1101696,3751892,14993295, 6993626,1312650,4158196,13946038, 7081457,1317428,4711389, 12800640, 6923012, 1345159, 4911780, 12314663, 6449919, 1395973,5004046,12612704,6968110,1507382,5745079,15311213,8958588,1849069,6819488)))
colnames(premiumtable) <- c("Var1", "Var2", "Freq")
currentPrem <- filter(as.data.table(premiumtable), Var2 == rptyear, Freq != 0)
prempie <- ggplot(currentPrem, aes(x="", y = as.numeric(currentPrem$Freq), fill= Var1))
prempie <- prempie + geom_bar(width = 1, stat = "identity", colour = "black")
prempie <- prempie + ggtitle(paste0("YTD Numbers:")) + coord_polar("y", start = 0)
prempie <- prempie + scale_fill_manual(values = colours)
prempie <- prempie + theme_void()+ theme(plot.title = element_text(face = "bold", size = 20, hjust = .5), legend.position = "none", axis.title=element_text(size=20), axis.title.y = element_blank(), axis.title.x = element_blank())
prempie <- prempie + geom_text_repel(mapping = aes(label = paste0(Var1, "\n $",prettyNum(round(as.numeric(currentPrem$Freq)/1000), big.mark = ",")) , x = 2),position = position_stack( vjust = .5), size = 6, force = 5,direction = "both", segment.size = 0)
```
Thanks for providing the working data/code. If you are open to using the package plotly it is quite good at producing pie charts right out of the box, and requires less fiddling about than ggplot. Here is an example with your data:
library(dplyr)
library(plotly)
#
rptyear <- 2018
colours <- c("A" = "royalblue3", "B" = "red", "C" = "gold", "D" = "green4")
# data
premiumtable <- data.frame(Var1 = rep(c("A","B","C","D"),11),
Var2 = c(rep(2009,4),rep(2010,4),rep(2011,4),rep(2012,4),rep(2013,4),rep(2014,4),rep(2015, 4),rep(2016,4), rep(2017,4),rep(2018,4),rep(2019,4)),
Freq = as.numeric(c(13223284, 3379574,721217, 2272843,14946074,4274769, 753797,2655032, 15997384, 4952687, 722556,3035566,16244348,5541543,887109,3299966,15841630,6303443,1101696,3751892,14993295, 6993626,1312650,4158196,13946038, 7081457,1317428,4711389, 12800640, 6923012, 1345159, 4911780, 12314663, 6449919, 1395973,5004046,12612704,6968110,1507382,5745079,15311213,8958588,1849069,6819488)))
# prepare plot data
currentPrem <-
premiumtable %>%
filter(Var2 == rptyear, Freq != 0) %>%
mutate(Freq = as.numeric(Freq))
# create plot labels
labels = paste0(currentPrem$Var1, "\n $",prettyNum(round(as.numeric(currentPrem$Freq)/1000), big.mark = ","))
# create plot
plot_ly(currentPrem,
labels = ~labels,
values = ~Freq, type = 'pie',
textposition = 'outside',
textinfo = 'label',
colors = colours) %>%
layout(title = paste("YTD Numbers:", rptyear),
xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
showlegend = FALSE)

The ggplotly() ignores legend labels editing despite using scale_fill_manual()

I use the data frame below:
Name <- c("DCH", "DCH", "DCH", "DGI", "DGI", "DGI", "LDP", "LDP", "LDP",
"RH", "RH", "RH", "TC", "TC", "TC")
Class <- c("Class1", "Class2", "Overlap", "Class1", "Class2", "Overlap",
"Class1", "Class2", "Overlap", "Class1", "Class2", "Overlap", "Class1", "Class2", "Overlap")
count <- c(2077, 1642, 460, 1971, 5708, 566, 2316, 810, 221, 2124, 3601,
413, 2160, 1097, 377)
FinalDF <- data.frame(Name, Class, count)
in order to create the following ggplot.
with :
# Generate the horizontal stacked bar chart plot
stackedBarsDiagram <- function(data, numRows = 5,
barColors = c('lemonchiffon', 'palegreen3', 'deepskyblue2'),
leftlabels = c('MyDatabaseA'), rightlabels = c('MyDatabaseB', 'MyDatabaseC', 'MyDatabaseD', 'MyDatabaseE'),
headerLabels = c("Class1", "Overlap", "Class2"),
#put input$referenceDataset intead of Reference dataset"
headerLabels2 = c(paste("Unique to","DB"), "Overlap", "Unique to Comparison Dataset "),
barThickness = F, rowDensity = 'default', internalFontSize = 12, headerFontSize = 16,
internalFontColor = 'black', headerFontColor = 'black', internalFontWeight = 'standard',
externalFontWeight = 'bold', internalLabelsVisible = T, headerlLabelsVisible = T,
# Default file type of saved file is .png; .pdf is also supported
bordersVisible = T, borderWeight = 'default', plotheight = 25, plotwidth = 25, filename = "StackedBarPlot.png", plotsave = F) {
# Parameters to assist in bar width calculations
minBarWidth = 0.5
maxBarWidth = 0.7
# Calculate bar width parameter
barWidthFactor <- ((maxBarWidth - minBarWidth) / (numRows))
FinalDF <- data
# If proportional bars are specified, display them
if (barThickness == T) {
sumDF <- FinalDF %>%
group_by(Name) %>%
summarize(tot = sum(count)) %>%
mutate(RANK = rank(tot), width = minBarWidth + RANK * barWidthFactor) %>%
arrange(desc(Name))
barWidths <- rep(sumDF$width, each = 3)
print(barWidths)
} else { # If proportional bars aren't specified, just set bar thickness to 0.9
barWidths <- rep(0.9, 5)
}
# Create the stacked bar plot using ggplot()
stackedBarPlot <- ggplot(data = FinalDF) +
geom_col(mapping = aes(x = Name, y = count, fill = Class), width = rep(0.9, 5),
color = "black", position = position_fill(reverse = T)) +
geom_text(size = 4, position = position_fill(reverse = T, vjust = 0.50), color = "black",
mapping = aes(x = Name, y = count, group = Class, label = round(count))) +
annotate('text', size = 5, x = (5 + 1) / 2, y = -0.1, label = c('A'), angle = 90) +
coord_flip() +
scale_fill_manual(values = c('lemonchiffon', 'palegreen3', 'deepskyblue2'), breaks = c("Class1", "Overlap", "Class2"), labels = c(paste("Unique to","DB"), "Overlap", "Unique to Comparison Dataset "),
guide = guide_legend(label.position = 'left', label.hjust = 0, label.vjust = 0.5)) +
# The limits = rev(...) function call ensures that the labels for the bars are plotted in the order
# in which they are specified in the rightLabels and leftLabels parameters in the main stackedBarChart() function call.
# This is necessary since the finalDF$Name order is reversed from the desired order.
scale_x_discrete(limits = rev(levels(FinalDF$Name)), position = 'top') +
# Blank out any default labels of ggplot() for the x and y axes
xlab('') +
ylab('') +
# Specify the style of the full plot area, including the background, legend & text sizes
theme(panel.background = element_rect(fill = 'white'),
plot.margin = unit(c(0.25, 0.25, 0.25, 0.25), 'inches'),
legend.title = element_blank(),
legend.position = 'top',
legend.direction = 'vertical',
legend.key.width = unit(0.15, 'inches'),
legend.key.height = unit(0.15, 'inches'),
legend.text = element_text(face = 'bold', size = 12, color = "black"),
axis.text = element_text(size = 12),
axis.text.x = element_blank(),
axis.ticks = element_blank())
# Display the plotly
print(stackedBarPlot)
}
print(stackedBarsDiagram(data = FinalDF,leftlabels ="DB" , numRows = 6,
barThickness = F,
barColors = c("#FFFACD","#7CCD7C","#00B2EE")))
However when I convert it to interactive with ggplotly():
ggplotly(stackedBarsDiagram(data = FinalDF,leftlabels ="DB" , numRows = 6,
barThickness = F,
barColors = c("#FFFACD", "#7CCD7C", "#00B2EE")))%>%
layout(title = "New plot title", legend = list(orientation = "h", y = -.132, x = 0), annotations = list())
my legend names are not edited properly despite using :
scale_fill_manual(values = c('lemonchiffon', 'palegreen3', 'deepskyblue2'),
breaks = c("Class1", "Overlap", "Class2"),
labels = c(paste("Unique to","DB"), "Overlap", "Unique to Comparison Dataset "),
guide = guide_legend(label.position = 'left', label.hjust = 0, label.vjust = 0.5))
they return to their default names "Class1", "Overlap", "Class2"
I don't know what plotly looks for exactly, but it looks like it doesn't care what your scale_fill_manual labels are and just pulls your fill factor groups as names. So one way would be to just create a label group in your data.
A hacky way is to manually edit the plotly_build() of the plot.
p1 <- plotly_build(p)
p1$x$data[[1]]$name <- "Unique to DB"
Start looking in there and you'll see the attributes of the plot, including hover-text. So this method would be annoying. You could do an lapply with some regex or a gsub, but the first method is likely easier.

Resources