How to colour xlabs with the corresponding colour of its jitter in a geom_jitter? - r

I am trying to colour the xlabs with the same colour as the point they are labelling, but I am having some trouble.
Each jitter is coloured depending on a specified variable levels, and I want the same for the xlabs.
This is my code to plot the figure:
ggplot(coverage_data, aes(x=x_values, y=coverage_data$mean, fill=coverage_data$frecuency))+
geom_jitter(size=2.5, shape=21, stroke=1.5)+
scale_fill_manual(name = "frecuency", values =c("deepskyblue4", "gray67", "darkgoldenrod2", "springgreen4", "brown1", "white"))+
xlab("Id")+
ylab("max coverage")+
theme(axis.text.x=element_text(hjust=1, colour = 'black', size = 9))
If I declare colour ( in theme(axis.text.x(element_text)) ) as a vector I get an error. Do you know how can I achieve that?

Passing a vector of colors generates a warning, but with ggplot2 3.3.0 (what I'm running) it does work.
Since you didn't share any data I've made some up:
frecuency <- rep(c("A", "B", "C", "D", "E", "F"), 10)
mean <- runif(60, 10, 20)
x_values <- runif(60, 1, 100)
coverage_data <- data.frame(frecuency, mean, x_values, stringsAsFactors = FALSE)
ggplot(coverage_data, aes(x= x_values, y= mean, fill= frecuency))+
geom_jitter(size=2.5, shape=21, stroke=1.5)+
scale_fill_manual(name = "frecuency", values =c("deepskyblue4", "gray67", "darkgoldenrod2", "springgreen4", "brown1", "white"))+
xlab("Id")+
ylab("max coverage")+
theme(axis.text.x=element_text(hjust=1, colour = c("black", "blue", "green", "yellow", "red"), size = 9))
Warning message: Vectorized input to element_text() is not
officially supported. Results may be unexpected or may change in
future versions of ggplot2.
sessionInfo()
R version 3.6.2 (2019-12-12)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 18363)
other attached packages:
[1] ggQC_0.0.31 readxl_1.3.1 forcats_0.5.0
[4] stringr_1.4.0 dplyr_0.8.3 purrr_0.3.3
[7] readr_1.3.1 tidyr_1.0.2 tibble_2.1.3
[10] ggplot2_3.3.0 tidyverse_1.3.0

Related

Manually sort labels in plot_ly [duplicate]

