Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

making a WCS Munsell color chart in R, problems with order in scale_fill_manual, ggplot2

Tags:

r

colors

ggplot2

I want to make a Munsell for color chart for the chips used by the World Color Survey. It should look like this:

enter image description here

The information needed can be found on the WCS page, here, I take the following steps:

library(munsell) # https://cran.r-project.org/web/packages/munsell/munsell.pdf
library(ggplot2)

# take the "cnum-vhcm-lab-new.txt" file from: https://www1.icsi.berkeley.edu/wcs/data.html#wmt
# change by replacing .50 with .5 removing .00 after hue values

WCS <- read.csv("cnum-vhcm-lab-new.txt", sep = "\t", header = T)
WCS$hex <- mnsl2hex(hvc2mnsl(hue = WCS$MunH, value = ceiling(WCS$MunV), chroma = WCS$C), fix = T)

# this works, but the order of tiles is messed up
ggplot(aes(x=H, y=V, fill=hex), data = WCS) +   
  geom_tile(aes(x=H, y=V), show.legend = F) +
  scale_fill_manual(values = WCS$hex) +
  scale_x_continuous(breaks = scales::pretty_breaks(n = 40)) 

The result:

enter image description here

Clearly, the chips are not ordered along hue and value but with reference to some other dimension, perhaps even order in the original data frame. I also have to revert the order on the y-axis. I guess the solution will have to do with factor() and reorder(), but how to do it?

like image 403
Annemarie Avatar asked Dec 30 '25 16:12

Annemarie


2 Answers

OP. TL;DR - you should be using scale_fill_identity() rather than scale_fill_manual().

Now for the long description: At its core, ggplot2 functions on mapping the columns of your data to specific features on the plot, which ggplot2 refers to as "aesthetics" using the aes() function. Positioning is defined by mapping certain columns of your data to x and y aesthetics, and the different colors in your tiles are mapped to fill using aes() as well.

The mapping for fill does not specify color, but only specifies which things should be different colors. When mapped this way, it means that rows in your data (observations) that have the same value in column mapped to the fill aesthetic will be the same color, and observations that have different values in the column mapped to the fill aesthetic will be different colors. Importantly, this does not specify the color, but only specifies if colors should be different!

The default behavior is that ggplot2 will determine the colors to use by applying a default scale. For continuous (numeric) values, a continuous scale is applied, and for discrete values (like a vector of characters), a discrete scale is applied.

To see the default behavior, just remove scale_fill_manual(...) from your plot code. I've recopied your code below and added the needed revisions to programmatically remove and adjust the ".50" and ".00" changes to WCS$MunH. The code below should work entirely if you have downloaded the original .txt file from the link you provided.

library(munsell)
library(ggplot2)

WCS <- read.csv("cnum-vhcm-lab-new.txt", sep = "\t", header = T)
WCS$MunH <- gsub('.50','.5', WCS$MunH)  # remove trailing "0" after ".50"
WCS$MunH <- gsub('.00',  '', WCS$MunH)  # remove ".00" altogether

WCS$V <- factor(WCS$V)  # needed to flip the axis

WCS$hex <- mnsl2hex(hvc2mnsl(hue = WCS$MunH, value = ceiling(WCS$MunV), chroma = WCS$C), fix = T)

ggplot(aes(x=H, y=V, fill=hex), data = WCS) +   
  geom_tile(aes(x=H, y=V), show.legend = F, width=0.8, height=0.8) +
  scale_y_discrete(limits = rev(levels(WCS$V))) +  # flipping the axis
  scale_x_continuous(breaks = scales::pretty_breaks(n = 40)) +
  coord_fixed() + # force all tiles to be "square"
  theme(
    panel.grid = element_blank()
  )

enter image description here

You have show.legend = F in there, but there should be 324 different values mapped to the WCS$hex column (i.e. length(unique(WCS$hex))).

When using scale_fill_manual(values=...), you are supplying the names of the colors to be used, but they are not mapped to the same positions in your column WCS$hex. They are applied according to the way in which ggplot2 decides to organize the levels of WCS$hex as if it were a factor.

In order to tell ggplot2 to basically ignore the mapping and just color according to the actual color name you see in the column mapped to fill, you use scale_fill_identity(). This will necessarily remove the ability to show any legend, since it kind of removes the mapping and recoloring that is the default behavior of aes(fill=...). Regardless, this should solve your issue:

ggplot(aes(x=H, y=V, fill=hex), data = WCS) +   
  geom_tile(aes(x=H, y=V), width=0.8, height=0.8) +
  scale_fill_identity() +   # assign color based on text
  scale_y_discrete(limits = rev(levels(WCS$V))) +  # flipping the axis
  scale_x_continuous(breaks = scales::pretty_breaks(n = 40)) +
  coord_fixed() + # force all tiles to be "square"
  theme(
    panel.grid = element_blank()
  )

enter image description here

like image 106
chemdork123 Avatar answered Jan 02 '26 06:01

chemdork123


The main thing is to use the right color scale (scale_fill_identity). This ensures the hex values are uses as the color for the tiles.

library(munsell) # https://cran.r-project.org/web/packages/munsell/munsell.pdf
library(ggplot2)

WCS <- read.csv(url('https://www1.icsi.berkeley.edu/wcs/data/cnum-maps/cnum-vhcm-lab-new.txt'), sep = "\t", header = T)
WCS$hex <- mnsl2hex(hvc2mnsl(hue = gsub('.00','',gsub('.50', '.5',WCS$MunH)), value = ceiling(WCS$MunV), chroma = WCS$C), fix = T)

# this works, but the order of tiles is messed up
ggplot(aes(x=H, y=V, fill=hex), data = WCS) +   
  geom_tile(aes(x=H, y=V), show.legend = F) +
  scale_fill_identity() +
  scale_x_continuous(breaks = scales::pretty_breaks(n = 40)) 

Created on 2021-10-05 by the reprex package (v2.0.1)

like image 31
Bart Avatar answered Jan 02 '26 05:01

Bart



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!