19

I'm trying to generate an equatorial coordinates plot that should look more or less like this one:

enter image description here

(The figure is taken from this article, and it shows the position of the Large and Small MCs in equatorial coordinates)

Important things to notice about this plot:

  • The theta axis (ie: the right ascension) is in h:m:s (hours, minutes, seconds) as it is accustomed in astronomy, rather than in degrees as the default polar option does in matplotlib.
  • The r axis (ie: the declination) increases outward from -90º and the grid is centered in (0h, -90º).
  • The plot is clipped, meaning only a portion of it shows as opposed to the entire circle (as matplotlib does by default).

Using the polar=True option in matplotlib, the closest plot I've managed to produce is this (MWE below, data file here; some points are not present compared to the image above since the data file is a bit smaller):

enter image description here

I also need to add a third column of data to the plot, which is why I add a colorbar and color each point accordingly to a z array:

enter image description here

So what I mostly need right now is a way to clip the plot. Based mostly on this question and this example @cphlewis came quite close with his answer, but several things are still missing (mentioned in his answer).

Any help and/or pointers with this issue will be greatly appreciated.


MWE

(Notice I use gridspec to position the subplot because I need to generate several of these in the same output image file)

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

def skip_comments(f):
    '''
    Read lines that DO NOT start with a # symbol.
    '''
    for line in f:
        if not line.strip().startswith('#'):
            yield line

def get_data_bb():
    '''RA, DEC data file.
    '''

    # Path to data file.
    out_file = 'bb_cat.dat'

    # Read data file
    with open(out_file) as f:
        ra, dec = [], []

        for line in skip_comments(f):
            ra.append(float(line.split()[0]))
            dec.append(float(line.split()[1]))

    return ra, dec

# Read RA, DEC data from file.
ra, dec = get_data_bb()
# Convert RA from decimal degrees to radians.
ra = [x / 180.0 * 3.141593 for x in ra]

# Make plot.
fig = plt.figure(figsize=(20, 20))
gs = gridspec.GridSpec(4, 2)
# Position plot in figure using gridspec.
ax = plt.subplot(gs[0], polar=True)
ax.set_ylim(-90, -55)

# Set x,y ticks
angs = np.array([330., 345., 0., 15., 30., 45., 60., 75., 90., 105., 120.])
plt.xticks(angs * np.pi / 180., fontsize=8)
plt.yticks(np.arange(-80, -59, 10), fontsize=8)
ax.set_rlabel_position(120)
ax.set_xticklabels(['$22^h$', '$23^h$', '$0^h$', '$1^h$', '$2^h$', '$3^h$',
    '$4^h$', '$5^h$', '$6^h$', '$7^h$', '$8^h$'], fontsize=10)
ax.set_yticklabels(['$-80^{\circ}$', '$-70^{\circ}$', '$-60^{\circ}$'],
    fontsize=10)

# Plot points.
ax.scatter(ra, dec, marker='o', c='k', s=1, lw=0.)

# Use this block to generate colored points with a colorbar.
#cm = plt.cm.get_cmap('RdYlBu_r')
#z = np.random.random((len(ra), 1))  # RGB values
#SC = ax.scatter(ra, dec, marker='o', c=z, s=10, lw=0., cmap=cm)
# Colorbar
#cbar = plt.colorbar(SC, shrink=1., pad=0.05)
#cbar.ax.tick_params(labelsize=8)
#cbar.set_label('colorbar', fontsize=8)

