Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add second axis to polar plot

I try to plot two polar plots in one figure. See code below:

fig = super(PlotWindPowerDensity, self).get_figure()
    rect = [0.1, 0.1, 0.8, 0.8]
    ax = WindSpeedDirectionAxes(fig, rect)

    self.values_dict = collections.OrderedDict(sorted(self.values_dict.items()))
    values = self.values_dict.items()
    di, wpd = zip(*values)
    wpd = np.array(wpd).astype(np.double)
    wpdmask = np.isfinite(wpd)
    theta = self.radar_factory(int(len(wpd)))

    # spider plot
    ax.plot(theta[wpdmask], wpd[wpdmask], color = 'b', alpha = 0.5)
    ax.fill(theta[wpdmask], wpd[wpdmask], facecolor = 'b', alpha = 0.5)

    # bar plot
    ax.plot_bar(table=self.table, sectors=self.sectors, speedbins=self.wpdbins, option='wind_power_density', colorfn=get_sequential_colors)

    fig.add_axes(ax)
    return fig

Result till now

The length of the bar is the data base (how many sampling points for this sector). The colors of the bars show the frequency of certain value bins (eg. 2.5-5 m/s) in the correspondent sector (blue: low, red: high). The blue spider plot shows the mean value for each sector.

In the shown figure, the values of each plot are similar, but this is rare. I need to assign the second plot to another axis and show this axis in another direction.

EDIT:

After the nice answer of Joe, i get the result of the figure. provisional result That's almost everything i wanted to achieve. But there are some points i wasn't able to figure out.

  1. The plot is made for dynamicly changing data bases. Therefore i need a dynamic way to get the same location of the circles. Till now I solve it with:

    start, end = ax2.get_ylim()
    ax2.yaxis.set_ticks(np.arange(0, end, end / len(ax.yaxis.get_ticklocs())))
    

    means: for second axis i alter the ticks in order to fit the ticklocs to the one's of first axis. In most cases i get some decimal places, but i don't want that, because it corrupts the clearness of the plot. Is there a way to solve this problem more smartly?

  2. The ytics (the radial one's) range from 0 to the next-to-last circle. How can i achieve that the values range from the first circle to the very last (the border)? The same like for the first axis.

like image 992
fidelitas Avatar asked Oct 25 '13 12:10

fidelitas


People also ask

How do you customize a polar plot in Matlab?

Create Polar PlotPlot a line in polar coordinates and add a title. Before R2022a, polar axes do not include degree symbols by default. To add them, get the polar axes using pax = gca . Then modify the tick labels using pax.

How do you enter polar coordinates in Matlab?

The built-in MATLAB function "cart2pol" converts cartesian coordinates (x,y) to polar coordinates (Theta,R). Repeat this for b to get [Theta_b, R_b]. Repeat this for [Theta_b, R_b] to get the original b back.

How do you plot a polar plot?

Rules for Drawing Polar PlotsFind the starting magnitude and the phase of G(jω)H(jω) by substituting ω=0. So, the polar plot starts with this magnitude and the phase angle. Find the ending magnitude and the phase of G(jω)H(jω) by substituting ω=∞. So, the polar plot ends with this magnitude and the phase angle.

How do you plot a polar plot in octave?

Create a 2-D plot from polar coordinates theta and rho . If a single complex input cplx is given then the real part is used for theta and the imaginary part is used for rho . The optional argument fmt specifies the line format in the same way as plot .


1 Answers

So, as I understand it, you want to display data with very different magnitudes on the same polar plot. Basically you're asking how to do something similar to twinx for polar axes.

As an example to illustrate the problem, it would be nice to display the green series on the plot below at a different scale than the blue series, while keeping them on the same polar axes for easy comparison.:

import numpy as np
import matplotlib.pyplot as plt

numpoints = 30
theta = np.linspace(0, 2*np.pi, numpoints)
r1 = np.random.random(numpoints)
r2 = 5 * np.random.random(numpoints)

params = dict(projection='polar', theta_direction=-1, theta_offset=np.pi/2)
fig, ax = plt.subplots(subplot_kw=params)

ax.fill_between(theta, r2, color='blue', alpha=0.5)
ax.fill_between(theta, r1, color='green', alpha=0.5)

plt.show()

enter image description here

However, ax.twinx() doesn't work for polar plots.

It is possible to work around this, but it's not very straight-forward. Here's an example:

import numpy as np
import matplotlib.pyplot as plt

def main():
    numpoints = 30
    theta = np.linspace(0, 2*np.pi, numpoints)
    r1 = np.random.random(numpoints)
    r2 = 5 * np.random.random(numpoints)

    params = dict(projection='polar', theta_direction=-1, theta_offset=np.pi/2)
    fig, ax = plt.subplots(subplot_kw=params)
    ax2 = polar_twin(ax)

    ax.fill_between(theta, r2, color='blue', alpha=0.5)
    ax2.fill_between(theta, r1, color='green', alpha=0.5)
    plt.show()

def polar_twin(ax):
    ax2 = ax.figure.add_axes(ax.get_position(), projection='polar', 
                             label='twin', frameon=False,
                             theta_direction=ax.get_theta_direction(),
                             theta_offset=ax.get_theta_offset())
    ax2.xaxis.set_visible(False)
    # There should be a method for this, but there isn't... Pull request?
    ax2._r_label_position._t = (22.5 + 180, 0.0)
    ax2._r_label_position.invalidate()
    # Ensure that original axes tick labels are on top of plots in twinned axes
    for label in ax.get_yticklabels():
        ax.figure.texts.append(label)
    return ax2

main()

enter image description here

That does what we want, but it looks fairly bad at first. One improvement would be to the tick labels to correspond to what we're plotting:

plt.setp(ax2.get_yticklabels(), color='darkgreen')
plt.setp(ax.get_yticklabels(), color='darkblue')

enter image description here

However, we still have the double-grids, which are rather confusing. One easy way around this is to manually set the r-limits (and/or r-ticks) such that the grids will fall on top of each other. Alternately, you could write a custom locator to do this automatically. Let's stick with the simple approach here:

ax.set_rlim([0, 5])
ax2.set_rlim([0, 1])

enter image description here

Caveat: Because shared axes don't work for polar plots, the implmentation I have above will have problems with anything that changes the position of the original axes. For example, adding a colorbar to the figure will cause all sorts of problems. It's possible to work around this, but I've left that part out. If you need it, let me know, and I'll add an example.

At any rate, here's the full, stand-alone code to generate the final figure:

import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)

