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))
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))
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!
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"))
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.
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.You can create a new numeric -continuous- column from your character/factor column
Interest
with the same magnitude and then usescale_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' .
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"
)
Related Topics
Counting Unique Items in Data Frame
How to Use a MACro Variable in R? (Similar to %Let in Sas)
Plotting a Large Number of Custom Functions in Ggplot in R Using Stat_Function()
Shiny Selectinput to Select All from Dropdown
How to Find the Polygon Nearest to a Point in R
R List Get First Item of Each Element
Linear Regression and Storing Results in Data Frame
How to Align a Group of Checkboxgroupinput in R Shiny
Filter One Selectinput Based on Selection from Another Selectinput
R Shiny Checkboxgroupinput - Select All Checkboxes by Click
Find Consecutive Sequence of Zeros in R
In R, What Does "Loaded via a Namespace (And Not Attached)" Mean
Condition a ..Count.. Summation on the Faceting Variable
Remove Plot Margins in Ggplot2
What Is the Correct Way to Ask for User Input in an R Program