# Output png file.
fig.tight_layout()
plt.savefig(ra_dec_plot.png', dpi=300)
Community
  • 1
  • 1
Gabriel
  • 40,504
  • 73
  • 230
  • 404
  • if what you want is the first plot, that is not a polar plot but a standard cartesian plot, which can be reproduced by plotting all the grid lines manually – Julien Spronck Apr 08 '15 at 21:14
  • Hi again Julien :) This is not a cartesian plot since the lines of equal values are curved and not straight. Was that what you meant? – Gabriel Apr 08 '15 at 21:18
  • Hi back :) what I meant is that this can be done using standard axes vs polar axes. You can calculate the x, y coordinates of the curves that make up the grid and plot that on standard axes. You might not want to do that because it is cumbersome but that is what the first plot looks like. – Julien Spronck Apr 08 '15 at 21:21
  • As for the colormap, can't you use the exact same trick I gave you in your earlier question => use AxesDivider? – Julien Spronck Apr 08 '15 at 21:25
  • The first plot is a rectangular section of a polar plot. All of the constant-theta lines go through the polar origin, in contrast to only one of the constant-x and constant-y lines that cross a cartesian origin. So, three problems: relabeling what's usually _theta_ to hours (is there a straightforward calculation for that, Gabriel?), clipping to a rectangle, and the colorbar. – cphlewis Apr 08 '15 at 22:00
  • @JulienSpronck I'l see about treating it as a cartesian plot making the needed transformations to plot the grid. As for the colormap, if you check the MWE you'll see that the colorbar trick you gave me is commented out. If I use it, I get the messed up plot I show last in the post. – Gabriel Apr 08 '15 at 22:17
  • @cphlewis see updated question for how to convert degrees to _h:m:s_ & _d:m:s_. – Gabriel Apr 08 '15 at 22:24
  • is your y-axis linear? It is not in the first plot you show (the distance between -80 and -70 is different between -70 to -60 due to the spherical projection – Julien Spronck Apr 08 '15 at 22:29
  • Ah, the linked solution about clipping polar plots is helpful -- the built-in polar plot has made enough default choices that they recommend making a custom artist. Hm. – cphlewis Apr 08 '15 at 22:40
  • @JulienSpronck it is not, the y-axis is in degrees:minutes:seconds (declination coordinates). – Gabriel Apr 08 '15 at 22:50
  • might be another reason to for a standard plot vs polar (since your grid needs to be calculated anyway) – Julien Spronck Apr 08 '15 at 22:53
  • What units do you actually get the data in? one way or another you're defining a Transform, might as well make it do the unit-conversions by default. -- The least standard thing about it, possibly, is the right ascension ticklabels continuing onto the upper border. – cphlewis Apr 08 '15 at 23:02
  • @cphlewis I get the data in decimal degrees but using `astropy` I can convert them to whatever units needed (e.g: d:m:s, radians). The issue is producing something similar to the first plot. Perhaps it is best to transform to linear coordinates and calculate how to produce the curved grid for the declination and the radial lines for the right ascension. – Gabriel Apr 08 '15 at 23:06
  • I'm quite sure that's the easiest thing to do incrementally -- I'm sure because I've started working out custom Transforms twice and ended up shipping something faked onto the linear plot because I could get that done. Transforms are nice and simple in theory and baffle me in practice. (One might mention this problem in the python chatroom -- when they like a problem it gets DONE.) – cphlewis Apr 08 '15 at 23:09
  • 1
    Oho! Check out the curvilinear grid example in AxisArtist: http://matplotlib.org/mpl_toolkits/axes_grid/users/overview.html#curvilinear-grid It's a rectilinear window on a polar grid, and the ticks on the outer rectangle are spaced unevenly (because they're on even _theta_ distances). – cphlewis Apr 09 '15 at 00:32
  • That looks very promising, thanks @cphlewis! I'll look at it tomorrow morning (it's very late here now) and post back here as soon as I can. – Gabriel Apr 09 '15 at 00:55
  • In "Important things", I'd refer to the `theta` and `r` axes. Other than that, seems clear to me. If the bounty doesn't do it, try to contact the author of AxisArtist? – cphlewis Apr 13 '15 at 08:11
  • @cphlewis like that? I'll leave a message in the python chatroom now to see if anyone there can shed some light on this. – Gabriel Apr 13 '15 at 11:41

3 Answers3

8

Getting the colorbar can be done with a merging of the OP code with @cphlewis's excellent answer. I've posted this as a turnkey solution on the request of the OP in chat. The first version of code simply adds a color bar, the final version (under EDIT 2) does an axes affine translation and corrects a few parameters / simplifies the code to suit OP spec exactly.

"""
An experimental support for curvilinear grid.
"""
import numpy as np
import  mpl_toolkits.axisartist.angle_helper as angle_helper
import matplotlib.cm as cmap
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D

