Issues with ggplotly within Shiny context - r

Using the code below, I could create the plot shown in the image. I am trying to fix some issues as follows:
1- How can I place the legend between title and plot. I would like to have the legend in a horizontal format rather than the current vertical form.
2- How can I bold, and center the plot title?
3- How can use different shapes in the geom_point function, depending on the "Disability" variable?
4- How can I rename the label so it reads "Age at Death" instead of the current name: "AgeatDeath"?
Thanks,
Nader
IDD_line <-
ggplot(IDD_plot) +
geom_line(aes(x = Year, y = AgeatDeath, color = Disability), size = 1) +
geom_point(aes(x = Year,
y = AgeatDeath,
color = Disability),
size = 1.5,
shape = 21) +
scale_x_continuous(breaks = seq(2008, 2017, 1)) +
labs(
x = "Year",
y = "Age at Death",
caption = (""),
face = "bold"
) +
theme_bw() +
scale_y_continuous(breaks = seq(35, 80, 5)) +
ggtitle(paste("Age of Death by Year (2008 to 2017)")) +
theme(
legend.title = element_blank(),
axis.text = (blue.bold.10.text),
legend.position = "top"
)
output$IDD_plot <- renderPlotly(ggplotly(IDD_line) %>%
config(displayModeBar = F))

