R ggplot // Multiple Grouping in X-axis - r

I am trying to implement the diagram 1 from Excel to Shiny. So far I got this code with the resulting diagram 2.
ggplot(filteredData(), aes(x=interaction(month, year), y=sum))
+ geom_bar(stat="identity") + facet_grid(. ~ X) + theme(legend.position="none")
I want to group month and year like in the Excel example, so hat you have only the month counter ("1", "2", ...) in the first row of the legend and the year ("2016", "2017", ...) in the second. The number of months can vary.
The data set looks like:
X year month sum
10 2016 1 450
10 2016 2 670
... ... ... ...
10 2017 1 200
11 2016 1 460

I slightly changed the data set, this is the closest I got to your specs:
df <- read.table(text = "X year month sum
10 2016 1 450
10 2016 2 670
10 2017 1 200
11 2016 1 460
11 2017 2 500", header = T)
# Notice the variable type for month and year
df$month <- as.factor(df$month)
df$year <- as.factor(df$year)
df$X <- as.factor(df$X)
ggplot(df, aes(x = month, y = sum)) + geom_bar(stat = "identity") +
facet_grid(.~X + year,
switch = "x", # Moves the labels from the top to the bottom
labeller = label_both # Adds the labels to the year and X variables
) +
xlab("") # Removes the month label
Result:
Or if you want to drop unused levels:
ggplot(df, aes(x = month, y = sum)) + geom_bar(stat = "identity") +
facet_grid(.~X + year,
switch = "x", # Moves the labels from the top to the bottom
labeller = label_both, # Adds the labels to the year and X variables
scales = "free_x") +
xlab("") # Removes the month legend

You can get a little more complex and use cowplot to merge the plots together. You could automate this using lapply to loop through your unique values, though that is probably overkill for just two groups.
library(ggplot2)
library(cowplot)
library(dplyr)
# Return to default theme, as cowplot sets its own
theme_set(theme_gray())
# Save y limits to get same scale
myYlims <- c(0, ceiling(max(df$sum)/100)*100)
# Generate each plot
x10 <-
ggplot(df %>%
filter(X == 10)
, aes(x = month, y = sum)) + geom_bar(stat = "identity") +
facet_grid(~ year,
switch = "x") +
panel_border() +
coord_cartesian(ylim = myYlims) +
xlab("X = 10")
x11 <-
ggplot(df %>%
filter(X == 11)
, aes(x = month, y = sum)) + geom_bar(stat = "identity") +
facet_grid(~ year,
switch = "x") +
panel_border() +
coord_cartesian(ylim = myYlims) +
xlab("X = 11")
# Put the plots together
plot_grid(x10
, x11 +
theme(axis.title.y = element_blank()
, axis.text.y = element_blank()
, axis.ticks.y = element_blank())
, rel_widths = c(1.1,1)
)
Here is an approach to automate this, including more complex data to justify the automation. Note that you will need to play with the aspect ratio of your output and with the rel_widths option to make it look decent:
df <-
data.frame(
X = rep(1:6, each = 9)
, year = rep(rep(2016:2018, each = 3),3)
, month = rep(1:3, 6)
, sum = rnorm(9*6, 700, 100)
)
# Notice the variable type for month and year
df$month <- as.factor(df$month)
df$year <- as.factor(df$year)
df$X <- as.factor(df$X)
# Save y limits to get same scale
myYlims <- c(0, ceiling(max(df$sum)/100)*100)
# Generate each plot
eachPlot <- lapply(levels(df$X), function(thisX){
ggplot(df %>%
filter(X == thisX)
, aes(x = month, y = sum)) +
geom_bar(stat = "identity") +
facet_grid(~ year,
switch = "x") +
panel_border() +
coord_cartesian(ylim = myYlims) +
xlab(paste("X =", thisX))
})
# Remove axes from all but the first
eachPlot[-1] <- lapply(eachPlot[-1], function(x){
x +
theme(axis.title.y = element_blank()
, axis.text.y = element_blank()
, axis.ticks.y = element_blank()
)
})
# Put the plots together
plot_grid(plotlist = eachPlot
, rel_widths = c(1.4, rep(1, length(eachPlot)-1))
, nrow = 1
)

Related

Highlight top 3 values

I have a plot like this with the following code:
aus_cases <- ggplot(data = daily_cases,aes(x= date, as.numeric(V1)))+
geom_col(fill = 'blue', alpha= 0.6)+
theme_minimal(base_size =14)+
xlab(NULL)+
ylab(NULL)+
theme_bw()+
scale_x_date(date_labels = "%d/%m/%Y")
And I wanted to highlight the top 3 value within the plot and show the date in the plot as well, was thinking to use gghighlight but am not sure how to do it.
Using the ggplot2::economics dataset as example data you can highlight and label the top 3 values like so:
Add an indicator for the top3 values to your df using e.g. the rank function.
To highlight, map the top3 indicator on fill.
To add the dates use geom_text to add labels only for the top3 values
Try this:
library(ggplot2)
library(dplyr)
# Example data
d <- filter(economics, date >= as.Date("2010-01-01"))
# Add top3 indicator
d <- mutate(d, top3 = rank(-psavert) %in% 1:3)
ggplot(data = d, aes(date, psavert, fill = top3)) +
geom_col(alpha = 0.6) +
geom_text(aes(label = ifelse(top3, as.character(date), "")), nudge_y = .1) +
scale_fill_manual(values = c("TRUE" = "red", "FALSE" = "blue")) +
theme_minimal(base_size = 14) +
xlab(NULL) +
ylab(NULL) +
theme_bw() +
scale_x_date(date_labels = "%d/%m/%Y")
Here is one way to do it. You didn't dputyour data, therefore I used this test data.
library(lubridate)
library(tidyverse)
library(gghighlight)
daily_cases <- data.frame(V1 = c(10,20,30, 10, 5, 10, 10, 40, 50, 10),
date = ymd("2020-02-01", "2020-02-02",
"2020-02-03","2020-02-04",
"2020-02-05","2020-02-06",
"2020-02-07","2020-02-08",
"2020-02-09","2020-02-10"))
At first I specified the top 3 values and their date in top. And used these information in ggplot in gghighlight (highlighting the three bars) and scale_x_date (just show the dates of the highlightes bars).
top <- daily_cases %>%
arrange(desc(V1)) %>%
slice(1:3)
aus_cases <- ggplot(data = daily_cases,aes(x= date, as.numeric(V1)))+
geom_col(fill = 'blue', alpha= 0.6)+
gghighlight(V1 >= min(top$V1)) +
theme_minimal(base_size = 14)+
xlab(NULL)+
ylab(NULL)+
theme_bw()+
scale_x_date(breaks = top$date, date_labels = "%d/%m/%Y")
Here is the plot.

Plot labels at ends of lines in stacked area chart

I have the following code
library(ggplot2)
library(dplyr)
# create data
time <- as.numeric(rep(seq(1,7),each=7)) # x Axis
value <- runif(49, 10, 100) # y Axis
group <- rep(LETTERS[1:7],times=7) # group, one shape per group
data <- data.frame(time, value, group)
# stacked area chart
ggplot(data, aes(x=time, y=value, fill=group)) +
geom_area()+
geom_text(data = data %>% filter(time == last(time)), aes(label = group,
x = time + 0.5,
y = value,
color = group)) +
guides(color = FALSE) + theme_bw() +
scale_x_continuous(breaks = scales::pretty_breaks(10))
Where i get
But i am aiming for link
Is there any solution for stacked area plot?
The question code is plotting the text labels in the value's of the last time, when in fact the areas are cumulative. And in reverse order.
Also, the following graph plots data created with the same code but with
set.seed(1234)
Then the data creation code is the same as in the question.
# stacked area chart
ggplot(data, aes(x=time, y=value, fill=group)) +
geom_area()+
geom_text(data = data %>%
filter(time == last(time)) %>%
mutate(value = cumsum(rev(value))),
aes(label = rev(group),
x = time + 0.5,
y = value,
color = rev(group))) +
guides(color = FALSE) + theme_bw() +
scale_x_continuous(breaks = scales::pretty_breaks(10))
Edit.
Following the discussion in the comments to this answer, I have decided to post code based on the comment by user Jake Kaupp.
ggplot(data, aes(x = time, y = value, fill = group)) +
geom_area()+
geom_text(data = data %>% filter(time == last(time)),
aes(x = time + 0.5, y = value,
label = rev(group), color = rev(group)),
position = position_stack(vjust = 0.5)) +
guides(color = FALSE) +
theme_bw() +
scale_x_continuous(breaks = scales::pretty_breaks(10))
You can use the text function to put text wherever you want. For example:
text(7.2, 350, "B", col="brown")
Here we go
time <- as.numeric(rep(seq(1,7),each=8)) # x Axis
value <- runif(56, 10, 100) # y Axis
group <- rep(LETTERS[1:8],times=7) # group, one shape per group
data <- data.frame(time, value, group)
round_df <- function(x, digits) {
# round all numeric variables
# x: data frame
# digits: number of digits to round
numeric_columns <- sapply(x, mode) == 'numeric'
x[numeric_columns] <- round(x[numeric_columns], digits)
x
}
data$value<- round_df(data$value, 2)
# stacked area chart
ggplot(data, aes(x=time, y=value, fill=group)) +
geom_area()+
geom_text(aes(x = time + 0.5, y = value, label=ifelse(time == max(time), group, NA)),position = position_stack(vjust = 0.5),check_overlap = TRUE)+
guides(color = FALSE) + theme_bw()+
scale_x_continuous(breaks = scales::pretty_breaks(10)) +
geom_text(aes(label=ifelse(time != min(time) & time != max(time),value, NA)),position = position_stack(vjust = 0.5),check_overlap = TRUE)+
geom_text(aes(x = time + 0.18,label=ifelse(time == min(time),value, NA)),position = position_stack(vjust = 0.5),check_overlap = TRUE)+
geom_text(aes(x = time - 0.18,label=ifelse(time == max(time),value, NA)),position = position_stack(vjust = 0.5),check_overlap = TRUE)
And get
Factor levels but why not letters? That is the next step :)
UPDATED
just converted factor to char data$group <- as.character(data$group)