Is it possible to order the legend entries in R?
If I e.g. specify a pie chart like this:
plot_ly(df, labels = Product, values = Patients, type = "pie",
marker = list(colors = Color), textfont=list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
The legend gets sorted by which Product has the highest number of Patients. I would like the legend to be sorted in alphabetical order by Product.
Is this possible?
Yes, it's possible. Chart options are here:
https://plot.ly/r/reference/#pie.
An example:
library(plotly)
library(dplyr)
# Dummy data
df <- data.frame(Product = c('Kramer', 'George', 'Jerry', 'Elaine', 'Newman'),
Patients = c(3, 6, 4, 2, 7))
# Make alphabetical
df <- df %>%
arrange(Product)
# Sorts legend largest to smallest
plot_ly(df,
labels = ~Product,
values = ~Patients,
type = "pie",
textfont = list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
# Set sort argument to FALSE and now orders like the data frame
plot_ly(df,
labels = ~Product,
values = ~Patients,
type = "pie",
sort = FALSE,
textfont = list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
# I prefer clockwise
plot_ly(df,
labels = ~Product,
values = ~Patients,
type = "pie",
sort = FALSE,
direction = "clockwise",
textfont = list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
Session info:
R version 3.5.0 (2018-04-23)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)
Matrix products: default
locale:
[1] LC_COLLATE=English_Australia.1252 LC_CTYPE=English_Australia.1252 LC_MONETARY=English_Australia.1252 LC_NUMERIC=C LC_TIME=English_Australia.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] bindrcpp_0.2.2 dplyr_0.7.5 plotly_4.7.1 ggplot2_2.2.1
EDIT:
Modified to work with plotly 4.x.x (i.e. added ~)

ggplot loses scale_color_manual when saving to png with ggsave

I am having a strange issue where saving a ggplot figure that I make does not maintain the colors I set using scale_color_manual. I have made a reproducible example (with some editing) using the mtcars dataset.
plot1 <- ggplot(data = mtcars %>% rownames_to_column("type") %>%
dplyr::filter(between(cyl, 6, 8)) %>%
dplyr::filter(between(gear, 4, 5))
) +
aes(y = wt, x = type) +
geom_boxplot(outlier.size = 0) +
geom_jitter(aes(color = factor(cyl), shape = factor(gear)), size = 10, position=position_jitter(width=.25, height=0)) +
#geom_smooth(method = lm, se = TRUE) +
scale_shape_manual(values=c("👧","👦"), name = "Gear", labels = c("4", "5")) + # I need 9 values (I for each ID)
scale_color_manual(values=c('red4', 'springgreen4'), name = "cyl", labels = c("4 cylinder", "5 cylinder")) +
# # geom_jitter(size=8, aes(shape=Sex, color=Sex), position = position_dodge(.4)) +
theme(legend.position = "top",
plot.title = element_text(hjust = 0.5) # Center the text title)
)
ggsave("images/review/mean_AllAgents_test.png",plot1, width=11, height=6.5, dpi=400)
The figure in the RStudio "Plots" pane has cyl colored in red and green shown below
Whereas the file saved using ggsave does not show these colors.
I have tried using the fix from this SO post. I also have tried using cowplot::save_plot. The colors do remain if I manually Export the figure from the "Plots" pane.
Does anyone know why this is occurring?
R version 4.0.4 (2021-02-15)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19043)
Matrix products: default
locale:
[1] LC_COLLATE=English_Canada.1252 LC_CTYPE=English_Canada.1252 LC_MONETARY=English_Canada.1252 LC_NUMERIC=C
[5] LC_TIME=English_Canada.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] apastats_0.3 ggstatsplot_0.8.0 rstatix_0.7.0 hrbrthemes_0.8.0 gtsummary_1.4.2.9011 car_3.0-11
[7] carData_3.0-4 forcats_0.5.1 stringr_1.4.0 dplyr_1.0.6 purrr_0.3.4 readr_2.0.0
[13] tidyr_1.1.3 tibble_3.1.2 tidyverse_1.3.1 Rmisc_1.5 plyr_1.8.6 lattice_0.20-41
[19] ggplot2_3.3.5 rio_0.5.27 pacman_0.5.1
EDIT
I was asked to provide additional detail in my Preferences

ggplot2 both axis labels inside plot area

