Related
I am trying to create a function on Rthat creates and saves a dotplot with facets on my wd. Code for the function below:
get_dotplot <- function(df, xvalue, avgvalue, sdvalue, svalue, gvalue, main, xaxis, yaxis, glegend, figure_title)
{
dp <- ggplot(df, aes(x = xvalue, y = avgvalue, color = gvalue)) +
geom_point(stat = 'identity', aes(shape=svalue, color=gvalue))+
geom_errorbar(aes(ymin=avgvalue-sdvalue, ymax=avgvalue+sdvalue))+
facet_grid(cols = vars(svalue), scales = "fixed")+
labs(x = xaxis, y = yaxis, title = main, color=glegend)+
theme(axis.title.x.bottom = element_text(hjust = 0.5, vjust = 1),
axis.title.y = element_text(hjust = 0.5, vjust = 1),
axis.ticks.x = element_line(),
axis.text.x = element_text(angle = 0, hjust = 1, vjust = 0.5, size = 7),
axis.ticks.x.bottom = element_line(colour = "grey", size = (0.5)),
axis.ticks.y.left = element_line(colour = "black", size = (0.4)),
panel.background=element_rect(colour = "black", size = 0.5, fill=NA),
panel.grid = element_blank())
print(dp)
ggsave(paste(figure_title, "png", sep = "."), plot = dp, scale = 1, dpi = 600)
}
get_dotplot(df, xvalue, avgvalue, sdvalue, svalue, gvalue, main, xaxis, yaxis, glegend, figure_title)
However, I always get this error message:
Error in `combine_vars()`:
! At least one layer must contain all faceting variables: `svalue`.
* Plot is missing `svalue`
* Layer 1 is missing `svalue`
* Layer 2 is missing `svalue`
Backtrace:
1. global get_dotplot_errorbar_yaxis(...)
3. ggplot2:::print.ggplot(dp)
5. ggplot2:::ggplot_build.ggplot(x)
6. layout$setup(data, plot$data, plot$plot_env)
7. ggplot2 f(..., self = self)
8. self$facet$compute_layout(data, self$facet_params)
9. ggplot2 f(...)
10. ggplot2::combine_vars(data, params$plot_env, cols, drop = params$drop)
I suspect it's because of the facetting so I played around between facet_wrap() and facet_grid() with no result. Could someone please help me with that ?
I checked and I have the svalue variable in my dataframe, and it is spelled correctly. I also consulted previous questions about the topic but they were not helpful.
the dataset looks something like this, but with a larger number of individuals and numbers of days:
set.seed(108)
n <- 1:12
treatment <- factor(paste("trt", 1:2))
individuals <- sample(LETTERS, 2)
days <- c("12", "20", "25")
avg_var1 <- sample(1:100, 12)
sd_var1 <- sample(1:50, 12)
avg_var2 <- sample(1:100, 12)
sd_var2 <- sample(1:50, 12)
avg_var3 <- sample(1:100, 12)
sd_var3 <- sample(1:50, 12)
test <- data.frame(n, treatment, individuals, days,avg_var1, sd_var1, avg_var2, sd_var2, avg_var3, sd_var3)
I define the variables for the function as follows on R:
df=test
xvalue=test$days
avgvalue=test$avg_var1
sdvalue = test$sd_var1
svalue=test$treatment
gvalue=test$individuals
main= "var1 in function of days"
xaxis="days"
yaxis="var1"
glegend="individuals"
figure_title ="var1_days"
As written, your code passes columns into the function repeating the data in the dataframe. This doesn't seem to "play nicely" with the non-standard evaluation used in ggplot. Essentially ggplot is looking for a column in df called "svalue" to use for faceting (it doesn't find it). Once this has been fixed, the same sort of problem occurs with the error bars.
One way round this is to just pass in the column names, and use aes_string for the variables. This doesn't work for the faceting or the calculated values, so those are calculated at the start of the function. This would give:
get_dotplot <- function(df, xvalue, avgvalue, sdvalue, svalue, gvalue, main, xaxis, yaxis, glegend, figure_title)
{
df$ymin <- df[[avgvalue]] - df[[sdvalue]]
df$ymax <- df[[avgvalue]] + df[[sdvalue]]
df$facets <- df[[svalue]]
dp <- ggplot(df, aes_string(x = xvalue, y = avgvalue, color = gvalue)) +
geom_point(stat = 'identity', aes_string(shape=svalue, color=gvalue)) +
geom_errorbar(aes(ymin=ymin, ymax=ymax))+
facet_grid(cols = vars(facets), scales = "fixed")+
labs(x = xaxis, y = yaxis, title = main, color=glegend)+
theme(axis.title.x.bottom = element_text(hjust = 0.5, vjust = 1),
axis.title.y = element_text(hjust = 0.5, vjust = 1),
axis.ticks.x = element_line(),
axis.text.x = element_text(angle = 0, hjust = 1, vjust = 0.5, size = 7),
axis.ticks.x.bottom = element_line(colour = "grey", size = (0.5)),
axis.ticks.y.left = element_line(colour = "black", size = (0.4)),
panel.background=element_rect(colour = "black", size = 0.5, fill=NA),
panel.grid = element_blank())
print(dp)
ggsave(paste(figure_title, "png", sep = "."), plot = dp, scale = 1, dpi = 600)
}
get_dotplot(df=test,
xvalue="days",
avgvalue="avg_var1",
sdvalue = "sd_var1",
svalue="treatment",
gvalue="individuals",
main= "var1 in function of days",
xaxis="days",
yaxis="var1",
glegend="individuals",
figure_title ="var1_days")
I want to create a radar chart with ggirahExtra::ggRadar. The problem is that I have long labels and they are clipped. I thought I could create more space between label and plot by adding margin = margin(0,0,2,0, "cm") to element_text in axis.text, but its not working.
Any ideas how to increase the label space are welcome (apart from making the font smaller).
Add: As #tjebo suggests in the comments, it might be easier, or maybe the only way to make it work, to change the underlying functions in ggRadar especially coord_radar. Any suggestions of how to do this are welcome.
library(ggplot2)
library(ggiraphExtra)
dat <- data.frame("Item_A_Long" = 2,
"Item_B_Very_Very_Long"= 0,
"Label_Item_C" = 1,
"Item_D_Label" = 4,
"Another_very_long_label" = 3)
ggRadar(dat,
aes(
x = c(Item_A_Long,
Item_B_Very_Very_Long,
Label_Item_C,
Item_D_Label,
Another_very_long_label)
),
legend.position = "top",
colour = "white",
rescale = FALSE,
use.label = FALSE
) +
scale_y_continuous(expand = c(0,0),
limits = c(0,4)
) +
theme(panel.background = element_rect(fill = "#001957"),
# adding margin = margin(0,0,2,0, "cm") to element_text below does not help
axis.text = element_text(color = "#FFFFFF"),
panel.grid.major.y = element_blank())
Created on 2021-04-30 by the reprex package (v0.3.0)
It's a matter of clipping. The problem is also the white standard background of your drawing device. Below a hacky workaround.
turn off clipping with a modified version of ggiraphExtra::coord_radar as well as ggiraphExtra::ggRadar. Note I have removed a (very) few bits from the original ggRadar function, so if you need all arguments, you'd need to modify the function yourself.
Turn all background elements blue
Superimpose all onto a pure blue background, I am using cowplot.
library(cowplot)
library(ggplot2)
p1 <- ggRadar2(dat,
aes(
x = c(
Item_A_Long,
Item_B_Very_Very_Long,
Label_Item_C,
Item_D_Label,
Another_very_long_label
)
),
colour = "white",
rescale = FALSE,
clip = "off"
) +
theme(
plot.background = element_rect(fill = "#001957", color = "#001957"),
panel.background = element_rect(fill = "#001957"),
# adding margin = margin(0,0,2,0, "cm") to element_text below does not help
axis.text = element_text(color = "#FFFFFF"),
panel.grid.major.y = element_blank()
)
p2 <-
ggplot() +
theme_void()+
theme(panel.background = element_rect(fill = "#001957"))
ggdraw(p2) + draw_plot(p1)
the modified functions
coord_radar2 <- function(theta = "x", start = 0, direction = 1, clip = "off") {
theta <- match.arg(theta, c("x", "y"))
r <- if (theta == "x") {
"y"
} else {
"x"
}
ggproto("CoordRadar", ggplot2::CoordPolar,
theta = theta,
r = r, start = start, clip = clip,
direction = sign(direction), is_linear = function(coord) TRUE
)
}
ggRadar2 <- function(data, mapping = NULL, rescale = TRUE, legend.position = "top",
colour = "red", alpha = 0.3, size = 3, ylim = NULL, scales = "fixed",
use.label = FALSE, interactive = FALSE, clip = "off", ...) {
data <- as.data.frame(data)
(groupname <- setdiff(names(mapping), c("x", "y")))
groupname
mapping
length(groupname)
if (length(groupname) == 0) {
groupvar <- NULL
}
else {
groupvar <- ggiraphExtra:::getMapping(mapping, groupname)
}
groupvar
facetname <- colorname <- NULL
if ("facet" %in% names(mapping)) {
facetname <- ggiraphExtra:::getMapping(mapping, "facet")
}
(colorname <- setdiff(groupvar, facetname))
if ((length(colorname) == 0) & !is.null(facetname)) {
colorname <- facetname
}
data <- ggiraphExtra:::num2factorDf(data, groupvar)
(select <- sapply(data, is.numeric))
if ("x" %in% names(mapping)) {
xvars <- ggiraphExtra:::getMapping(mapping, "x")
xvars
if (length(xvars) < 3) {
warning("At least three variables are required")
}
}
else {
xvars <- colnames(data)[select]
}
(xvars <- setdiff(xvars, groupvar))
if (rescale) {
data <- ggiraphExtra:::rescale_df(data, groupvar)
}
temp <- sjlabelled::get_label(data)
cols <- ifelse(temp == "", colnames(data), temp)
if (is.null(groupvar)) {
id <- ggiraphExtra:::newColName(data)
data[[id]] <- 1
longdf <- reshape2::melt(data, id.vars = id, measure.vars = xvars)
}
else {
cols <- setdiff(cols, groupvar)
longdf <- reshape2::melt(data, id.vars = groupvar, measure.vars = xvars)
}
temp <- paste0("plyr::ddply(longdf,c(groupvar,'variable'), dplyr::summarize,mean=mean(value,na.rm=TRUE))")
df <- eval(parse(text = temp))
colnames(df)[length(df)] <- "value"
df
groupvar
if (is.null(groupvar)) {
id2 <- ggiraphExtra:::newColName(df)
df[[id2]] <- "all"
id3 <- ggiraphExtra:::newColName(df)
df[[id3]] <- 1:nrow(df)
df$tooltip <- paste0(df$variable, "=", round(
df$value,
1
))
df$tooltip2 <- paste0("all")
p <- ggplot(data = df, aes_string(
x = "variable", y = "value",
group = 1
)) +
ggiraph::geom_polygon_interactive(aes_string(tooltip = "tooltip2"),
colour = colour, fill = colour, alpha = alpha
) +
ggiraph::geom_point_interactive(aes_string(
data_id = id3,
tooltip = "tooltip"
), colour = colour, size = size)
}
else {
if (!is.null(colorname)) {
id2 <- ggiraphExtra:::newColName(df)
df[[id2]] <- df[[colorname]]
}
id3 <- ggiraphExtra:::newColName(df)
df[[id3]] <- 1:nrow(df)
df$tooltip <- paste0(
groupvar, "=", df[[colorname]], "<br>",
df$variable, "=", round(df$value, 1)
)
df$tooltip2 <- paste0(groupvar, "=", df[[colorname]])
p <- ggplot(data = df, aes_string(
x = "variable", y = "value",
colour = colorname, fill = colorname, group = colorname
)) +
ggiraph::geom_polygon_interactive(aes_string(tooltip = "tooltip2"),
alpha = alpha
) +
ggiraph::geom_point_interactive(aes_string(
data_id = id3,
tooltip = "tooltip"
), size = size)
}
p
if (!is.null(facetname)) {
formula1 <- as.formula(paste0("~", facetname))
p <- p + facet_wrap(formula1, scales = scales)
}
p <- p + xlab("") + ylab("") + theme(legend.position = legend.position)
p <- p + coord_radar2(clip = clip)
if (!is.null(ylim)) {
p <- p + expand_limits(y = ylim)
}
p
p
}
You can use the labelled package to create labels with line breaks and then set label = TRUE in ggRadar(). You can add more than one break for super long labels.
library(ggplot2)
library(ggiraphExtra)
library(labelled)
dat <- data.frame("Item_A_Long" = 2,
"Item_B_Very_Very_Long"= 0,
"Label_Item_C" = 1,
"Item_D_Label" = 4,
"Another_very_long_label" = 3)
var_label(dat$Item_A_Long ) <- "Item \nA long"
var_label(dat$Item_B_Very_Very_Long ) <- "Item_B_\nVery_\nVery_Long"
var_label(dat$Label_Item_C ) <- "Label_\nItem_C "
var_label(dat$Item_D_Label ) <- "Item_\nD_Label"
var_label(dat$Another_very_long_label ) <- "Another_very_\nlong_label"
ggRadar(dat,
aes(
x = c(Item_A_Long,
Item_B_Very_Very_Long,
Label_Item_C,
Item_D_Label,
Another_very_long_label)
),
legend.position = "top",
colour = "white",
rescale = FALSE,
use.label = TRUE
) +
scale_y_continuous(expand = c(0,0),
limits = c(0,4)
) +
theme(panel.background = element_rect(fill = "#001957"),
# adding margin = margin(0,0,2,0, "cm") to element_text below does not help
axis.text = element_text(color = "#FFFFFF"),
panel.grid.major.y = element_blank())
I have the following R code to plot from a given data in a listing format called probData_com. This code is fine but the problem is if I replace "ranks", "value", and "variable" inside the ggplot and the geom_line by dat$ranks, dat$value, and dat$variable, then 'g' only show the last plot three times. Can anyone tell me why it is happening?
g <- list()
for(i in 1:3)
{
probData <- probData_com[[i]]
colnames(probData) <- c("ranks","SH0","SH1","EH0","EH1","AH0","AH1")
dat <- melt(probData, id.var = "ranks")
ey_val <- ifelse(effectType == "binary", i-1,
ifelse(i==1, 0, paste0("U(",i-2,", ", i-1,")")))
eySm <- ifelse(effectType == "binary", "ey = ", "ey ~ ")
g[[i]] = ggplot(dat, aes(x = ranks, y = value, group = variable,
colour = variable)) +
geom_line(aes(linetype = variable), size = 1.5) +
labs(x = "Ranks", y = "p(rank | effect)", size = 20,
subtitle = paste0(eySm, ey_val, ", e.one = ", e.one)) +
theme(legend.title = element_blank(),
axis.title.x = element_text(size = rel(.7)),
axis.title.y = element_text(size = rel(.7)))
}
g
I am looking at producing a faceted histogram in a loop. The problem occurs when I am calling the facet_wrap within the loop. I tried different options but all of them failed with the following message:
Error in layout_base(data, vars, drop = drop) : At least one layer must contain all variables used for facetting
Below is a reproducible example.
library(ggplot2)
library(scales) #date_format
## Reproducible example
datatest <- data.frame(
column1 = sample(c("yes", "no"), 50, replace = TRUE),
column2 = sample(c("yes", "no"), 50, replace = TRUE),
column3 = sample(c("yes", "no"), 50, replace = TRUE),
column4 = sample(c("yes", "no"), 50, replace = TRUE)
)
#This function will generate a uniform sample of dates from
#within a designated start and end date:
rand.date=function(start.day,end.day,data){
size=dim(data)[1]
days=seq.Date(as.Date(start.day),as.Date(end.day),by="day")
pick.day=runif(size,1,length(days))
date=days[pick.day]
}
#This will create a new column within your data frame called date:
datatest$date=rand.date("2016-01-01","2016-09-21",datatest)
## Simple frequency plot that works well
histotest <- ggplot(datatest, aes(x = date)) +
geom_histogram(binwidth = 7, fill="#2a87c8", colour="white") +
scale_x_date(limits = c(Sys.Date() - 250, NA), labels = date_format("%b %Y")) +
labs(x = "Period", y = "Count") +
facet_wrap(~ column1 , ncol=1) +
theme(plot.title=element_text(face="bold", size=9),
panel.grid.major = element_line(colour = "white"),
panel.grid.minor = element_blank())
ggsave("out/column1_histo.png", plot=histotest, width=12, height=6,units="in", dpi=300)
Here comes the problem:
## Same plot generated through a loop
## The pb is with the facet_wrap
for (i in 1:4 ) {
rm(variablename)
variablename <- names(datatest)[i]
## histogramme to display event occurence over time
histoloop <- ggplot(datatest, aes(x = date)) +
geom_histogram(binwidth = 7, fill="#2a87c8", colour="white") +
scale_x_date(limits = c(Sys.Date() - 250, NA), labels = date_format("%b %Y")) +
labs(x = "Period", y = "Count") +
## I tried different options but none of them is working
## If I comment the facet_wrap everything's fine...
facet_wrap(~ variablename , ncol=1) +
#facet_wrap(~ names(datatest)[i] , ncol=1) +
#facet_wrap(~ aes_string(names(datatest)[i]) , ncol=1) +
theme(plot.title=element_text(face="bold", size=9),
panel.grid.major = element_line(colour = "white"),
panel.grid.minor = element_blank())
ggsave(filename=paste("out/",variablename,"_histo.png",sep=""), plot=histoloop, width=12, height=6,units="in", dpi=300)
}
Change the code as follows (since the variablename is a string):
facet_wrap(as.formula(paste("~", variablename)) , ncol=1)
Reformat data for plotting, then plot with split data in a loop:
library(tidyr) #gather
# prepare the data, wide to long
plotDat <- gather(datatest, key = "Column", value = "Value", -c(date) )
# then use loop, I prefer lapply
lapply(split(plotDat, plotDat$Column), function(i){
ggplot(i, aes(x = date)) +
geom_histogram(binwidth = 7, fill="#2a87c8", colour="white") +
scale_x_date(limits = c(Sys.Date() - 250, NA), labels = date_format("%b %Y")) +
labs(x = "Period", y = "Count") +
facet_wrap(~ Value , ncol = 1) +
theme(plot.title=element_text(face="bold", size=9),
panel.grid.major = element_line(colour = "white"),
panel.grid.minor = element_blank())
})
I have seen many solutions for aligning the plotting regions of ggplot2 plots. However, I have a couple of plots that have been made by a function that outputs a result from arrangeGrob. I would like to plot them in a column, with both the beginning and end of the x-axis aligned. Here is a runnable example of what happens.
To run the example,
library(ggplot2)
library(gridExtra)
topPlot <- f(TRUE, TRUE, TRUE, "Dataset 1")
bottomPlot <- f(FALSE, FALSE, FALSE, "Dataset 2")
grid.draw(arrangeGrob(topPlot, bottomPlot, nrow = 2))
The definition of f :
f <- function(x,y, z, t)
{
showLegends = x
knownClasses <- rep(c("Healthy", "Sick"), each = 5)
plotData <- data.frame(name = LETTERS[1:10],
type = rep(c("Method 1", "Method 2"), each = 10),
class = rep(c("Healthy", "Sick"), each = 5),
Error = runif(20))
classesPlot <- ggplot(data.frame(Class = knownClasses), aes(1:length(knownClasses), factor(1)), environment = environment()) +
geom_tile(aes(fill = Class, height = 10)) +
scale_x_discrete(expand = c(0, 0), breaks = NULL, limits = c(1, length(knownClasses))) +
scale_y_discrete(expand = c(0, 0), breaks = NULL) +
labs(x = '', y = '')+ theme(legend.position = ifelse(showLegends, "right", "none"))
errorPlot <- ggplot(plotData, aes(name, type)) + geom_tile(aes(fill = Error)) + theme_bw() +
theme(legend.position = ifelse(showLegends, "right", "none"),
axis.text.y = if(y) element_text(size = 8) else element_blank()) + ylab(if(z) "Label" else NULL)
classGrob <- ggplot_gtable(ggplot_build(classesPlot))
errorGrob <- ggplot_gtable(ggplot_build(errorPlot))
commonWidth <- unit.pmax(classGrob[["widths"]], errorGrob[["widths"]])
classGrob[["widths"]] <- commonWidth
errorGrob[["widths"]] <- commonWidth
arrangeGrob(classGrob, errorGrob, nrow = 2, heights = c(1, 3), main = t)
}
In a real scenario, the two datasets will have different samples in different proportions of classes, so getting rid of the class colour scale above each error plot is not an option, unless multiple colour scales per plot were allowed in ggplot2.
How can this code be modified to align the plot areas ?