2D plot of 3 variables - python

I have three dataset xx, yy, tau, for which I would like to realize a 2D plot.
Hence, I produce the following code, which return a contour plot by tricontourf. In particular at each value of tau(i), a given value of xx(i), yy(i) is associated.
fig, ax = plt.subplots(figsize = (10,8))
ax.set_xlabel('$\lambda$', fontsize=22)
ax.set_ylabel('$l_0$', fontsize = 22)
tcf = ax.tricontourf(xx, yy, tau, levels = 100)
cbar = fig.colorbar(tcf)
cbar.set_label(' \u03C4$_k$', fontsize =22)
plt.show()
All good for now. But how can I plot the value of tau simply as points/ dashed lines within this contour plot?

Related

Adding a 2d histogram to a python cartopy plot causes the extent to be reset

I am writing a program that displays a heat map of lightning strikes over a cartopy plot. Using a scatter plot work; however when I try to implement the 2d histogram, the map becomes zoomed out all the way.
Plot before adding histogram
Plot after adding histogram
Here is the code
lon_small and lat_small are the coordinate arrays.
proj = ccrs.LambertConformal()
fig, ax = plt.subplots(figsize=(10,10),subplot_kw=dict(projection=proj))
#FEATURES
#state_borders = cfeat.NaturalEarthFeature(category='cultural', name="admin_1_states_provinces_lakes", scale = '10m', facecolor = 'none')
extent = ([-99,-92, 27, 31.3])
xynps = ax2.projection.transform_points(ccrs.PlateCarree(), lon_array, lat_array)
ax.set_extent(extent)
ax.set_extent(extent)
ax.add_feature(USCOUNTIES.with_scale('5m'), edgecolor = "gray")
ax.add_feature(state_borders, linestyle='solid', edgecolor='black')
ax.gridlines(draw_labels=True, dms=True, x_inline=False, y_inline=False)
#Adding scatter plots
ax.scatter(-95.36, 29.76, transform = ccrs.PlateCarree(), marker='*', s = 400, color = "orange") #Houston
ax.scatter(lon_small, lat_small, transform = ccrs.PlateCarree(), marker='.', s=0.001, c = "red")
#Adding Histogram
h = ax.hist2d(xynps[:,0], xynps[:,1], bins=100, zorder=10, alpha=0.75, cmin=120)
plt.show()
I have checked online for people with a similar problem, but I can't find anything like this problem.
There's a note in the docstring of hist2d stating:
Currently hist2d calculates its own axis limits, and any limits previously set are ignored.
... so i guess the easiest way to maintain initial limits is to do something like this:
...
extent = ax.get_extent(crs=ax.projection)
ax.hist2d(...)
ax.set_extent(extent, crs=ax.projection)
...

Need help in plotting a polar contour plot

I have data for few points along the polar coordinates. The data is aligned as shown below
Data
I need help in plotting a 2D polar plot as shown below. I cant understand how do I reshape the values needed to plot contour plot.
enter image description here
Borrowed this code to modify and use for my test case :
theta = np.radians(azimuths)
zeniths = np.array(zeniths)
values = np.array(values)
values = values.reshape((len(azimuths), len(zeniths)))
r, theta = np.meshgrid(zeniths, np.radians(azimuths))
print(r,theta)
print(r.shape)
print(theta.shape)
fig, ax = subplots(subplot_kw=dict(projection='polar'))
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
autumn()
cax = ax.contourf(theta, r, values, 30)
autumn()
cb = fig.colorbar(cax)
return fig, ax, cax

How can I force ticks to have a certain spacing and range using contourf() in Python?

