How to Get Axis Ticks Labels with Different Colors Within a Single Axis for a Ggplot Graph

How to get axis ticks labels with different colors within a single axis for a ggplot graph?

A simpler way (IMO) to do this is just create a conditional color vector and parse it into axis.text.y

dat <- data.frame(name=c("apple", "orange", "plum"),value=c(3,8,2),outlier=c(FALSE,TRUE,FALSE))
colvec <- character(dim(dat)[1])
colvec <- ifelse(dat$outlier, "red", "black")

library(ggplot2)
ggplot(dat) +
geom_point(data = dat, aes(x=value,y=name)) +
theme(axis.text.y = element_text(colour=colvec))

Sample Image

how to add colored tick label to ggplot2?

One option to add some colored squares beneath your colored labels would be via an additional geom_text layer. As the label I use a filled square with UTF-8 code "\u25A0".

Using some fake random data to mimic your real data:

library(ggplot2)

ggplot(df, aes(x = Var2, y = Var1)) +
geom_tile(fill = "white", color = "gray") +
geom_point(aes(color = cor, size = log.P)) +
labs(color = "Correlation") +
scale_size_continuous(
range = c(1, 12), name = "P value",
guide = guide_legend(override.aes = list(colour = "grey")), breaks = bb, labels = labels
) +
scale_x_discrete(position = "top", limits = x) +
geom_text(aes(label = cor), color = "black", size = 4, vjust = 2.5) +
# Add square ticks
geom_text(data = data.frame(x = x, y = 7.5), aes(x = x, y = y), color = x, label = "\u25A0", size = 6, vjust = 0) +
scale_color_gradient2(low = "blue", mid = "white", high = "red") +
theme_minimal() +
theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_text(size = 15, angle = 40, hjust = 0.1, vjust = 0.1, color = x),
axis.text.y = element_text(size = 15),
legend.text = element_text(size = 15),
legend.title = element_text(size = 15),
legend.key.height = unit(0.7, "cm"),
panel.grid.major.x = element_blank()
)

DATA

set.seed(123)

x <- c("Blue", "Magenta", "Yellow", "Brown", "Pink", "Red", "Grey")

df <- data.frame(
Var2 = rep(x, 7),
Var1 = rep(1:7, each = 7),
cor = round(runif(49, -1, 1), 2),
log.P = runif(49, 0, 10)
)

customize ggplot2 axis labels with different colors

You can provide a vector of colors to the axis.text.x option of theme():

a <- ifelse(data$category == 0, "red", "blue")

ggplot(data, aes(x = x, y = y)) +
geom_bar(stat = "identity", aes(fill = category)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1, colour = a))

Sample Image

Ggplot2 vary x axis label colors

One option to achieve your desired result would be to make use of the ggtext package which allows for styling of labels and theme elements via markdown or HTML.

As you offered no information about the type of chart you are trying to achieve I went for a bar chart:

dates = seq.Date(from =as.Date("01/01/2020", "%d/%m/%Y"), 
to=as.Date("01/03/2021", "%d/%m/%Y"), by="month")

y = c(1:1:15)
df = data.frame(dates,y)

mylabs <- function(x) {
cols <- ifelse(lubridate::year(x) == 2020, "blue", "green")
glue::glue("<span style = 'color: {cols}'>{format(x, '%b %Y')}</span>")
}

library(ggplot2)
library(ggtext)
library(lubridate)

ggplot(df, aes(dates, y, fill = lubridate::year(dates) == 2020)) +
geom_col() +
scale_x_date(date_breaks = "1 month",
labels = mylabs,
guide = guide_axis(n.dodge = 2),
expand = c(.01, .01)) +
scale_fill_manual(values = c("TRUE" = "grey", "FALSE" = "grey"),
labels = c("TRUE" = 2020, "FALSE" = 2021)) +
theme(axis.text.x = ggtext::element_markdown()) +
labs(fill = NULL, x = NULL, y = NULL) +
guides(fill = guide_legend(override.aes = list(fill = c("blue", "green"))))

R: Discrete axis labels with multiple colors in ggplot2

The vectorised input may not be supported by element_text(), but ggplot2 does support custom theme elements (ie, element_markdown()). Hence, you can write your own custom theme elements that just handles the vectorisation of the colour variable. Now, you don't have a scary warning, don't depend on ggtext, but do have the maintainer's responsibility to ensure this keeps working. If you're worried that the good folks of ggplot2 might change element_text() or element_grob.element_text(), you can write out your class' methods in full, instead of relying that the ggplot2 classes stay the same into perpetuity.