ggplot bar plot comparing multiple groups with a reference group

dat <-
data.frame(keyId = rep(c('A','B','C','D'), times = 4),
group = rep(1:4, each = 4),
value = sample(1:100, 16))
ggplot(dat, aes(x = as.factor(keyId), y = as.factor(value))) +
geom_bar(position = 'dodge', stat = 'identity') +
facet_wrap(~group)
What I want to is:
temp1 <- dat %>% dplyr::filter(group %in% c(1, 4))
temp2 <- dat %>% dplyr::filter(group %in% c(2, 4))
temp3 <- dat %>% dplyr::filter(group %in% c(3, 4))
ggplot(temp1, aes(x = as.factor(keyId), y = value, fill = as.factor(group))) +
geom_bar(position = 'dodge', stat = 'identity')
ggplot(temp2, aes(x = as.factor(keyId), y = value, fill = as.factor(group))) +
geom_bar(position = 'dodge', stat = 'identity')
ggplot(temp3, aes(x = as.factor(keyId), y = value, fill = as.factor(group))) +
geom_bar(position = 'dodge', stat = 'identity')
i.e I want to plot bar plots comparing group 4 against group 1, 2, 3. I am trying to do this in a single panel using facet_wrap(~group). How can I do it?
Consider adding a new indicator and then expand the data frame by group where each subset concatenates the 4th group with a new_group indicator.
Below uses base R methods: ifelse (for conditional assignment of indicator column), by (for grouping), rbind + do.call (for concatenating data frames), transform (to add new_group column), subset (to remove redundant row).
# ADD INDICATOR
dat$indicator <- factor(ifelse(dat$group == 4, 'control', 'treatment'))
# ITERATIVELY EXPAND BY GROUP
df_list <- by(dat, dat$group, function(sub)
transform(rbind(sub, dat[dat$group == 4,]),
new_group = sub$group[[1]])
)
# BIND ALL DFs AND FILTER OUT 4 AND 4 PAIRINGS
graph_df <- subset(do.call(rbind, df_list), new_group != 4)
# PLOT USING fill AND LABEL CLEANUP
ggplot(graph_df, aes(x = as.factor(keyId), y = value, fill=indicator)) +
geom_bar(position = 'dodge', stat = 'identity') +
labs(title="Treatments vs Control Group", x="keyId") +
theme(plot.title = element_text(hjust = 0.5),
legend.position="bottom") +
facet_wrap(~new_group)
Maybe I'm missing something that makes this more complicated, but you can forgo the temporary data frames with a list, and just make one long data frame of all those subsets. If you give the list names, you can use that with the .id parameter of dplyr::bind_rows, and that becomes your faceting variable.
library(dplyr)
library(ggplot2)
dat <- data.frame(keyId = rep(c('A','B','C','D'), times = 4),
group = rep(1:4, each = 4),
value = sample(1:100, 16))
dat_paired <- list(
set1 = filter(dat, group %in% c(1, 4)),
set2 = filter(dat, group %in% c(2, 4)),
set3 = filter(dat, group %in% c(3, 4))
) %>%
bind_rows(.id = "set")
head(dat_paired)
#> set keyId group value
#> 1 set1 A 1 21
#> 2 set1 B 1 57
#> 3 set1 C 1 66
#> 4 set1 D 1 33
#> 5 set1 A 4 1
#> 6 set1 B 4 32
ggplot(dat_paired, aes(x = as.factor(keyId), y = value, fill = as.factor(group))) +
geom_col(position = "dodge") +
facet_wrap(vars(set))
Also an aside: geom_bar(stat = "identity") is the same as geom_col().
Here is an alternate solution using dplyr and ggplot2, where the values of group 4 are subtracted from the other 3 groups, then the differences are plotted.
dat %>% filter(group==4) %>% select(-group,value.4=value) %>%
left_join(dat,.) %>% mutate(diff=value-value.4) %>%
filter(group!=4) %>%
ggplot(aes(keyId,diff,fill=keyId)) + geom_bar(stat='identity') +
facet_wrap(~group) + theme(legend.position = 'none') +
ylab('Difference of Group 4')