from mpl_toolkits.axisartist import SubplotHost

from mpl_toolkits.axisartist import GridHelperCurveLinear


def curvelinear_test2(fig):
    """
    polar projection, but in a rectangular box.
    """
    global ax1

    # see demo_curvelinear_grid.py for details
    tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()

    extreme_finder = angle_helper.ExtremeFinderCycle(10, 60,
                                                     lon_cycle = 360,
                                                     lat_cycle = None,
                                                     lon_minmax = None,
                                                     lat_minmax = (0, np.inf),
                                                     )

    grid_locator1 = angle_helper.LocatorHMS(12) #changes theta gridline count
    tick_formatter1 = angle_helper.FormatterHMS()

    grid_locator2 = angle_helper.LocatorDMS(6)
    tick_formatter2 = angle_helper.FormatterDMS()

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1,
                                        grid_locator2=grid_locator2,
                                        tick_formatter2=tick_formatter2
                                        )


    ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)

    # make ticklabels of right and top axis visible.
    ax1.axis["right"].major_ticklabels.set_visible(True)
    ax1.axis["top"].major_ticklabels.set_visible(True)
    ax1.axis["bottom"].major_ticklabels.set_visible(True) #Turn off? 
    # let right and bottom axis show ticklabels for 1st coordinate (angle)
    ax1.axis["right"].get_helper().nth_coord_ticks=0
    ax1.axis["bottom"].get_helper().nth_coord_ticks=0



    fig.add_subplot(ax1)

    grid_helper = ax1.get_grid_helper()

    ax1.set_aspect(1.)
    ax1.set_xlim(-4,15) # moves the origin left-right in ax1
    ax1.set_ylim(-3, 20) # moves the origin up-down

    ax1.set_ylabel('90$^\circ$ + Declination')
    ax1.set_xlabel('Ascension')
    ax1.grid(True)
    #ax1.grid(linestyle='--', which='x') # either keyword applies to both
    #ax1.grid(linestyle=':', which='y')  # sets of gridlines

    return tr

import matplotlib.pyplot as plt
fig = plt.figure(1, figsize=(5, 5))
fig.clf()

tr = curvelinear_test2(fig) # tr.transform_point((x, 0)) is always (0,0)
                            # => (theta, r) in but (r, theta) out...
r_test =   [0, 1.2, 2.8, 3.8, 5,  8,  10, 13.3, 17]  # distance from origin
deg_test = [0,  -7, 12,  28,  45, 70, 79, 90,   100] # degrees ascension
out_test = tr.transform(zip(deg_test, r_test))

sizes = [40, 30, 10, 30, 80, 33, 12, 48, 45]
#hues = [.9, .3, .2, .8, .6, .1, .4, .5,.7] # Oddly, floats-to-colormap worked for a while.
hues = np.random.random((9,3)) #RGB values

# Use this block to generate colored points with a colorbar.
cm = plt.cm.get_cmap('RdYlBu_r')
z = np.random.random((len(r_test), 1))  # RGB values

SC = ax1.scatter(out_test[:,0], #ax1 is a global
            out_test[:,1],
            s=sizes,
            c=z,
            cmap=cm,
            zorder=9) #on top of gridlines
            
# Colorbar
cbar = plt.colorbar(SC, shrink=1., pad=0.05)
cbar.ax.tick_params(labelsize=8)
cbar.set_label('colorbar', fontsize=8)


plt.show()

EDIT

Bit of tidying parameters, adding in OP data, removing redundancy yields the following plot. Still need to centre the data on -90 instead of 0 - at the moment this is hacked, but I'm sure curvelinear_test2() can be changed to account for it...

PIcture matching OP desired format

EDIT 2

Following OP comment on intermediate version in this answer, a final version as below gives the plot at the very end of the post - with -90 on the dec axis and subplot demo

"""
An experimental support for curvilinear grid.
"""
import numpy as np
import  mpl_toolkits.axisartist.angle_helper as angle_helper
import matplotlib.cm as cmap
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D

from mpl_toolkits.axisartist import SubplotHost