library(ggplot2)

element_text2 <- function(..., color = NULL) {
# Explicitly don't pass colour
# Note: user can still pass `colour`, but I'm not here to write perfect code,
# just to give a working example
elem <- element_text(...)
elem$colour <- color # Assign after element is constructed
class(elem) <- c("element_text2", "element_text", "element") # Re-class
elem
}

# S3 Method for your custom class' drawing code
element_grob.element_text2 <- function(element, label = "", ...,
colour = NULL) {
# Repeat colour to match length of label, if colour exists
if (length(colour)) {
colour <- rep_len(colour, length(label))
}
# Re-class to old class
class(element) <- c("element_text", "element")
# Call element_grob.element_text method
element_grob(element, label = label, ..., colour = colour)
}

mtcars |>
dplyr::mutate(
color =
c("BLUE", "RED") |> rep_len(dplyr::n()) |>
factor()
) |>
ggplot(aes(x = mpg, y = color)) +
geom_point() +
theme(axis.text.y.left = element_text2(color = c("blue", "red"))) +
labs(y = "")

Created on 2022-08-09 by the reprex package (v2.0.1)

R: Changing the color of secondary axis tick labels with ggplot

Your code is fine, just add in theme():

axis.text.y.right = element_text(colour = "red")

Like this:

df %>% ggplot2::ggplot() +
geom_line(
mapping = aes(x = x, y = y1),
color = "black") +
geom_point(
mapping = aes(x = x, y = y1),
color = "black") +
geom_line(
mapping = aes(x = x, y = y2),
color = "red") +
geom_point(
mapping = aes(x = x, y = y2),
color = "red") +
scale_y_continuous(
"Primary",
sec.axis = sec_axis(~ ., name = "Secondary")
) +
theme_bw() +
theme(
axis.title.y = element_text(color = "black"),
axis.title.y.right = element_text(color = "red"),
axis.text.y.right = element_text(colour = "red")) #ADD THIS

Good luck!

Color ticks

Dynamically color a portion of axis tick labels with ggplot

I think the ggtext package is our friend here. I was reading the github page of the package (https://github.com/clauswilke/ggtext) and this question. This is manual work, but you can assign specific colors to specific words. In your case, you want to tweak labels on y-axis. Hence, I used scale_y_discrete(). I hope this will help you.

library(ggplot2)
library(ggridges)
library(ggtext)

ggplot(data, aes(x = value, y = key)) +
geom_density_ridges() +
theme_ridges() +
scale_y_discrete(labels = c("<b style='color:#ff0000'>T1</b>.C1",
"<b style='color:#000000'>T2</b>.C2",
"<b style='color:#ff0000'>T3</b>.C3",
"<b style='color:#000000'>T4</b>.C4")) +
theme(axis.text.y = element_markdown(color = "black"))

If you want to avoid hard coding, you can do that too. The following is one way for you.

library(dplyr)

mutate(data,
mylabel = if_else(complete.cases(hl),
gsub(x = key, pattern = "^([A-Z][0-9])(\\.[A-Z][0-9]$)",
replacement = "<b style='color:#ff0000'>\\1</b>\\2"),
key)) %>%
distinct(key, .keep_all = TRUE) %>%
pull(mylabel) -> mylabel

ggplot(data, aes(x = value, y = key)) +
geom_density_ridges() +
theme_ridges() +
scale_y_discrete(labels = mylabel) +
theme(axis.text.y = element_markdown(color = "black"))

Sample Image

Can we make the ticks width non linear in ggplot2?

The problem is you are using as x-axis a list of factors and ggplot take them as discrete data, plotting them with equal distance.

  1. You can use scale_x_discrete() and manually write every break and every label for each break which is a bit tedious and you have to re-adapt it if you add new rows.

  2. You can create a new numeric -continuous- column from your character/factor column Interest with the same magnitude and then use scale_x_continuous().

Example:

example = data.frame(
Interest = c('1 Mo', '2 Mo', '6 Mo', '1 Y', '2 Y', '5 Y'),
February = c(2.186,2.626,3.128,3.290,3.214,2.923),
August = c(0.035,0.279,0.590,0.900,1.368,1.824)
)

library(dplyr)

example %>% # use extract() to quickly split numbers and letters
extract(Interest, into=c("Num", "time"), "([0-9]*)(.*)") %>%
mutate(Num = as.numeric(Num),
Year_period = ifelse(time == " Mo", Num/12, Num)) %>% # change months magnitude to years

ggplot(aes(x=Year_period,y=August,group=1))+
geom_line(color="darkred")+
geom_point(color="darkred")+
geom_point(aes(y=February),color="darkblue")+
geom_line(aes(y=February),color="darkblue")+
scale_x_continuous(name="Years",breaks = seq(1,5,1), labels = str_c(seq(1,5,1),"Y"))+ # Here you re-scale the x-axis
theme_bw()

Within scale_x_continuous(), breaks= says where to tick the labels, giving an array of integers for the years, labels=str_c(seq(1,5,1),"Y")) is creating the names for each tick. Because all "Mo" rows are divided by 12, so < 1, now the x-axis is transformer to represent years.

