A.Piquer
A.Piquer

Reputation: 424

Place xticks and yticks in the pixel center on imshow plot

I am working with matplotlib to plot a heat map with some information and I want to move the xticks and the yticks to the center. I have searched in stackoverflow for previous questions but I couldn't reach one suitable for the problem. I attach my code and the image that I get:

import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np

def plot():
    intensity= np.random.rand(10,10)

    matrix_intensity=np.matrix(intensity)
    max_intensity=matrix_intensity.max()
    min_intensity = matrix_intensity.min()
    for e in range(len(intensity)):
        for i in range(len(intensity[e])):
            intensity[e][i]=float(intensity[e][i])/float(max_intensity)
    np.random.seed(101)
    cmap = colors.ListedColormap(['white','khaki', 'goldenrod','yellowgreen','mediumseagreen','darkcyan','tomato','indianred' ,'sienna','maroon'])
    bounds = np.linspace(min_intensity/max_intensity,1,11).tolist()
    norm = colors.BoundaryNorm(bounds, cmap.N)

    img = plt.imshow(intensity, interpolation='none', origin='lower',extent=[0,len(intensity),0,len(intensity)],
                     cmap=cmap, norm=norm)

    cb=plt.colorbar(img, fraction=0.1,cmap=cmap, norm=norm, boundaries=bounds,format='%.2f') #'%.2f')
    cb.set_label(label='Ratio',fontsize=12,labelpad=10)
    plt.ylabel('Origin',fontsize=11)
    plt.xlabel('Destination',fontsize=11)
    plt.title('Best route:',fontsize=10)
    plt.suptitle('Best Solution:',fontsize=10)
    plt.xticks(range(1,len(intensity)+1))
    plt.yticks(range(1,len(intensity)+1))
    plt.savefig('images/hello.png')
    plt.show()

The fact is that I would like the x and the y ticks to point out the center of every square because otherwise, it doesn't make sense to plot the squares. Does somebody know how to fix this? Maybe this question is obvious but the matplotlib documentation for all the statements sometimes is difficult to understand.

Image of the problem

Upvotes: 2

Views: 3101

Answers (3)

Vincenzooo
Vincenzooo

Reputation: 2441

The other answers are both good, however I would like to provide a more general implementation that also doesn't alter default ticks, as I have a function that can be used to calculate the axis limits and set them as in @ImportanceOfBeingErnest answer.

import numpy as np  

def span_from_pixels(p,n=None):
    """From positions of pixel centers p returns a range from side to side. Useful to adjust plot extent in imshow.
    In alternative, p can be provided as range and number of pixels.
    Note that np.linspace has flag retsteps to return step size."""

    if n is None:
        n=len(p)

    dx=(np.max(p)-np.min(p))/(n-1)
    return (np.min(p)-dx/2,np.max(p)+dx/2)

def test_span_from_pixels():
    print (span_from_pixels([0,3],4)) #[-0.5,3.5]
    print (span_from_pixels([0,2],3)) #[-0.5,2.5]
    print (span_from_pixels([0,1,2])) #[-0.5,2.5]
    print (span_from_pixels([0,0.5,1,1.5,2])) #[-0.25,2.25]

Please let me know if something doesn't work, these are tested in my code, but I made some change to remove dependencies. I assume I didn't break anything, but I cannot test it now.

Upvotes: 0

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339350

The obvious solution would probably to use a different extent, namely to let the image live in the range between 0.5 and len(intensity)+0.5.

extent=[.5, len(intensity)+.5, .5, len(intensity)+.5]
img = plt.imshow(intensity, interpolation='none', origin='lower',extent=extent,
                     cmap=cmap, norm=norm)

Upvotes: 4

harpan
harpan

Reputation: 8631

You need to change the way you set your xticks and yticks loc and labels to below:

plt.xticks([x-0.5 for x in list(range(1,len(intensity)+1))], range(1,len(intensity)+1))
plt.yticks([x-0.5 for x in list(range(1,len(intensity)+1))], range(1,len(intensity)+1))

Output:

enter image description here

Upvotes: 1

Related Questions