Stata graphs: Define your own color schemes
By: Asjad Naqvi
Tags: Color Theory, Stata, Data Visualization, Colors, Automation

As an additional note, the Stata screenshot is using the default Dark theme that comes with version 16.1.

Let’s try a simple graph by just plotting the first five countries that they appear in the id variable:

twoway ///

(line total_cases_pop_ma7 date if id == 1) ///

(line total_cases_pop_ma7 date if id == 2) ///

(line total_cases_pop_ma7 date if id == 3) ///

(line total_cases_pop_ma7 date if id == 4) ///

(line total_cases_pop_ma7 date if id == 5) ///

, legend(off)

which gives us the default Cleanplots scheme line colors:

I use Cleanplot in all of my guides for a neat and minimalistic background.

We can also manually defined the line colors. Let’s start by using the default s2color scheme that everyone is familiar with:

twoway ///

(line total_cases_pop_ma7 date if id == 1, lc(navy)) ///

(line total_cases_pop_ma7 date if id == 2, lc(maroon)) ///

(line total_cases_pop_ma7 date if id == 3, lc(forest_green)) ///

(line total_cases_pop_ma7 date if id == 4, lc(dkorange)) ///

(line total_cases_pop_ma7 date if id == 5, lc(teal)) ///

, legend(off)

and we get this graph:

We can also use the colrspace green palette that we defined earlier. Note that the new colors cannot be used as names directly. They need to be first generated using the colorpalette command from which they can be recovered as locals:

colorpalette LimeGreen YellowGreen Olivedrab SeaGreen Olive, nograph

return list

This will give us a menu that looks like this. Here the colors have their RGB code defined in a macro of all the color RGB values in r(p) and a macro for each color RGB value separately as r(p1) to r(p5) . We can copy these values in the line color lc :

twoway ///

(line total_cases_pop_ma7 date if id == 1, lc("50 205 50")) ///

(line total_cases_pop_ma7 date if id == 2, lc("154 205 50")) ///

(line total_cases_pop_ma7 date if id == 3, lc("107 142 35")) ///

(line total_cases_pop_ma7 date if id == 4, lc("46 139 87")) ///

(line total_cases_pop_ma7 date if id == 5, lc("128 128 0")) ///

, legend(off)

and get this graph:

Instead of manual pasting this color data, we can also directly the use locals we generate from the colorpalette command:

colorpalette LimeGreen YellowGreen Olivedrab SeaGreen Olive, nograph twoway ///

(line total_cases_pop_ma7 date if id == 1, lc("`r(p1)'")) ///

(line total_cases_pop_ma7 date if id == 2, lc("`r(p2)'")) ///

(line total_cases_pop_ma7 date if id == 3, lc("`r(p3)'")) ///

(line total_cases_pop_ma7 date if id == 4, lc("`r(p4)'")) ///

(line total_cases_pop_ma7 date if id == 5, lc("`r(p5)'")) ///

, legend(off)

from which we will get exactly the same graph as shown above.

Instead of specifying the colors manually, we can also just use the interpolation of two colors to come up with a color scheme:

colorpalette FireBrick Gold, ipolate(5) nograph twoway ///

(line total_cases_pop_ma7 date if id == 1, lc("`r(p1)'")) ///

(line total_cases_pop_ma7 date if id == 2, lc("`r(p2)'")) ///

(line total_cases_pop_ma7 date if id == 3, lc("`r(p3)'")) ///

(line total_cases_pop_ma7 date if id == 4, lc("`r(p4)'")) ///

(line total_cases_pop_ma7 date if id == 5, lc("`r(p5)'")) ///

, legend(off)

We can also use the custom color scheme we manually defined from https://www.color-hex.com/color-palette/96712:

colorpalette ///

"245 171 41" ///

"219 153 37" ///

"138 47 99" ///

"127 32 86" ///

" 82 32 61", nograph twoway ///

(line total_cases_pop_ma7 date if id == 1, lc("`r(p1)'")) ///

(line total_cases_pop_ma7 date if id == 2, lc("`r(p2)'")) ///

(line total_cases_pop_ma7 date if id == 3, lc("`r(p3)'")) ///

(line total_cases_pop_ma7 date if id == 4, lc("`r(p4)'")) ///

(line total_cases_pop_ma7 date if id == 5, lc("`r(p5)'")) ///

, legend(off)

which gives us this image:

Note that for the graph above, we could have used the RGB color numbers directly since we have only five colors and five lines. In case more lines are added, manually adjusting colors quickly becomes cumbersome.

In short we can use all possible combinations of colors, intensities, hues, saturation schemes and interpolations to come with up a large variety of combinations.

Going for full color automation in Stata

In this part, we will learn how to completely automate and customize the color schemes and use them such that they are also visually appealing. Here the idea is to color the lines in a specific order such that the color ramp appears in the order as it is displayed in the colorpalette window. In order to do this we need to first define a rank for the last available date, and use this sorting to also generate the color names for the lines. This is achieved as follows:

**** generate a rank based on the last date

summ date

gen tick = 1 if date == `r(max)' // mark the last entry

egen rank = rank(total_cases_pop_ma7) if tick==1, f *** mark all the calendar entries with the above rank

levelsof country, local(lvls)

foreach x of local lvls { display "`x'"



qui summ rank if country=="`x'"

cap replace rank = `r(max)' if country=="`x'" & rank==.

}

For a slower line-by-line introduction to this type of coding see Guide 2.

We can also generate a marker to label the end points of the lines. If you have followed my previous guides, I strongly recommend dropping the legend if there are more than four or five items. The markers can be generate for the last data point as follows:

*** generate a marker for the countries gen marker = country + " (" + string(total_cases_pop, "%9.0fc") + ")" if tick==1

Next we test the code using a default matplotlib colorpalette scheme, that we also discussed in Guide 2:

levelsof rank, local(lvls)

local items = r(r) foreach x of local lvls {



colorpalette viridis, n(`items') nograph

local customline `customline' (line total_cases_pop_ma7 date if rank == `x', lc("`r(p`x')'") lp(solid) lw(*0.8)) ||

} summ date

local start = r(min)

local end = r(max) + 30 twoway `customline' ///

(scatter total_cases_pop_ma7 date if tick==1, mcolor(black%25) msymbol(circle) msize(*0.1) mlabel(marker) mlabsize(*0.55) mlabcolor(black)) ///

, ///

xlabel(`start'(15)`end', labsize(vsmall) angle(vertical)) ///

ytitle(, size(small)) ///

xtitle("") ///

title("{fontface Arial Bold: COVID-19 cases per million for European countries}") ///

note("Source: ECDC via Our World in Data", size(vsmall)) ///

legend(off)

Essentially what we are doing is here is generating the code for each line using a loop such that the lines are ordered by their rank.