This was a bit harder than I initially anticipated. Maybe somebody else will come up with some better solutions, but here's what I have.
How can I place the legend between title and plot. I would like to have the legend in a horizontal format rather than the current vertical form.
You can do this in layout after call to ggplotly. To put into horizontal form, use orientation = "h". If you specify y = 1.2 this will go above the plot (if you just set yanchor at "top" this will go on the plot itself near the top).
In addition, you'll want to add extra margin so the legend does not overlap with the title. Use margin to set left, right, bottom, and top margins (l, r, b, and t).
layout(legend = list(x = 0, y = 1.2, orientation = "h"),
margin = list(l = 50, r = 50, b = 100, t = 100)...
How can I bold, and center the plot title?
To bold the title, you can add HTML tags, such as <b>title</b> in ggtitle. To center the title, add that to layout as in #1, using xanchor:
layout(legend = list(x = 0, y = 1.2, orientation = "h"),
margin = list(l = 50, r = 50, b = 100, t = 100),
title = list(xanchor = "center", x = .5))
How can use different shapes in the geom_point function, depending on the "Disability" variable?
To get the shape to depend on "Disability", you will need shape = Disability in your aesthetic. The tricky part is that if you do this inside aes inside geom_point, you will get duplicate legends after converting to ggplotly. You can get around this with a separate aes outside of geom_point.
For more information see this github issue comment.
How can I rename the label so it reads "Age at Death" instead of the current name: "AgeatDeath"?
One method is to use text in your aes and clarify your labels this way. This will include line breaks with <br>, %d for integer values, and %s for string (for Disability):
text = sprintf("Year: %d<br>Age at Death: %d<br>Disability: %s",
Year, AgeatDeath, Disability)
Then, in your ggplotly call, include tooltip = "text".
So putting this all together, you have:
IDD_line <- ggplot(IDD_plot) +
aes(color = Disability,
shape = Disability,
group = Disability,
text = sprintf("Year: %d<br>Age at Death: %d<br>Disability: %s",
Year, AgeatDeath, Disability)) +
geom_line(aes(x = Year, y = AgeatDeath), size = 1) +
geom_point(aes(x = Year,
y = AgeatDeath),
size = 1.5) +
scale_x_continuous(breaks = seq(2008, 2017, 1)) +
labs(
x = "Year",
y = "Age at Death",
caption = (""),
face = "bold"
) +
theme_bw() +
scale_y_continuous(breaks = seq(35, 80, 5)) +
ggtitle(paste("<b>Age of Death by Year (2008 to 2017)</b>")) +
theme(
legend.title = element_blank(),
axis.text = (blue.bold.10.text) #,
#legend.position = "top"
)
And,
output$IDD_plot <- renderPlotly(
ggplotly(IDD_line, tooltip = "text") %>%
config(displayModeBar = F) %>%
layout(legend = list(x = 0, y = 1.2, orientation = "h"),
margin = list(l = 50, r = 50, b = 100, t = 100),
title = list(xanchor = "center", x = .5))
)

Related

How to make icons in geom_pictogram in r start at 0

I am working on making a pictogram using r where the categories are along the x axis and the amount of the thing that belongs in each category is represented by the number of icons. I would like to leave the ticks on the y axis to make it easy to see how many of the thing there are, but the icons start a little above 0 and finish a little above the real value, giving the appearance that the value is higher than it really is.
# install.packages("waffle", repos = "https://cinc.rud.is")
library(waffle)
data <- data.frame(
x = c('John', 'James', 'Jeff', 'Joe', 'Jake'),
ht = c(72, 71, 73, 69, 66),
icon = rep('rocket', 5)
)
ggplot(data, aes(label= x,
values = ht,
color=icon)) +
geom_pictogram(n_rows=5, make_proportional=FALSE, size=5, flip=TRUE) +
facet_wrap(~x, nrow = 1, strip.position = "bottom") +
scale_x_discrete() +
scale_y_continuous(labels=function(x) x * 5, # multiplyer should be same as n_rows
expand = c(0,0),
limits = c(0,20)) +
scale_label_pictogram(
name = NULL,
values = c(
'rocket' = 'rocket'
)) +
theme(legend.position = "none")
This results in a good approximation of what I'm looking for, but without adjusting the alignment of the icons and the y-axis ticks/labels, it doesn't work.
I have been unable to find any way to move the y-axis ticks/labels higher or to shift the icons lower, either of which would work for this purpose. I have considered removing the y-axis ticks/labels and labeling the amount of the data just above each set of icons, but have been unable to get annotate or geom_text to work. I expect there's something simple I'm missing, but have no idea what it is at this point. I've also tried making this chart with echarts4r and waffle, though I've run into different issues with each that led me back to geom_pictogram.
One option would be to use geom_text with stat="waffle". Doing so allows to shift the icons aka labels via position_nudge:
library(ggplot2)
library(waffle)
ggplot(data, aes(
label = x,
values = ht,
color = icon
)) +
geom_text(
stat = "waffle", n_rows = 5, make_proportional = FALSE, size = 5, flip = TRUE,
family = "Font Awesome 5 Free",
position = position_nudge(y = -.9), vjust = 0
) +
facet_wrap(~x, nrow = 1, strip.position = "bottom") +
scale_x_discrete() +
scale_y_continuous(
labels = function(x) x * 5,
expand = c(0, 0),
limits = c(0, 20)
) +
scale_label_pictogram(
name = NULL,
values = c(
"rocket" = "rocket"
)
) +
theme(legend.position = "none")
If you want to use geom_pictogram (which always seems to start at 1), you could set the scale limits and add a custom label function to remove 1 from the values.
library(ggplot2)
library(waffle)
ggplot(data, aes(label= x,
values = ht,
color=icon)) +
geom_pictogram(n_rows=5,
size=5,
flip=TRUE) +
facet_wrap(~x, nrow = 1, strip.position = "bottom") +
scale_x_discrete() +
scale_label_pictogram(
name = NULL,
values = c(
'rocket' = 'rocket'
)) +
scale_y_continuous(
expand = c(0,0),
## here
limits = c(1, NA), labels = ~ .x-1, breaks = seq(1,20,5)) +
theme(legend.position = "none")

ggplot2 function scale_fill_stepsn colors do not match

I have some codes that want to show a map with unequal color breaks and assign each category with a specific color. I used the scale_fill_stepsn function.
Here are my codes:
ggplot(data = rate) +
geom_sf(aes(fill = rate))+theme_bw()+
scale_fill_stepsn(name = "Test Rate \n (n/1000)",
colors =c("#999999","#6666FF", "#FFFF66","#FF6633"),
breaks = c(0,1, 10, 100),
labels=scales::label_number(accuracy=1),
show.limits = TRUE) +
annotation_north_arrow(location = "tl", which_north = "true",
pad_x = unit(0.1, "in"), pad_y = unit(0.05, "in"),
height = unit(1, "cm"),
width = unit(1, "cm"),
style = north_arrow_fancy_orienteering)+
annotation_scale(location = "bl", width_hint = 0.2)
The exact colours I put in the function shows below, which do not match the colour in the result figure.
scales::show_col(c("#999999","#6666FF", "#FFFF66","#FF6633"))
data
If I add trans = scales::log10_trans(), to the scale_fill_stepsn I get better results for this reproducible example, because my breaks are log distributed, so my gradient should be too. Otherwise the gradient will apply linearly along 0:107, and most values will be in muddled gray land.
ggplot(data.frame(val = c(0, 1, 10, 100, 107), x = 1:5),
aes(x, 0, fill = val)) +
geom_tile() +
scale_fill_stepsn(name = "Test Rate \n (n/1000)",
colors =c("#999999","#6666FF", "#FFFF66","#FF6633"),
breaks = c(0,1, 10, 100),
trans = scales::pseudo_log_trans(), # ADDED THIS LINE
labels=scales::label_number(accuracy=1),
show.limits = TRUE)
With
Without

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")))

How to stop ggrepel labels moving between gganimate frames in R/ggplot2?

I would like to add labels to the end of lines in ggplot, avoid them overlapping, and avoid them moving around during animation.
So far I can put the labels in the right place and hold them static using geom_text, but the labels overlap, or I can prevent them overlapping using geom_text_repel but the labels do not appear where I want them to and then dance about once the plot is animated (this latter version is in the code below).
I thought a solution might involve effectively creating a static layer in ggplot (p1 below) then adding an animated layer (p2 below), but it seems not.
How do I hold some elements of a plot constant (i.e. static) in an animated ggplot? (In this case, the labels at the end of lines.)
Additionally, with geom_text the labels appear as I want them - at the end of each line, outside of the plot - but with geom_text_repel, the labels all move inside the plotting area. Why is this?
Here is some example data:
library(dplyr)
library(ggplot2)
library(gganimate)
library(ggrepel)
set.seed(99)
# data
static_data <- data.frame(
hline_label = c("fixed_label_1", "fixed_label_2", "fixed_label_3", "fixed_label_4",
"fixed_label_5", "fixed_label_6", "fixed_label_7", "fixed_label_8",
"fixed_label_9", "fixed_label_10"),
fixed_score = c(2.63, 2.45, 2.13, 2.29, 2.26, 2.34, 2.34, 2.11, 2.26, 2.37))
animated_data <- data.frame(condition = c("a", "b")) %>%
slice(rep(1:n(), each = 10)) %>%
group_by(condition) %>%
mutate(time_point = row_number()) %>%
ungroup() %>%
mutate(score = runif(20, 2, 3))
and this is the code I am using for my animated plot:
# colours for use in plot
condition_colours <- c("red", "blue")
# plot static background layer
p1 <- ggplot(static_data, aes(x = time_point)) +
scale_x_continuous(breaks = seq(0, 10, by = 2), expand = c(0, 0)) +
scale_y_continuous(breaks = seq(2, 3, by = 0.10), limits = c(2, 3), expand = c(0, 0)) +
# add horizontal line to show existing scores
geom_hline(aes(yintercept = fixed_score), alpha = 0.75) +
# add fixed labels to the end of lines (off plot)
geom_text_repel(aes(x = 11, y = fixed_score, label = hline_label),
hjust = 0, size = 4, direction = "y", box.padding = 1.0) +
coord_cartesian(clip = 'off') +
guides(col = F) +
labs(title = "[Title Here]", x = "Time", y = "Mean score") +
theme_minimal() +
theme(panel.grid.minor = element_blank(),
plot.margin = margin(5.5, 120, 5.5, 5.5))
# animated layer
p2 <- p1 +
geom_point(data = animated_data,
aes(x = time_point, y = score, colour = condition, group = condition)) +
geom_line(data = animated_data,
aes(x = time_point, y = score, colour = condition, group = condition),
show.legend = FALSE) +
scale_color_manual(values = condition_colours) +
geom_segment(data = animated_data,
aes(xend = time_point, yend = score, y = score, colour = condition),
linetype = 2) +
geom_text(data = animated_data,
aes(x = max(time_point) + 1, y = score, label = condition, colour = condition),
hjust = 0, size = 4) +
transition_reveal(time_point) +
ease_aes('linear')
# render animation
animate(p2, nframes = 50, end_pause = 5, height = 1000, width = 1250, res = 120)
Suggestions for consideration:
The specific repelling direction / amount / etc. in geom_text_repel is determined by a random seed. You can set seed to a constant value in order to get the same repelled positions in each frame of animation.
I don't think it's possible for repelled text to go beyond the plot area, even if you turn off clipping & specify some repel range outside plot limits. The whole point of that package is to keep text labels away from one another while remaining within the plot area. However, you can extend the plot area & use geom_segment instead of geom_hline to plot the horizontal lines, such that these lines stop before they reach the repelled text labels.
Since there are more geom layers using animated_data as their data source, it would be cleaner to put animated_data & associated common aesthetic mappings in the top level ggplot() call, rather than static_data.
Here's a possible implementation. Explanation in annotations:
p3 <- ggplot(animated_data,
aes(x = time_point, y = score, colour = condition, group = condition)) +
# static layers (assuming 11 is the desired ending point)
geom_segment(data = static_data,
aes(x = 0, xend = 11, y = fixed_score, yend = fixed_score),
inherit.aes = FALSE, colour = "grey25") +
geom_text_repel(data = static_data,
aes(x = 11, y = fixed_score, label = hline_label),
hjust = 0, size = 4, direction = "y", box.padding = 1.0, inherit.aes = FALSE,
seed = 123, # set a constant random seed
xlim = c(11, NA)) + # specify repel range to be from 11 onwards
# animated layers (only specify additional aesthetic mappings not mentioned above)
geom_point() +
geom_line() +
geom_segment(aes(xend = time_point, yend = score), linetype = 2) +
geom_text(aes(x = max(time_point) + 1, label = condition),
hjust = 0, size = 4) +
# static aesthetic settings (limits / expand arguments are specified in coordinates
# rather than scales, margin is no longer specified in theme since it's no longer
# necessary)
scale_x_continuous(breaks = seq(0, 10, by = 2)) +
scale_y_continuous(breaks = seq(2, 3, by = 0.10)) +
scale_color_manual(values = condition_colours) +
coord_cartesian(xlim = c(0, 13), ylim = c(2, 3), expand = FALSE) +
guides(col = F) +
labs(title = "[Title Here]", x = "Time", y = "Mean score") +
theme_minimal() +
theme(panel.grid.minor = element_blank()) +
# animation settings (unchanged)
transition_reveal(time_point) +
ease_aes('linear')
animate(p3, nframes = 50, end_pause = 5, height = 1000, width = 1250, res = 120)

ggplot2 fill not converting ggplotly color mapping correctly

I'm trying to convert a ggplot2 image using ggplotly(), but it's not converting correctly. My ggplot image takes in two lines all of the time, and has a color variable for the geom_line component and a fill variable for the geom_point component.
Here is my code for the strictly the basic ggplot part:
test_data1 = data.frame(
filter = "Filter 1",
time = seq(as.Date("2017-01-01"), as.Date("2017-03-01"), "days"),
ovr_perc = rnorm(n = 60, mean = 8, sd = 2),
neg_perc = rnorm(n = 60, mean = 6, sd = 2),
count = sample(50:250,60,replace=T)
)
test_data2 = data.frame(
filter = "Filter 2",
time = seq(as.Date("2017-01-01"), as.Date("2017-03-01"), "days"),
ovr_perc = rnorm(n = 60, mean = 20, sd = 6),
neg_perc = rnorm(n = 60, mean = 6, sd = 2),
count = sample(50:250,60,replace=T)
)
test_data = rbind(test_data1, test_data2)
p = ggplot(test_data, aes(x=time, y=ovr_perc, group = factor(filter))) +
geom_line(aes(color=factor(filter))) +
geom_point(aes(fill=neg_perc, size = count), shape=21) +
scale_fill_gradient2(low = "darkgreen", mid = "yellow", high = "red1",
midpoint = 5, limits = c(0, 10), oob = squish) +
scale_colour_manual(values = c("pink", "green")) +
theme_classic()
p
The image should look something like this, which is almost exactly how I want it to look:
However, when I try adding the ggplotly code to get the tooltip feature, it turns into this:
ggplotly(p)
The entire color scheme on each of the points is gone. How can I change this so it keeps the same color scale from the ggplot image to the ggplotly image?
As noted in the open bug report linked above, geom_point's aesthetic mapping for fill doesn't work properly, while color works fine. According to the bug report, the problem goes away in the package's dev version, but if you don't have easy access to that (neither do I), the following works, at least on my end:
library(dplyr)
library(plotly)
p2 <- ggplot(test_data, aes(x=time, y=ovr_perc, linetype = filter)) +
geom_line(data = . %>% filter(filter == "Filter 1"),
colour = "pink") +
geom_line(data = . %>% filter(filter == "Filter 2"),
colour = "green") +
geom_point(aes(color = neg_perc, size = count)) +
scale_color_gradient2(low = "darkgreen", mid = "yellow", high = "red1",
midpoint = 5, limits = c(0, 10), oob = squish) +
scale_linetype_manual(values = c("solid", "solid"),
guide = guide_legend(override.aes = list(
color = c("pink", "green")
))) +
theme_classic(); p2
ggplotly(p2)
Explanations:
This uses color rather than fill for geom_point, which works fine.
The lines are plotted in separate geom_line layers, with their colours specified directly, outside aes().
The lines' linetype is mapped inside aes(), to force the creation of a legend for each filter value, while the actual mapping specified in scale_linetype_manual sets both lines to be solid.

Resources