# Auto Color Scale

(back to overview offline,online)

## TL;DR

• use plot_by_id(run_id, auto_color_scale=True, cutoff_percentile=(0.5, 0.6) to enable automatic color scaling

• this maximally cuts 0.5% of the low valued data points and 0.6% of the high valued data points.

• You can also use it for matplotlib with the supplied auto scaling function.

• Set your defaults in qcodesrc.json.

## Introduction to the problem and risks

It is a fairly common issue that a heat map plot does not properly show the right details due to some outliers that push the range of the color scale beyond the desired limits, as it is shown in the image below: Here there are two pixels (black and white) corresponding to high and low outliers. The remaining data is uniformly displayed as red and the actual structure of it is drowned. One can of course specify the limits of the color bar manually to view the full structure as shown here: But for measurements that takes a lot of time to perform, manual intervention becomes an unwieldy task.

In this notebook an automatic color scaling is presented that tries to empower the experimenter to circumvent this difficulty, while keeping possible risks in mind.

The risk narrowing the color scale is clearly that the image representing the data saturates in some regions and is no longer fully representative of the actual data. Especially for automatic color scaling this can become risky because without a theoretical model describing the specific measurement one cannot scientifically argue for the choice of the new limits of the color scale and possible meaningful features might be disregarded.

For this reason automatic color scaling is deactivated by default and has to be activated manually.

## Using the automatic color scale

The following section presents an example of the usage of the automatic color scaling with the new DataSet and the plot_dataset function for plotting. Let’s first make the necessary imports:

[1]:

# store data in a separate db in order not to spam the actual measurement db
from qcodes import initialise_or_create_database_at, new_data_set, new_experiment
from qcodes.dataset.plotting import plot_dataset
initialise_or_create_database_at('../example.db')

# create an experiment for this tutorial
new_experiment(name='tutorial_auto_color_scale', sample_name="outliers")

# import methods for creating datasets with outliers:
from qcodes.tests.dataset_generators import dataset_with_outliers_generator

Logging hadn't been started.
Activating auto-logging. Current session state plus future input saved.
Filename       : /home/runner/.qcodes/logs/command_history.log
Mode           : append
Output logging : True
Raw input log  : False
Timestamping   : True
State          : active
Qcodes Logfile : /home/runner/.qcodes/logs/220701-19727-qcodes.log
Upgrading database; v0 -> v1: : 0it [00:00, ?it/s]
Upgrading database; v1 -> v2: 100%|██████████| 1/1 [00:00<00:00, 478.91it/s]
Upgrading database; v2 -> v3: : 0it [00:00, ?it/s]
Upgrading database; v3 -> v4: : 0it [00:00, ?it/s]
Upgrading database; v4 -> v5: 100%|██████████| 1/1 [00:00<00:00, 877.65it/s]
Upgrading database; v5 -> v6: : 0it [00:00, ?it/s]
Upgrading database; v6 -> v7: 100%|██████████| 1/1 [00:00<00:00, 414.95it/s]
Upgrading database; v7 -> v8: 100%|██████████| 1/1 [00:00<00:00, 951.31it/s]
Upgrading database; v8 -> v9: 100%|██████████| 1/1 [00:00<00:00, 1340.46it/s]


### Exampe 1: outliers in homogeneous data

Here we basically reproduce the images of the introduction, where we have some smoothly structured data with some outliers that lie far outside of the range of the remaining data:

[2]:

# create a new dataset with outliers
ds = dataset_with_outliers_generator(new_data_set('data_with_outliers'),
low_outlier=-3, high_outlier=3,
background_noise=False)


by plotting it simply by plot_dataset we get the full range

[3]:

ax, cb = plot_dataset(ds);


By adding the keyword auto_color_scale=True one activates the scaling:

[4]:

ax, cb = plot_dataset(ds, auto_color_scale=True);


and the actual structure of the data becomes visible.

Please note two further details: - The triangles that appeared at the top and the bottom of the color bar: These indicate that the color bar does not cover the full range of the data spectrum. - The clipped regions are marked with colors that are clearly not part of the color scale in order to clearly separate those regions where we cannot make any claim about any structure.

## Details of the calculation

The new limits are calculated in three steps: - Determine the inter quartile range (IQR), i.e. the distance between the first (q1) and the third (q3) quartile (see image below). - Expand the region spanned by [q1, q3] by 1.5 x the IQR (yet not beyond the original limits of the min/max of the data). - Limit the amount of data points clipped on each side to an amount that is described by the lower (pl) and upper (pu) cutoff percentiles. E.g. for pu=pl=0.5%, no more the white and gray areas may not take up more than half a percent each of the total area.

To understand how this works lets consider the histogram of the previous example:

[5]:

import matplotlib.pyplot as plt
import numpy as np
data = cb[0].mappable.get_array()
pl, q3, q1, pu = np.percentile(data, [97,75, 25, 3])
plt.hist(data, bins=100);
# IQR
plt.axvline(q3, color='k')
plt.axvline(q1, color='k')
# cut off
plt.axvline(q1-1.5*(q3-q1), color='b')
plt.axvline(q3+1.5*(q3-q1), color='b')
# limit through percentiles
plt.axvline(pl, color='r')
plt.axvline(pu, color='r')

2022-07-01 15:02:05,984 ¦ py.warnings ¦ WARNING ¦ warnings ¦ _showwarnmsg ¦ 109 ¦ /opt/hostedtoolcache/Python/3.9.13/x64/lib/python3.9/site-packages/numpy/lib/function_base.py:4691: UserWarning: Warning: 'partition' will ignore the 'mask' of the MaskedArray.
arr.partition(


[5]:

<matplotlib.lines.Line2D at 0x7f55e42b3df0>


The black lines mark q1 and q3 and the blue lines correspond to the interval widened by 1.5 x IQR. The red lines present the lines of the percentile cutoff, in this case 3% on each side. The red lines limit how much of the data may be cut away through the blue lines. In this specific case of 3% they are not limiting the cutoff through the IQR estimation. Please also note the barely filled bins at +-10, that represent the outliers. The new color scale therefore roughly corresponds to a threefold increase of the steepness of color gradient.

## Cuttoff Percentiles

Limiting the effect of an automatic color scale by the cutoff percentiles can be very important. To understand that consider the following example:

### Example 2: data on noisy background with outliers

[6]:

# create a new dataset with outliers
ds = dataset_with_outliers_generator(new_data_set('data_outside_iqr'),
data_offset=2, low_outlier=-2,
high_outlier=3)

[7]:

ax, cb = plot_dataset(ds)


In this example dataset the region of interest shall be represented by the center region. Again there are outliers that render the color grading almost useless.

Looking at the same plot with an automatic color scale without limiting cutoff percentiles (by setting the cutoff to 50% we basically deactivate them) does not give a better result:

[8]:

ax, cb = plot_dataset(ds, auto_color_scale=True, cutoff_percentile=50);


Here all the relevant region is considered as outliers. To make this clearer let’s draw the histogram again:

[9]:

import matplotlib.pyplot as plt
import numpy as np
data = cb[0].mappable.get_array()
pl, q3, q1, pu = np.percentile(data, [99.5,75, 25, 0.5])
plt.hist(data, bins=100);
# IQR
plt.axvline(q3, color='k')
plt.axvline(q1, color='k')
# cut off
plt.axvline(q1-1.5*(q3-q1), color='b')
plt.axvline(q3+1.5*(q3-q1), color='b')
# limit through percentiles
plt.axvline(pl, color='r')
plt.axvline(pu, color='r')

2022-07-01 15:02:06,590 ¦ py.warnings ¦ WARNING ¦ warnings ¦ _showwarnmsg ¦ 109 ¦ /opt/hostedtoolcache/Python/3.9.13/x64/lib/python3.9/site-packages/numpy/lib/function_base.py:4691: UserWarning: Warning: 'partition' will ignore the 'mask' of the MaskedArray.
arr.partition(


[9]:

<matplotlib.lines.Line2D at 0x7f55dfff0f40>


The region of interest is represented by the right hand side structure around 0. The IQR induced limits (blue) does not capture these values. The percentile limits (0.5% here) however save the day:

[10]:

ax, cb = plot_dataset(ds, auto_color_scale=True, cutoff_percentile=0.5);


There is some undesired clipping in the top right corner of the ROI but the structure within the ROI is relatively well resolved.

### Asymmetric cutoffs

Asymmetric cutoffs can be simply defined via a tuples. So for the data of the first example we can disregard the lower outliers that take up up to five percent of the data but not allow any clipping on the upper end:

[11]:

ds = dataset_with_outliers_generator(new_data_set('data_with_outliers'),
low_outlier=-3,
high_outlier=3,
background_noise=False)
ax, cb = plot_dataset(ds, auto_color_scale=True, cutoff_percentile=(0,5));


## Defaults and customizing the auto color scaling

The defaults used by plot_dataset can be set in the qcodesrc.json files. The system default is described in qcodes/config/qcodesrc.json (online) To override the default edit these values in your custom qcodesrc.json file in your home directory.

The defaults are (for detailed description see the schema file)

"auto_color_scale":{
"enabled": false,
"cutoff_percentile": [0.5, 0.5],
"color_over": "white",
"color_under": "gray"
}


Because of the possible risks due to auto color scaling it is deactivated by default. Please remember to change the colors marking the outliers in case you should use a color map the includes white and gray.

## Using auto color scaling in custom plotting

If you are using matplotlib but do not want to rely on plot_dataset, you can simply call: qcodes.utils.plotting.apply_auto_color_scale(colorbar, ...) and provide any matplotlib color bar to achieve the described effects.

If you want to use the qcodes config system for defaults call qcodes.utils.plotting.auto_color_scale_from_config(colorbar,...) instead.

[ ]: