Colors in matplotlib

Exploring color in mapplotlib!

00_exploring_color_2

I am by no means an expert on color in matplotlib, but I have created this tutorial so you can work through understanding how color and color palletttes are programmed. My goal is for you to understand enough, so that you can go through what you need to know to simulate the color palettes found with the Ziess software.

In [73]:
## Importing necessary libraries
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

import czifile

%matplotlib inline

## Turn off warning messages
import warnings
warnings.filterwarnings('ignore')

Plotting

From what I understand, plotting is mostly accomplished using the pyplot library within matplotlib. Just like matplotlib, pyplot was created to work like Matlab, but more "pythonic", which is overall always confusing to me, but whatever. My take still is that ggplot works better, but let's all dig in.

The best way to learn how to plot what you need is to visit a matplotlib visulaization gallery and try to replicate what someone else has coded, but first lets understand the basics of plotting.

In [74]:
# Initializing the datsframe to play with
df=pd.DataFrame({'x1': range(1,101), 'y1': np.random.randn(100)*15+range(1,101), 'z': (np.random.randn(100)*15+range(1,101))*2 })

df
Out[74]:
x1 y1 z
0 1 -15.931532 -6.150032
1 2 -12.096979 18.785079
2 3 15.045268 -28.200907
3 4 10.656998 31.864701
4 5 21.093385 24.071525
... ... ... ...
95 96 117.953045 192.518747
96 97 93.324332 244.283630
97 98 96.859908 185.354578
98 99 65.287416 199.624861
99 100 98.627856 242.589437

100 rows × 3 columns

In [16]:
# basic bitch plot
plt.plot( 'x1', 'y1', data=df)
Out[16]:
[<matplotlib.lines.Line2D at 0x110307bd0>]
In [24]:
## Some arguments for a plot that can be tweaked.
plt.plot( 'x1', 'y1', data=df, color='green', marker='o', linestyle='dashed', linewidth=2, markersize=5)
Out[24]:
[<matplotlib.lines.Line2D at 0x1108e5f50>]

That is how you plot using the basic plt.plot() function. But, like we discussed before, if you want to make a publication ready plot, you have to build it from the bottom up by 1. Setting up your figure 2. add layers of fancy 3. show the plot

In [79]:
# Example data
people = ('Ciera', 'Jenna', 'Stadler', 'Xiaoyong', 'Victoria', 'Marc', 'Ashley', 'Augusto', 'Holli', 'Boss Mike', 'Colleen')
y_pos = np.arange(len(people))
performance = 3 + 10 * np.random.rand(len(people))
error = np.random.rand(len(people))

## Setting the figure: One plot
fig, ax = plt.subplots()

# 2. add layers of fancy
ax.barh(y_pos, performance, xerr=error, align='center')
ax.set_yticks(y_pos)
ax.set_yticklabels(people)
ax.invert_yaxis()  # labels read top-to-bottom
ax.set_xlabel('Performance')
ax.set_title('Productivity during quarantine?')

# 3. Show the plot
plt.show()

There are endless ways to tweak your plot and a lot of beautiful strategies to making a well designed plot, but this is beyond the scope of this tutorial. Is there anything we should go into more detail?

Ideas:

  1. advanced matplotlib plotting
  2. plotting with dataframes (pandas)
  3. while we are at it, pandas
  4. digging into other visualization packages (seaborn, plotly)
  5. what makes an effective graph? Design principles, ect.

Color

Choosing one color

To start, color is defined in a lot of ways and the same is true for matplot lib. The three main ways are

  1. hex
  2. RGB
  3. name

Below is an example of using the defined matplot lib color names.

In [81]:
# plot by color name
plt.plot( 'x1', 'y1', data=df, color='lavender')
Out[81]:
[<matplotlib.lines.Line2D at 0x119ae1710>]

Take a minute to play with changing the color based on the matplotlib defined color names.

RGB and hex are essentially the same. RGB stands for Red, Green, Blue, and is represented by three numbers. Hex is just 6 digit ID made of numbers and letters. The first two digits identify the red components, second two digits green , and last two blue. Hex and RGB are interchangable and can be converted to each other. If you want to learn more just check out wikipedia.

Palettes / Colormaps

Color Palettes are just a collection of colors. Matplotlib has a collection of defined color palettes to use and they are called "Colormaps". These are all named collection of colors and exisit in several groups:

  • Sequential: change in lightness and often saturation of color incrementally, often using a single hue; should be used for representing information that has ordering.
  • Diverging: change in lightness and possibly saturation of two different colors that meet in the middle at an unsaturated color; should be used when the information being plotted has a critical middle value, such as topography or when the data deviates around zero.
  • Cyclic: change in lightness of two different colors that meet in the middle and beginning/end at an unsaturated color; should be used for values that wrap around at the endpoints, such as phase angle, wind direction, or time of day.
  • Qualitative: often are miscellaneous colors; should be used to represent information which does not have ordering or relationships.

Look at all the colors pallette's here: https://matplotlib.org/tutorials/colors/colormaps.html#classes-of-colormaps

In [90]:
## Displays all the color palettes in matplotlib

mpl.rc('text', usetex=False)
a=np.outer(np.arange(0,1,0.01),np.ones(10))
plt.figure(figsize=(10,5))
plt.subplots_adjust(top=0.8,bottom=0.05,left=0.01,right=0.99)
maps=[m for m in mpl.cm.datad if not m.endswith("_r")]
maps.sort()
l=len(maps)+1
for i, m in enumerate(maps):
    plt.subplot(1,l,i+1)
    plt.axis("off")
    plt.imshow(a,aspect='auto',cmap=plt.get_cmap(m),origin="lower")
    plt.title(m,rotation=90,fontsize=10)
plt.show()
In [97]:
# Below is just one example
# Setting the figure size 
fig = plt.figure(figsize=(10, 8))

# Initializing the data
num = 1000
x1 = np.linspace(-0.5,1,num) + (0.5 - np.random.rand(num))
y1 = np.linspace(-5,5,num) + (0.5 - np.random.rand(num))

# Sequential Colormap
ax1 = fig.add_subplot(221)
ax1.scatter(x1, y1, c=x1, cmap=plt.cm.get_cmap('Pastel2'))
ax1.set_title('Sequential: Pastel2', fontsize=16, weight='bold')

# Displaying the figure
plt.show()
In [100]:
## this is how you ask what is in each palette
## Each row in the array corresponds to RGB(red, green, blue, alpha)
## The last column, alpha = transparency
plt.cm.get_cmap('magma', 15).colors # picking ten colors from magma.
Out[100]:
array([[1.46200e-03, 4.66000e-04, 1.38660e-02, 1.00000e+00],
       [4.80620e-02, 3.66070e-02, 1.50327e-01, 1.00000e+00],
       [1.35053e-01, 6.83910e-02, 3.15000e-01, 1.00000e+00],
       [2.52220e-01, 5.94150e-02, 4.53248e-01, 1.00000e+00],
       [3.72116e-01, 9.28160e-02, 4.99053e-01, 1.00000e+00],
       [4.81929e-01, 1.36891e-01, 5.07989e-01, 1.00000e+00],
       [5.94508e-01, 1.75701e-01, 5.01241e-01, 1.00000e+00],
       [7.16387e-01, 2.14982e-01, 4.75290e-01, 1.00000e+00],
       [8.28886e-01, 2.62229e-01, 4.30644e-01, 1.00000e+00],
       [9.21884e-01, 3.41098e-01, 3.77376e-01, 1.00000e+00],
       [9.73381e-01, 4.61520e-01, 3.61965e-01, 1.00000e+00],
       [9.93326e-01, 6.02275e-01, 4.14390e-01, 1.00000e+00],
       [9.97341e-01, 7.33545e-01, 5.05167e-01, 1.00000e+00],
       [9.93545e-01, 8.62859e-01, 6.19299e-01, 1.00000e+00],
       [9.87053e-01, 9.91438e-01, 7.49504e-01, 1.00000e+00]])

Making your own color map

In order to make your own color map, you have to define an array like above. The easiest way is to just use the LinearSegmentedColormap.from_list function. This essentially allows you to name two colors and creates a colormap with the two colors and the far end of the spectrum.

In [107]:
## Make a costum color map called custom_magenta
custom_magenta = mpl.colors.LinearSegmentedColormap.from_list("", ["black","magenta"])

# Setting the figure size 
fig = plt.figure(figsize=(10, 8))

# Initializing the data
num = 1000
x1 = np.linspace(-0.5,1,num) + (0.5 - np.random.rand(num))
y1 = np.linspace(-5,5,num) + (0.5 - np.random.rand(num))


# Sequential Colormap
ax1 = fig.add_subplot(221)
ax1.scatter(x1, y1, c=x1, cmap=custom_magenta)
ax1.set_title('Sequential: custom_magenta', fontsize=16, weight='bold')

# Displaying the figure
plt.show()

Now with confocal images

Above, I purposely choose black as the lower limit, because black is the lower limit for almost all confocal image color palettes. Remember, images from the confocal are just a dataframe composed of pixel intensities, when we created a confocal color palette, we are just assigning a color to each pixel intensity. Let's explore a bit how to make an effective confocal color palette.

Download 100118-oligopaint3-2-02.czi.

Below are a few color pallettes I picked, but go ahead and make your own.

In [108]:
## Color Pallettes
magenta = mpl.colors.LinearSegmentedColormap.from_list("", ["black","magenta"])
teal = mpl.colors.LinearSegmentedColormap.from_list("", ["black","aqua"])
green  = mpl.colors.LinearSegmentedColormap.from_list("", ["black","lime"])
blue = mpl.colors.LinearSegmentedColormap.from_list("", ["black","blue"])
purple = mpl.colors.LinearSegmentedColormap.from_list("", ["black","orchid"])
yellow = mpl.colors.LinearSegmentedColormap.from_list("", ["black","yellow"])
In [112]:
## Again, to see the color pallette you made just use
## - [ ] Look into how to actually see the color array??
teal
Out[112]:
<matplotlib.colors.LinearSegmentedColormap at 0x114f7ecd0>
In [103]:
array = czifile.imread("/Users/cierapink/Desktop/100118-oligopaint3-2-02.czi")
print(np.shape(array)) # look at shape.
(1, 1, 1, 4, 19, 678, 678, 1)
In [104]:
# Check that 'img' is a variable of type 'ndarray' - use Python's built-in function 'type'.
print("Loaded array is of type:", type(array))

# Print the shape of the array
print("Loaded array has shape:", array.shape)

# Check the datatype of the individual numbers in the array. You can use the array attribute 'dtype' to do so.
# Make sure you understand the output!
print("Loaded values are of type:", array.dtype)
Loaded array is of type: <class 'numpy.ndarray'>
Loaded array has shape: (1, 1, 1, 4, 19, 678, 678, 1)
Loaded values are of type: uint16
In [113]:
## Take away channels we don't need
## What are these ones?
czi_array = array.squeeze()
print(np.shape(array)) # look at shape
(1, 1, 1, 4, 19, 678, 678, 1)
In [114]:
ChannelStack = czi_array[2,...] 
print(ChannelStack.shape)
(19, 678, 678)
In [65]:
plt.imshow(ChannelStack[15]) ## Default ## Viridis
Out[65]:
<matplotlib.image.AxesImage at 0x1128502d0>
In [71]:
# whoops doesnt work
plt.imshow(ChannelStack[15], cmap = purple)
Out[71]:
<matplotlib.image.AxesImage at 0x1128e7fd0>

Next Overlay the other channel!

But I can't find the code for that and I don't have the time to do it. Do we have time to do it together? Can someone else do it as homework and I will post here?

Conclusion

Colormaps are created for a particular purpose and human do not percieve color uniformly in a population, but also humans do not uniformly percieve transition from one color to another uniformly. That is why the color palette 'viridis' (created at BIDS!) was created which is now the default in R and Python for intensity.

Check out the talk about the creation of viridis: A Better Default Colormap for Matplotlib. It is a really fun talk!!