I would like to create a ggplot2 with both the y-axis and x-axis labels on the inside, i.e., facing inwards and placed inside the plot area.
This previous SO answer by Z.Lin solves it for the case of the y-axis, and I've got that working just fine. But extending that approach to both axes has me stumped. grobs is hard, I think.
So I attempted to start small, by adapting Z.Lin's code to work for the x-axis instead of the y-axis, but I have not been able to achieve even that. grobs is really complicated. My attempt (below) runs without errors/warnings until grid.draw(), where it crashes and burns (I think I'm misusing some args somewhere, but I can't identify which and at this point I'm just guessing).
# locate the grob that corresponds to x-axis labels
x.label.grob <- gp$grobs[[which(gp$layout$name == "axis-b")]]$children$axis
# remove x-axis labels from the plot, & shrink the space occupied by them
gp$grobs[[which(gp$layout$name == "axis-b")]] <- zeroGrob()
gp$widths[gp$layout$l[which(gp$layout$name == "axis-b")]] <- unit(0, "cm")
# create new gtable
new.x.label.grob <- gtable::gtable(widths = unit(1, "npc"))
# place axis ticks in the first row
new.x.label.grob <-
gtable::gtable_add_rows(
new.x.label.grob,
heights = x.label.grob[["heights"]][1])
new.x.label.grob <-
gtable::gtable_add_grob(
new.x.label.grob,
x.label.grob[["grobs"]][[1]],
t = 1, l = 1)
# place axis labels in the second row
new.x.label.grob <-
gtable::gtable_add_rows(
new.x.label.grob,
heights = x.label.grob[["heights"]][2])
new.x.label.grob <-
gtable::gtable_add_grob(
new.x.label.grob,
x.label.grob[["grobs"]][[2]],
t = 1, l = 2)
# add third row that takes up all the remaining space
new.x.label.grob <-
gtable::gtable_add_rows(
new.x.label.grob,
heights = unit(1, "null"))
gp <-
gtable::gtable_add_grob(
x = gp,
grobs = new.x.label.grob,
t = gp$layout$t[which(gp$layout$name == "panel")],
l = gp$layout$l[which(gp$layout$name == "panel")])
grid.draw(gp)
# Error in unit(widths, default.units) :
# 'x' and 'units' must have length > 0
I guess my question can be split into three semi-independent parts, where each subsequent question supersedes the earlier ones (so if you can answer a later question, there will be no need to bother with the earlier ones):
can anyone adapt the existing answer to the x-axis?
can anyone provide code in that vein to get both axes inside?
does anyone know of a neater way to achieve both axes inside for ggplot2?
Here's my MWE (mostly replicating Z.Lin's answer, but with new data):
library(dplyr)
library(magrittr)
library(ggplot2)
library(grid)
library(gtable)
library(errors)
df <- structure(list(
temperature = c(200, 300, 400, 500, 600, 700, 800, 900),
diameter =
structure(
c(13.54317, 10.32521, 10.23137, 17.90464, 29.98183, 55.65514, 101.60747, 147.3074),
id = "<environment>",
errors = c(1.24849, 0.46666, 0.36781, 0.48463, 0.94639, 1.61459, 6.98346, 12.18353),
class = "errors")),
row.names = c(NA, -8L),
class = "data.frame")
p <- ggplot() +
geom_smooth(data = df %>% filter(temperature >= 400),
aes(x = temperature, y = diameter),
method = "lm", formula = "y ~ x",
se = FALSE, fullrange = TRUE) +
# experimental errors as red ribbon (instead of errorbars)
geom_ribbon(data = df,
aes(x = temperature,
ymin = errors_min(diameter),
ymax = errors_max(diameter)),
fill = alpha("red", 0.2),
colour = alpha("red", 0.2)) +
geom_point(data = df,
aes(x = temperature, y = diameter),
size = 0.7) +
geom_line(data = df,
aes(x = temperature, y = diameter),
size = 0.15) +
scale_x_continuous(breaks = seq(200, 900, 200)) +
scale_y_log10(breaks = c(10, seq(30, 150, 30)),
labels = c("10", "30", "60", "90", "120", "150=d/nm")) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_text(hjust = 0))
# convert from ggplot to grob object
gp <- ggplotGrob(p)
y.label.grob <- gp$grobs[[which(gp$layout$name == "axis-l")]]$children$axis
gp$grobs[[which(gp$layout$name == "axis-l")]] <- zeroGrob()
gp$widths[gp$layout$l[which(gp$layout$name == "axis-l")]] <- unit(0, "cm")
new.y.label.grob <- gtable::gtable(heights = unit(1, "npc"))
new.y.label.grob <-
gtable::gtable_add_cols(
new.y.label.grob,
widths = y.label.grob[["widths"]][2])
new.y.label.grob <-
gtable::gtable_add_grob(
new.y.label.grob,
y.label.grob[["grobs"]][[2]],
t = 1, l = 1)
new.y.label.grob <-
gtable::gtable_add_cols(
new.y.label.grob,
widths = y.label.grob[["widths"]][1])
new.y.label.grob <-
gtable::gtable_add_grob(
new.y.label.grob,
y.label.grob[["grobs"]][[1]],
t = 1, l = 2)
new.y.label.grob <-
gtable::gtable_add_cols(
new.y.label.grob,
widths = unit(1, "null"))
gp <-
gtable::gtable_add_grob(
x = gp,
grobs = new.y.label.grob,
t = gp$layout$t[which(gp$layout$name == "panel")],
l = gp$layout$l[which(gp$layout$name == "panel")])
grid.draw(gp)
> sessionInfo()
R version 3.6.2 (2019-12-12)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.5 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
locale:
[1] LC_CTYPE=en_GB.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_GB.UTF-8 LC_COLLATE=en_GB.UTF-8
[5] LC_MONETARY=en_GB.UTF-8 LC_MESSAGES=en_GB.UTF-8
[7] LC_PAPER=en_GB.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] grid stats graphics grDevices utils datasets methods
[8] base
other attached packages:
[1] errors_0.3.4 gtable_0.3.0 ggplot2_3.3.2 magrittr_1.5 dplyr_1.0.2
loaded via a namespace (and not attached):
[1] rstudioapi_0.11 splines_3.6.2 tidyselect_1.1.0 munsell_0.5.0
[5] lattice_0.20-41 colorspace_1.4-1 R6_2.5.0 rlang_0.4.8
[9] tools_3.6.2 nlme_3.1-148 mgcv_1.8-31 withr_2.3.0
[13] ellipsis_0.3.1 digest_0.6.27 yaml_2.2.1 tibble_3.0.4
[17] lifecycle_0.2.0 crayon_1.3.4 Matrix_1.2-18 purrr_0.3.4
[21] farver_2.0.3 vctrs_0.3.4 glue_1.4.2 compiler_3.6.2
[25] pillar_1.4.6 generics_0.1.0 scales_1.1.1 pkgconfig_2.0.3
Rather than "freezing" the plot as a grob tree then hacking the grobs, I thought it might be useful to see how we could move the axes inside but keep the object as a ggplot. The way to do this is to write a function that takes your plot, extracts the necessary information, then builds axes and adds them as annotations.
The returned object is a normal ggplot, to which you can add layers, scales and modify themes as normal:
move_axes_inside <- function(p)
{
b <- ggplot_build(p)
x_breaks <- b$layout$panel_scales_x[[1]]$break_info()
y_breaks <- b$layout$panel_scales_y[[1]]$break_info()
x_range <- b$layout$panel_params[[1]]$x.range
y_range <- b$layout$panel_params[[1]]$y.range
y_breaks$major <- diff(y_breaks$range)/diff(y_range) * y_breaks$major +
(y_breaks$range[1] - y_range[1])/diff(y_range)
x_breaks$major <- diff(x_breaks$range)/diff(x_range) * x_breaks$major +
(x_breaks$range[1] - x_range[1])/diff(x_range)
y <- grid::yaxisGrob(at = y_breaks$major, label = y_breaks$labels, main = FALSE)
x <- grid::xaxisGrob(at = x_breaks$major, label = x_breaks$labels, main = FALSE)
p + annotation_custom(y, xmin = x_range[1], xmax = x_range[1]) +
annotation_custom(x, ymin = y_range[1], ymax = y_range[1]) +
theme(axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.text.x = element_blank())
}
So testing it with your plot we get:
p2 <- move_axes_inside(p)
p2
And we can change theme elements etc:
p2 + theme(panel.grid.major = element_line())
This would need a bit of development and testing to get it working with discrete axes and so on, but it should work for arbitrary continuous axes as-is.
In case anyone else happens to be looking for a way to make a compact plot using ggplot2, for example for placement inside a page margin, I perhaps you'll be helped by the full code for a fairly publication-ready inside-the-margin plot made possible by Allan Cameron's elegant approach in the answer above.
Placing a plot inside a page margin is usually not advisable, and depends on the available margin, the type of document, etc. In any case, it's probably smart to make the plot as clutter-free and stream-lined as possible. That's why, in my case, I was looking for a way to keep as much of the plot inside the panel's footprint, so to speak.
Enough background, here's the code:
library(dplyr)
library(magrittr)
library(ggplot2)
library(grid)
library(gtable)
library(errors)
theme_set(theme_grey())
move_axes_inside <- function(p) {
b <- ggplot_build(p)
x_breaks <- b$layout$panel_scales_x[[1]]$break_info()
y_breaks <- b$layout$panel_scales_y[[1]]$break_info()
x_range <- b$layout$panel_params[[1]]$x.range
y_range <- b$layout$panel_params[[1]]$y.range
y_breaks$major <-
diff(y_breaks$range) / diff(y_range) * y_breaks$major +
(y_breaks$range[1] - y_range[1]) / diff(y_range)
x_breaks$major <-
diff(x_breaks$range) / diff(x_range) * x_breaks$major +
(x_breaks$range[1] - x_range[1]) / diff(x_range)
y <-
grid::yaxisGrob(
at = y_breaks$major,
label = y_breaks$labels,
gp =
gpar(
lwd = 1, # line width of axis and tick marks
fontsize = 8,
cex = 0.8, # multiplier to font size
lineheight = 0.8), # tick mark length
main = FALSE)
x <-
grid::xaxisGrob(
at = x_breaks$major,
label = x_breaks$labels,
gp =
gpar(
lwd = 2, # draw axis with thicker line width
fontsize = 8,
cex = 0.8, # multiplier to font size
lineheight = 0.8), # tick mark length
main = FALSE)
p <-
p +
annotation_custom(
# draw y-axis, shifted slightly inwards (so that axis is inside panel.border)
grob = y,
xmin = x_range[1] + 0.01 * diff(x_range),
xmax = x_range[1] + 0.01 * diff(x_range)) +
annotation_custom(
grob = x,
ymin = y_range[1] + 0.01 * diff(y_range),
ymax = y_range[1] + 0.01 * diff(y_range)) +
theme(
axis.ticks = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_blank())
return(p)
}
p <- ggplot() +
geom_line(
stat = "smooth", method = lm, formula = "y ~ x",
se = FALSE, fullrange = TRUE,
data = df %>% filter(temperature >= 400),
aes(x = temperature, y = diameter),
colour = "blue", size = 2, alpha = 0.35) +
# experimental errors as red ribbon (instead of errorbars)
geom_ribbon(
data = df,
aes(x = temperature,
ymin = errors_min(diameter),
ymax = errors_max(diameter)),
fill = alpha("red", 0.25),
colour = NA) +
# data points excluded in linear fit
geom_point(
data = df %>% filter(temperature < 400),
aes(x = temperature, y = diameter),
# by default, shape=19 (filled circle)
# https://blog.albertkuo.me/post/point-shape-options-in-ggplot/
# I'd like a solid circle, so shape 16 it is
size = 1.2, shape = 16, colour = alpha("red", 0.25)) +
# data points included in linear fit
geom_point(
data = df %>% filter(temperature >= 400),
aes(x = temperature, y = diameter),
size = 1.2, shape = 16, colour = alpha("red", 0.45)) +
# I ended up putting the x-axis unit label on the outside because
# however I tried, it would not fit inside and I was not able to
# rotate the x-axis labels on the inside.
labs(x = "$T_\\mathrm{a}/\\si{\\celsius}$") +
scale_x_continuous(
breaks = seq(200, 900, 100),
# first element can't be empty string - if so then all labels dont print (weird bug?)
labels = c(" ", " ", "400", " ", "600", " ", "800", " ")) +
scale_y_log10(
breaks = c(10, 50, 90, 130),
labels = c("\\num{10}", "\\num{50}", "\\num{90}", "$\\num{130}=d/\\si{\\nm}$")) +
# note that we set some theme settings inside the move_axes_inside() function
theme(
# l = -1 was required to completely fill the space with plot panel
# b = 0 because we are making room for x-axis title on the outside
plot.margin = margin(t = 0, r = 0, b = 0, l = -1, "mm"),
# smaller text size in x-axis title, trying to conform with fontsize inside axis
# vjust moves the title closer to the x-axis line, value optimised optically
axis.title.x = element_text(size = 8 * 0.8, vjust = 2.0),
# grid lines just look busy in such a small plot
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
move_axes_inside(p)
Here's a screen-shot of the result, in a document compiled with knitr and LaTeX and with the plot inside \marginpar{}:

Blank Plot Output when using "geom_xspline" in ggalt package

When trying to use geom_xspline from ggalt in conjunction with ggarrange from ggpubr, the output is blank and no other plot can be made before clearing with dev.off().
In my use-case I wanted the geom_xspline to replace some exisitng geom_line in my ggplot object. Is anyone aware of issues using geoms added from other R packages?
Here is some code to compare, nothing of interest really, just to give a reproducible example:
Initial Working Code w/o geom_xspline
library(ggplot2)
library(ggpubr)
myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_line()
ggarrange(myplot, myplot) # Works and outputs fine
Code that fails with ggalt package
library(ggalt)
library(ggplot2)
library(ggpubr)
myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_xspline()
ggarrange(myplot, myplot) # Output becomes blank and freezes the plot panel
Alternative Method
Instead of using ggarrange I tried the function grid_arrange_shared_legend from this link, which uses grid and gridExtra. However, I am still curious as to why ggarrange does not work.
Here is my session info:
R version 3.5.1 (2018-07-02)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)
Matrix products: default
locale:
[1] LC_COLLATE=English_United States.1252 LC_CTYPE=English_United States.1252 LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C LC_TIME=English_United States.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggpubr_0.1.8 magrittr_1.5 ggplot2_3.0.0
loaded via a namespace (and not attached):
[1] Rcpp_0.12.18 pillar_1.3.0 compiler_3.5.1 RColorBrewer_1.1-2 plyr_1.8.4 bindr_0.1.1
[7] tools_3.5.1 extrafont_0.17 tibble_1.4.2 gtable_0.2.0 pkgconfig_2.0.1 rlang_0.2.1
[13] rstudioapi_0.7 yaml_2.2.0 bindrcpp_0.2.2 Rttf2pt1_1.3.7 withr_2.1.2 dplyr_0.7.6
[19] maps_3.3.0 grid_3.5.1 ggalt_0.4.0 tidyselect_0.2.4 cowplot_0.9.3 glue_1.3.0
[25] R6_2.2.2 purrr_0.2.5 extrafontdb_1.0 scales_1.0.0 MASS_7.3-50 assertthat_0.2.0
[31] proj4_1.0-8 colorspace_1.3-2 labeling_0.3 KernSmooth_2.23-15 ash_1.0-15 lazyeval_0.2.1
[37] munsell_0.5.0 crayon_1.3.4
Quick addition, if I convert the object to a ggplotGrob(), it will work with ggarrange, but it will fail when I attempt to use common.legend = T.
Well I am not sure why ggpubr::ggarrange causes failure of Plots pannel when used with ggalt::geom_xspline but I can tell you that plots are still getting created but just now shown on the plot pannel.
So it seems that using those together causes failure in the graphing device and it is only happening for ggalt::geom_xspline and not all the geoms in ggalt. That is a bug so you are on the right track posting to GitHub.
You can check that by running the code below:
library(ggalt)
library(ggplot2)
library(ggpubr)
myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_xspline()
myplot
g <- ggarrange(myplot, myplot) # Output becomes blank and freezes the plot panel
g
jpeg('rplot.jpg')
g
dev.off()
#> pdf
#> 3
Created on 2019-05-30 by the reprex package (v0.3.0)
And this is the saved plot:
The xspline function, upon whichgeom_xspline is based, typically automatically plots using graphics. This led the ggalt package authors to find a few work-arounds to ensure it would play nicely with ggplot. My rough solutions both involve creating or adjusting a geom or stat from ggplot without using xspline. This makes it easier to use without a lot of pre-processing the data prior to ingesting with ggplot.
(1) New stat using splines
Using spline for interpolation of points instead of xspline.
# Create a new stat (adjusted from ggalt GitHub page)
stat_spline <- function(mapping = NULL, data = NULL, geom = "line",
position = "identity", na.rm = TRUE, show.legend = NA, inherit.aes = TRUE,
n=200, method = "fmm", ...) { # Just picking a rough default for n
layer(
stat = StatSpline,
data = data,
mapping = mapping,
geom = geom,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
params = list(n=n,
method=method,
na.rm = na.rm,
...
)
)
}
StatSpline <- ggproto("StatSpline", Stat,
required_aes = c("x", "y"),
compute_group = function(self, data, scales, params,
n=200, method = "fmm") {
tmp <- spline(data$x, data$y, n = n, method = method, ties = mean)
data.frame(x=tmp$x, y=tmp$y)
}
)
# Plot with ggarrange
myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
stat_spline(mapping = aes(x = wt, y = mpg)) +
geom_point()
ggpubr::ggarrange(myplot, myplot)
This method isn't ideal if you want splines similar to Catmull-Rom instead of Cubic; you can see some large bends between control points.
(2) New geom using xsplineGrob
This is a slightly adjusted version of geom_xspline2 from ggalt
# Create new geom based upon code from ggalt GitHub page
GeomXSpline3 <- ggproto("GeomXSpline3", Geom,
required_aes = c("x", "y"),
default_aes = aes(colour = "black", shape=-1, open=T),
draw_key = draw_key_point,
draw_panel = function(data, panel_params, coord) {
coords <- coord$transform(data, panel_params)
grid::xsplineGrob(
coords$x, coords$y,
shape = coords$shape,
open = coords$open[1],
gp = grid::gpar(col = coords$colour)
)
}
)
geom_xspline3 <- function(mapping = NULL, data = NULL, stat = "identity",
position = "identity", na.rm = FALSE, show.legend = NA,
inherit.aes = TRUE, ...) {
layer(
geom = GeomXSpline3, mapping = mapping, data = data, stat = stat,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, ...)
)
}
# Plot with ggarrange
myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_xspline3(shape = -.25) + geom_point()
ggpubr::ggarrange(myplot, myplot)
There were a couple issues with ensuring the shape parameter still accepted inputs between -1 and 1, however, this seems to be working okay now with ggarrange.
I used the following resources while writing this solution:
A blog post from an author from ggalt
The GitHub page for geom_xspline and geom_xspline2
ggplot vignette on extending ggplot