Connecting points of a shifted plot with another plot using ggplot in R

I have two dataframes df1 and df2 as follows:
> df1
dateTime value
1 1 6
2 2 2
3 3 3
4 4 1
> df2
dateTime value
1 1 3
2 2 8
3 3 4
4 4 5
I want to plot these dataframes in just one diagram, split them to two different plots with same x axis, shift df1 by 1 to the right, and connect each value of df1 to the corresponding value of df2. Here is my code:
#Shift df1 by 1 to the right
df1$value <- lag(df1$value, 1)
plot1 <- df1 %>%
select(dateTime, value) %>%
ggplot(aes(dateTime, value)) +
geom_point() +
geom_line(color = "green") +
geom_segment(aes(xend = dateTime, yend = -Inf), linetype = "dashed") +
theme(axis.text=element_text(size = 14), axis.title=element_text(size = 14),
axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank())
plot2 <- df2 %>%
select(dateTime, value) %>%
ggplot(aes(dateTime, value)) +
geom_point() +
geom_line(color = "red") +
geom_segment(aes(xend = dateTime, yend = Inf), linetype = "dashed") +
xlab("dateTime") +
theme(axis.text=element_text(size = 14), axis.title=element_text(size = 14))
gt <- rbind(ggplotGrob(plot1), ggplotGrob(plot2), size = "last")
# Panel positioning
is_panel <- which(gt$layout$name == "panel")
panel_x <- unique(gt$layout$l[is_panel])
panel_y <- gt$layout$t[is_panel]
# Coordinates and graphical parameters for segments
x_coords <- gt$grobs[[is_panel[1]]]$children[[5]]$x0
gpar <- gt$grobs[[is_panel[1]]]$children[[5]]$gp
linkgrob <- segmentsGrob(x0 = x_coords, y0 = 0, x1 = x_coords, y1 = 1, gp = gpar)
gt <- gtable_add_grob(gt, linkgrob,
t = panel_y[1] + 1, l = panel_x, b = panel_y[2] - 1)
grid.newpage()
grid.draw(gt)
Here is the result, but actually there is an additional line which I want to remove it and also there is no point for the last value of df1 which I also want to show the last point:
Lag
I guess that lag is maybe the wrong function:
lag(1:3)
# [1] NA 1 2
If I understand you correctly, you want to shift your data and this depends on your real data, but for this dummy example something like
df1 <- df1 %>%
mutate(dateTime = dateTime + 1)
should do the trick.
Lines
You need to adapt your base plots a bit:
plot1 <- df1 %>%
select(dateTime, value) %>%
## create a temp variable to which we can map the line type to
mutate(lty = ifelse(dateTime == max(dateTime), "none", "dashed")) %>%
ggplot(aes(dateTime, value)) +
geom_point() +
geom_line(color = "green") +
## map the linetype to this variable
geom_segment(aes(xend = dateTime, yend = -Inf, linetype = lty)) +
## use a manual scale to map the variable to dashed and blank linetype
scale_linetype_manual(values = c(dashed = "dashed", none = "blank"),
guide = "none") +
## add xlim to align scales properly in both plots
xlim(c(1, 5)) +
theme(axis.text=element_text(size = 14), axis.title=element_text(size = 14),
axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank())
plot2 <- df2 %>%
select(dateTime, value) %>%
mutate(lty = ifelse(dateTime == min(dateTime), "none", "dashed")) %>%
ggplot(aes(dateTime, value)) +
geom_point() +
geom_line(color = "red") +
geom_segment(aes(xend = dateTime, yend = Inf, linetype = lty)) +
scale_linetype_manual(values = c(dashed = "dashed", none = "blank"),
guide = "none") +
xlab("dateTime") +
xlim(c(1, 5)) +
theme(axis.text=element_text(size = 14), axis.title=element_text(size = 14))
This gives you this plot:

Automate tick max and min in faceted ggplot

I am trying to just mark the max and min of each x-axis in a faceted ggplot. I have several facets with different x scales and the same y scale, and the x axis tick labels overlap each other. Rather than having to manually determine the limits and breaks for each facet x axis, I am looking for a way to just label the min and max values for each.
Code using example data of the CO2 dataset (see ?CO2):
CO2$num <- 1:nrow(CO2)
library(reshape2)
CO2.melt <- melt(CO2,
id.var=c("Type",
"Plant",
"Treatment",
"num"))
CO2.melt <- CO2.melt[order(CO2.melt$num),]
library(ggplot2)
ggplot(CO2.melt,
aes(x = value,
y = num)) +
geom_path(aes(color = Treatment)) +
facet_wrap( ~ variable, scales = "free_x",nrow=1)
Purpose is to replicate well log displays such as this one.
When you want to implemented this for the tick-labels, the use of scales = "free_x" in a faceted plot makes this hard to automate this. However, with a bit of tinkering and the help of several other packages, you could also use the following approach:
1) Summarise the data in order to get an idea which tick-labels / breaks you need on the x-axis:
library(data.table)
minmax <- melt(setDT(CO2.melt)[, .(min.val = min(value), max.val = max(value),
floor.end = 10*ceiling(min(value)/10),
ceil.end = 10*floor((max(value)-1)/10)),
variable][],
measure.vars = patterns('.val','.end'),
variable.name = 'var',
value.name = c('minmax','ends'))
which gives:
> minmax
variable var minmax ends
1: conc 1 95.0 100
2: uptake 1 7.7 10
3: conc 2 1000.0 990
4: uptake 2 45.5 40
2) Create break vecors for each facet:
brks1 <- c(95,250,500,750,1000)
brks2 <- c(7.7,10,20,30,40,45.5)
3) Create the facets:
p1 <- ggplot(CO2.melt[CO2.melt$variable=="conc",],
aes(x = value, y = num, colour = Treatment)) +
geom_path() +
scale_x_continuous(breaks = brks1) +
theme_minimal(base_size = 14) +
theme(axis.text.x = element_text(colour = c('red','black')[c(1,2,2,2,1)],
face = c('bold','plain')[c(1,2,2,2,1)]),
axis.title = element_blank(),
panel.grid.major = element_line(colour = "grey60"),
panel.grid.minor = element_blank())
p2 <- ggplot(CO2.melt[CO2.melt$variable=="uptake",],
aes(x = value, y = num, colour = Treatment)) +
geom_path() +
scale_x_continuous(breaks = brks2) +
theme_minimal(base_size = 14) +
theme(axis.text.x = element_text(colour = c('red','black')[c(1,2,2,2,2,1)],
face = c('bold','plain')[c(1,2,2,2,2,1)]),
axis.title = element_blank(),
panel.grid.major = element_line(colour = "grey60"),
panel.grid.minor = element_blank())
4) Extract the legend into a separate object:
library(grid)
library(gtable)
fill.legend <- gtable_filter(ggplot_gtable(ggplot_build(p2)), "guide-box")
legGrob <- grobTree(fill.legend)
5) Create the final plot:
library(gridExtra)
grid.arrange(p1 + theme(legend.position="none"),
p2 + theme(legend.position="none"),
legGrob, ncol=3, widths = c(4,4,1))
which results in:
A possible alternative solution to do this automatically, is either use geom_text or geom_label. An example to show how you can achieve this:
# create a summary
library(dplyr)
library(tidyr)
minmax <- CO2.melt %>%
group_by(variable) %>%
summarise(minx = min(value), maxx = max(value)) %>%
gather(lbl, val, -1)
# create the plot
ggplot(CO2.melt, aes(x = value, y = num, color = Treatment)) +
geom_path() +
geom_text(data = minmax,
aes(x = val, y = -3, label = val),
colour = "red", fontface = "bold", size = 5) +
facet_wrap( ~ variable, scales = "free_x", nrow=1) +
theme_minimal()
which gives:
You can also get the minimum and maximum values on the fly inside ggplot (credit to #eipi10). Another example using geom_label:
ggplot(CO2.melt, aes(x = value, y = num, color = Treatment)) +
geom_path() +
geom_label(data = CO2.melt %>%
group_by(variable) %>%
summarise(minx = min(value), maxx = max(value)) %>%
gather(lbl, val, -1),
aes(x = val, y = -3, label = val),
colour = "red", fontface = "bold", size = 5) +
facet_wrap( ~ variable, scales = "free_x", nrow=1) +
theme_minimal()
which gives:
Edit Updating to ggplot2 ver 3.0.0
This approach modifies the labels in the ggplot build data (i.e., ggplot_build(plot)). I've removed the x-axis expansions so that the maximum and minimum values fall on the panel boundaries.
# Packages
library(grid)
library(ggplot2)
library(reshape2)
# Data
CO2$num <- 1:nrow(CO2)
library(reshape2)
CO2.melt <- melt(CO2,
id.var=c("Type",
"Plant",
"Treatment",
"num"))
CO2.melt <- CO2.melt[order(CO2.melt$num),]
# Plot
(p <- ggplot(CO2.melt,
aes(x = value,
y = num)) +
scale_x_continuous(expand = c(0, 0)) +
geom_path(aes(color = Treatment)) +
facet_wrap( ~ variable, scales = "free_x", nrow=1))
# Get the build data
gb <- ggplot_build(p)
# Get number of panels
panels = length(gb$layout$panel_params)
# Get x tick mark labels
x.labels = lapply(1:panels, function(N) gb$layout$panel_params[[N]]$x.labels)
# Get range of x values
x.range = lapply(1:panels, function(N) gb$layout$panel_params[[N]]$x.range)
# Get position of x tick mark labels
x.pos = lapply(1:panels, function(N) gb$layout$panel_params[[N]]$x.major)
# Get new x tick mark labels - includes max and min
new.labels = lapply(1:panels, function(N) as.character(sort(unique(c(as.numeric(x.labels[[N]]), x.range[[N]])))))
# Tag min and max values with "min" and "max"
new.labelsC = new.labels
minmax = c("min", "max")
new.labelsC = lapply(1:panels, function(N) {
x = c(new.labelsC[[N]][1], new.labelsC[[N]][length(new.labels[[N]])])
x = paste0(x, "\n", minmax)
c(x[1], new.labelsC[[N]][2:(length(new.labels[[N]])-1)], x[2])
} )
# # Get position of new labels
new.pos = lapply(1:panels, function(N) (as.numeric(new.labels[[N]]) - x.range[[N]][1])/(x.range[[N]][2] - x.range[[N]][1]))
# Put them back into the build data
for(i in 1:panels) {
gb$layout$panel_params[[i]]$x.labels = new.labelsC[[i]]
gb$layout$panel_params[[i]]$x.major_source = as.numeric(new.labels[[i]])
gb$layout$panel_params[[i]]$x.major = new.pos[[i]]
}
# Get the ggplot grob
gp = ggplot_gtable(gb)
# Add some additional space between the panels
pos = gp$layout$l[grep("panel", gp$layout$name)] # Positions of the panels
for(i in 1:(panels-1)) gp$widths[[pos[i]+1]] = unit(1, "cm")
# Colour the min and max labels using `grid` editing functions
for(i in 1:panels) {
gp = editGrob(grid.force(gp), gPath(paste0("axis-b-", i), "axis", "axis", "GRID.text"),
grep = TRUE, gp = gpar(col = c("red", rep("black", length(new.labels[[i]])-2), "red")))
}
# Draw it
grid.newpage()
grid.draw(gp)

Resources