How to set axis ticks with non periodical increment in matplolib

192 Views Asked by At

I have a 2D array representing the efficiency of a process for a given set of parameters A and B. The parameter A along the columns changes periodically, starting from 0 to 225 with increment one. The problem is with the rows where the parameter was changed in the following order:

[16 ,18 ,20 ,21 ,22 ,23 ,24 ,25 ,26 ,27 ,28 ,29 ,30 ,31 ,32 ,33 ,35 ,40 ,45 ,50 ,55 ,60 ,65 ,70 ,75 ,80 ,85 ,90 ,95 ,100 ,105 ,110 ,115 ,120 ,125]

So even though the rows increase with increment one, they represent a non-uniform increment of the parameter B. What I need is to showcase the values of the parameter B on the y-axis. Using axes.set_yticks() does not give me what I am looking for, and I do understand why but I do not know how to solve it.

A minimum example:

# Define parameter B values
parb_increment = [16, 18, 20] + list(range(21,34)) + list(range(35,126,5))

print(len(parb_increment))
print(x.shape)

# Figure and axes
figure, axes = plt.subplots(figsize=(10, 8))

# Plotting
im = axes.imshow(x, aspect='auto',
                 origin="lower",
                 cmap='Blues',
                 interpolation='none',
                 extent=(0, x.shape[1], 0, parb_increment[-1]))

# Unsuccessful trial for yticks
axes.set_yticks(parb_increment, labels=parb_increment)

# Colorbar
cb = figure.colorbar(im, ax=axes)

The previous code gives the figure and output below, and you can see how the ticks are not only misplaced but also start from an incorrect position.

35
(35, 225)

enter image description here

1

There are 1 best solutions below

4
On

The item that controls the width/height of each pixel is aspect. Unfortunately you can't make it variable. The aspect won't change even if you modify/update y-axis ticks. That's why in your example ticks are mis-aligned with the rows of pixels.

Therefore, the solution to your problem is to duplicate those rows that increment non-uniformly.

See example below:

import numpy as np
import matplotlib.pyplot as plt

# Generate fake data
x = np.random.random((3, 4))

# Create uniform x-ticks and non-uniform y-ticks
x_increment = np.arange(0, x.shape[1]+1, 1)
y_increment = np.arange(0, x.shape[0]+1, 1) * np.arange(0, x.shape[0]+1, 1)

# Plot the data
fig, ax = plt.subplots(figsize=(6, 10))

img = ax.imshow(
    x,
    extent=(
        0, x.shape[1], 0, y_increment[-1]
    )
)

fig.colorbar(img, ax=ax)
ax.set_xlim(0, x.shape[1])
ax.set_xticks(x_increment)
ax.set_ylim(0, y_increment[-1])
ax.set_yticks(y_increment);

This replicates your problem and produces the following outcome.

The solution

First, determine the number of repeats of each row in the array:

nr_of_repeats_per_row =np.diff(y_increment)
nr_of_repeats_per_row = nr_of_repeats_per_row[::-1]

You need to reverse the order as the top row in the image is the first row in the array and y_increments provide the difference between rows starting from the last row in the array.

Now you can repeat each row in the array a specific number of times:

x_extended = np.repeat(x, nr_of_repeats_per_row, axis=0)

Replot with the x_extended:

fig, ax = plt.subplots(figsize=(6, 10))

img = ax.imshow(
    x_extended,
    extent=(
        0, x.shape[1], 0, y_increment[-1]
    ),
    interpolation="none"
)
fig.colorbar(img, ax=ax)
ax.set_xlim(0, x.shape[1])
ax.set_xticks(x_increment)
ax.set_ylim(0, y_increment[-1])
ax.set_yticks(y_increment);

And you should get this.