from mpl_toolkits.axisartist import GridHelperCurveLinear


def curvelinear_test2(fig, rect=111):
    """
    polar projection, but in a rectangular box.
    """

    # see demo_curvelinear_grid.py for details
    tr = Affine2D().translate(0,90) + Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()

    extreme_finder = angle_helper.ExtremeFinderCycle(10, 60,
                                                     lon_cycle = 360,
                                                     lat_cycle = None,
                                                     lon_minmax = None,
                                                     lat_minmax = (-90, np.inf),
                                                     )

    grid_locator1 = angle_helper.LocatorHMS(12) #changes theta gridline count
    tick_formatter1 = angle_helper.FormatterHMS()

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1
                                        )


    ax1 = SubplotHost(fig, rect, grid_helper=grid_helper)

    # make ticklabels of right and top axis visible.
    ax1.axis["right"].major_ticklabels.set_visible(True)
    ax1.axis["top"].major_ticklabels.set_visible(True)
    ax1.axis["bottom"].major_ticklabels.set_visible(True) #Turn off? 
    # let right and bottom axis show ticklabels for 1st coordinate (angle)
    ax1.axis["right"].get_helper().nth_coord_ticks=0
    ax1.axis["bottom"].get_helper().nth_coord_ticks=0



    fig.add_subplot(ax1)

    grid_helper = ax1.get_grid_helper()

    # You may or may not need these - they set the view window explicitly rather than using the
    # default as determined by matplotlib with extreme finder.
    ax1.set_aspect(1.)
    ax1.set_xlim(-4,25) # moves the origin left-right in ax1
    ax1.set_ylim(-3, 30) # moves the origin up-down

    ax1.set_ylabel('Declination')
    ax1.set_xlabel('Ascension')
    ax1.grid(True)
    #ax1.grid(linestyle='--', which='x') # either keyword applies to both
    #ax1.grid(linestyle=':', which='y')  # sets of gridlines

    return ax1,tr
    
    
def skip_comments(f):
    '''
    Read lines that DO NOT start with a # symbol.
    '''
    for line in f:
        if not line.strip().startswith('#'):
            yield line
            
def get_data_bb():
    '''RA, DEC data file.
    '''

    # Path to data file.
    out_file = 'bb_cat.dat'

    # Read data file
    with open(out_file) as f:
        ra, dec = [], []

        for line in skip_comments(f):
            ra.append(float(line.split()[0]))
            dec.append(float(line.split()[1]))

    return ra, dec


import matplotlib.pyplot as plt
fig = plt.figure(1, figsize=(5, 5))
fig.clf()

ax1, tr = curvelinear_test2(fig,121) # tr.transform_point((x, 0)) is always (0,0)
                            # => (theta, r) in but (r, theta) out...             

# Read RA, DEC data from file.
ra, dec = get_data_bb()
out_test = tr.transform(zip(ra, dec))

# Use this block to generate colored points with a colorbar.
cm = plt.cm.get_cmap('RdYlBu_r')
z = np.random.random((len(ra), 1))  # RGB values

SC = ax1.scatter(out_test[:,0], #ax1 is a global
            out_test[:,1],
            marker = 'o',
            c=z,
            cmap=cm,
            lw = 0.,
            zorder=9) #on top of gridlines
            
# Colorbar
cbar = plt.colorbar(SC, shrink=1., pad=0.1)
cbar.ax.tick_params(labelsize=8)
cbar.set_label('colorbar', fontsize=8)

ax2, tr = curvelinear_test2(fig,122) # tr.transform_point((x, 0)) is always (0,0)
                            # => (theta, r) in but (r, theta) out...             

# Read RA, DEC data from file.
ra, dec = get_data_bb()
out_test = tr.transform(zip(ra, dec))

# Use this block to generate colored points with a colorbar.
cm = plt.cm.get_cmap('RdYlBu_r')
z = np.random.random((len(ra), 1))  # RGB values

SC = ax2.scatter(out_test[:,0], #ax1 is a global
            out_test[:,1],
            marker = 'o',
            c=z,
            cmap=cm,
            lw = 0.,
            zorder=9) #on top of gridlines
            
# Colorbar
cbar = plt.colorbar(SC, shrink=1., pad=0.1)
cbar.ax.tick_params(labelsize=8)
cbar.set_label('colorbar', fontsize=8)

plt.show()

Final plot:

Picture showing proper subplot

Community
  • 1
  • 1
J Richard Snape
  • 20,116
  • 5
  • 51
  • 79
  • So close! I can see only two (minor) things missing: 1- locating the subplot using `gridspec`. I need this because I need to produce several of these plots in an image file, next to one another. 2- The y axis (declination) should show the values -80, -70, -60. If you can manage to do that, I'll mark your answer as accepted (and hopefully I can give both you and cphlewis 50 pts each). – Gabriel Apr 13 '15 at 13:36
  • Done those now. You should be able to see how to manipulate the plots as you wish I hope - from the subplotting example and the comments in the function. They could use some renaming, potentially, to make them easily reusable... – J Richard Snape Apr 13 '15 at 14:01
  • Excellent answer J Richard, you have helped me very much indeed. I've added a `tick_formatter2 = angle_helper.FormatterDMS()` line before defining the `grid_helper` and a `tick_formatter2=tick_formatter2` inside it so the degrees in the y axis show with a proper º (degree) symbol. I just need to figure out how to make it show with no decimal places (ie: `80º` instead of `80.0º` ) but that's a very minor tweak. I'm marking this as the accepted answer and I'll see if I can give you both the 50 pts I promised. Thanks you very much again! – Gabriel Apr 13 '15 at 14:37
  • Ok, managed to solve the `80.0º` issue by adding a `grid_locator2 = angle_helper.LocatorDMS(6)` line before the `grid_helper` and a `grid_locator2=grid_locator2` line inside it. – Gabriel Apr 13 '15 at 15:39
  • OK - I thought that was redundant so removed it from the original cphlewis version - sorry about that :( Glad you could get it back how you wanted it. – J Richard Snape Apr 13 '15 at 15:40
7

Chewing on the AxisArtist example is actually pretty promising (this combines two AxisArtist examples -- I wouldn't be surprised if AxisArtist was written with RA plots in mind):

enter image description here

Still to do:

  1. Declination should run from -90 at the origin to 0
  2. Be able to use and add a colorbar
  3. adjust limits if plotting outside them

aesthetic:

  1. Serif font in axis labels
  2. Dashed gridlines for ascension

anything else?

"""
An experimental support for curvilinear grid.
"""
import numpy as np
import  mpl_toolkits.axisartist.angle_helper as angle_helper
import matplotlib.cm as cmap
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D

from mpl_toolkits.axisartist import SubplotHost

from mpl_toolkits.axisartist import GridHelperCurveLinear


def curvelinear_test2(fig):
    """
    polar projection, but in a rectangular box.
    """
    global ax1

    # see demo_curvelinear_grid.py for details
    tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()

    extreme_finder = angle_helper.ExtremeFinderCycle(10, 60,
                                                     lon_cycle = 360,
                                                     lat_cycle = None,
                                                     lon_minmax = None,
                                                     lat_minmax = (0, np.inf),
                                                     )

    grid_locator1 = angle_helper.LocatorHMS(12) #changes theta gridline count
    tick_formatter1 = angle_helper.FormatterHMS()

    grid_locator2 = angle_helper.LocatorDMS(6)
    tick_formatter2 = angle_helper.FormatterDMS()

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1,
                                        grid_locator2=grid_locator2,
                                        tick_formatter2=tick_formatter2
                                        )


    ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)

    # make ticklabels of right and top axis visible.
    ax1.axis["right"].major_ticklabels.set_visible(True)
    ax1.axis["top"].major_ticklabels.set_visible(True)
    ax1.axis["bottom"].major_ticklabels.set_visible(True) #Turn off? 
    # let right and bottom axis show ticklabels for 1st coordinate (angle)
    ax1.axis["right"].get_helper().nth_coord_ticks=0
    ax1.axis["bottom"].get_helper().nth_coord_ticks=0



    fig.add_subplot(ax1)

    grid_helper = ax1.get_grid_helper()

    ax1.set_aspect(1.)
    ax1.set_xlim(-4,15) # moves the origin left-right in ax1
    ax1.set_ylim(-3, 20) # moves the origin up-down

    ax1.set_ylabel('90$^\circ$ + Declination')
    ax1.set_xlabel('Ascension')
    ax1.grid(True)
    #ax1.grid(linestyle='--', which='x') # either keyword applies to both
    #ax1.grid(linestyle=':', which='y')  # sets of gridlines

    return tr

import matplotlib.pyplot as plt
fig = plt.figure(1, figsize=(5, 5))
fig.clf()

tr = curvelinear_test2(fig) # tr.transform_point((x, 0)) is always (0,0)
                            # => (theta, r) in but (r, theta) out...
r_test =   [0, 1.2, 2.8, 3.8, 5,  8,  10, 13.3, 17]  # distance from origin
deg_test = [0,  -7, 12,  28,  45, 70, 79, 90,   100] # degrees ascension
out_test = tr.transform(zip(deg_test, r_test))

sizes = [40, 30, 10, 30, 80, 33, 12, 48, 45]
#hues = [.9, .3, .2, .8, .6, .1, .4, .5,.7] # Oddly, floats-to-colormap worked for a while.
hues = np.random.random((9,3)) #RGB values

ax1.scatter(out_test[:,0], #ax1 is a global
            out_test[:,1],
            s=sizes,
            c=hues,
            #cmap=cmap.RdYlBu_r,
            zorder=9) #on top of gridlines


plt.show()
cphlewis
  • 15,759
  • 4
  • 46
  • 55
  • Thank cphlewis! This answer looks really nice, but two things bother me: 1- I loose the colorbar and I _really_ need it (tried to add it back but I keep getting a `TypeError: You must first set_array for mappable`). 2- The arrangement of the _declination_ axis is confusing. For example, if I try to plot the point with coords: `(ra=11.76, dec=-73.5)` (which I input as: `r_test = [11.76], deg_test = [-73.5]`) I get http://i.imgur.com/r8Mwvag.png. If I invert the values in `r_test, deg_test` then `tr.transform` returns `nan`. I'm clearly missing something here. – Gabriel Apr 10 '15 at 13:15
  • It appears I have to invert the declination values and then process them as: `tr.transform(zip(ra, inv_dec))`, where `ra` is the right ascension and `inv_dec` the inverted (ie: `* -1`) declination, both originally in decimal degrees. – Gabriel Apr 10 '15 at 13:48
  • I had to play around with `tr([(0,1), (0,10), (10,0), (1, 0)])` to be confident of which axis got which part of the tuple. Apparently `tr` changes the order as well as doing the trig. Ooookay. And I never figured out how to evert the Dec axis *or* even change its axis labels, and got *different* errors trying to add a colorbar. (Which can be done inelegantly -- get RGB values from a color-mapping, draw the colorbar independent of this AxisArtist.) – cphlewis Apr 10 '15 at 17:59
  • But! Really, I suspect someone designing this was doing these plots, and also it seems interesting enough that the Python chatroom enthusiasts might be interested in making a real RA-Dec plotting class. Would you edit the original into a simpler set of To-Do and Part-Done examples? Including a sample input-data and output-image even if you have to draw the scatter on paper and take a phone snapshot? – cphlewis Apr 10 '15 at 18:03
  • Ok, I'll re-write the question tomorrow morning to be as clear as possible. Thanks for all the help cphlewis! – Gabriel Apr 10 '15 at 23:17
  • I've re-written the answer to provide more details and better explained images. Let me know what you think cphlewis. – Gabriel Apr 12 '15 at 17:06
  • cphlewis: 100 pts for you my friend. Thank you very much for all your help! – Gabriel Apr 15 '15 at 12:55
0

I think it may be a problem with Python 3+ , now the line

out_test = tr.transform(zip(deg_test, r_test))

returns the error:

ValueError: cannot reshape array of size 1 into shape (2)

Changing the line to

out_test = tr.transform(list(zip(deg_test, r_test)))

fixes the problem and allows the plot to be generated correctly.