Output. Note I haven't use the whole data. Adapt breaks and labels for all your rows (if your max year is '30Y' then breaks = seq(1,30,1) and labels=str_c(seq(1,30,1),"Y")). Now the first values ('Mo') are closer and < '1Y' .

Sample Image

Matching axis.text labels to colors contained in data frame variable in ggplot

Your factor levels are not mapping with the changes to your factor order.

Note that I made a change to your df so that it does indeed change when reordered, the change was in the Amount column.

df <- data.frame(x=1:4, Type = c("Metals", "Foodstuff", "Textiles", "Machinery"), 
myColour = c('blue', 'red', 'green', 'orange'), Amount = c(50, 75, 25, 5))

Do yourself a favor and load tidyverse

library(tidyverse)

Then use theme_set

theme_set(theme_classic()+
theme(panel.grid.major.x = element_line(colour = 'grey60', linetype = 'dashed'),
panel.grid.major.y = element_line(colour = 'grey60', linetype = 'dashed'),
axis.ticks.y = element_blank(),
axis.text.x = element_text(colour = 'black', size = 16),
axis.ticks.x = element_line(colour = 'grey60'),
axis.ticks.length = unit(3, "mm"),
aspect.ratio = (600/450),
axis.title.x=element_blank(),
axis.title.y=element_blank()))

You can then 'hack' and relevel the factors (maybe not the best method, but gets it done).

df %>% arrange(Amount) %>% 
mutate(myColour = factor(myColour, myColour),
Type = factor(Type, Type)) -> df1

It is then easier to pull out the color levels as a vector for plotting.

mycols <- as.vector(levels(df1$myColour))

Then plot

ggplot(df1, aes(Type, Amount, color = myColour, fill = myColour)) + 
geom_bar(stat = 'identity', position = 'dodge', show.legend = FALSE, width = .85) +
theme(axis.text.y = element_text(colour = mycols, size = 18, face = 'bold')) +
coord_flip() +
scale_fill_manual(values = mycols) +
scale_color_manual(values = mycols)

Hopefully that works for you.

This is the original edit that didn't work so can be ignore: Change the df$myColour to myColour in two instances in your code.

With that many theme tweaks you should really think about using theme_set as well.

How do I use multiple colors for a single value in a ggplot2 axis?

What you're looking to do isn't inherently built into the axis label formatting. theme(axis.text = ...) is controlled using element_text() and only takes single values. My solution is a bit of a hack but I think it does what you are looking for

mtcars %>%
mutate(label = case_when(
cyl == 4 ~ "n = 12 \n more text",
cyl == 6 ~ "n = 7 \n more text",
cyl == 8 ~ "n = 14 \n more text")
) %>%
ggplot(aes(cyl)) +
geom_bar() + # this ends the normal part of your chart
# add a rectangle to make it look like the chart plot area has ended
geom_rect(aes(xmin = 2, xmax = 10, ymin = -4, ymax = -0.3), fill = "white") +
# add the first label (colors) followed by the labels with black text
geom_text(aes(y = -0.8, label = cyl, color = label)) +
geom_text(aes(y = -2.3, label = label)) +
# adjust the scales so you don't see the rectangle
scale_x_continuous(expand = expand_scale(0)) +
scale_y_continuous(expand = expand_scale(c(0, .1))) +
# assign your colors but don't show the legend
scale_color_manual(values = c("black", "blue", "red")) +
# hide the evidence (remove the actual x-axis labels)
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
legend.position = "none"

)

Sample Image



Related Topics



Leave a reply



Submit