I'm creating a series of contours to be presented simultaneously; some plots have data in the range (0,6) and some are (4,6), but I'd like all contours to have the same colorbar which ranges from 0 to 6.
For the plots with data in the range (0,6), the colorbar has a range (0,6), but for the other plots in the range (4,6) the colorbar ticks are spaced strangely, making it appear that multiple colors correspond to the same value. The plot has data binned into integer values.
Plot with data in range (0,6)
Plot with data in range (4,6)
Here's what I've written to plot them, please let me know if additional information would be useful:
plt.contourf(r,mass2,D_stdev_coarse)
plt.title('Likely Binary Parameters for System with M={}, Plot 1'.format(m_tot))
plt.xlabel('R [km]')
plt.ylabel('M2, M1={}'.format(mass1))
plt.colorbar(ticks=[1.0,2.0,3.0,4.0,5.0,6.0])
plt.show()
Thanks so much in advance for any help you can provide.
You can explicitly set the levels for the contour plot. That way the corresponding colorbar takes these levels into account:
import matplotlib.pyplot as plt
import numpy as np
x, y = np.meshgrid(np.linspace(-2, 2, 200), np.linspace(-2, 2, 200))
z1 = 3 + 1.5 * (np.sin(x) + np.cos(y*y))
z2 = 5 + 0.5 * (np.sin(x) + np.cos(y*y))
#cmap = mcolors.LinearSegmentedColormap.from_list('', ['dodgerblue', 'crimson'], 2)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12, 4))
contour1 = ax1.contourf(x, y, z1, levels=range(7), alpha=0.8)
plt.colorbar(contour1, ax=ax1)
contour2 = ax2.contourf(x, y, z2, levels=range(4, 7), alpha=0.8)
plt.colorbar(contour2, ax=ax2)
plt.show()

Align twinx with second axis with non linear scale

I'm facing some problems in the alignment of the ticks of two different y-axes with the first characterized by a linear range and the second by a non linear range as depicted in the following picture.
HS, TMN = np.meshgrid(hs, period)
r = function(HS, TMN)
cax = plt.contourf(HS, TMN, np.log10(HS), cmap=plt.cm.RdYlGn_r)
ax = plt.gca()
ax2 = ax.twinx()
ticks2 = get_y2values(ax.get_yticks()) # Non linear function
ax2.yaxis.set_major_locator(mpl.ticker.FixedLocator(ticks))
ax2.set_ylim([0, 700])
ax.grid()
ax.set_ylabel('Y1', fontsize=14)
ax2.set_ylabel('Y2', fontsize=14)
plt.show()
More precisely, the right axis requires a different scale from the one on the left. And as final outcome, the idea is to have ticks values on the left aligned with the ticks values on the right (due to the non-linear function depicted below). E.g.: the value 8.08 from Y1 aligned with 101.5; 16.07 aligned with 309.5...
The new scale is required in order to insert new plot in the new scale.
As suggested in the comments the definition of a new scale works perfectly.
Referring to the SegmentedScale defined at the following link, the code that worked for me is the following:
hs = np.linspace(0.1, 15, 1000) # [meters]
period = np.linspace(0.1, 35, 1000) # [seconds]
HS, TMN = np.meshgrid(hs, period)
cax = plt.contourf(HS, TMN, np.log10(HS), cmap=plt.cm.RdYlGn_r)
ax1 = plt.gca()
ax2 = ax.twinx()
ticks = get_y2values(ax1.get_yticks()) # Non linear function
ax2.set_yscale('segmented', points=ticks)
ax1.grid()
ax1.set_yticks(ax1.get_yticks())
ax2.set_yticks(ticks)
ax1.set_ylabel('Y1', fontsize=14)
ax2.set_ylabel('Y2', fontsize=14)
plt.show()
If it is necessary to add new plots on the ax2 axis, it is required to do the plot before the application of the new custom scale.

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
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.
That's almost everything i wanted to achieve. But there are some points i wasn't able to figure out.
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?
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.
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()
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()
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')
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])
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()
Just to add onto #JoeKington 's (great) answer, I found that the "hack to ensure that the original axes tick labels are on top of whatever is plotted in the twinned axes" didn't work for me so as an alternative I've used:
from matplotlib.ticker import MaxNLocator
#Match the tick point locations by setting the same number of ticks in the
# 2nd axis as the first
ax2.yaxis.set_major_locator(MaxNLocator(nbins=len(ax1.get_yticks())))
#Set the last tick as the plot limit
ax2.set_ylim(0, ax2.get_yticks()[-1])
#Remove the tick label at zero
ax2.yaxis.get_major_ticks()[0].label1.set_visible(False)

Categories