Color Usage#
Effective visualization of scientific data requires careful attention to color selection. Choosing the right colors can be a complex task, involving considerations such as color perception, accessibility, and aesthetics. Fortunately, there are a number of resources available to help simplify this process, including pre-designed color schemes and tools for exploring and modifying color scales. In this guide, we’ll explore some of the basics of color selection for scientific data visualization, including different types of color scales and examples of their use.
The basics of color selection#
Color selection is an essential aspect of creating clear and informative visualizations of scientific data. To make the most effective choices, it’s important to understand the basics of color perception and theory.
Color perception and theory#
We will explain the basics using two colors, blue
and orange
:
Now, lets say we want to define a gradient between blue
and orange
.
That means we have to go gradually from blue
to orange
.
The interpolation between colors can follow several methods.
It could be done like this (interpolate='rgb'
):
Or like this (interpolate='hcl-long'
):
Or like this (interpolate='hcl'
):
As you can observe, all three color ramps are different,
but all three color ramps start with blue
and finish with orange
.
So what is the correct interpolation method to go from blue
to orange
?
There is no single answer to this question.
It also based how we see colors.
To better understand the perception of colors to the human-eye, we
can make use of a color space. A color space is a diagram to help
understand how we see perceive colors and how they are distributed compare to each other.
Here we show a common used color space (‘CIE 1931 color space’) from where we can see that the human eye has more attenuation to the color green:
Click to show code
import lzma
import io
compressed_df_cie1931 = b'\xfd7zXZ\x00\x00\x04\xe6\xd6\xb4F\x02\x00!\x01\x16\x00\x00\x00t/\xe5\xa3\xe10\x9e\x1dp]\x00@\x01Nm\xa9\xb6E\x9b\x19_AS\x16\xb8\xb2s\xd2\x1b\xc9\x1e\xda\xbbR\x0b\x11\x9a\x98;K\xf9\xdaH\xf5\xb8K\x08\xe47\x8e\xd3/\x80\xfc\xad\xaeQ\xc7@\x98=\xcc\x04\xb6\xc0N\xd0\x17\x10\xdd\x85d9j\xd5=\x01\xff\xdf\xfcM\x8c\x1a;r\x02\x97\xd6O\xf0(\xe7\x82\x96\xfe4\x08\xf9\xc6U}\xff\xfa,\x14#4\x93\xd2a\xc5\x18\xcb\xd16\x00Qa\xd39mx\x90\x16\xec6SV&\xfe\x86\x18,\xe1X\xa72\xd3u\x92\xe9\x10\xb6@\x0c\x87\x94\x05"\xf4N\xaa^|\xec\x9co\xa5\xdccF|u\x89\xf0\x94m\x13%\xf9\xd5\xfc\x86\xe0\xf8\xe0\xd8\x85\x1a\x9e=\xb8\xcf\x81\x8a\xd5\x99\xf5\xc0\xfb\xb7=ft\xbasMXW\xe0\x8dU\x03\xa7\x87\xb8\x1e\x9a\x8a)\xf6\x0e\xfb\x812\xf0I\x05dW\xd6\xb8e\x1f!<\xc2\xd8\xfc)[\xf3\xf5\xa1\xac\xbf\x1b\xb5&\xd3\x16\xb8*[5n\xe0mW\x1d(#\xac\xa6\xea\xb3\xa4`u\xb5\xcak\x08\xaf\xb3\x02\xa4\xbf=\x9e\x8d`\xff\x80\xd9q\x98\xa6\'\xf9\x94\xed(\xc8\'\xad\xb9e\x01\xd9\xf2X\x99b\xbbp\xec\\\xb5\xadk#\x88~\xfe\x80 \xd8\x17|\x1buI\xc9\xb6\xae\xaeLS\x9d\x9f\x12\t\x82\xa9\xaf\xcfx\x87\t\xc9\xdb\xa43J\x9cD\xbed\x8c\x18\x8f\x1c\x15\x1a.\xfd\xc9*Z\x9b\x16\x95\xc9\x03ip\xbd\x87*\xa2I\xf4\xa3N\x9e\x1e\xd0\xf1w\x88\xe9#\x10\xe0\x86\xb1OQ \xe1\xd0Mj^\xf0\x89\xe5\xb5\x08\xd2\xabk\x84i\x1eA\xabg\x17_\x881\xfb:\x14\xc5\xfd[n\xden\xbdx\xca\xd7\xfb\x01\x82\xaf\xe6\'\xe6\x90v\xc3\x9eSq\xc9\x7ft\x80\x03X\xfa#3\n\x95+\xef\x9c+l\x1c\xe5\x9f\xcc\xe7\xdd\x85\xf5\xf5^:V\x81}\xb6(\x952\x0e)\x91\x94\xb8\x02js\x05y\x88\xc7m\x85_\x11*\x1b\xb5\xf4\xf2\xc36tWB\xe6\xc0p.H\xf4\x94\xdb\xd1\xe1m\xec9\xa3o\xd5I\xd8\x8d\x8f\xb6\xa0\x0e\xaf\x00\xa0b3= \xc9,\xa0\xa8b\x97`\x0e.<\xf2T\x9f\x1d7>\xc9y9v\xae^x\xca\xdd\xcaA\xf1K\xea\xb5)\xd7\xc5\x1d\xcd\xd4\xb6\xf7\x90V\x9a\xeaa\xaaX\xa1u\xc1\x17H\x84\xe5?\xf9\x8aPGB\r\xb0\xf29\xbf\x05^y{\xe4\xaf\x93\x08\x8dS\xd8\xd4\x91\xc3/\x1e:\xf8\xb4\xc8J\n\xe7\xf9ZB\xe2\x18\x0c\x1e\xa5G\xc9\x9et^\xed\xc1\x9d\xbc>A.\xa2\xee\x1b\xe70\x03\xeanD\xa2H\xb0\x98\'\x93\x1eA\x8bjOwz/\xf7\xb7l\r&\xa2\x11\xdfB\x88\xb4^<\x0b\xde_\xa3\x99?\x1dV#\x84i\xb2\xf1\xf2\xb6\x13FiT\xb3\xc6\x8e\x93\xc6qU{\xf6\x19\xe3R\xfa`\x0e%\xc7\x98\xe3[X\x04\x8f\xcd\x85\xee\xa7\xc1\x91j\xcb\xa9s\xcd\xd2\xe5\n#m<e\xb8B\xdb\xb7\xff\xab\x0e\x1a\xb9^Y\x82\xd0\xbb\xf2\x0b\xab\x02\xfd\xbf\xa4\x9e\t\xadG)\x1dF\x1a\xd0K\x85\x82E%Y\x13@\x1eB$\xb6\xa4\x95\xc8\x88\xb5\xcf\xd4?\xa8\x10Q7\x85 \xfb\xf1\xcet\xc9\x16\xd1\xbc\x8b\r\x82\x95*p\xa5E!\xab\xee\xa6\x9d\xcb\xeaj\xa5V\x9aUR$\t\xf91\xc2\xee\xc8\x96?\xeb\xd7\x1e\xb4\xe5\x95\xdbD\xdb\xfd"\x9c\xf0\xf1)[<;\xdc\x93\xd5v5\xcep\xc5\x9az\xa9?\xb7\x8e\x7f\x03\x89\xeef\x9eQ;!\x11\xfa\xfa\x82\x160\xd99\xa0D\xfb\'_\xb4\x10s\xc0\x97A\t\xf2\x9a\x02\x878\xf0d\x01N\xd6~\x8dc;\xff\xac,[\xdd\x1f\x1d\xe7s\x804\xf8H\xd5\x9c\x7f\xbam5$\xb9L\x06\xc3\xdc\xcc$\xac\xb0\xb0w-`\xd5\xdb8\xea\x81\xc9\x18\xe3\xf2\xaa\xe9q!0uq\xdc*\x0f\x1fe\x16\xe3\xad%\xba\xbbU\x8e\x13v\x19\xe7ylo\x15\x92\x19\x93C\xd2<\x88\x04\xe5\xdb\x826\x9a\xb7\xdc\x9a\x85Z\x89J\xb4+A\x92\xec\x83\xc4\xa6\x1d\x08\xb9-\xce0\xc3\xac\x0b\xbb\xb3\x1fg\x15\xeb\x07\x03\x13\x05r9\xdf+\xd8\xe5\xb3\xe7\x97\x96\x0e\xa7 nZ\xb0\x8d\x80\x84\x16\x8cB\xe6j"D\x8e\x86"\x0b\xa6\xad\xb1\xb1\xedu,BY\xc5\xb8\xb0\x84#\x9f\xef+yq\'\xd0\xcc\x11\xb9.\xaa\xc0\xb2\x8du\xf6\x9b\xb0\x1a\'\xb0\x89\xa4m\x95\x1e%\x13\x11\x93\x80u\xa81H(\xa9\xb0\xa4\xb4\xde\x04\xebK\x8ab9\x16\xe0c\x81\xff\xf6ni8\xdbr\xbfW\x08\xd1\xe8\x8c\x9d{\xda\xef\xda\x85\x9d!6\x9b\xcb\xa6\xcd\x9b{h-x"\xbc\xd5\xacR8 \xd0\x99\xae\xd8\xfe\xef\x97\x8d/\xeefU~\xf3!\xf0S-\xc2\x8e:\xd7M\xfe\xed?\xea\xe8\x0b\xa56{g|\x0fS.\x9f\xab\xdb\xc9\x83\x16\xa7\xee\xfc\x84\xb7\x91\xf6%\xaa\x1d\xfc\xc41\xb4\x80T\xb0\x8a\xff^f\x83\x8b\x94@\xe3\xc3\x0f\xa7\xad\xa8-W\xa7\xce\x0cQO\xe7k\xd2B\x9aD\xc8\xcd\xb3\x03\xc2Mm\xac\xf2x\xa4\xaa\xc3\xda\x17\xa6\xb7!\x9b\xd2>\xaf\x9a\xe5\x7f\x84\x00\xafi\x19\x07K:\xbbw\xd4\r\xb1\x97\xee\xc7\x85JQ6=q\xf2\xe2IV|\x0c\x120\x1a:}\x0e\x10>\xf9\x82\xbc\xe3`\xd33~\xc3\xfca\x06\xfc\xbf\xb5u\xa6r%\x9faBk=]2QH;\xe6\x96:m\x15z\x84\xc3\xe8\xf9\xa5\x87Y\xf8\x9deVIm\x8c\xa1\xc8-\x0b\x8d\x15\xf1}\x8c{x\xce\x9c\\"\\!v\'\xcddL\x9c\xe2\x1e\xc8!X\x9f(\x0fn\xdf\x10>L\xa6\xab\xda\x15\xc6\xdbx\xc0v<\xf4\xab\x80T\xf4\x157\xd2\x97\x13#e-\x9c\x11\x87\x8b\xb4\xe9\'\x02\xa1U\x0f;Q\xf2Z\xd2/|:\x0b\xa8-#\x06,r\xfe\xea\x18\x8f%\xc5O\xc1\x92\xaflI$\x07\xd3\xba\xf45\xad\xaa\xe5\x18\xcc%/\xba\xef\xaa\xba8\xba\xab\xe1\x83:,\x95\xce\xf2\x8a\xc9N@`\x94+\xe6\xa53\x1dk\xc5\xc7kX7G\x00|\xa5\x1aL\xec\xf0\xe5p\xbaI\xd7\x97\xde\x1b\x15\xd5 \x888\'\xa3\x15i\x8b@\xef5\xe78lq\xa3\xe2j\x0b<\xb2~I[\xf6\xd0Xx\x86Dv\xae5\x81\xaa\xc2\x89r\xff\xa3B\xac\xf9\x07=\xc2I\xea\xbe\x03\x02\xc9f\xffJq\xd7\xbd%\x84\xee\xda\xec\x11\x00\xd5=-}\x98.u\xcd\xcbb`d\xc5\x87.h9\xb5\xdc\xd4nd\xd8\x06I\x9f\x81y\xeb\x07\xfb\xd4\x06Y\x97\xc5\x16\xdf\xd60\xdb\xfc\xa3L\x1cj\x0f\xe2\xc9\xd9;\xcbe\xc2\xfa\x94\xe8\x11\xb1\xc3\x95\xb3\x08\xae\x01s\x9bF\x86\x1e\xa3{\x89\x9b\xf2fx-\x9c\x0f\xe5X\xaaj\x85yP\x16\xe1\xd3Mg\xdd}\xc7\xe3h\xb5\xb0*\x85}\xf8\x15\x9a\x9b=\xed\xe4\x0e\x8do\x89\xca\xb5\xc4\xca\xc8\xd8\xecn\x83\xbf\x066}\xbb\xa98\xd6\x81\xfd]\xdcx\xbf\xceF\xd99\x02G\xcf\xdd\xed|\x18n\xb4\x0c]\xb5;\xb9\xcc\xb8\x9c\x88\xf9j!neq\x7f\xa2m\xf3\x05\x1e\x8dsL\x11kv\xdaO\xf5\xfc8^\x96>\xb0\xc8(:\xd9\xc1\xf5:>\x11\x1b \x1a2,\x9a\xc9~\x8c\xdd\x8a\xcb)\x80?:5\x8e\x85\xa3\xd3\x11g\xban35\xbf\xc3]\xf9\x94^p]\x87\xe3\xfdz\x9fd\x7f.\x80Q\xdc*\x07sP*%\xb2\x99\xaa;_\x1e\xee`\x91\xa5\xe8\xcb\x81\xa7g\x8a\xcd\rdd\xc7\xde]\xee\x05\x82\xe2s@{i\x04b\x92Q\x05nL\x0b;\x89>\xbex\xfc\x9d\xdb\xdc\x9e\xcd\xdahg:\xce\xb6Hm=,j^-~M\xedo\xca\x83w\xc5TK`K\xfa\xf8\x84\x17\xbd\xb2\xaf]#\x8bI\xdf \xbd\xee\x9f\xce\x19/<{\x82\xc4~\x1f\xc4P\x13j\xa3\xd0\x0b=\xb8\xe3y\xd9_\x8b@\xad&MF\xce\x03Z\'\xf7*%C\xd1g\xf1!\x93\x88\xbb\x01\x8e\xb4\xdbZd\xf3\x92\x1fF\x1b\xc6\xe1\x99\xbb\x8ec/g\xd9(\xd8%\xe4"\xce\xaf\x98\xb1\xf0\xa6\xe0\xd8\xcc\xebu\xd8\x8bT\x9e+x\x93\x0fw_\x82\x9d~U\xce\x8c(\x99L|y1\xd0\x0f\x9c\x1d7d=\x91\x17\x894A\xad\x88(\x94@aCi\xb2\xde\x1d\x1d\x97|\xb9l,\x9f\xbd\t\xce\xfd<\x98\x14\xf4X3\x0b\xef(g\xb2\xf8\xa4\x8c\xff\x1aj\xf4\xa0\x865B\xfaQ\xfa\x97\x81\'%\xf45\x19\xf52\x12\xd7\xd6\xd8\xdf2\x94kb\xd4\x8b\x07\xdd&X\xb2\x92e\xbd\xa0\xe6?\xfe_b]B]\x93P\xed\xcf.\x15}#Y\xa1\xe5\x90\xbe\xe7Lb_R?\x04I\x176d\xc68\xa0\xdc+\xcc|\xc0\xa5^\xb1X1f\xf2\x84\xea\xf1\r5Q\x0e\xa8\xae\xd2\x1fB\x04\xae\x04K\xfe\x08\xe6}\x84\x8d!n\x00\x0cPF!Z5\xab\x06\xdbxS\x85\xa8\xa8\x80\xa1\xfdq\xb1\x93\xdaCcn\x8d\xc0>\xe9\x91Ax\x81\x98G-\x0c\xa9\xd74\xa3\xaf\xf9\n`\xe4\xb9\xd9\xca\x02\xe2Vp\xa9E,Q\xbf\xc6\x8c\x0cDY\xd7\x01"\x82\x87\x80Pd\x9b18\xe1\xf0d\xad\xc6x\xa6\xd5\x8ba\xe4\x8b-\x08\x02"\rtlN\xd3]21yu\xf9 \x89\xc7r\x8e&\xc1\x06O\xf7n$\x99\xf0\xab(\xc4\x0f\xd6O\xe6\xfcJ\xc2:\xbc\xac\xa1.H\xa6\xd6\x04\r\x0b\xe3>M\'\x04\xb5\x05\xf6\x1e\x96!Y\xbd:\xe3\x03/,xri\r\xc0u\xe5\x0b\xb8\x0b<\xd9V\xf3C:\xd92\x0fK\xdf\xdd\x11R\x0e\x03u\x85v\xa2}\xd0f\xf4"\xde\xe6\xceC\xadrP\x140c\xf8\xd8\xd0\xd2\x00\xe4NSw\xf9\xd1\x15,\x9c\x1f\xda\xe4\x0cAt\xc8\x8b\x15\xefcV\x010\x10\xaa[\xddd\x81\xf4\xcaSgx\\\xa6\xb7\xb4\x88R5G=\xfd\x03\x0f@>\xbfW!\xee\xa6\xfc\x8d\xbc\xbb\xb7.Q5\x00\xc3\xaf\xfb\xcb\x8f\xf9\xdb[\x0b]lZ\x8fALcx\nvb\xdd\x00\xf75T\xb9\xa4\xfc\xfa\xafm\x10z%\nc[rj\x8d\xb9\xea2C}d\x8ee\xed\x0b\t\xc3T\xa0n\x91\x82\xd9\xa0\x14\xd5A\x10\xben\xfa\x0f\xf4\xce\x009\xde\xdf\x8b\x14,k5?\xef\x184u\x9c\x1b\xea\t\xef\x0c\xec\xb3}QA=\xe0K\n\xf7\x0fW\xc1R4&\x9a\xdc\xef\x1dz\x1c}\x89$\x02\xe0eWV\xc7\xfb\x98]9u.\xf6m\n\x8d3\x17\x89n\xb5\xe8\x14\x86]\xb6\xecM\x03*&Rt\xfe\xd3\tA\xd2\xa3\\+]\xf7W\xe5Z\x0b\x91\xc3$\xf3\x13\xbdf\x90\x9d\xe6\xba\xde\xe6\xc3\xa1\xf0\xdf;(\xc9v\x89\x93\x9e~\xf1\x00\xcd$$\xd5f\x03W\xed\xd6\xb9\x88\xb9\x7f\xd1J\x06\xb4\xe2<\xe9\xdb\xed_\x06\xb0T\x0c3C\xf2`\xa8\xa4${"6Z\xe8\x86\x8d*|\xec\x18\xa7\xc5\xbd\x8d\x19s9!\xdeI\xa7\xf1\x8a\xb2\x13g\xb2\x11\x9c\x13\xb4\x8c~O\x0c\xc0\xbf\xbfD\x07\xadm\xc2wz \xd5\x9f\x80\xe7\x0b\x1e\x86n\x1ef\xae\xfa\xa1\x84aF\x85\xf5@\x05\x97\x9a9\x15j\xf6\xc34vr\xc6\xbc/N\xb7\xa6\xae\x1f\xa0\xe6M\x83\xf5@/-j\x10dZM\x82\xbfnW\xe8\xb7oU\x98F\x1b\x93\xcfzj\xaa\x0fS\n\xdb\t?f\x80\x9e\xf7\xcd`\xf5\xb7\x1d(a.\xfb\xe1\n\xe7\xfcu{l\xc7\xc9\x08Y\x1f\x15\xfb\x82\x83\xa8\xc9\n\xf7;\x0f\xdb\xb0\x02\xda\xde\t\xd0\xeb\xa9K\x85(\x03\x86B!\xcd\xc2\x9e\xe9\x92\x9b\xb1\xca\xdd7b\x17\x13#\x9bz\xd7\xe8|\xd9\x0c\xd2\x14\x8d5\xf1\xef\x95;c\x7f\xfe.\xb2\xc5\xdc0\xbb\x18\xaa\xaa\xc6jG^\xd5\x92\xd4\xf5\xbc\x84\x0ftAp}\xcd\x16\xf4\x1a\xc6\xc0\x15\xb4\xd5\xcf`\x01\x05\xdc\x1d\xdd\xd8\xf6\x81\x8b\x15\xf5\xe1\xe3\xae\x17\xd1A\xda$\xc5\x9a\xd0\x03\xec\n\xb7\xd0\x1f\x82x\x7f\x87\x83\xa6\x06\xd0\xd9\x1a#>_nB\xc7\xa5\xddN\xc6\x1b?\xda\xda@\x1d\xbc\x1aM\xba\xa5\x1fB\xc0\x03\xc3x\x8fC\xe0]\xc4\x06\x0cE}9\xee\xa1\xf0\xf6\x91\xc0HH{{(0\x88\xd4mZ\xf7B\x91\x19\x8c~\xd9\x18\xf7\xdf\xab\x1e\x89$\xc9\xc8\xe9\xb2\xbaG:\xb1s\xa8U\xb5\xa8\xf3\xf7\xf7d\xe8\xb5\x97w5R4\x00\x85,\xd39-\x07\xfb\xcai\xdb\xce\xdb\xd4\xfd\x10\xca\x8d\xe6/\x03\rL\x85\x047\x10\xbeZ\xd6\x18O\x87lJ\xe2\xf7\xd4X\xcc\xd6z\x18w\x8c\x05\x97"\xca\x18\xf5$\xf4\x04\xda4\x90U\x99\xfe\x04\xbfO\x95\x8e\x8d\xac\xe3\x8c\n\xaa\xd4z\x14\x0c\x14i"\xea\x81#\xc5\x03?\xc3\xa5\x94\x19q\x16\xace1\xf1\x18\xc3?\xd8&\xe7\x82\xb2*\x06\xc3\x85,J#n\x9a\xe8\xc5\xb8>\x18\x8e(\xdc\xec\xa0em*\x0ei\x92\xb0\xc6JPE\xcc\xb1)\xc2\x86\x93P,\xad\xbd\xd5\xe12\xa5M\x0f#0\x0e|\x0e\xe1:\xd6\xf1\x13\x93 \xd64\xe6m\xbd\x88\xd2\xf1>\x07\xf4-\xc57!^)\xed\x05[\xeaa\x97x\x1a>\xf4\xe5\x19\xcfH\x9b$g\x15\x07\x9b\x98\xde\xa4:c\x8c\xaf*)I\x03r\xc1\xf5 \xdf\x88\x98k\xcc\xb8\x94S\x9fu9\xe0\x7fU>\xccc/0\xae}1\xfa\x94yy\x9d\xb8\xa63\x8cIm\xef\xd0\xe7\xa0\xe7\\\x987r\x8a\x96\xc8H6\x14\x15\x0c\x06\x99>Z,\xb7\xdd\xa0\t\xa9\xc4&\xb1\xda\x16y1\x91\xdb\xc6\xa1\xd9\x06\xfc\xa2\xec\'\xa2}\x0cS\x00\xa8\x86\x06\x14?,\xf5\x0f?CH\xc2,\x83\xef\x8a\x06eVL\x9b_|\xdbh$\xc2\xe4\xa0\x1d\x84\x17\xe2\xda\xbb\x9bY\xd0\xc9z\x91\xa76\xbb\x08\xb3\x17C\xd5\xd2\x01\xca\xf7\x8e\xc5\xa3%\xd4\xc8u\xbfW!\x06\x80\xda\xbc\x1b3T{\xa8\x85G@\x04\xe1f\xfb%\xb1w\x84\xd1\xf1\xc9x\xfa\xdb\x01`\x89\xd2w/\xf0\x96\xb1\xd5\x0e\x14x1\xd4\x06\xa4\x01\xe5\xe6\xa5\x04.\xf5\x05e>\xde\x98\xf0\x10\xff\xd2g^\x8e\xc1\xb5n_\xbc\xa4\x8e\xd6\xe7I\x0b\xf9@\xb5\xad,\xaf\x86\xfbW8\x13\xb4\x82\xde\xd5/\xdb\xdbD\xafH\x87\x12\xb1\x7f\xb3\xe5x\x07\xf1\xcd\xe3\xabh\x04U]\xb8?j1\x8d\xba\x81\xdbD\xa8\xce^\\\x86\xf2K\x1d\x82\xa6\xbc\xa5\x83,!\x8e+\x7f\x12\xe9\xb3\x92(31\x8d\xe6\xd3f|\x08t(\xb6(\x99\xfe\xc9\xcd\xe2\xcc\xa6\x02Vd\xae\x82\xe8\x9c\xa6\xc0y\xbc\x0e\x12\xd3\x9e\x94\xe7e/\xb2\xf1"c\x84y\xd8\xa1\x05\xeb\xabmR\xc9\xac\x07.\xd8\x12\xd9\xe2C\xba\tl\xa0\x0cY\xc4 \xda\xa4K\xdb\xf6\x9ax\xc1\xf9\x8c\x0b\x03 \xe7x\xf3\x05i\xd1w\'\xaa\x89~\x1b\xb1\x159p\xb7\x1e\x00\xa2\x05\xb7\xdd\xb5+L\x03\xf9\xde\x1f\x1e\x98\x81x;Z4\xac\x1e3M?\xcb\xa0\xb9h\xe0\x03\xfd\xd2\x1b\xf0P\xdf\xbe\xd5\r\xc9 \x14C&\\\xd8\xb3\xd8\xdc\xa9\xd6\x15\x19\xb2\xb6\x95\x17\x92]\xa4\xc0\x91T\xe0\xdb\xd1\xf0\x9c\xa6\x17\xd9g\xc0\xd9s\x1c\xf7\x11\x83\xc2\xbe\xf9\xda\x11.B\x8d\x96\xad\xd1\xe7\x91Qd:d@\x9e\xd8j\xf3oo\xe3VA.x\x1b\xe9\xd9\xc6\x14l\xf5\xc9\xa4\xe7\xb6%;\xe7@\x08&B+\x18\x0c\x1e\x1a\xd9ec\x93\xa4\x98\x19cv{\xe6\x08\x10\x1e\xaa\x03*\xdeQ,\xd9n5Yb\xdf\x04|Px\x85c\nN\xfai\\\xee\xc8K%@v\x14\x94\xf4\xce|\xf9\x195\xc3i\xe6\xde\x9a\xfef\xdd\xdb\x13\x1c\x19\xc6~\xb6\xf7\xf3B\xaf\x11@\xb1\xbd\xc4\xb8bc\xaeI\xf93\x8a*\x93\xe1SiW\xda\x98\xb0\xaa;J#\xcb\tW\xaa\x1fQ/w\xaf\xf1\x10\xbc\xadG\xe3&SV\xd9>\xb4\x15c[\xefMF\xb9\xe7\xce\x1e\xae\xcc\x85\x04\r\x92\x8b\xf7\xd2\x1f\xbb\xf5\xda\xb4\xb9\xca)\x94[q$\xdd0m\xd3\xb8+\xa8Y[\x14\xe5N\x84\x11\xb2\x8f)\xaf\x1e\xaa\xae\xb8\x01\x14\x9dNF\xcbn\xdc\xd7\x0f\x83zt\xa1\xb9\xf4A4\x7f\xb3b\xb4\x1eBp\xfb\xd9\xcc{gN\x9e}|\xb3\xdc\xdc\xda\xda-\x7f\x9aa\xd5Yv\xdc\'\xd3U\x19\xe1\xe6g\xb6\x08\xfct`hO\x80E\' \x9d5\xb4\xca\xfd\xa1!\xe0\xf8#C\x08\x11\xb4\xe5\x98\x10\xa6\x9e\xb7\xb6\xcb\xcc\xef\x80\xc7\xce\xde\xbd\xdeQ\x9f\x04\xf6\xf5f\x17A\xe9\xda\xb2qYtJ\xac\x95\xac\xe6\xd6#\xb5=\x94\xd5\x17R\xbd\xaf\x18\xb8\xe5\xaar\xd70\xaa/Z\xd8\x10zc\xb1\x1d<>M\xdb\'\xc7CR\xf2\xd1\x0bhF\xba\x91J\x81,j\x94}R\x0c83tO\x135Y\xddO:8\xda\xf6\xdd\x97\xbf\x986\x8c\xc0\x8aG+\xa3\x02\x12\xfa\xc6yq>\x02\xdf(\xbeITYk\x9d\x18\xa2\x89.K+\x87\x88\xc9`z7\x01a\x94\xe2\x1f$\xcbT;\xdcpI\xbb\x17\xdb1{\xcbK\x99\xea\xc5\xc5\xe1\xb9"\xf5\xe0@\x14\xa6\x8a\x1e\x85\x95\xe4\x89\xddec,\x80\xef(Q\xd4\xaf\x8fC`\xafX\xb5\x0eph)\xf9\xf0\xdc\x10aO`&\x92Li\xca\xdc\xe0\\\xb1\xf2\x8al\xb8p\xa9\xed\x99~;\xe6\xcf\x1a\xeef\xe2\xe3\xf9\x03\xe1\x07\x84O\xcf\xdbB.\x1dvP\xc5\x8c\x06s,\xc7+\xe9w\x9e\x9b1I\x08G\xb4\'\x95_\x0ct\xbfK\x9f\xf5\xa0\x8d>\xcc\x9apy\x9c{\xda\x92\xc1\xf7Q\x1d\xc6\\r\x9f\x10\x06\x1a\x8f\xce\xc4Ni\xbcdXC\xe7p\x16\nP\x07\xc2\x11\xf0\xe3A\xdckN\x85\x96\xebc!O\xcb\x8c\x0f\x07\x00\x89,RUB qmkp1\xed\x8b>\xd38\n<_\x13\x06\xa1\xf0\x97\xbbb\xf9\x8d\x82qv\x8d\xca\xe0\xa8\xbfbtwJY\xe6\xbd\xc9\x8c\x8f\x8cL\x98\xe5\x12\xc8X\xa85\x94_\x05\x95uO((lz\x00\x9f}5\xfcb\xe15\x9ah\x8bZ\x07\x8a\xd5\xd4\xfc\x13\x03r\xa4\xd6\x11\xdc\xae\x1d\x0f\x8aCB\x1a]\xc8\x93\x04A\xc9Z_\xc3\xb8\xacu\x1akl]d\x11w\xf3\xd7t\xa2M\x900\xd8y\xcd\xda\xc8\xb3i<\x82\x16H\xb4} :o\x8a\x97\x1a\x8c\xca8@\x82\xb1\x8c.\xb8~L\xb2}\xbc\xe3\x02\x03G\x10"\x9c\xc2D\x00\xedy\xf5J\x1fDj\x13S\xc8\xe0)\x83\xa1w\x1b\x0c^\x9fa\xed\xec\x8e;J\xee;\x95\xfd\xceo/tE"\xaf\x95\ne\xd6\xa1KZ\x7fJ\x15\x9e\x1f\x9a \xa5X\x81;$\xa6\xf7\x9b\xd8s\xd5\xc7\xba\xf7b{@h#\x7f[\x13\xba\x19\x9eW\xf5V\xe63\xd5o>o\x7f\xaa\rhG7\x0e\xa50v+\x8dG\xea\xad\x90\xe6W\xce\xc9\xbc\xdep\\\x1f\xabf\x99\xee\x06n\xac\xc5\xc5\xf3na\x8ap\xdb\x97\x99zG\xe3\x88\xd9U\xf1\x8f\x10\x81\x1a\xf36\x82\xccp\x04\x19\xe29\x88\x01\x1d\x07\xa1\xf07\xe7>\xbaOB\xd1\x81\xaf\xca\x00O\x80\xbe\x10\x8d\xb2]\x08u\r\xa4\xd6==\x0b\x91eR\x17\\Y\xd4\x1f\xbf\xee\x08\xe9}\xc0>\xa0\xd3\x9d\x85kED\x90-`s/\xbc^\xa0\xa6xZ\xcd\xa7$BgV\xd4\xe7\x0f\xdf\xbf\xee\x01s\xa5\xf0uU"\xcdu\x16\x03\xb2\x04O\xfb\x94\xaf\x08C\xc6\xba+\x06,^\r$J\xd7\xe9\xf8\x0e\xf4\xabb\x13\xda\xb3\xf5\xb92\xefV\xb0\x9b\xd9B^\x80>\xe7\xd1B\x87\xc3\xc2\xae\xe4\xc5\xf9\\*US\xbb\x03\xe3!T\x9a\xf7s\xdc\xec\xe2[\xa9\xdb@\xcf\xd0\x8a\xbd\xf8\xc8J\xc4Q/\xb0\x00(_\x1f\xf1Z(\x10\xbc\x91\xec\x95\xc0\xca8Erj>\x06\xb7<\x89Ei\x1fP\xd9s\xc4>6\x10\x01\xd4\x8aduK\x19\x80\xea\x1b>\xbc\xb0\xcc\xf6\x1d1\\W\xba\x0f6\xec\xb3A\x8276\x88\xd2\xaa*s\x1ao\xf0S\x7f\xd4kzo\x1f4\xfapQ\x16\x13\x00\xab6\x18\xd7\xcb\xf2qW\x88\x9b\'x\xe0\xf4\xf6&\xb2\xc6\x8b\xa8\xdf\xa2#\x10\x14s-\x96\xfa\x0fF\x9d\xc0\xb2\xd2\x07\x07;\xd77\'yY\x86Z9L\xd5k\xd0\x8c\x8f\xce$\x12\xec\xc8\xd3/\xb5\xf661\x1f\xd3X\xe1\r\xc1\x8d\x94I-\xb5aU\x9a\x8b\x82\xc7u\xad\x8b\xf8\x86\xd5\x1dbu\xbc\xcef\x14d1\xf7?\xb5\xb3c\x8f1\xe0\xa1\xaf\xf0\xa6j8O&>#\xc6\x08\x9f\xf67\x19\xf6\xcf\xea\x82[\x92\xe1\xa4\x8a\x01\xb3\xa0>\xdd\xae/DV\xa3h\x9c\xff(\xc0{\x00omP\xbd\x18\xdb\x86\x9c\x9b{\xbd\xeb\xca\xc5l\x86P\'\xf5\xbc\xc1\xe8\x15b\xe5lJ\x8c\x15p2\xa1\xcb\xceq\xba\x03#\xfc\x83\xf3hC\xce\xda\xf8\x11[\x03\x98\xff%\xc2\xce\xbf\xe2\xf9\xa9\xa0\xc8\xed\x1a\xcd\x93\xe1\xbc0T\x1eD\xc2\'\xc1o"f%\t\xf8i;\xd07\x01q\xea\xa2TR\x1e\x8bQ\xc2r\x86)QkNf\xc7\xf9e9\xe7\x8c\x8b\x9d\xb6\xe7\x0b\xf6\x86\xfa$js\xfc_\xc0J\x02/U|c\x06(\xb8H\xcf?O\x9f\x87\xc0\xffxa\xfc_\xe2\x1d\'i\x99\x88V\xef\xee\x92\x80S\xe4\xb4\xbeF\x81\xf4\x03+mg"=\x162TS\xd5^\xef\x9f~\x06\x99\x9d\x80@h`*w\xa2\xed\x84*\xcfpF{\x07\xaf\xca\xa4\x18k\xd1Y\xd2\x17\xbc\x07\x8e\x1c\x1b\xf7A\x0b\xc6\xbe\xa8\x03\x7f\xea\xd2\xe0\x13\x8c\xe0\xf2\x98\xa6\x1fJ\x01\x07+/b\xde-\x049\x9c\x83\xa5\xaa\xb6\x8dk]- \xc7\x1a\x91\xa6V\x1a\xc6\r\x08\x16\xb7\xb1\xda{\xe3}\xa6z\xe6`Y\x88\t\xe7\xebK\xb9\r\xf1\xb3\xd10\x10\rR\x1f$y%\xa6\x85\x8bw|&U\x95\xfa\xb8\x1d\xdb\xb2;\xee\xee\xa6\xd6\xab\x0bK\x9a\xed\x8b\xe6z\x97"\x15\xe0p-6\xd6K\xaa|\x00#o*\x076\x9d\r\xc2_*pcM2\xe8\xfd9\xaaY\xb0{d^\xaf\xb2]\x8d\x03#cQ\x04\x19\x8amo\x08\xe7e\xe0E\x11\xf9,\xe8\xb5k\xaa\x07\xe9\x82\xf4tw\x83<<\x92\xe4[\xbb\xd7\xb55\x19\xb7\xed\xe9\x7fd\xf0\xbe\x112\xc5\x186\xf0L%\xaa\x9d\x17\xe8\x94\xdd\xf1\x83\xd7\x89\x8e_\x07\x03\xa7\\\x1bz\xa71\x96\x84m\xda_[\xa1\x91o\x13\x0b\xca\xf3\x8e\xf2\x96J\xf3\x11k\x0b\xae\x86|\xb6[\xd40\xc6\x907\xf5\x92\x1bK3\xfejR^1"\xb5\xeb\x87\xf7\x06\xb3\xd0\xac\xdf\x8dq]\x84\xf0\xb7\x05\xf2,r\\\xa5]<\xc4\x95u_\x9c\xb8\x1b\xe3\x05\\\xbd\x18\xb5\xbe\xbc\xd4\xc3\x03#\x95R\x11\xa0\xe4[\x10@\xff\x9b\xa2\xb9{\xbe\x802\xbc\x84y\xa4h\xdduEs\xdcC\x0cV5\xd0\x97;\xd1\xcd:\x95~\xa3\x1fi\x86\x0f\x8aaP#*\xa5(mhS\x1d\x7f\x1e\x02`b.|\xef&\xeb\x84?<St\xf8\xc5f\x92_@$N\x86h;X\xf6N\xe1?\xd2W]t\x940\xd5\x88T\x92\xd1\x8b^/9\x97\xae \x00(I\xde\x0fY\xbe2\xc3\xe7\xce\xc3WR\x97\x8a8LC\x16c\xb3R\xd9{\x87s\x91J\xd1,\x1d\x05\x13\x85\xd5\x16\xd2*\x16\x87\x92R>&\x9c9\x01\xbc<9\x02\x19\x0eS\x1ce\x14\x10\xfe\x1c\xa9l!&\x04\x0bc<|5I\xc2\xce\xe3\x14>f\x8c\xe0a\x10\'\xa7\x91r\xe6J\x9d\x7f=\x14AjQ\xb6\x9d\x05\xa7#\x93\xed&\x1c\xcd\x123\xa2\x18;\xe8\xdess\xa5\x1c\x80\xb5\xadr\x95F\xdb\x85@%\xe5\xf9\xec@V\xcdM\xd6\xbf5\xfc\x90\xc9\xb3\x90\x99\x00\x1b\xec\xd3\xc4\xb71\xd0\x00\tb\x18\xbe\x17=\x1f<\xd0\xbd\xcc7\xb8_C\xb4XU\x01\x83\x1d\x08\x88\x17\xb0c\xa8\xb9\xb3\xf9O\xd1K\xddc\xda\xb2Q>\x1cU\xe9\xca(\x1aMN\xd2\xcd(\x81\xf6\x1a\xc5\xef\x0f\xcf\x04\x1f\xfbW/a\x85\xb7\xbb\xe6\x83\xeb\xb2N&\xaayh\x07\x14@C\xc1\x8b\xee\x047\x8fj\x13\xbf\x15\x8e\xa6\xd7B\xf2\xc4\r\xf1\xd9)\xd6\xa77\xcad\x81MC\xcaPM\x93b\xab7,\xe7V\xa4\xbc\x132\x9d\xd8\xc5\x16\x04\xbaD.#\xc3\x9d;,| \xa3,\x92\x99QT\x994t\xb5\xbb\xd3L\xaa\xb9\x00\xb22\xaeV\xeb^a.&O\xc0y\xab>\xc9\xa8\xfc\xa8\xb6\xd6\xd3V\xa8\xb6\x8er\xe1]\xde\x8eo\xd8@\xdd\xe2\xf9\xe5R\x88k_S\xc7\x08\x1e\x80\xd0\t\x11\xba\xa3]QP\x83\xceQ\x06\x1e{1\xa4\x0fM\xd8\xd3\\[\xb27\x82\xdd9*B\xb6b!\xd9\xe1\xee3\xca\xd0\xfe\x94\xa5`m\xb5\x19\xb0\x90\x17\xa77\xf4\xa98\xdc\xd0\xd7f\xd3o(\xd8#D\xcf\xa9\xed\x97\xb5\xfd\x88\xb17\xaa\x17SQX\x9eY<\x1fa\x8eO\xeb\x17\x99\x02\xe29\xdf\xb6\xd9\x7f\x1d$\x1c}J\xe0>\x83\xcdD\x14\x05p\xea\xf8N\xe3>\xf5R\xd3x\x86\xf6N0\xd5\xb7\r\xea7\xef\x05Q\xb1=\xc5\xc5\x01\rjA\x80kT\'\xe3B\xd7/uj\x87\xa1\x1cC\x17k:E\xed\xd5\x14\xd1\xc5\x89\x998\xeb\x80\tfTjox\x10A\x81\x0f\xc1=\xa8\xf4$\x86Y\r\x88\x96\xd0\xe5y\xe4s\xa9\x1a\xf1+\xe7\x87\xc6\xf5\xa2\xce8\xb3_\r\xe18\x87\xe3\xf00(\xa3w5y[\x19\xf6\x940\x95?M!\t\xfd\xd9\xc6&\xd8\x1dl"\xabf\x89\xd6A\x16$`\xed\x97\xaa\x16\x98\x9bT+\xb4\xb3)q\x12\xda?\x03\xdc\x83\xd8\x91\xba\xf0\xed5\x89\x8e!\x8c\xc9\xed\xe7\xc5\x00\x0f\xf2-\x8ef\x88\x0f6\xcb\xe6\xf1\xf3\xae\x85*\xc6\xe4<\t=\xb2\x8a\xafP3\x89\xed\xadM\x11\xb3\xdc\xe5r\x1f\xbd\x90\xc2\xd0\xb9O\x8b\x83\xd5-\x14\x8cm\xdd\x1a<\xc0\xf4\x85_\x9e\xab\xfa\x87\xb2N\x7f\xd8E\xc0\x98\xff9b\xbbn\xb5\x07F\x80\x05\xd3\xc5\x15\x86\xc6\x12\x17\x86\xc0(C\x85\xaa\x87i9\xdd5\xe4\x06[z\'\x8di\xa7n\x08)L\x9a\xa5r\xfb\x1de\x8a\\\x1b\x8b\x97Q\x91\xa6\x826Uz \xe5S>\xe3\xd3\xdd\x8e\xa6\xa3IU\xbfZ\x9eL%\xe4\xb4\x1a\x87\x942\x94\x9fp\xd4\xf0\x17\xe9\x118Q\xca\x1cva\xca\xbc\xc52\xb5\xdbk^U\x12^P\xa0\xe0DS=\xa0kR\xbe\xb5\xd3\xec\x13<D\xf3\x07\xf8-\xfa\x82\xdb\xb9\xe2>\x0f!E\xaf5\xc1\xfe5U\xf8\xcfj:\x88\x8dS\x93\x9b\xe9\xd6\x8e\xd6i\xa3\x97*o\x8a\xd2.\x0b\xf4\xfb\x86u\x9d\x08\x86\xe9\xb8\xbb_\xee\xa5RQ\xddQ\xed\xc4\x00\x93X\x88\x94X\xbe\xbf\x05\xa3L\xac(M\xa97\xce7\xe8*\xa2\xd2\xbc\xdd\xa4p\x8f)\xda\xe8\x04\xaa9\xc4f\\\x8a\xe88\x1b\xe6\xaf\xf4w\xb2t\xfb\x07\xd7\xda\x17\xb6\xfa\x01O\xc9l\x8a\xbb\x13\x11\xcf,\xf3\xf7\x84u4R\xe4X\xa0\xc7\xed\xefO\xe3\xed\xbf\xa2\xa9\xf2{\x02\xb0\x89~\x9bd\xfd\xdb\t\xeb\x9e\x0c\x12M\xb6\xd9\x8c6%\x1e\xf0\xb1"\xf9R\xa5\xf4\x93\xc9\x92\x91G\xdb\xd71 W\xcb\xfeTJ\x85\xdb_u\x87\x16Y\xf74\x1f\x11Ur6\x80\xa8\xe4oSG\xdc\xd7\x100\xcf\x11@\xb8\xba\xe8\x89\xd68a\x1c\x86I\x0e\x12#\x9c\t\xa2\xc2S*J\xce7\xfc\x85\xfa3\xcb\xb2Y\x8b\xfb\x89\xa5\xa6#\x9e\xb9\x83\xa2f\xa1q.\x16\xe7<\x92\xec:\xab\xc6G\x17\xae\x0f9\xab\xbc\xce\x97\xbf\x1f\xb9:\x94w\xe3\x9f\xe4";\xc7O\xa6\x02\x02\xd8\xc9\xc7\x9a\xca\x85\x9c\xea\x89\x0f)bD\xeb\xf7\x15\xd5h \x92\x00V\x05\\\\\xf3\n\x8e\xdbd\x9c\x08\xcb@%\x96-\xe2\xe1<\xc6~r\xb7\xf2\xd5\x1dH\x89\xc4\xcb\xcc\xa9\xa3\x98\xf4\xa6\xe0\xfc\xe1\xc7\xc5n\xf7\x1f\xba}\x17\t\xff\x07\\Iu\xb9z5u:\x18\x9c\xfc\xf6\x88\n\xb6?H\xb0\xf4\x94\xfa\x8dq\xcc9\x1d\xfcl7\xdc\xc0Bp|*\x14;R\xb1#\xc63\x03\xab\x81\x99\x9d\x82\x1f\xbd+u\xaf\xf9"\xd29\x84\xbc\xc1z\x07\xa0\x19\x05(\xc15\xb7\xe1\xe9\xda4\x03!\xdb\x1b\xfd\x87\xcd\x98\x91\xd7\xf0#>\xe6\xae\xfbe\x14\xec\xcd\x08\xc4\x87\xe6\xf8\xa5u\x05\xaa7*\xd5\xa77\xbe\x06\x80\x82\xe7v\x17\x8bG\xc7\xdd\xed\x88j>\x1fe\x14^\xffw\x15\xad6s\xb6\xec\x97\xc8\xe9\xe5m5+\xbd\x92\xb7\xa7\xcbe\xe4yt\x98F_\x10\x88\xf7_\x1f\xd3\xc0_D\xf4\xf8\x1b:z\xda\xe07;\xf5\x16\xc0?\xcc1P\x98\xfc\x0f\xe4\xbd\xb1T\xeaF\xab\xcd\x04&22\x14\xe8\x97\xd9\xb8\n\xe3\xf7\x00\xe7}\xc4\x89*\r\xd1M\x9e\xfb0\x92R\xa3\xc0<\xa3sB\xbb\xf7\x04\xf4h\x9d\x1b#\xa7T\x00;ulO\xc4P+\x8cJ|\xb4\x0fQ%\x07c\x8eDB\x92\xae/\xff\xb2\xc0\xb3\x92\xf1\xf3\x85\x87V\xaa\xcb\x1c\xcd\x13.#c\xde\xa4\x08+\xd5-\xa8\xa4\xf2\x06\xa7\xabTl-c\x05\x84\xf2\x0e\xa1\xa2\xafpQH\x015\x86A;\xa9\xd1\x93\xcf\xaaOJL\xbeez\xbb\xb2E\xcc\x14\x0e\xe0\xdd\x11\x0eJ\xd0X\x10G\xa0\x05\x96Y^$i\xcc\x7fi\xc4\xf8\x82\xa0\x99\n\xa5$W\x94\xea\xbdA\x06\xc0RL\xb6z\xe8\x1e\x8c\x99J\\\x00t\xf6\xdc\x8d\xd4\xeb\x9aa\xaf\xa4s7\xb9\x0e\xa9\x1b\x9e\x99 \xc1s\xed\x9d\x03\x1dj\x0e\x83\xd1\xe3\xe6\x81"\xb1+\x7f\xc1\xf9\xb8\x19\xf4\xcc\xc2\xe4\x15T\x8aM@\x87\x1ais\xb4n\x9b\xdc\x9f\x01"sN\xb4\xff\x17\xf4\t\x93mm\x1f"\xcaGKP\xa9B\x08\x17D=\x17\x0eTqv\xcc\x92S\x11YP\xcfN\xd4.\xac\xc0 \x8a\xaa&%\xde\x1e\x8b\x8e\xf2\x8e\xebw\xd9m1p\xbd\x89\xee\xf4\x8d\\\xc3\xc7\xd0\xa4:q}\x86\x8d8W-\xf3\xc0\x1b\xbd\xaf7\xb6C\xd0\x97\xa1`\x82\x16\xc4\x1d4.\xb1\xf1l1H%;\xcf\xc2\xe6\xa6H\x01\xf5c\x9b\xa7\xa0\x10\xa2\xa3\xb3\xc9I\xf5\x805F\xf3L\xa2\xa6e\x9e+\xc7XO\xbc\xc7\xf2\xad\xc9\xa3Wp\xb5\x0f\xaa\xf5\xa8)x\xb6\x0cPzz\xfb\x8a\x91\x98(\x8f\xda\xbd_Y\x17W\xb5\xf5\x96\x06\x92m&\xf1\xc3\xd6\xab~\xddv\xcd9\xdf\xb1\xbb\x9dM\x83\xe8\x8e\xa5\\C\xd7\xb1\xabMBi\x895dm\xc6\xebY\x8f\xbfi\x03\x94\\\xa7!W\x1e\xbd$t?\x83\xbe"i\xc7y\xc0\xe0i\x0eu\xb9\xe5\xc4_\x9bz\xaa\xc9\xe8\xca\xbbi\xfc8&\xe9\x96\xfc\xec\x1emb\xb0I\x9aCi{\xef@\xf0-\xc4%\xbb%\x1d\x05\x057,\xb5\x1cI\\~#\xfb<l^}\xd5*\x89\xa0\xd34\x8f\x96\xb1\x11\x87\xc1\x9e\xc2c\xdc\xdcx\r.\xfd\xfc%:\x86\xd2\xb6\x1eG\x9d\x12\x1f\xaf\xdb<X\x95c\'\xfe\x07\xe4\x8eT\xfd\xd4\x1fN\x00\xb9[\x19\x87\x0e\x8apq\x00\x01\x8c;\x9f\xe1\x04\x00\x88\x07\xb9U\xb1\xc4g\xfb\x02\x00\x00\x00\x00\x04YZ'
compressed_df_intp_routes = b'\xfd7zXZ\x00\x00\x04\xe6\xd6\xb4F\x02\x00!\x01\x16\x00\x00\x00t/\xe5\xa3\xe0\x06\xe0\x02\xfa]\x00@\x01N{W5\xf4i\xa5\x1d|\xf2\x9b{\n\x9f|c\xd7\'q|\xc9g%\x14\x132%\x94\x13\\\xe0`"\x04+\x84m_\xfb\xf8v\xe6\xde\xeb)V\xd1Y\xcai\x93j\x97\x002L\x15b\xc7s\xe8\x12p(\xdbm\xd8Fe\x05\'\x8d\x07\x18\x01 \n2\xc4\xb5\xa4\xba\xef\x9e\x01\xa2\xa2m\xadN\x1d#\x87\xed\x1f\xf1o\xbeKk\xf9\r\xcf\x89\xe5O\xe8\xf3\xc2\xd6\x0e\xf6\x81w\x1el\xcd\xd6\xfb\x18\x1cSb\x9e\xefq\xf3\xc9\x87(U\x9c\xbf_H0\xc0ZC\xae\xc4\xf1SJ\xa4\xf8@\xed\x14\xc8\x19>\x1b\xb4y\'9A\x16\xb4\xf2o\xe5\th\x08)\x0c\xd77\xc0fy\xd7\xa0\xa9^*\'6\x13\xf2\n}\xf3\xc6cl!/\xb2-\xaf9\xdf[\x07\xdck\xd4d\x0c[\x9ciV\xd1\xb5\xa9\x14"\x114\xc6E>y\xdd\x05\x112\x98e\xe4\x90\xe5\xd0<\xcc\xac\x01\x16\xbek\xdb\x00\xc7\xbdm\xe7\xb7\xec\xbb1\x1fA\x15^4\xeaLi\x9a\x1e/\x9dB\x99#6Z\xc4b\xa4\x7f>\xb0\x13(L\xed\x0c\xa5&\x83\xd7\xeev\xfb\xa42?V\x13\xfd\xb7\xa4RQ\xdfG4\xd2\n\xa6\xfb\x97~\xb3\xed\x1d\x8bTfFFlL4~\x06\xa1\xd0\xa1(\x1f.\x1a\xc4\x8cH\xf0\xfd\xc0\xe5\xf4_BaC\x9a\xc2iHR\x9d+\x9a\xea\x1a\xf9\xab\xc7\xe8\xb5\x1e\x0e1M\x8b"\xce\xb9\xb3I\x972\xbe+\xec\xf1J\xc8\xef\xd1\xa1-\xa2\x1fV\xd8\x90\xf7{\xb6%\xa8\x81\xeaQZ\xea]T\x98 \xa4[\x1e\xb8\xca\x91s\x13i\x95Sto\x8b\xba\xa7\x91{H\xee>F\xcf\xa1\x03\xe5\xa8\xfb\x94\xa8\n\x05\xccO1\\\xcdH1\xb4\x87\xb9\xec\xea_\n;v\xbc Go\xc8~\xf7\x88\x16\x03]\xa7\xcdrx}[\xdc\xec\xa761\xba\xfb>\x8932\x94I\xc3\x97\xd0\t\xe8Z\xb1vo\xfe\xb9\xfc\x9b\xbeA\xde\xe8\xbb\x93\xc4\xb7\xb4\xb4\xa95\xbdET!4\xe0\x91S\xda8\xb3\x9b\xee!5\xf6c\x9e\x99\t\xd2pu\xe9\xfa\xda:,\xd0\xd06\x0c\xb6\xf5\x9a\xd2\xd4]\xea\xceW\x97(Z\x02\xa1\xad\xc0\xd6\x92\x8e\xb9\x1e\xd5Z+\xff\x95\x8f\x80\xd1.\x05\xef\x059\x9bD\x0e\x935\xd2\xb9\xf3\x0b\x16K)\xfdz\x8bT\xde\x91\xf9\xfe#@)7\x9b\x94\xbe1\xa5.G|\xf2d\x83\xd3\x19\xb9c\xc4\xbb\xda\xd1:Wy\x8c\xb8P\xc7\x97\xdb\xee\x15\xde\xb8\x9ckq\xc9\x7f\xc0\xeb\xe2\xa4Z\'\xeb{a\xcc4$\xf5T`\xaa\x89FB\xb4\x91\xde\x19\xf6q-\x0e\xf5\x80J\xf4\xd8\x91\x9a\x0e^p\x88lji\xd5\xd8\xcd<\xca\xb7\xdb\x14\xe8k\xa8|\xf2@\x94\x0b\xdf\x95Ja\xb4\xbe\xd6\x8fS\xac\x98I\xf7"\xd0\xfe\xdfQ\xa2g\xe7\xd4\xa3\xb1\xa4F\x1a"\xf4\xd2\xf5\xd8t\x07O\xf8\xca\xfdh5am@\xa4\x96\x9f\xaa#s\x98z\xe8\x8cW\xea\xf5\xa8\x91\xd0\t\xf6\xeb\xc5}tx\x87\xcb\xef\x86\x8d\x12P\xb7\x160\x92\xf9wO\xd5\xc6\xa1\x1aq\x8e\x18\x00\x00\x00\xd4o\xd5a\xa6\xa3\xb7]\x00\x01\x96\x06\xe1\r\x00\x00-a1\xe1\xb1\xc4g\xfb\x02\x00\x00\x00\x00\x04YZ'
# read the compressed df_cie1931 dataframe
decompressed_df_cie1931 = io.BytesIO(lzma.decompress(compressed_df_cie1931))
df_cie1931 = pd.read_pickle(decompressed_df_cie1931)
# read the compressed df_intp_routes dataframe
decompressed_df_intp_routes = io.BytesIO(lzma.decompress(compressed_df_intp_routes))
df_intp_routes = pd.read_pickle(decompressed_df_intp_routes)
# Create the scatter plot using Altair
rect = alt.Chart(
df_cie1931,
width=alt.Step(3),
height=alt.Step(3)
).transform_calculate(
rgb="rgb(datum.color)"
).mark_rect(tooltip=True).encode(
x=alt.X('x:N').axis(None),
y=alt.Y('y:N').axis(None).scale(reverse=True),
fill=alt.Fill('color').scale(None),
stroke=alt.Stroke('color').scale(None),
tooltip=[alt.Tooltip('color:N'), alt.Tooltip('rgb:N')]
)
line = alt.Chart(df_intp_routes).mark_line(
tooltip=True,
interpolate='basis',
strokeWidth=3
).encode(
x='x:N',
y='y:N',
color=alt.Color('method')
.scale(range=['#44aa99', '#994455', '#117733']),
strokeDash=alt.StrokeDash('method'),
order=alt.Order('index'),
tooltip=[
alt.Tooltip('color'),
alt.Tooltip('x'),
alt.Tooltip('y'),
alt.Tooltip('index')
]
)
(rect + line).configure_view(stroke=None)
In the color space are the trajectories of the blue/orange color
gradient overlayed based on the three interpolation methods
(rgb
, hcl
, hcl-long
).
You can see that all interpolation methods chose a different route
in the color space. The “rgb
” trajectory a direct route. The
hcl
trajectory is choosing a route on the bottom part of the space.
We now also understand why there is a -long
suffix
in the interpolation method hcl-long
.
The interpolation trajectory is indeed very long
.
The default interpolation between colors within Altair is hcl
.
Dependening on your use-case you can choose another interpolation method,
for a complete list of interpolation options see ScaleInterpolateEnum
.
Color-blind viewers#
Around 8% of men and 0.5% of women have some form of color vision deficiency. To make sure your visualizations are accessible to all viewers, it’s important to choose colors that are distinguishable even for those with color vision deficiencies. By using colors that have a high degree of contrast, you can create visualizations that are not only informative but also inclusive.
Click to show code
color_vision_deficiency = {
'redyellowblue': 9,
'redyellowgreen' : 9
}
Example |
---|
More friendly diverging color scheme ( |
Unfriendly diverging color scale ( |
Guidelines for using colors#
While accuracy is the primary goal of visualizations, aesthetics also play an important role in engaging viewers and making data more memorable. By using a limited color palette, choosing harmonious colors, and balancing colors to create a sense of visual hierarchy, you can create visualizations that are not only informative but also visually attractive.
Intuitive colors |
Non-intuitive colors |
---|---|
Choose colors that are easy to understand by selecting ones that naturally relate to your data. Click to show codesource = pd.DataFrame({
'land cover': ['Land', 'Water'],
'value': [28, 55]
})
intuitive = alt.Chart(
source,
height=alt.Step(80),
width=200
).mark_bar().encode(
x=alt.X('value'),
y=alt.Y('land cover'),
color=alt.Color('land cover')
.scale(range=['#55AA22', '#5566AA'],
domain=['Land', 'Water'])
.legend(None)
)
intuitive
|
It is confusing and difficult to understand if you choose colors which are non-intuive. Eg. green for water and blue for land. Click to show codenon_intuitive = alt.Chart(
source,
height=alt.Step(80),
width=200
).mark_bar().encode(
x=alt.X('value'),
y=alt.Y('land cover'),
color=alt.Color('land cover')
.scale(range=['#5566AA', '#55AA22'],
domain=['Land', 'Water'])
.legend(None)
)
non_intuitive
|
Colors in moderation |
Colors in excess |
---|---|
Try not to use too many colors. If you choose more colors, use them thoughtfully to emphasize the most important parts of your visualization. Click to show codeimport altair as alt
from vega_datasets import data
source = data.barley.url
labels = ["Crookston", "Grand Rapids"]
cond_weight = alt.condition(
alt.FieldOneOfPredicate(
field="value", oneOf=labels
),
alt.value("bolder"), # predicate True
alt.value("normal"), # predicate False
)
cond_color = alt.condition(
alt.FieldOneOfPredicate(
field="site", oneOf=labels
),
alt.value("#6A8AD5"), # predicate True
alt.value("#CCCCCC"), # predicate False
)
moderate = (
alt.Chart(source, width=200)
.mark_bar()
.encode(
x="sum(yield):Q",
y=alt.Y("site:N")
.sort("-x")
.axis(labelFontWeight=cond_weight),
color=cond_color,
)
)
moderate
|
Using too many colors in graphs or charts can make them confusing and hard to read, which can lead to misunderstandings. To make it easy to understand, use colors carefully and don’t use too many. Click to show codeexcess = (
alt.Chart(source, width=200)
.mark_bar()
.encode(
x="sum(yield):Q",
y=alt.Y("site:N").sort("-x"),
color=alt.Color("site:N")
.scale(scheme="set1")
.legend(None),
)
)
excess
|
Consistency color usage between plots |
In-consistent color usage between plots |
---|---|
Using the same colors for the same data in charts or graphs helps people understand the information better. Click to show codesource = data.movies()
line = (
alt.Chart(source, width=200, height=200)
.transform_fold(
fold=["US_Gross", "Worldwide_Gross"]
)
.mark_line()
.encode(
x=alt.X("IMDB_Rating:Q").bin(True),
y=alt.Y("value:Q")
.aggregate("mean")
.sort("-x"),
color=alt.Color(
"key:N",
sort=["Worldwide_Gross"],
).legend(
orient="top",
direction="horizontal",
),
)
)
bar = (
alt.Chart(source, width=200)
.transform_fold(
fold=["US_Gross", "Worldwide_Gross"]
)
.mark_bar()
.encode(
x=alt.X("value:Q")
.aggregate("sum")
.title(None),
y=alt.Y("key:N").sort("-x"),
color=alt.Color(
"key:N",
sort=["Worldwide_Gross"],
)
.scale(range=["#6A8AD5", "#CCCCCC"])
.legend(
orient="top",
direction="horizontal",
),
)
)
equal = (line & bar).resolve_scale(
color="shared"
)
equal
|
Choosing different colors for the same data can lead to misunderstandings or incorrect conclusions. Click to show codeinequal = (line & bar).resolve_scale(
color="independent"
)
inequal
|
Clarity in colors |
Indistinct color usage |
---|---|
Use colors in order to make the data easier to read. If the items in the visualiation can be easily distinguished leads to better understanding of the data. Click to show codesource = data.stocks()
col_range = [
"#E69F00",
"#57B4E9",
"#019E73",
"#F0E442",
"#0072B2",
]
col_sort = [
"GOOG",
"AAPL",
"IBM",
"AMZN",
"MSFT",
]
alt.Chart(
source, width=200, height=200
).mark_line().encode(
x="date:T",
y=alt.Y("price:Q"),
color=alt.Color(
"symbol:N", sort=col_sort
).scale(range=col_range),
)
|
It is hard to understand the data correctly if you use colors that make the items in your visualization unclear from each other. Click to show codeimport altair as alt
from vega_datasets import data
source = data.stocks()
alt.Chart(
source, width=200, height=200
).mark_line().encode(
x="date:T",
y="price:Q",
color=alt.Color("symbol:N").scale(
scheme="reds"
),
)
|
Distinct classification of categories |
No gradient colors for categories |
---|---|
Often categories are unique and distinct from each other. Emphasize this by chosing colors that are clearly distinct from each other. Click to show codeimport altair as alt
from vega_datasets import data
source = data.stocks()
col_range = [
"#E69F00",
"#57B4E9",
"#019E73",
"#F0E442",
"#0072B2",
]
col_sort = [
"GOOG",
"AAPL",
"IBM",
"AMZN",
"MSFT",
]
alt.Chart(
source, width=200, height=200
).mark_bar().encode(
x=alt.X('symbol:N', sort='-y'),
y=alt.Y('price:Q').aggregate('sum'),
color=alt.Color(
"symbol:N", sort=col_sort
).scale(range=col_range),
)
|
When unique categories are not distinguishable by their color usage, it can cause confusion and misunderstandings. Click to show codesource = data.stocks()
alt.Chart(
source, width=200, height=200
).mark_bar().encode(
x=alt.X('symbol:N', sort=col_sort),
y=alt.Y('price:Q').aggregate('sum'),
color=alt.Color("symbol:N").scale(
scheme="purples"
),
)
|
Explain colors using a colorbar |
No understanding of used colors |
---|---|
When you use colors in your visualization, it’s important to explain what each color means so that people can understand your message better. Click to show codeimport altair as alt
from vega_datasets import data
source = data.iowa_electricity()
alt.Chart(
source, width=200, height=200
).mark_area().encode(
x="year:T",
y="net_generation:Q",
color=alt.Color("source:N").legend(
orient="bottom",
direction="horizontal",
columns=2
)
)
|
If you do not explain what your colors mean, people might be confused and not understand your visualization properly. Click to show codeimport altair as alt
from vega_datasets import data
source = data.iowa_electricity()
alt.Chart(
source, width=200, height=200
).mark_area().encode(
x="year:T",
y="net_generation:Q",
color=alt.Color("source:N").legend(
None
)
)
|
Color schemes#
This sections presents the three primary color schemes used in data visualization: sequential, diverging, and categorical.
Sequential schemes#
Sequential schemes are best suited for data that has an inherent ordering, such as data that varies from low to high, or over time. Examples of sequential data include temperature or population density, where the range of values is continuous and unbroken.
Click to show code
seqs_schemes = {
"blues": 9,
"tealblues": 9,
"teals": 9,
"greens": 9,
"browns": 9,
"greys": 9,
"purples": 9,
"warmgreys": 9,
"reds": 9,
"oranges": 9,
}
Scheme |
Example |
---|---|
blues
|
|
tealblues
|
|
teals
|
|
greens
|
|
browns
|
|
greys
|
|
purples
|
|
warmgreys
|
|
reds
|
|
oranges
|
|
Diverging schemes#
Diverging schemes are best suited for data that varies above and below a center point, such as positive and negative deviations from a mean. Examples of diverging data include measures of deviation, such as temperature anomalies or changes in sea level.
Click to show code
divg_schemes = {
"blueorange": 9,
"brownbluegreen": 9,
"purplegreen": 9,
"pinkyellowgreen": 9,
"purpleorange": 9,
"redblue": 9,
"redgrey": 9,
"redyellowblue": 9,
"redyellowgreen": 9,
"spectral": 9,
}
Scheme |
Example |
---|---|
blueorange
|
|
brownbluegreen
|
|
purplegreen
|
|
pinkyellowgreen
|
|
purpleorange
|
|
redblue
|
|
redgrey
|
|
redyellowblue
|
|
redyellowgreen
|
|
spectral
|
|
Cyclical schemes#
Cyclical color schemes may be used to highlight periodic patterns in continuous data. However, these schemes are not well suited to accurately convey value differences.
Click to show code
cycl_schemes = {
"rainbow": 9,
"sinebow": 9
}
Scheme |
Example |
---|---|
rainbow
|
|
sinebow
|
|
Categorical schemes#
Categorical schemes are best suited for data that is unordered, such as different species or categories. Examples of categorical data include the types of flowers in a garden, or different political affiliations.
Click to show code
catg_schemes = {
"accent": 8,
"category10": 10,
"category20": 20,
"category20b": 20,
"category20c": 20,
"dark2": 8,
"paired": 12,
"pastel1": 9,
"pastel2": 8,
"set1": 9,
"set2": 8,
"set3": 12,
"tableau10": 10,
"tableau20": 20,
}
Scheme |
Example |
---|---|
accent
|
|
category10
|
|
category20
|
|
category20b
|
|
category20c
|
|
dark2
|
|
paired
|
|
pastel1
|
|
pastel2
|
|
set1
|
|
set2
|
|
set3
|
|
tableau10
|
|
tableau20
|
|
Palettes from other sources#
This subsection focuses on pre-designed color palettes that can be used in data visualization from other sources.
Here some color schemes from Paul Tol, https://personal.sron.nl/~pault/ which are picked as being:
distinct for all people, including colour-blind readers
distinct from black and white
distinct on screen and paper
matching well together
tol_schemes = {
"tol_bright" : ['#4477AA', '#EE6677', '#228833', '#CCBB44', '#66CCEE', '#AA3377', '#BBBBBB'],
"tol_highcontrast" : ['#FFFFFF', '#DDAA33', '#BB5566', '#004488', '#000000'],
"tol_vibrant": ['#0077BB', '#33BBEE', '#009988', '#EE7733', '#CC3311', '#EE3377', '#BBBBBB'],
"tol_muted": ['#332288', '#88CCEE', '#44AA99', '#117733', '#999933', '#DDCC77' ,'#CC6677' , '#882255', '#AA4499', '#DDDDDD'],
"tol_mediumcontrast": ['#FFFFFF', '#EECC66', '#994455','#6699CC', '#997700', '#994455', '#004488', '#000000'],
"tol_pale": ['#BBCCEE', '#CCEEFF', '#CCDDAA', '#EEEEBB', '#FFCCCC', '#DDDDDD'],
"tol_dark": ['#222255', '#225555', '#225522', '#666633', '#663333', '#555555'],
"tol_light": ['#77AADD', '#99DDFF', '#44BB99', '#BBCC33', '#AAAA00', '#EEDD88', '#EE8866', '#FFAABB', '#DDDDDD'],
"tol_sunset": ['#364B9A', '#4A7BB7', '#6EA6CD', '#98CAE1', '#C2E4EF', '#EAECCC', '#FEDA8B', '#FDB366', '#F67E4B', '#DD3D2D', '#A50026'],
"tol_nightfall": ['#125A56', '#00767B', '#238F9D', '#42A7C6', '#60BCE9', '#9DCCEF', '#C6DBED', '#DEE6E7', '#ECEADA', '#F0E6B2', '#F9D576', '#FFB954', '#FD9A44', '#F57634', '#E94C1F', '#D11807', '#A01813'],
"tol_PRGn": ['#762A83', '#9970AB', '#C2A5CF', '#E7D4E8', '#F7F7F7', '#D9F0D3', '#ACD39E', '#5AAE61', '#1B7837'],
"tol_YlOrBr": ['#FFFFE5', '#FFF7BC', '#FEE391', '#FEC44F', '#FB9A29', '#EC7014', '#CC4C02', '#993404', '#662506'],
"tol_iridescent": ['#FEFBE9', '#FCF7D5', '#F5F3C1', '#EAF0B5', '#DDECBF', '#D0E7CA', '#C2E3D2', '#B5DDD8', '#A8D8DC', '#9BD2E1', '#8DCBE4', '#81C4E7', '#7BBCE7', '#7EB2E4', '#88A5DD', '#9398D2', '#9B8AC4', '#9D7DB2', '#9A709E', '#906388', '#805770', '#684957', '#46353A'],
"tol_incandescent": [ '#CEFFFF', '#C6F7D6', '#A2F49B', '#BBE453', '#D5CE04', '#E7B503', '#F19903', '#F6790B', '#F94902', '#E40515', '#A80003']
}
Scheme |
Example |
---|---|
tol_bright
|
|
tol_highcontrast
|
|
tol_vibrant
|
|
tol_muted
|
|
tol_mediumcontrast
|
|
tol_pale
|
|
tol_dark
|
|
tol_light
|
|
tol_light
|
|
tol_sunset
|
|
tol_nightfall
|
|
tol_iridescent
|
|
tol_incandescent
|
|
Expressions and colors#
We can make use of the measured values regarding colors in a visualization.
In the following example we will make use of the lumniance
to detect if
the color of a text overlay on top of a bar should be colored black
or
white
. The luminance describes the brightnes, normalized to 0 for darkest
black and 1 for lightest white.
In the following chart we have a sorted bar chart where the text overlay describes
the sum of yield as is expressed on the x
encoding channel.
import altair as alt
from vega_datasets import data
source = data.barley()
base = alt.Chart(source).encode(
x=alt.X('sum(yield):Q').stack('zero'),
y=alt.Y('site:O').sort('-x'),
text=alt.Text('sum(yield):Q', format='.0f')
)
bars = base.mark_bar().encode(color='sum(yield):Q')
text = base.mark_text(align='right', dx=-3, color='black')
bars + text
As you can see, when the bar becomes darker, the text overlay becomes less visible.
At this moment we can make use of the measured luminance to decide if the text overlay
should be colored black
or white
.
import altair as alt
from vega_datasets import data
source = data.barley()
base = alt.Chart(source).encode(
x=alt.X('sum(yield):Q').stack('zero'),
y=alt.Y('site:O').sort('-x'),
text=alt.Text('sum(yield):Q', format='.0f')
)
bars = base.mark_bar(
tooltip=alt.expr("luminance(scale('color', datum.sum_yield))")
).encode(
color='sum(yield):Q'
)
text = base.mark_text(
align='right',
dx=-3,
color=alt.expr("luminance(scale('color', datum.sum_yield)) > 0.5 ? 'black' : 'white'")
)
bars + text
The lighter the bar, the higher the luminance. If the bar is light we like a text overlay that is black. The darker the bar, the lower the luminance. If the bar is dark, we like a text overlay that is white.
In the expression above we have written this as a predicate. The text
appear black
if the luminance is above 0.5
and white
when
the luminance is below 0.5
. The luminance is computed using the
'color'
scale in combination with the interal computed data field
datum.sum_yield
. You can inspect the luminance through the tooltip
by hovering the bars.
Conclusion#
If you use the right colors when making visualizations with scientific data, you can make them look good and easy to understand. You can use pre-designed color schemes or choose your own, but make sure they look good, are easy to see and make sense with the information you’re presenting.