Search code examples
pythonmatplotlibfigureaxesfits

How do I change the axes' units in a figure?


I'm making figures of some galaxies velocities with matplotlib, from some .fits files. The problem is that the axes in the figure show the galaxy's size in pixels, and I want to display them as Declination and RightAcension (in angle units). I already know that each pixel has a size of 0.396 arcseconds. How can I convert pixels to arcseconds in both X and Y axes?

The code is the folowing:

##############################################################################
# Generally the image information is located in the Primary HDU, also known
# as extension 0. Here, we use `astropy.io.fits.getdata()` to read the image
# data from this first extension using the keyword argument ``ext=0``:

image_data = fits.getdata(image_file, ext=0)

##############################################################################
# The data is now stored as a 2D numpy array. Print the dimensions using the
# shape attribute:

print(image_data.shape)

##############################################################################
# Display the image data:

fig = plt.figure()
plt.imshow(image_data, cmap='Spectral_r', origin='lower', vmin=-maior_pixel, vmax=maior_pixel)
plt.colorbar()

fig.suptitle(f'{gals_header["MANGAID"]}', fontsize=20, fontweight='bold')

ax = fig.add_subplot(111)
fig.subplots_adjust(top=0.85)
ax.set_title('RC')

ax.set_xlabel('pixelsx')
ax.set_ylabel('pixelsy')

There is more code than that, but I just want to show what I believe to be the relevant part (I can put more of it in the coments if necessary). This code is based on an example code from this link: https://docs.astropy.org/en/stable/generated/examples/io/plot_fits-image.html#sphx-glr-download-generated-examples-io-plot-fits-image-py

I already tried some things like Axes.convert_xunits and some pyplot.axes functions, but nothing worked (or maybe I just couldn't figure out how to properly use them).

That is how the Image is currently

Can someone help? Thank you in advance.


Solution

  • You can use whatever you want as the tick labels using a plt.FuncFormatter object.

    Here an example (a very silly one indeed), please refer to the excellent Matplotlib docs for the details.

    import matplotlib.pyplot as plt
    from numpy import arange
    
    img = arange(21*21).reshape(21,21)
    
    ax = plt.axes()
    plt.imshow(img, origin='lower')
    ax.xaxis.set_major_formatter(
        plt.FuncFormatter(lambda x, pos: "$\\frac{%d}{20}$"%(200+x**2)))
    

    enter image description here

    Each axis has a major_formatter that is responsible for generating the tick labels.

    A formatter must be an instance of a class subclassed from Formatter, above we used the FuncFormatter.

    To initialize a FuncFormatter we pass to it a formatting function that we have to define with the following required characteristics

    • has two inputs, x and pos, x being the abscissa (or the ordinate) to be formatted while pos could safely be ignored,
    • returns a string to be used as the label.

    In the example the function has been defined on the spot using the lambda syntax, the gist of it being a format string ("$\\frac{%d}{20}$"%(200+x**2)) that formats as a LaTeX fraction a function of the abscissa, as you can see in the picture above.

    Re the pos parameter, as far as I know it's used only in some methods, e.g.

    In [69]: ff = plt.FuncFormatter(lambda x, pos: "%r ፨ %05.2f"%(pos,x))
    
    In [70]: ff.format_ticks((0,4,8,12))
    Out[70]: ['0 ፨ 00.00', '1 ፨ 04.00', '2 ፨ 08.00', '3 ፨ 12.00']
    

    but in general you can ignore the pos argument in the body of the function.