Color is everywhere around us. It makes us feel certain ways. Sometimes it pushes to buy things, eat things – it often dictates our actions. Color usage is everywhere, in powerpoint, in infographics, in marketing, etc. One of the ways we can study color is through color theory. Color theory refers to how we perceive color and how colors mix and/or contrast each other. If we think back to elementary school, you may remember terms like complimentary colors, color wheels, etc and these terms are still very much important in understanding how to choose and use colors.
Color theory can often be overlooked in data science; however, it is crucial we have a basic understanding of how to use color to our advantage given that a big chunk of data science is communicating complex results. Color can help with that. I do think there is a degree of subjectivity when it comes to choosing colors, but it is important to select colors that work with each other and will help explain whatever it is you are trying to explain. If you only care about the python code, please scroll down to the last section.
1. the basics
Let’s start with the color wheel.
The color wheel is a one of the many ways we can represent and visualize color. In the most general sense, the color wheel consists of three primary colors: red, yellow and blue. Three secondary colors; green, orange and purple, which are created when primary colors are mixed. Six tertiary colors which are created from a mix of primary and secondary colors; see list below.
- Red + Purple = Magenta
- Red + Orange = Vermillion
- Blue + Purple = Violet
- Blue + Green = Teal
- Yellow + Orange = Amber
- Yellow + Green = Chartreuse
Of course, we know these are not the only colors available to us. Go to your color tab in your favorite design tool or package and you’ll see thousands of options. We use hue, tint, tone and shade to create new variants from the original color.
Hue is what we commonly know as color. Both primary and secondary colors are hues. So, how can we create new colors? By playing with the tint, shade and tone of a hue. If you take a look at the picture above, I have highlight green as our hue, moving up/down/left/right will alter the tone, tint and shade of said hue turning it into a new variant of green.
Shade refers to the color we get when we mix black with another hue.
Tint refers to the color we get when we mix white with another hue.
Tone (or sometimes referred to as Saturation) refers to the color we get when we mix both black and white with another hue.
2. color models
All this terminology is good and all, but we need a way to measure, add, subtract and manipulate color in a way that a computer can understand it. Here comes the myriad of color models! You’ve seen them already too. If you’ve ever played with changing colors in your preferred design tool, you’ll have seen numbers like (12, 3, 233) or #ffffff – these are ways we can describe and manipulate colors in a machine.
Color models have 3 dimensions. Just like units, we can easily switch from one color unit to the other using a mathematical equation.
Red Green Blue aka RGB
RBG models are based on an additive color model meaning that we can create colors by adding, subtracting red, green and blue. Each dimension (red, green and blue) can have a value between 0 and 255. If we were to mix all three colors, e.g. rgb = (255, 255, 255), we’d get white, rgb = (0, 0, 0) would represent black.
Hexadecimal or hex values are another way we can define colors on a machine. In a hex value, e.g. #ffffff, there are 16 possible characters (base-16 system); mainly, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E and F. Each character represents an integer from zero to fifteen. The list below should help clarify the two system:
Decimal: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Hexadecimal: 0 1 2 3 4 5 6 7 8 9 A B C D E F
For example, lets say we wanted to convert rgb(23, 45, 12) to a hex value we’d do the following:
r -> 23/ 16 = 1.4375 so the first number is 1. Then, we multiply 0.43 by 16 and we get 7. So, the first two numbers are 17.
g -> 45/16 = 2.8125, similarly 2D (0.8125 * 16 = 13 which is equivalent to D)
b -> 12/16 = 0.75, 0C
Hence, the hex value would be #172D0C
HSL (Hue, Saturation, Lightness)
HSL is yet another color model. In this model, colors of each hue are arranged in a radial slice, meaning we use degrees to define colors. This is very useful when, say, we are trying to compute color relationships as we will see soon.
3. color relationships and python examples.
While there are guidelines to what colors work with each other, it is true that creating good color palettes simply takes time and practice. Rules are meant to be followed and broken. Take this section as a way to familiarize yourself with how colors mix with each other, but not set-in-stone rules as to how to select a good color palette.
On each color relationship subsection, I will share a python snippet. These can come in quite handy when doing data design work. Usually, I will be working on an infographic and or presentation so these functions are a quick and dirty way to get color combinations. For the purpose of these examples, we will use the Pantone color in Eden.
Before we dive in, here are two functions to convert between rgb and hex values that we will need to study color relationships.
%matplotlib inline #only if working in Jupiter import matplotlib.pyplot as plt import colorsys def rgb2hex(val): """ Takes tuple and converts to hex value. """ conversion = '#%02x%02x%02x' % val return conversion def hex2rgb(val): """ Takes hex string and converts to rgb tuple. """ hexNum = val.strip('#') hexLen = len(hexNum) conversion = tuple(int(hexNum[i:i+hexLen//3], 16) for i in range(0, hexLen, hexLen//3)) return conversion
A complementary color is color that is 180 degrees apart from another color on the color wheel.
In the case of Eden, the purple tint below would be the complimentary color.
As you can see, complimentary colors can create a bit too much contrast, so I wouldn’t necessarily use both as main colors. Adding some white around Eden already softens the look quite a bit. It is best to use one complimentary color as the main color and use the other for accents in your design.
In python, we can do the following:
rgb_eden = (38, 78, 58) def complimentary(val): """ Takes rgb tuple and produces complimentary color. """ #value has to be 0 < x 1 in order to convert to hls r, g, b = map(lambda x: x/255.0, val) #hls provides color in radial scale h, l, s = colorsys.rgb_to_hls(r, g, b) #get hue changes at 150 and 210 degrees deg_180_hue = h + (180.0 / 360.0) color_180_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_180_hue, l, s))) return color_180_rgb comp_col = comp(rgb) comp_col #Visualize Colors hexVal = rgb2hex((78, 38, 58)) edenHex = rgb2hex(rgb_eden) fig = plt.gcf() fig.set_size_inches(10, 10, forward=True) plt.axes() circle = plt.Circle((0, 0), radius=0.75, fc=edenHex) circle2 = plt.Circle((1, 1), radius=0.75, fc=hexVal) plt.gca().add_patch(circle) plt.gca().add_patch(circle2) plt.axis('scaled') plt.show()
Note that in the code we had to convert from RGB to HSL. If you recall HSL is measured in degrees and we know that complimentary colors lie 180 degrees away from each other on the color wheel. It is simply easier to use HSL units when calculating color families and we’ll continue to do so in remaining color relationships we will discuss.
split complimentary colors
A split-complementary color scheme is a three-color combination that consists of a base color and two colors that are 150 degrees and 210 degrees apart from the base color respectively. Given that these will all be contrasting colors, it is best to bring in perhaps other colors to soften or balance this scheme out. Ideally, we’d change the tone, tint and shade of some of the split complimentary colors to create a nice flowing palette.
In python, we would do the following:
def splitComplimentary(val): """ Takes rgb tuple and produces list of split complimentary colors. """ #value has to be 0 <span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>< x 1 in order to convert to hls r, g, b = map(lambda x: x/255.0, val) #hls provides color in radial scale h, l, s = colorsys.rgb_to_hls(r, g, b) #get hue changes at 150 and 210 degrees deg_150_hue = h + (150.0 / 360.0) deg_210_hue = h + (210.0 / 360.0) #convert to rgb color_150_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_150_hue, l, s))) color_210_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_210_hue, l, s))) return [color_150_rgb, color_210_rgb]
Analogous color schemes are created by pairing one main color with the two colors directly next to it on the color wheel. We can specify the angle between the main color and the other two colors.
For Eden, the analogous color are below.
As you can see there isn’t a lot of contrast between analogous colors. The way I would use analogous colors is by using them on top or behind the main color as a shading tone, perhaps changing the tint or tone to brighten it up.
In python, we would do the following:
def analogous(val, d): """ Takes rgb tuple and angle (out of 100) and produces list of analogous colors) """ analogous_list =  #set color wheel angle d = d /360.0 #value has to be 0 <span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>< x 1 in order to convert to hls r, g, b = map(lambda x: x/255.0, val) #hls provides color in radial scale h, l, s = colorsys.rgb_to_hls(r, g, b) #rotate hue by d h = [(h+d) % 1 for d in (-d, d)] for nh in h: new_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(nh, l, s))) analogous_list.append(new_rgb) return analogous_list
Triadic colors are a combination of three colors that consists of a main color and two colors that are 120 degrees and 240 degrees apart from the main color respectively; hence, they are equally placed in lines around the color wheel. Similar to complimentary colors, triadic colors provide high contrast and can be overpowering. Ideally, you’d pick one main color and use the remaining as accents.
In python, we would do the following:
def triadic(val): """ Takes rgb tuple and produces list of triadic colors. """ #value has to be 0 < x 1 in order to convert to hls r, g, b = map(lambda x: x/255.0, val) #hls provides color in radial scale h, l, s = colorsys.rgb_to_hls(r, g, b) #get hue changes at 120 and 240 degrees deg_120_hue = h + (120.0 / 360.0) deg_240_hue = h + (240.0 / 360.0) #convert to rgb color_120_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_120_hue, l, s))) color_240_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_240_hue, l, s))) return [color_120_rgb, color_240_rgb]
Tetradic colors are four-color combination that consists of a main color and three colors that are 90 degrees, 180 degrees, and 270 degrees apart from the main color respectively.
In python, we would do the following:
def tetradic(val): """ Takes rgb tuple and produces list of tetradic colors. """ #value has to be 0 <span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>< x 1 in order to convert to hls r, g, b = map(lambda x: x/255.0, val) #hls provides color in radial scale h, l, s = colorsys.rgb_to_hls(r, g, b) #get hue changes at 120 and 240 degrees deg_60_hue = h + (60.0 / 360.0) deg_180_hue = h + (180.0 / 360.0) deg_240_hue = h + (240.0 / 360.0) #convert to rgb color_60_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_60_hue, l, s))) color_180_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_180_hue, l, s))) color_240_rgb = list(map(lambda x: round(x * 255),colorsys.hls_to_rgb(deg_240_hue, l, s))) return [color_60_rgb, color_180_rgb, color_240_rgb]
4. final thoughts
Picking colors is FUN. Color families are meant to be guidelines not rules by which you should pick your palettes. You want to pick two in-your-face colors? Go for it! Just make sure you use accents, tones and tints that can make your design work for you.
The best tip I can give you is to start with one color and branch out from there. Save the colors as you go so you can go back to them and decide which work best on your palette and which look best for your purposes, brand or message. For example, if I am working on a data plot within Python or R, I usually will keep a file with color families readily available and I will change them up as needed in my script. If I am working on an illustration, I usually will do a rough sketch, then I rough color it and decide which colors I like best before working on the final piece.
At the end of the day, it is all about practice and patience. Below are some examples of my process when picking colors for plots, illustrations and infographics.
One thought on “Coding: Navigating through #000000 and #ffffff: Color Theory in Python”
A slight correction:
comp_col = complimentary(rgb_eden)