def main():
    numpoints = 30
    theta = np.linspace(0, 2*np.pi, numpoints)
    r1 = np.random.random(numpoints)
    r2 = 5 * np.random.random(numpoints)

    params = dict(projection='polar', theta_direction=-1, theta_offset=np.pi/2)
    fig, ax = plt.subplots(subplot_kw=params)
    ax2 = polar_twin(ax)

    ax.fill_between(theta, r2, color='blue', alpha=0.5)
    ax2.fill_between(theta, r1, color='green', alpha=0.5)

    plt.setp(ax2.get_yticklabels(), color='darkgreen')
    plt.setp(ax.get_yticklabels(), color='darkblue')
    ax.set_ylim([0, 5])
    ax2.set_ylim([0, 1])

    plt.show()

def polar_twin(ax):
    ax2 = ax.figure.add_axes(ax.get_position(), projection='polar', 
                             label='twin', frameon=False,
                             theta_direction=ax.get_theta_direction(),
                             theta_offset=ax.get_theta_offset())
    ax2.xaxis.set_visible(False)

    # There should be a method for this, but there isn't... Pull request?
    ax2._r_label_position._t = (22.5 + 180, 0.0)
    ax2._r_label_position.invalidate()

    # Bit of a hack to ensure that the original axes tick labels are on top of
    # whatever is plotted in the twinned axes. Tick labels will be drawn twice.
    for label in ax.get_yticklabels():
        ax.figure.texts.append(label)

    return ax2

if __name__ == '__main__':
    main()
like image 58
Joe Kington Avatar answered Oct 27 '22 13:10

Joe Kington