I'm trying to combine two heatmaps. I want var_a and var_x on the y axis with for example: var_a first and then var_x. I don't know if I should do this by changing the dataframe or combining them, or if I can do this in ggplot.
Below I have some example code and a drawing of what I want (since I don't know if I explained it right).
I hope someone has ideas how I can do this either in the dataframe or in ggplot!
Example code:
df_one <- data.frame(
vars = c("var_a", "var_b", "var_c"),
corresponding_vars = c("var_x", "var_y", "var_z"),
expression_organ_1_vars = c(5, 10, 20),
expression_organ_2_vars = c(50, 2, 10),
expression_organ_3_vars = c(5, 10, 3)
)
df_one_long <- pivot_longer(df_one,
cols=3:5,
names_to = "tissueType",
values_to = "Expression")
expression.df_one <- ggplot(df_one_long,
mapping = aes(y=tissueType, x=vars, fill = Expression)) +
geom_tile()
expression.df_one
df_two <- data.frame(
corresponding_vars = c("var_x", "var_y", "var_z"),
expression_organ_1_corresponding_vars = c(100, 320, 120),
expression_organ_2_corresponding_vars = c(23, 30, 150),
expression_organ_3_corresponding_vars = c(89, 7, 200)
)
df_two_long <- pivot_longer(df_one,
cols=3:5,
names_to = "tissueType",
values_to = "Expression")
expression.df_two <- ggplot(df_two_long,
mapping = aes(y=tissueType, x=vars, fill = Expression)) +
geom_tile()
expression.df_two
Drawing:
You can bind your data frames together and pivot into a longer format so that vars and corresponding vars are in the same column, but retain a grouping variable to facet by:
df_two %>%
mutate(cor = corresponding_vars) %>%
rename_with(~sub('corresponding_', '', .x)) %>%
bind_rows(df_one %>% rename(cor = corresponding_vars)) %>%
pivot_longer(contains('expression'), names_to = 'organ') %>%
mutate(organ = gsub('expression_|_vars', '', organ)) %>%
group_by(cor) %>%
summarize(vars = vars, organ = organ, value = value,
cor = paste(sort(unique(vars)), collapse = ' cor ')) %>%
ggplot(aes(vars, organ, fill = value)) +
geom_tile(color = 'white', linewidth = 1) +
facet_grid(.~cor, scales = 'free_x', switch = 'x') +
scale_fill_viridis_c() +
coord_cartesian(clip = 'off') +
scale_x_discrete(expand = c(0, 0)) +
theme_minimal(base_size = 16) +
theme(strip.placement = 'outside',
axis.text.x = element_blank(),
axis.ticks.x.bottom = element_line(),
panel.spacing.x = unit(3, 'mm'))
Okay, so I solved the issue for my own project, which is to convert it to a scatter plot. I combined both datasets and then used a simple scatterplot.
df.combined <- dplyr::full_join(df_two_long, df_one_long,
by = c("vars", "corresponding_vars", "tissueType"))
ggplot(df.combined,
aes(x=vars, y=tissueType, colour=Expression.x, size = Expression.y)) +
geom_point()
It's not a solution with heatmaps, but I don't know how to do that at the moment.
Related
so basically I have created the bins and the have the means of each bin, having these two columns in a dataframe. Now I am plotting these two columns, but I want the exact number as x lable instead of bins. I am considering renaming each bin by its mid-point. please look at the pictures. The first one is my current plot and the second is the plot I want to acheive.
my current plot:
what I want to have:
my data frame is like this:
To reproduce the style of the plot image you included, you can do:
library(tidyverse)
df %>%
mutate(bin_group = gsub("\\(|\\]", "", bin_group)) %>%
separate(bin_group, sep = ",", into = c("lower", "upper")) %>%
mutate(across(lower:upper, as.numeric)) %>%
mutate(`Birth weight (g)` = (upper + lower) / 2) %>%
ggplot(aes(`Birth weight (g)`, mean_28_day_mortality)) +
geom_vline(xintercept = 1500) +
geom_point(shape = 18, size = 4) +
scale_x_continuous(labels = scales::comma) +
labs(title = "One-year mortality", y = NULL) +
theme_bw(base_family = "serif", base_size = 20) +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major.y = element_line(color = "black", size = 0.5),
plot.title = element_text(hjust = 0.5))
Edit
To make the specific changes to the range use the limits argument in scale_x_continuous and scale_y_continuous you can do:
library(tidyverse)
df %>%
mutate(bin_group = gsub("\\(|\\]", "", bin_group)) %>%
separate(bin_group, sep = ",", into = c("lower", "upper")) %>%
mutate(across(lower:upper, as.numeric)) %>%
mutate(`Birth weight (g)` = (upper + lower) / 2) %>%
ggplot(aes(`Birth weight (g)`, mean_28_day_mortality)) +
geom_vline(xintercept = 1500) +
geom_point(shape = 18, size = 4) +
scale_x_continuous(labels = scales::comma, limits = c(1350, 1650),
breaks = seq(1350, 1650, 50)) +
scale_y_continuous(limits = c(0, 0.1), name = NULL) +
labs(title = "One-year mortality") +
theme_bw(base_family = "serif", base_size = 20) +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major.y = element_line(color = "black", size = 0.5),
plot.title = element_text(hjust = 0.5))
Data used (obtained from image in question using OCR)
df <- structure(list(bin_group = structure(1:10,
levels = c("(1.35e+03,1.38e+03]",
"(1.38e+03,1.41e+03]", "(1.41e+03,1.44e+03]", "(1.44e+03,1.47e+03]",
"(1.47e+03,1.5e+03]", "(1.5e+03,1.53e+03]", "(1.53e+03,1.56e+03]",
"(1.56e+03,1.59e+03]", "(1.59e+03,1.62e+03]", "(1.62e+03,1.65e+03]"
), class = "factor"), mean_28_day_mortality = c(0.0563498, 0.04886257,
0.04467626, 0.04256053, 0.04248667, 0.04009187, 0.03625538, 0.03455094,
0.03349542, 0.02892909)), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -10L))
If you have groups that (I assume) you made with cut, you could pull out the max and min and then calc the mean before you summarize and plot. Note that I made the regex pretty long because I don't personally know if cut always makes left or inclusive or exclusive.
library(tidyverse)
#example like yours
mtcars |>
mutate(grp = cut(hp, 10)) |>
group_by(grp) |>
summarise(mpg_mean = mean(mpg)) |>
ggplot(aes(grp, mpg_mean))+
geom_point()
#solution
mtcars |>
mutate(grp = cut(hp, 10)) |>
extract(grp,
into = c("min", "max"),
remove = FALSE,
regex = "(?:\\(|\\[)(.*),(.*)(?:\\)|\\])",
convert = TRUE) |>
mutate(mean_grp = (min + max)/2)|>
group_by(mean_grp) |>
summarise(mpg_mean = mean(mpg)) |>
ggplot(aes(mean_grp, mpg_mean))+
geom_point()
EDIT
here is another option if you just want to re-label and not actually transform the data:
lab_fun <- function(x){
str_split(x, ",") |>
map_dbl(~parse_number(.x)
|> mean())
}
mtcars |>
mutate(grp = cut(hp, 10)) |>
group_by(grp) |>
summarise(mpg_mean = mean(mpg)) |>
ggplot(aes(grp, mpg_mean))+
geom_point()+
scale_x_discrete(labels = lab_fun)
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())
I want text labels were above or under of bar cap depending on where is more space for them. Now it's always down which is not always looks good:
Here is my code:
library(tidyr)
library(ggplot2)
library(dplyr)
library(stringr)
library(purrr)
numa.nodes <- tibble (
numa_name = c("numa_01","numa_01","numa_01","numa_01","numa_01","numa_01","numa_02","numa_02","numa_02","numa_02"),
counter_name =c("cpu01","cpu02","cpu03","cpu04","memory_used","memory_total","cpu01","cpu02","memory_used","memory_total"),
value = c(sample(0:100,4), sample(0:32,1), 32, sample(0:100,1), sample(0:100,1), sample(0:128,1), 128)
)
numa.nodes <- numa.nodes %>% add_row(
numa_name = c("numa_03","numa_03","numa_03","numa_03","numa_03","numa_03","numa_04","numa_04","numa_04","numa_04"),
counter_name =c("cpu01","cpu02","cpu03","cpu04","memory_used","memory_total","cpu01","cpu02","memory_used","memory_total"),
value = c(sample(0:100,4), sample(0:32,1), 32, sample(0:100,1), sample(0:100,1), sample(0:128,1), 128)
)
numa.nodes <- numa.nodes %>% add_row(
numa_name = c("numa_05","numa_05","numa_05","numa_05","numa_05","numa_05","numa_05"),
counter_name =c("cpu01","cpu02","cpu03","cpu04","cpu05","memory_used","memory_total"),
value = c(sample(1:100,5), sample(1:64,1), 64)
)
numa.nodes <- numa.nodes %>% mutate(counter_name=factor(counter_name,levels = unique(counter_name),ordered = T))
memory_columns <- numa.nodes %>% filter(counter_name=='memory_total')
memory_y_scale <- max(memory_columns$value, na.rm = TRUE) + 6
plot_numa = function(num){
df = numa.nodes %>% filter(str_detect(numa_name, num))
cpu_plot = df %>%
filter(str_detect(counter_name, "cpu")) %>%
ggplot(aes(x = counter_name)) +
geom_col(aes(y = 100), fill = "white", color = "black") +
geom_col(aes(y = value), fill = "#00AFBB", color = "black") +
geom_text(aes(y = value, label = paste0(value,"%")), nudge_y = 5, color = "black") +
theme_bw() +
labs(x = "CPU", y = "")
memory_plot = df %>%
filter(str_detect(counter_name, "memory")) %>%
pivot_wider(names_from = counter_name, values_from = value) %>%
ggplot(aes(x = "") ) +
geom_col(aes(y = memory_total), fill = "white", color = "black") +
geom_col(aes(y = memory_used), fill = "#FC4E07", color = "black") +
geom_text(aes(label = paste(memory_total, "GB"), y = memory_total), nudge_y = 5, color = "black") +
geom_text(aes(label = paste(memory_used, "GB"), y = memory_used), nudge_y = -3, color = "black") +
theme_bw() +
ylim(0, memory_y_scale) +
labs(x = "Memory", y = "")
ggpubr::ggarrange(cpu_plot, memory_plot, ncol = 2) %>% ggpubr::annotate_figure(top = paste("NUMA",num))
}
numa_numbers <- unique(numa.nodes$numa_name) %>% str_remove ("numa_")
ggpubr::ggarrange(plotlist = map(.x = numa_numbers, .f = ~plot_numa(num = .x)))
I tried to change this line:
geom_text(aes(label = paste(memory_used, "GB"), y = memory_used), nudge_y = -3, color = "black")
to something like that:
geom_text(aes(label = paste(memory_used, "GB"), y = memory_used),nudge_y = ifelse( (memory_total-memory_used) > 10, 5, -3)
, color = "black")
But I've got an error:
Error in ifelse((memory_total - memory_used) > 10, 5, -3) :
object 'memory_total' not found
Is there a better way to print labels optimal way?
What am I doing wrong?
How to change color of label to more contrast ie black on white, white on red?
Think of it this way: The nudge value will be different (potentially) for every observation in your data frame. That means that this is something that should be handled within aes(), where stuff is designed to change with your data, rather than nudge_y, which is designed to be a constant (and complains if used otherwise).
So, the solution is to do away entirely with nudge_y and build your ifelse() statement directly into aes(y=...).
In this case, here's the replacement for that particular geom_text() line:
# to see the same plot posted here, put this at the top of your code
set.seed(7331)
...
# plot code...
... +
geom_text(aes(
label = paste(memory_used, "GB"),
y = ifelse((memory_total-memory_used > 10), memory_used + 5, memory_used - 3)),
color = "black") +
Not specific to any particular piece of code, is there a relatively straightforward way to change the color of the text in a geom_label_repel box?
Specifically, I have code that produces the below chart
The percentage in the label box is the percent change in 7-day moving average for the most recent week over the week prior. I'd simply like to color the text red when the value is positive and green when it is negative.
The dataframe for this chart can be copied from here.
The plot code is
#endpoint layer
BaseEndpoints <- smDailyBaseData %>% filter(Base %in% AFMCbases) %>%
group_by(Base) %>%
filter(DaysSince == max(DaysSince)) %>%
select(Base, abbv, DaysSince, newRate,label) %>%
ungroup()
ZoomEndpoints <- BaseEndpoints %>% filter(Base != 'Edwards') %>%
mutate(zoom = TRUE)
CAEndPoint <- BaseEndpoints %>% filter(Base == 'Edwards') %>%
mutate(zoom = FALSE)
ZoomEndpoints <- rbind(ZoomEndpoints, CAEndPoint)
BasePlot <- smDailyBaseData %>% filter(Base %in% AFMCbases) %>%
ggplot(mapping = aes(x = as.numeric(DaysSince), y = newRate)) +
geom_line(aes(color=abbv),show.legend = FALSE) +
scale_color_ucscgb() +
geom_point(data = BaseEndpoints,size = 1.5,shape = 21,
aes(color = abbv,fill = abbv), show.legend = FALSE) +
geom_label_repel(data=ZoomEndpoints, aes(label=label), show.legend = FALSE,
vjust = 0, xlim=c(105,200), size=3, direction='y') +
labs(x = "Days Since First Confirmed Case",
y = "% Local Population Infected Daily") +
theme(plot.title = element_text(size = rel(1), face = "bold"),
plot.subtitle = element_text(size = rel(0.7)),
plot.caption = element_text(size = rel(1))) +
facet_zoom(xlim = c(50,120), ylim=c(0,0.011),zoom.data=zoom)
print(BasePlot)
Yes, it's as simple as this:
library(ggplot2)
df <- data.frame(x = c(-1, -1, 1, 1), y = c(-1, 1, 1, -1), value = c(-2, -1, 1, 2))
ggplot(df, aes(x, y)) +
geom_point(size = 3) +
ggrepel::geom_label_repel(aes(label = value, colour = factor(sign(value)))) +
lims(x = c(-100, 100), y = c(-100, 100)) +
scale_colour_manual(values = c("red", "forestgreen"))
EDIT
Now we have a more concrete example, I can see the problem more clearly. There are workarounds such as using ggnewscale or a hand-crafted solution such as Ian Campbell's thorough example. Personally, I would just note that you haven't used the fill scale yet, and this looks pretty good to my eye:
Here's a bit of a hacky solution since you can't have two scale_color_*'s at the same time:
The approach centers on manually assigning the color outside of aes in the geom_label_repel call. Adding one to the grepl result that searches for the minus sign in the label allows you to subset the two colors. You need two colors for each label, I assume for the box and for the text, so I used rep.
smDailyBaseData %>%
ggplot(mapping = aes(x = as.numeric(DaysSince), y = newRate)) +
geom_line(aes(color=abbv),show.legend = FALSE) +
scale_color_ucscgb() +
geom_point(data = BaseEndpoints,size = 1.5,shape = 21,
aes(color = abbv,fill = abbv), show.legend = FALSE) +
geom_label_repel(data=ZoomEndpoints, aes(label=label),
color = rep(c("green","red")[1+grepl("\\-\\d",as.factor(ZoomEndpoints$label))],times = 2),
show.legend = FALSE, vjust = 0, xlim=c(105,200),
size=3, direction='y') +
labs(x = "Days Since First Confirmed Case",
y = "% Local Population Infected Daily") +
theme(plot.title = element_text(size = rel(1), face = "bold"),
plot.subtitle = element_text(size = rel(0.7)),
plot.caption = element_text(size = rel(1))) +
facet_zoom(xlim = c(50,120), ylim=c(0,0.011),zoom.data=zoom)
Data Setup
#source("https://pastebin.com/raw/Vn2abQ4a")
BaseEndpoints <- smDailyBaseData %>%
group_by(Base) %>%
dplyr::filter(DaysSince == max(DaysSince)) %>%
dplyr::select(Base, abbv, DaysSince, newRate,label) %>%
ungroup()
ZoomEndpoints <- BaseEndpoints %>% filter(Base != 'Edwards') %>%
mutate(zoom = TRUE)
CAEndPoint <- BaseEndpoints %>% filter(Base == 'Edwards') %>%
mutate(zoom = FALSE)
ZoomEndpoints <- rbind(ZoomEndpoints, CAEndPoint)
I am trying to create boxplot that would compare ob vs A and B at multiple location (I.e., Start, Mid, End) for Top and Low values (10% in this case). I am trying to use the gather, facet_wrap, grid.arrange, ggplot functionality in R but can not put things together. here is my code so far- I would appreciate help moving forward.
library(tidyverse)
library(gridExtra)
DF_1 = data.frame(Ob = runif(100, 10,80), A = runif(100, 5, 90), B = runif(100, 3,85), loc = rep("Start",100))
DF_2 = data.frame(Ob = runif(100, 10,80), A = runif(100, 5, 90), B = runif(100, 3,85), loc = rep("Mid",100))
DF_3 = data.frame(Ob = runif(100, 10,80), A = runif(100, 5, 90), B = runif(100, 3,85), loc = rep("End",100))
DF_1_Top = DF_1[order(DF_1$Ob,decreasing = TRUE),][1:10,]
DF_1_Low = DF_1[order(DF_1$Ob,decreasing = FALSE),][1:10,]
DF_2_Top = DF_2[order(DF_2$Ob,decreasing = TRUE),][1:10,]
DF_2_Low = DF_2[order(DF_2$Ob,decreasing = FALSE),][1:10,]
DF_3_Top = DF_1[order(DF_3$Ob,decreasing = TRUE),][1:10,]
DF_3_Low = DF_1[order(DF_3$Ob,decreasing = FALSE),][1:10,]
DF_Top = rbind(DF_1_Top, DF_2_Top, DF_3_Top)
DF_Low = rbind(DF_1_Low, DF_2_Low, DF_3_Low)
DF_T = gather(DF_Top, key = "Variable", value = "Value", - "loc")
DF_L = gather(DF_Low, key = "Variable", value = "Value", - "loc")
P1 = ggplot(DF_T, aes(x = Variable, y = Value))+
geom_boxplot()+facet_wrap(~loc, nrow = 1)
P2 = ggplot(DF_L, aes(x = Variable, y = Value))+
geom_boxplot()+facet_wrap(~loc, nrow = 1)
grid.arrange(P1,P2, nrow = 2)
Here is a manually drawn figure that i would like to achieve
You could stack all of your data into a single data frame and create a single graph. For example:
d = bind_rows(High=DF_Top, Low=DF_Low, .id='source') %>%
mutate(source=factor(source, levels=c("High","Low")))
d %>%
gather(key, value, Ob:B) %>%
mutate(key = fct_relevel(key, "Ob")) %>%
ggplot(aes(key, value)) +
geom_hline(yintercept=0) +
geom_boxplot() +
facet_grid(source ~ loc, switch="x") +
labs(x="", y="") +
scale_y_continuous(expand=expand_scale(mult=c(0.0, 0.02))) +
theme_classic() +
theme(strip.placement="outside",
strip.background.x=element_rect(colour=NA, fill=NA),
strip.text.x=element_text(size=11, face="bold"))
Responding to your comment, I'm not wild about moving the key labels to a legend, but...
d %>%
gather(key, value, Ob:B) %>%
mutate(key = fct_relevel(key, "Ob")) %>%
ggplot(aes(loc, value, colour=key)) +
geom_hline(yintercept=0) +
geom_boxplot() +
facet_grid(source ~ ., switch="x") +
labs(x="", y="", colour="") +
scale_y_continuous(expand=expand_scale(mult=c(0.0, 0.02))) +
theme_classic() +
theme(legend.position="bottom",
legend.box.margin=margin(t=-20))