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).
Related
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)))
I made this plot using the following code:
ggplot(all, aes(x = year, color = layer)) +
geom_histogram(binwidth = 0.5, fill = "white", alpha = 0.5, position = "dodge") +
scale_x_continuous(breaks = pretty(all$year)) +
scale_color_discrete(name = "title", labels = c("A","B")) +
theme_light() +
theme(panel.grid.minor = element_blank(), panel.grid.major = element_blank(),
text = element_text(size = 20),
axis.title.x = element_text(margin = margin(t = 25, r = 0, b = 0, l = 0)),
axis.title.y = element_text(margin = margin(t = 0, r = 25, b = 0, l = 0)),
axis.text.x = element_text(angle = 50, hjust = 1, size = 18, color = "black"),
axis.text.y = element_text(size = 18, color = "black"))
I would now like to change the colors first, using colors from the viridis palette. Furthermore, there are blue and red strokes between the histograms, which I would like to remove.
Could someone help me to change the code?
Thanks in advance!
Test Data:
year <- runif(10, 2014, 2021)
year <- round(year, 0)
layer <- sample(c("A","B"), size=10, replace=T)
all <- as.data.frame(year,layer)
Seems like you want a bar plot not a histogram.
all <- data.frame(year,layer) ## fix the sample data creation
ggplot(all, aes(x = year, fill = layer)) + ## I think fill looks better...
geom_bar(position = position_dodge(preserve = "single")) + ## bar, not histogram
#scale_x_continuous(breaks = pretty(all$year)) + ## this line just confirmed defaults
scale_fill_viridis_d() +
theme_light() ## omitted the rest of the theme as irrelevant for the issue at hand
If you do want outline color, not fill, switching to geom_bar "fixes" the strokes between the bars:
ggplot(all, aes(x = year, color = layer)) +
geom_bar(position = position_dodge(preserve = "single"), fill = NA) +
scale_color_viridis_d() +
theme_light()
Thank you, this is helpful information!
I am trying to generate overlay density plots over time, comparing densities of males vs. females. Here is my output:
I am following the Australian athletes height example from https://cran.r-project.org/web/packages/ggridges/vignettes/gallery.html.
Here is my code:
ggplot(math_dat, aes(x = order_math, y = time, color = gender, point_color = gender, fill = gender)) +
geom_density_ridges(
jittered_points = TRUE, scale = .95, rel_min_height = .01,
point_shape = "|", point_size = 3, size = 0.25,
position = position_points_jitter(height = 0)
) +
scale_y_discrete(expand = c(0, 0)) +
scale_x_continuous(expand = c(0, 0), name = "Rankings") +
scale_fill_manual(values = c("#D55E0050", "#0072B250"), labels = c("female", "male")) +
scale_color_manual(values = c("#D55E00", "#0072B2"), guide = "none") +
scale_discrete_manual("point_color", values = c("#D55E00", "#0072B2"), guide = "none") +
coord_cartesian(clip = "off") +
guides(fill = guide_legend(
override.aes = list(
fill = c("#D55E00A0", "#0072B2A0"),
color = NA, point_color = NA)
)
) +
ggtitle("Ranks over time") +
theme_ridges(center = TRUE)
My problem is I am unable to generate any Y axis tick values and the example doesn't display any either. Any ideas how to get Y axis tick marks to display?
Here is some sample data similar to mine:
## generating dataset
order_math<-c(1,2,1,2,3,3,1,2,3,1,2,3)
gender<-c("M","F","M","M","M","F","F","M","F","M","M","F")
time<-c(1,1,2,3,3,2,1,2,3,2,3,1)
sample<-data.frame(order_math,gender,time)
UPdate:
After #Tomasu's suggestions I have updated my code, but it does not run:
ggplot(math_dat, aes(x = order_math, y = time, color = gender, point_color = gender, fill = gender)) +
geom_density_ridges(
jittered_points = TRUE, scale = .95, rel_min_height = .01,
point_shape = "|", point_size = 3, size = 0.25,
position = position_points_jitter(height = 0)
) +
scale_y_reverse(limits = c(1000, 500, 100),expand = c(0, 0)) +
scale_x_continuous(expand = c(0, 0), name = "Rankings") +
scale_fill_manual(values = c("#D55E0050", "#0072B250"), labels = c("female", "male")) +
scale_color_manual(values = c("#D55E00", "#0072B2"), guide = "none") +
scale_discrete_manual("point_color", values = c("#D55E00", "#0072B2"), guide = "none") +
coord_cartesian(clip = "off") +
guides(fill = guide_legend(
override.aes = list(
fill = c("#D55E00A0", "#0072B2A0"),
color = NA, point_color = NA)
)
) +
ggtitle("Ranks over time") +
theme_ridges(center = TRUE)+
theme(
axis.ticks = element_line(size=0.5), # turn ticks back on
axis.ticks.length = grid::unit(5, "pt"), # set length
axis.ticks.y = element_line(colour = "red"), # define tick line color
axis.text.y = element_text(vjust = .4) # center text with tick
)
An easy solution to this problem would be to use a theme_ that includes the axis ticks as theme_ridges() has them turned off. Just removing that theme all together and using the base ggplot2 theme achieves the desired outcome.
However, let's say we still want to use theme_ridges() and just turn ticks back on. This can be achieved with a theme() edit after the theme_ridges().
I'm using the example in the link provided as I couldn't get your sample data to work properly.
library(ggplot2)
library(ggplot2movies)
library(ggridges)
ggplot(movies[movies$year>1912,], aes(x = length, y = year, group = year)) +
geom_density_ridges(scale = 10, size = 0.25, rel_min_height = 0.03) +
theme_ridges() +
theme(
axis.ticks = element_line(size=0.5), # turn ticks back on
axis.ticks.length = grid::unit(5, "pt"), # set length
axis.ticks.y = element_line(colour = "red"), # define tick line color
axis.text.y = element_text(vjust = .4) # center text with tick
) +
scale_x_continuous(limits = c(1, 200), expand = c(0, 0)) +
scale_y_reverse(
breaks = c(2000, 1980, 1960, 1940, 1920, 1900),
expand = c(0, 0)
) +
coord_cartesian(clip = "off")
Created on 2021-05-11 by the reprex package (v1.0.0)
I think your problem is that you need to specify the group.
Related thread: geom_density_ridges requires the following missing aesthetics: y
Extending on code from user tomasu's answer +1
library(ggridges)
library(ggplot2)
order_math<-c(1,2,1,2,3,3,1,2,3,1,2,3)
gender<-c("M","F","M","M","M","F","F","M","F","M","M","F")
time<-c(1,1,2,3,3,2,1,2,3,2,3,1)
sample<-data.frame(order_math,gender,time)
ggplot(sample, aes(x = order_math, y = time, group = time,
color = gender, point_color = gender, fill = gender)) +
geom_density_ridges() +
theme(
axis.ticks = element_line(size=0.5), # turn ticks back on
axis.ticks.length = grid::unit(5, "pt"), # set length
axis.ticks.y = element_line(colour = "red"), # define tick line color
axis.text.y = element_text(vjust = .4) # center text with tick
)
#> Picking joint bandwidth of 0.555
Created on 2021-05-12 by the reprex package (v2.0.0)
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 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)