I am trying to make these plots by dodging points. The points radius differ by size. Ideally, there have to be four points in each "coordinate" as indicated by the dataframe. The didge does not seem to work for two of the points. I tried changing the width to 5 but I can't see the points. Any clue on what's going on?
library(ggplot2)
set.seed(123)
species <- rep(c("A","B","C","D"), each = 5)
x.axis <- rep(c(0.5, 0.5, 1, 1,0.75), each = 4)
y.axis <- rep(c(0.5, 1, 1, 0.5,0.75), each = 4)
value <- c(1,2,10,3,4,5,4,3,2,3,6,5,10,4,5,17,1,10,13,3)
data <- data.frame(specie,value, x.axis, y.axis)
# 2D plots
ggplot(data, aes(x = x.axis, y = y.axis, color = value, size = value)) +
theme_classic() +
geom_point(aes(fill = species, color = species), alpha = 0.7,position = position_dodge(width = 0.05)) +
theme(text=element_text(size=10, family="Arial", color = "black")) +
theme(aspect.ratio = 10/10) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_rect(colour = "black", size=1)) +
theme(axis.title.y = element_text(margin = margin(t = 0, r = 0, b = 0, l = 0)))
Maybe this is what you are looking for. The way your points get dodged is determined by the grouping. In your case the data by default gets grouped by the variable mapped on the color aes, i.e. species. Hence, at each position or coordinate only points with different species get dodged.
As far as I understand your question you want to dodge all points at the same position or coordinate instead. To this end you could add an id column to your dataset by position, i.e. as we have four points at each position the points are assigned an id running from 1 to 4. This id column could then be mapped on the group aesthetic:
library(dplyr)
library(ggplot2)
data <- data |>
group_by(x.axis, y.axis) |>
mutate(id = row_number())
# 2D plots
ggplot(data, aes(x = x.axis, y = y.axis, size = value)) +
theme_classic() +
geom_point(aes(color = species, group = id), alpha = 0.7, position = position_dodge(width = 0.05)) +
theme(text = element_text(size = 10, family = "Arial", color = "black")) +
theme(aspect.ratio = 10 / 10) +
theme(
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_rect(colour = "black", size = 1)
) +
theme(axis.title.y = element_text(margin = margin(t = 0, r = 0, b = 0, l = 0)))
Related
I have a dataframe where the x value (discrete) is present and want to include on the x-axis in the plot; however, its y value is NA
I still want to show the x value even though y is NA. Is there a way to do this in ggplot2?
Currently, it simply skips the first two rows that has the NA value.
ggplot(tChartDF()[['df']], aes(
x = factor(tChartDF()[['df']][['Rare event date']], levels = unique(tChartDF()[['df']][['Rare event date']])),
y = unlist(tChartDF()[['df']][['days_between']]),
)) +
geom_hline(yintercept = unlist(tChartDF()[['timeScaleCL']]), color = input$tChartCLColour, lwd = input$tChartCLWidth) +
geom_hline(yintercept = unlist(tChartDF()[['timeScaleUL']]), linetype = 'dashed', lwd = 1, color = 'red') +
geom_hline(yintercept = unlist(tChartDF()[['timeScaleLL']]), linetype = 'dashed', lwd = 1, color = 'red') +
scale_x_discrete(expand = c(0,0)) +
theme_classic() +
geom_line(aes(group = 1), lwd = input$tChartLineWidth, color= input$tChartLineColour) +
geom_point(size = input$tChartMarkerSize, color = input$tChartMarkerColour) +
labs(title = input$tChartPlotTitle, x = input$tChartPlotXLabel, y = input$tChartPlotYLabel) +
theme(
plot.title = element_text(size = 24, face = 'bold', family = 'Arial', hjust = 0.5),
plot.margin = margin(0, 1, 0, 0, "cm"),
axis.title = element_text(size = 20, face = 'bold', family = 'Arial'),
axis.text = element_text(size = 16, face = 'bold', family = 'Arial'),
axis.text.x = element_text(angle = as.numeric(input$tChartXOrientation), vjust = 0.5),
axis.ticks.length = unit(.25, 'cm'),
) +
coord_cartesian(clip = 'off')
As seen, it only starts plotting at date: 2022/12/15 (ignoring the previous values in the table) columns y and mr have the NA values.
For the plot, I only care about the first two columns (Rare events and days_between). I tried selecting only those two columns and plotting but it still ignores the first two rows.
Desired result:
If we start with something like
mt <- mtcars
mt$mpg[c(3,6,9)] <- NA
and plot the line as in
ggplot(mt, aes(disp, mpg)) +
geom_line()
we don't see the missing points (not a surprise). We can add them this way:
transform(mt, mpg2 = ifelse(is.na(mpg), max(mpg, na.rm = TRUE) + 1, mpg)) |>
ggplot(aes(disp, mpg)) +
geom_line() +
geom_point(aes(y = mpg2), data = ~ subset(., is.na(mpg)), shape = 1)
This can easily be adapted to be at the bottom, using different shapes/colors, perhaps even on an explicitly-gray background (top/bottom ribbon).
How to place the numbers centralized and in their correct positions? I have tested numerous parameters but I have not found a solution. Everything was very confusing after coordin_flip (). Observe the image and code below.
Code:
# Package
library(ggplot2)
# Create a dataframe
RATE <- c('IgG','IgG','IgA/IgG','IgA/IgG')
GROUP <- c('Asymptomatic','Symptomatic','Asymptomatic','Symptomatic')
N_POSITIVE <- c(12,100,14,107)
PORCENT <- c(7.05, 58.8, 7.73, 59.1)
df <- data.frame(RATE, GROUP, N_POSITIVE, PORCENT)
# Plot
ggplot(df, aes(x = RATE, y = PORCENT, fill = GROUP)) +
geom_bar(stat="identity", width = 0.5) +
geom_text(aes(label=N_POSITIVE),
vjust = -0.3, color = 'black',
size = 3) +
coord_flip() +
labs(x = '', y = 'Percentage (%)\n') +
scale_fill_manual(values = c("#0073c2", "#efc000")) +
theme_classic() +
theme(
legend.position = "top",
legend.title = element_blank(),
axis.text = element_text(angle = 0, color = "black", size = 10, face = 0),
axis.title.x = element_text(angle = 0, color = "black", size = 12, face = 0))
Image:
Does this do what you want? (minus the other formatting which I left out)
ggplot(df, aes(x = PORCENT, y = RATE, fill = GROUP)) +
geom_col(width = 0.5) +
geom_text(aes(label=N_POSITIVE),
vjust = -0.3, color = 'black', hjust = 1.1,
size = 3, position = "stack")
(Note, since ggplot2 3.3.0 in March 2020, most geom's don't need coord_flip if you assign them to the axis you want. If it doesn't interpret correctly, there's also an "orientation" parameter but that doesn't seem necessary here. Also, geom_col is equal to geom_bar(stat="identity").)
I want to plot the result of a paried t test with a plot containing points, lines and bars, like the plot on the paper (Bellmund, at al. Mapping sequence structure in the human lateral entorhinal cortex. Elife, 8. doi:10.7554/eLife.45333).
See the screenshot of the plot:
In my practise, it is easy to put geom_point, geom_line and geom_bar in the same plot, but the problem comes up when I try to change the positions of the points and lines while remain the bars unchanged.
I tried to use position_dodge() in point plot, however, it would move all the elements in the plot. Any ideas? Thanks for help!
library(ggplot2)
id <- rep(c(1:29), times = 2)
condition <- c(rep('vector.angle', times = 29), rep('baseline', times = 29))
value <- rnorm(58, mean = 22, sd = 27)
v.angle.gg <- data.frame(id, condition, value)
ggplot(data = v.angle.gg, aes(x = condition, y = value)) +
geom_hline(aes(yintercept = 0), color = "black", size = 1.5, linetype = 14) +
stat_summary(fun.y = mean, geom = "bar", aes(x = condition, y = value, fill = condition),
width = 0.3, position = position_identity()) +
geom_line(aes(group= id), size = 1, color = 'gray') +
geom_point(size = 4, position = position_dodge(width= 1)) +
scale_fill_brewer(palette="Paired") +
xlab('Conditions') + ylab('Parameter Estimations') +
theme(
title = element_text(size=rel(1.3),face="bold", colour = "black"),
axis.line.x = element_line(size = 1, colour = "black"),
axis.text.x=element_text(size=rel(1.3),face="bold", colour = "black"),
axis.ticks.x = element_line(size=rel(2), colour = "black"),
axis.line.y = element_line(size = 1, colour = "black"),
axis.text.y=element_text(size=rel(1.3),face="bold", colour = "black"),
axis.ticks.y = element_line(size=rel(2), colour = "black"),
panel.border = element_blank(),
panel.background = element_blank(),
panel.grid = element_blank()
) +
guides(fill=F,color=F)
The question asks for ideas, here is a hack.
Bar plots plot the bars at consecutive integers x, in this case c(1, 2). So you need the points and the lines connecting them at c(1.2, 1.8). Now, in the data set column condition has the alphabetically larger values first and they will be coerced to factor before plotting. So the new x coordinates vector will be subset with condition correctly. Not clear?
f <- v.angle.gg$condition
f <- c(1.2, 1.8)[as.integer(factor(f))]
These are the new coordinates. And they must be set in both geom_line and geom_point. Makes more sense now? I hope so.
I have simplified the graph so that only the relevant part is plotted.
ggplot(data = v.angle.gg, aes(x = condition, y = value)) +
geom_hline(aes(yintercept = 0), color = "black", size = 1.5, linetype = 14) +
stat_summary(fun = mean, geom = "bar", aes(x = condition, y = value, fill = condition),
width = 0.3, position = position_identity()) +
geom_line(aes(x = f, group= id), size = 1, color = 'gray') +
geom_point(aes(x = f), size = 4, position = position_dodge(width= 1))
This question already has answers here:
How do I get ggplot to read the colors I am feeding it
(1 answer)
geom_point color is not correct
(2 answers)
Closed 3 years ago.
I am plotting a circular bar plot using code below (simplified and using iris).
When I state the bars should be white outside AES, it works as expected (see inner plot).
But when I try to make the colour vary based on the data and specifying it within the AES, it either uses the standard colours (see outer colour).
library(tidyverse)
iris2 <- iris %>%
mutate(id = row_number(),
colour = if_else(Sepal.Length > 5, "Yellow","White"))
ggplot(iris2) +
theme_minimal() +
theme(
plot.background = element_rect(fill = "dimgrey"),
legend.position = "none",
axis.text = element_blank(),
axis.title = element_blank(),
panel.grid = element_blank(),
plot.margin = unit(rep(0,4), "cm")
) +
coord_polar() +
ylim(c(0, 20 + max(iris2$Sepal.Length))) +
# Colour as per fixed colour code (White)
geom_segment(aes(x = id, xend = id,
y = 10,
yend = 10 + Sepal.Length),
colour = "White", alpha = 1,
size = 1, inherit.aes = FALSE) +
# Colour not as per data
geom_segment(aes(x = id, xend = id,
y = 20,
yend = 20 + Sepal.Length,
colour = colour, alpha = 1),
size = 1, inherit.aes = FALSE)
I am trying to create a boxplot with a log y axis as I have some very small values and then some much higher values which do not work well in a boxplot with a continuous y axis. However, I have negative values which obviously do not work with a log scale. I was wondering if there was a way around this so that I can display my data on a boxplot which is still easy to interpret but has a more appropriate scale on the y axis.
p <- ggplot(data = Elstow.monthly.fluxes, aes(x = Month1, y = CH4.Flux)) + stat_boxplot(geom = "errorbar", linetype = 1, width = 0.5) + geom_boxplot() +
xlab(expression("Month")) + ylab(expression(~CH[4]~Flux~(µg~CH[4]~m^{-2}~d^{-1}))) +
scale_y_continuous(breaks = seq(-5000,40000,5000), limits = c(-5000,40000))+
theme(axis.text.x = element_text(colour = "black")) + theme(axis.text.y = element_text(colour =
"black")) +
theme(panel.background = element_rect("white", "black")) +
theme(panel.border = element_rect(colour = "black", fill=NA, size=0.5)) +
theme(axis.text = element_text(size = 12))+ theme(axis.title = element_text(size = 14))+
theme(axis.title.y = element_text(margin = margin(t = 0, r = 15, b = 0, l = 0))) +
theme(axis.title.x = element_text(margin = margin(t = 15, r = 0, b = 0, l = 0))) +
geom_hline(yintercept = 0, linetype ="dashed", colour = "black")
While you could indeed use the secondary axis to get the labels you want as Zhiqiang suggests, you could also use a transformation that fits your needs.
Consider the following skewed boxplots:
df <- data.frame(
x = rep(letters[1:2], each = 500),
y = rlnorm(1000) - 2
)
ggplot(df, aes(x, y)) +
geom_boxplot()
Instead, you could use the pseudo-log transformation to visualise your data:
ggplot(df, aes(x, y)) +
geom_boxplot() +
scale_y_continuous(trans = scales::pseudo_log_trans())
Alternatively, you could make any transformation you want. I personally like the inverse hyperbolic sine transformation, which is very much like the pseudo-log:
asinh_trans <- scales::trans_new(
"inverse_hyperbolic_sine",
transform = function(x) {asinh(x)},
inverse = function(x) {sinh(x)}
)
ggplot(df, aes(x, y)) +
geom_boxplot() +
scale_y_continuous(trans = asinh_trans)
I have a silly solution: trick the secondary axis to re-scale y axis. I do not have your data, just made up some numbers for the purpose of demonstration.
First convert y values as logy = log(y + 5000). When generating the graph, transform the values back to the original scale. I borrow the second axis to display the values. I am pretty sure others may have more elegant ways to do this.
I was lazy for not trying to find the right way to remove the primary y axis tick labels, just used breaks = c(0).
df<-data.frame(y = runif(33, min=-5000, max=40000),
x = rep(c("Aug", "Sep", "Oct"),33))
library(tidyverse)
df$logy = log(df$y+5000)
p <- ggplot(data = df, aes(x = x, y = logy)) +
stat_boxplot(geom = "errorbar", linetype = 1, width = 0.5) +
geom_boxplot() +
xlab(expression("Month")) +
ylab(expression(~CH[4]~Flux~(µg~CH[4]~m^{-2}~d^{-1}))) +
scale_y_continuous(sec.axis = sec_axis(~(exp(.) -5000),
breaks = c(-4000, 0, 5000, 10000, 20000, 40000)),
breaks = c(0))+
theme(axis.text.x = element_text(colour = "black")) +
theme(axis.text.y = element_text(colour = "black")) +
theme(panel.background = element_rect("white", "black")) +
theme(panel.border = element_rect(colour = "black", fill=NA, size=0.5)) +
theme(axis.text = element_text(size = 12))+
theme(axis.title = element_text(size = 14))+
theme(axis.title.y = element_text(margin = margin(t = 0, r = 15, b = 0, l = 0))) +
theme(axis.title.x = element_text(margin = margin(t = 15, r = 0, b = 0, l = 0))) +
geom_hline(yintercept = log(5000), linetype ="dashed", colour = "black")
p
coord_trans() is applied after the statistics are calculated (unlike scale). This can be combined with the pseudo_log_trans to cope with negatives.
library(plotly)
set.seed(1234)
dat <- data.frame(cond = factor(rep(c("A","B"), each=200)), rating = c(rnorm(200),rnorm(200, mean=500)))
pseudoLog <- scales::pseudo_log_trans(base = 10)
p <- ggplot(dat, aes(x=cond, y=rating)) + geom_boxplot() + coord_trans(y=pseudoLog)