Plotly R order legend entries

Is it possible to order the legend entries in R?
If I e.g. specify a pie chart like this:
plot_ly(df, labels = Product, values = Patients, type = "pie",
marker = list(colors = Color), textfont=list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
The legend gets sorted by which Product has the highest number of Patients. I would like the legend to be sorted in alphabetical order by Product.
Is this possible?
Yes, it's possible. Chart options are here:
https://plot.ly/r/reference/#pie.
An example:
library(plotly)
library(dplyr)
# Dummy data
df <- data.frame(Product = c('Kramer', 'George', 'Jerry', 'Elaine', 'Newman'),
Patients = c(3, 6, 4, 2, 7))
# Make alphabetical
df <- df %>%
arrange(Product)
# Sorts legend largest to smallest
plot_ly(df,
labels = ~Product,
values = ~Patients,
type = "pie",
textfont = list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
# Set sort argument to FALSE and now orders like the data frame
plot_ly(df,
labels = ~Product,
values = ~Patients,
type = "pie",
sort = FALSE,
textfont = list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
# I prefer clockwise
plot_ly(df,
labels = ~Product,
values = ~Patients,
type = "pie",
sort = FALSE,
direction = "clockwise",
textfont = list(color = "white")) %>%
layout(legend = list(x = 1, y = 0.5))
Session info:
R version 3.5.0 (2018-04-23)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)
Matrix products: default
locale:
[1] LC_COLLATE=English_Australia.1252 LC_CTYPE=English_Australia.1252 LC_MONETARY=English_Australia.1252 LC_NUMERIC=C LC_TIME=English_Australia.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] bindrcpp_0.2.2 dplyr_0.7.5 plotly_4.7.1 ggplot2_2.2.1
EDIT:
Modified to work with plotly 4.x.x (i.e. added ~)

Resources