I try to limit the x-axis of a plot with several figures to the current week. So if we are now in week 45 all weeks from 1 till 45 should be displayed, but not from 46 onwards. I cannot get any xlim command to work e.g. xlim(1,45) returns Error:
Discrete value supplied to continuous scale.
Perhaps this has to do with the fact that the variable week is a factor, but this is necessary for correct plotting (no decimals). Any solutions?
set.seed(1)
dat <- data.frame(object = sample(c("A","B","C","D"),100,replace = TRUE),
week = sample(c(1:52),100,replace = TRUE),
year = sample(c(2016,2017,2018),100,replace = TRUE),
count = sample(c(0:10),100,replace = TRUE))
ggplot(dat, aes(factor(week), count )) +
geom_bar(stat="identity" , aes(fill = factor(year)), position = position_dodge2(width = 0.9, preserve = "single")) +
facet_wrap(~ object, ncol = 2, scales = "free_y") +
labs(x = "Week", y = "Count") +
scale_fill_discrete(name = "Year")
You can restrict in data itself.
Try with the below code :
data<-dat%>% filter(week < format(Sys.Date(),"%V")) ## filtering based on current week
Plotting :
ggplot(data, aes(factor(week), count )) +
geom_bar(stat="identity" , aes(fill = factor(year)), position = position_dodge2(width = 0.9, preserve = "single")) +
facet_wrap(~ object, ncol = 2, scales = "free_y") +
labs(x = "Week", y = "Count") +
scale_fill_discrete(name = "Year") +theme(axis.text.x = element_text(angle = 45, vjust = 0.4))
Why not add a filter before the plot call for week:
set.seed(1)
dat <- data.frame(object = sample(c("A","B","C","D"),100,replace = TRUE),
week = sample(c(1:52),100,replace = TRUE),
year = sample(c(2016,2017,2018),100,replace = TRUE),
count = sample(c(0:10),100,replace = TRUE))
dat %>%
filter(week <= 45) %>% # add filter before plot
ggplot(aes(factor(week), count )) +
geom_bar(stat="identity" , aes(fill = factor(year)), position = position_dodge2(width = 0.9, preserve = "single")) +
facet_wrap(~ object, ncol = 2, scales = "free_y") +
labs(x = "Week", y = "Count") +
scale_fill_discrete(name = "Year")
Related
I have a dataframe where the first column has different types of bacteria, and the rest of the columns the samples, each sample belong to a specific time (T0, T1...) and the last 39 columns are the control group.
What I pretend is to plot each bacteria in one plot. And the plot must contain the different times in x-axis and the value in the y-axis (I was thinking in a bar plot or box plot with the errors coef.
Any idea about how can I group the data for different times and for different bacteria?
Here a small example of the data:
thanks!
structure(list(Bacteria = c("Methanobrevibacter", "Methanosphaera",
"Methanomassiliicoccus"), PIE2001_T0_TORUNDA = c(2.279974027,
0.670536115, -0.022611066), PIE2001_T1_TORUNDA = c(2.021643324,
-0.057798217, -0.057798217), PIE2001_T5_COMPL = c(2.788566988,
0.648500825, -0.044646356), PIE2006_T0_TORUNDA = c(0.07550014,
1.684938052, 0.07550014), PIE2007_T0_TORUNDA = c(2.072075243,
1.261145027, -0.125149334), PIE2007_T1_TORUNDA = c(2.601582257,
1.279826417, -0.106467944), PIE2007_T2 = c(2.81564899, 1.765826865,
-0.180083284), PIE2007_T3 = c(0.639040509, 3.081387545, -0.054106671
), PIE2013_T0_COMPLETA = c(2.683794403, -0.024255798, -0.024255798
), PIE2013_T1_COMPLETA = c(2.614756053, -0.024301277, -0.024301277
), PIE2013_T4_COMP = c(2.653056483, 0.013999154, 0.013999154),
PIE2013_T5_COMPL = c(1.861263144, -0.084647005, -0.084647005
), PIE2014_COMP = c(2.304771706, 1.005488722, -0.093123567
), PIE2016_T0_COMPLETA = c(-0.141271428, -0.141271428, -0.141271428
), PIE2016_T1_COMPLETA = c(-0.081696055, -0.081696055, -0.081696055
), PIE2016_T3 = c(-0.019385468, -0.019385468, -0.019385468
), PIE2016_T3_TOR = c(0.045856809, 0.045856809, 0.045856809
), PIE2017_T0_COMPLETA = c(4.493506636, 0.189441543, 0.189441543
), PIE2017_T1_COMPLETA = c(5.001671041, 0.71808448, 0.024937299
), PIE2017_T2_TOR = c(5.887191114, 0.672255357, -0.020891824
), PIE2017_T3 = c(3.306066839, 0.703377154, 0.010229973),
PIE2017_T4_COMP = c(5.560847286, 1.371192544, -0.015101817
), PIE2017_T5_COMPL = c(5.688626959, -0.025105846, -0.025105846
), PIE2018_T1 = c(0.158551089, 0.158551089, 0.158551089),
PIE2019_T1_COMPL = c(6.659430141, 0.833430034, 0.140282853
)), row.names = c(NA, 3L), class = "data.frame")
Script updated:
colnames(df)[363:401] <- gsub("T0", "T6", colnames(df)[363:401])
df %>%
pivot_longer(-Bacteria) %>%
mutate(group = gsub('_.*$', '', name),
time = gsub('^.*_(T\\d+).*$', '\\1', name)) %>%
filter(grepl('T\\d+', time)) %>%
ggplot(aes(time, value, fill = Bacteria)) +
geom_bar(stat = 'summary', fun = 'mean', position = 'dodge') +
stat_summary(fun.data = "mean_se", geom = "errorbar", width = 0.2, position = position_dodge(0.9)) +
theme_minimal() +
facet_grid(Bacteria ~ ., scale = 'free_y') +
scale_fill_brewer(palette = 'Set1') +
theme(panel.border = element_rect(fill = NA, color = 'gray75'))
You need to reshape your data. You can then do a comparative boxplot:
library(tidyverse)
df %>%
pivot_longer(-Bacteria) %>%
mutate(group = gsub('_.*$', '', name),
time = gsub('^.*_(T\\d+).*$', '\\1', name)) %>%
filter(grepl('T\\d+', time)) %>%
ggplot(aes(time, value, fill = Bacteria)) +
geom_boxplot() +
theme_minimal() +
facet_grid(Bacteria ~ ., scale = 'free_y') +
scale_fill_brewer(palette = 'Set2') +
theme(panel.border = element_rect(fill = NA, color = 'gray75'))
Pivot your data to long format, then facet by bacterium:
library(tidyr)
library(dplyr)
library(forcats)
library(ggplot2)
dat_long <- dat %>%
pivot_longer(!Bacteria, names_to = "sample") %>%
mutate(sample = fct_inorder(sample))
ggplot(dat_long, aes(sample, value)) +
geom_col() +
facet_wrap(vars(Bacteria), ncol = 1) +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
Or make a line graph with bacteria mapped to color:
ggplot(dat_long, aes(sample, value)) +
geom_line(aes(color = Bacteria, group = Bacteria)) +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
I have a ggplot with two y-axes by the sec.axis command, and I've been struggling with handling legends in these situations.
The code:
library(ggplot2)
library(ggrepel)
df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
total = rbinom(n=100,30,0.5),
prop = runif(100))
df <- df %>% arrange(df, by = day)
df$`percentage` <- label_percent(accuracy = 0.01)(df$prop)
ggplot(data = df,
aes(x = day, y = total)) +
geom_bar(aes(x = day, y = total), stat = "identity", fill = "lightgreen", width = 0.35) +
geom_line(data = df,
aes(x = day, y = (prop)*15, group = 1, color = prop),
color = "red", size = 1,inherit.aes = TRUE) +
scale_y_continuous(
labels = function(x) format(x, scientific = FALSE),
#breaks = seq(from = 0, to = 10000000,by = 100000),
sec.axis = sec_axis(trans = ~./15,
name = "Secondary axis",
breaks = seq(from = 0, to = 10, by = 0.1),
scales::percent))+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
geom_label_repel(data=df[nrow(df),],
aes(x = day,
y = prop*15,
label = round(prop*100,2)),
color = 'red',
nudge_x = 2,
segment.alpha = 0.5) +
scale_x_discrete(expand = expansion(add = c(0, 7)))
And I wanted to simply have the legend, instead of having the axis description, like this:
I know it seems reasonably easy to obtain, but given the fact that I don’t have any groups, I either: can't plot any legend what so ever; or I get plotted two squares (when I wanted the legend to consist of a line, with the geom_line color and a square with the geom_bar color).
With the code #divibisan provided, I get the following output:
Final update:
I finally found the solution. I still have no idea how I got a different output from what #divibisan posted, but here goes what worked for me:
library(dplyr)
library(ggplot2)
library(ggrepel)
df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
total = rbinom(n=100,30,0.5),
prop = runif(100))
df <- df %>% arrange(df, by = day)
df$`percentage` <- scales::label_percent(accuracy = 0.01)(df$prop)
ggplot(data = df,
aes(x = day, y = total)) +
geom_bar(aes(x = day, y = total, fill = "Total"), stat = "identity", width = 0.35) +
geom_line(data = df,
aes(x = day, y = (prop)*15, group = 1, color = 'Percentage'), size = 1,inherit.aes = TRUE) +
scale_y_continuous(
labels = function(x) format(x, scientific = FALSE),
#breaks = seq(from = 0, to = 10000000,by = 100000),
sec.axis = sec_axis(trans = ~./15,
name = "Secondary axis",
breaks = seq(from = 0, to = 10, by = 0.1),
scales::percent))+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
geom_label_repel(data=df[nrow(df),],
aes(x = day,
y = prop*15,
label = round(prop*100,2)),
color = 'red',
nudge_x = 2,
segment.alpha = 0.5) +
scale_x_discrete(expand = expansion(add = c(0, 7))) +
scale_fill_manual(values=c('Total' = 'lightgreen'), drop=TRUE, name='') +
scale_color_manual(values=c('Percentage' = "red"), drop=TRUE, name='') +
theme(legend.title = element_blank())
You just need to set the color/fill with a value in the aes, then use a scale function to set the color and create a legend. Here, we move the color= and fill= values from the bar and line into the aes. Then we add scale_fill/color_manual functions that set the color based on those names:
library(dplyr)
library(ggplot2)
library(ggrepel)
df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
total = rbinom(n=100,30,0.5),
prop = runif(100))
df <- df %>% arrange(df, by = day)
df$`percentage` <- scales::label_percent(accuracy = 0.01)(df$prop)
ggplot(data = df,
aes(x = day, y = total)) +
geom_bar(aes(x = day, y = total, fill = "Total"), stat = "identity", width = 0.35) +
geom_line(data = df,
aes(x = day, y = (prop)*15, group = 1, color = 'Percentage'), size = 1,inherit.aes = TRUE) +
scale_y_continuous(
labels = function(x) format(x, scientific = FALSE),
#breaks = seq(from = 0, to = 10000000,by = 100000),
sec.axis = sec_axis(trans = ~./15,
name = "Secondary axis",
breaks = seq(from = 0, to = 10, by = 0.1),
scales::percent))+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
geom_label_repel(data=df[nrow(df),],
aes(x = day,
y = prop*15,
label = round(prop*100,2)),
color = 'red',
nudge_x = 2,
segment.alpha = 0.5) +
scale_x_discrete(expand = expansion(add = c(0, 7))) +
scale_fill_manual(values=c('Total' = 'lightgreen', 'Percentage'='red'), drop=TRUE, name='') +
scale_color_manual(values=c('Total' = 'lightgreen', 'Percentage'='red'), drop=TRUE, name='')
If, for some reason, the drop argument isn't working and both colors show up in both scales, there's really no reason to include them in the scale if they're not expected to be there. Just only include the colors in the scale that are desired:
scale_fill_manual(values=c('Total' = 'lightgreen'), drop=TRUE, name='') +
scale_color_manual(values=c('Percentage'='red'), drop=TRUE, name='')
I am working with a ggplot that has two axis: one for the geom_bar component, and the other for the geom_linecomponent. And for this, I am using the sec.axis() command.
I wanted to insert a box to provide the last value of the geom_line component, but I am struggling because I believe that while using the commmand geom_label_repel, the aesthetic being used, is referent to the geom_barcomponent.
I'll provide a similar data to illustrate what I am saying.
df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
total = rbinom(n=100,30,0.5),
prop = runif(100))
df <- df %>% arrange(df, by = day)
df$`percentage` <- label_percent(accuracy = 0.01)(df$prop)
ggplot(data = df,
aes(x = day, y = total)) +
geom_bar(stat = "identity", fill = "lightgreen", width = 0.35) +
geom_line(data = df,
aes(x = day, y = (prop)*15, group = 1),
color = "red", size = 1,inherit.aes = TRUE) +
scale_y_continuous(
labels = function(x) format(x, scientific = FALSE),
#breaks = seq(from = 0, to = 10000000,by = 100000),
sec.axis = sec_axis(trans = ~./15,
name = "Secondary axis",
breaks = seq(from = 0, to = 10, by = 0.1),
scales::percent))+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
geom_label_repel(data=df[nrow(df),],
aes(x = day,
y = prop*1,
label = round(prop*100,2)),
color = 'red',
segment.alpha = 0.5) +
scale_x_discrete(expand = expansion(add = c(0, 7)))
Which outputs the following image:
As you can tell, it works well in regards to obtaining the last number of the prop column, which is intended, but it is not automatically placed beside the geom_line.
I have tried messing with the nudge_xand nudge_y commands but it didn't lead me to anywhere, given the fact that I want to have this "number placement" automatic.
Can anyone help?
The sec.axis is in some ways just decorative. ggplot is plotting everything by the main axis. To make the label follow the line, make the same transform as in your geom_line call (y = prop*15):
library(tidyverse)
library(ggrepel)
df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
total = rbinom(n=100,30,0.5),
prop = runif(100))
df <- df %>% arrange(df, by = day)
df$`percentage` <- scales::label_percent(accuracy = 0.01)(df$prop)
ggplot(data = df,
aes(x = day, y = total)) +
geom_bar(stat = "identity", fill = "lightgreen", width = 0.35) +
geom_line(data = df,
aes(x = day, y = (prop)*15, group = 1),
color = "red", size = 1,inherit.aes = TRUE) +
scale_y_continuous(
labels = function(x) format(x, scientific = FALSE),
#breaks = seq(from = 0, to = 10000000,by = 100000),
sec.axis = sec_axis(trans = ~./15,
name = "Secondary axis",
breaks = seq(from = 0, to = 10, by = 0.1),
scales::percent))+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
geom_label_repel(data=df[nrow(df),],
aes(x = day,
y = prop*15,
label = round(prop*100,2)),
color = 'red',
segment.alpha = 0.5) +
scale_x_discrete(expand = expansion(add = c(0, 7)))
#> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
#> ℹ Please use `linewidth` instead.
This question builds on from here:
Drawing a polar heatmap
> dput(names.d)
c("0050773", "0050774", "0050775", "0050776", "0050777", "0050778",
"0050779", "0050780", "0050781", "0050782", "0050783", "0050784",
"0050785", "0050786", "0050787", "0050788", "0050789", "0050790",
"0050808", "0050809", "0050810", "0050811", "0050812", "0050813",
"0050814", "0050818", "0050819", "0050820", "0050821", "0050822"
)
Based on this, I have come up with the following code:
set.seed(20220913)
arr <- matrix(runif(15*30), nrow = 30)
dff <- as.data.frame(arr)
names(dff) <- paste(sample(letters, replace = F), sample(letters, replace = F), sep = " ")[1:15]
library(tidyverse)
dff %>%
mutate(Site = seq(nrow(.))) %>%
pivot_longer(-Site, names_to = 'Species', values_to = 'Abundance') %>%
mutate(yval = match(Species, colnames(dff))) %>%
ggplot(aes(Site, yval, fill = Abundance)) +
geom_tile(color = "black") +
geom_text(aes(label = colnames(dff)), hjust = 1.1, size = 3,
data = data.frame(Site = 31.5, yval = 1:15, Abundance = 1)) +
coord_polar() +
scale_y_continuous(limits = c(-5, 15.5)) +
scale_x_continuous(limits = c(0.5, 31.5), breaks = 1:30, labels = names.d,
name = 'Breeding site') +
scale_fill_gradientn(colors = colorRampPalette(RColorBrewer::brewer.pal(name = "YlOrRd", n = 9))(25), values = 0:1, labels = scales::percent)+
theme_void(base_size = 16) +
theme(axis.text.x = element_text(size = 12),
axis.title.x = element_text())
which gives me the following figure:
Which is great, but I would like the labels on the rim of the figure to radiate out (or be tangent, for that matter). So, I wrote the angles as:
ang <- 1:30/31.5*360
However, I can not see where to pass this argument. Looking around, it would normally be in the aes function, but there the labels are for the y-axis in the figure (before being changed to the polar coordinates), and what I am wanting rotated should be in the x-axis. So, how do I do this? Thanks for any suggestions!
You can add this in the axis.text.x = element_text() :
ang <- 90 - (1:30/31.5*360)
dff %>%
mutate(Site = seq(nrow(.))) %>%
pivot_longer(-Site, names_to = 'Species', values_to = 'Abundance') %>%
mutate(yval = match(Species, colnames(dff))) %>%
ggplot(aes(Site, yval, fill = Abundance)) +
geom_tile(color = "black") +
geom_text(aes(label = colnames(dff)), hjust = 1.1, size = 3,
data = data.frame(Site = 31.5, yval = 1:15, Abundance = 1)) +
coord_polar() +
scale_y_continuous(limits = c(-5, 15.5)) +
scale_x_continuous(limits = c(0.5, 31.5), breaks = 1:30, labels = names.d,
name = 'Breeding site') +
scale_fill_gradientn(colors = colorRampPalette(RColorBrewer::brewer.pal(name = "YlOrRd", n = 9))(25), values = 0:1, labels = scales::percent)+
theme_void(base_size = 16) +
theme(axis.text.x = element_text(size = 12, angle = ang),
axis.title.x = element_text())
This question already has answers here:
Create a split violin plot with paired points and proper orientation
(2 answers)
Closed 10 months ago.
This post was edited and submitted for review 10 months ago and failed to reopen the post:
Original close reason(s) were not resolved
In this article: https://www.nature.com/articles/s41591-022-01744-z.epdf
I noticed an interesting plot:
2
Is there a simple way to do this in R?
EDIT: I am aware there are similar questions but none deal with the color-coding scheme that marks the improved / worsened cases.
The see package has a half violin geom like that:
ggplot(data = data.frame(id = rep(1:10, 2),
time = rep(c("A", "B"), each = 10),
value = runif(20)),
aes(time, value)) +
see::geom_violinhalf(aes(group = time, fill = time),
trim = FALSE, flip = 1, alpha = 0.2) +
geom_point(aes(color = time)) +
geom_line(aes(group = id))
You can get arbitrarily close to a chosen chart using ggplot:
ggplot(df, aes(xval, modularity, color = group)) +
geom_polygon(data = densdf, aes( x = y, y = x, fill = group), colour = NA) +
scale_fill_manual(values = c('#c2c2c2', '#fbc5b4')) +
scale_color_manual(values = c('#676767', '#ef453e')) +
geom_path(data = densdf, aes(x = y, y = x), size = 2) +
geom_segment(color = '#c2c2c2', inherit.aes = FALSE, size = 1.5,
data = df2[df2$`Post-treatment` > df2$Baseline,], alpha = 0.8,
aes(x = 1, xend = 2, y = Baseline, yend = `Post-treatment`)) +
geom_segment(color = '#ef453e', inherit.aes = FALSE, size = 1.5, alpha = 0.8,
data = df2[df2$`Post-treatment` < df2$Baseline,],
aes(x = 1, xend = 2, y = Baseline, yend = `Post-treatment`)) +
geom_point(size = 3) +
theme_classic() +
scale_x_continuous(breaks = 1:2, labels = c('Baseline', 'Post-treatment'),
name = '', expand = c(0.3, 0)) +
theme(legend.position = 'none',
text = element_text(size = 18, face = 2),
panel.background = element_rect(fill = NA, color = 'black', size = 1.5))
As long as you are prepared to do some work getting your data into the right format:
set.seed(4)
mod <- c(rnorm(16, 2.5, 0.25))
df <- data.frame(modularity = c(mod, mod + rnorm(16, -0.25, 0.2)),
xval = rep(c(1, 2), each = 16),
group = rep(c('Baseline', 'Post-treatment'), each = 16),
id = factor(rep(1:16, 2)))
df2 <- df %>% tidyr::pivot_wider(id_cols = id, names_from = group,
values_from = modularity)
BLdens <- as.data.frame(density(df$modularity[1:16])[c('x', 'y')])
PTdens <- as.data.frame(density(df$modularity[17:32])[c('x', 'y')])
BLdens$y <- 1 - 0.25 * BLdens$y
PTdens$y <- 2 + 0.25 * PTdens$y
densdf <- rbind(BLdens, PTdens)
densdf$group <- rep(c('Baseline', 'Post-treatment'), each = nrow(BLdens))