Plotting two y axes on the same scale on an implot (matplotlib) - python

I have an image plot, representing a matrix, with two axes. The y axis on th left of my image plot represents the rows and the x axis represents the column, while each grid cell represents the value as a function of x and y.
I'd like to plot my y-axis in another form on the right side of my image plot, which takes on much smaller values, but should still be in the same positions as the y-axis on the left, as the values are just different forms of one another. The problem is that when I use fig.twinx()and go to plot the y axis, it doesn't even show up! Does anyone know what's gong on? Thanks.
Current code:
# Setup the figure
fig5 = pyplot.figure(5, figsize=(10,9), facecolor='white')
pyplot.gcf().clear()
# plt.rc('xtick', labelsize=20)
# plt.rc('ytick', labelsize=20)
plt.rcParams.update({'font.size': 18})
fig5ax = pyplot.axes()
# Code to calculate extent based on min/max range and my x values
implot = pyplot.imshow(valgrid, extent=MyExtent , aspect='auto', vmin = myVmin, vmax = myVmax)
fig5ax.yaxis.set_major_formatter(plt.ticker.FixedFormatter([str(x) for x in ranges]))
fig5ax.yaxis.set_major_locator(plt.ticker.FixedLocator(ranges))
fig5Ax2 = fig5ax.twinx()
fig5Ax2.yaxis.set_major_formatter(plt.ticker.FixedFormatter([str(x) for x in time]))
# Setting locater the same as ranges, because I want them to line up
# as they are different forms of the same y value
fig5Ax2.yaxis.set_major_locator(plt.ticker.FixedLocator(ranges))
pyplot.show()

The answer was:
fig5Ax2.yaxis.set_view_interval(minRange, maxRange)

Related

Matplotlib - Contourf - How to have a non-uniform ticks spacing?

I would like to plot contourf with (lat,depth,temp) and then have similar spacing as in the figure below (the temperature vary more near the surface then at depth, so I want to emphasized this region).
My depth array is not uniform (i.e. depth = [5,15,...,4975,5185,...]. I want to have such non-uniform vertical spacing.
I would like to show yticks = [10,100,500,1000,1500,2000,3000,4000,5000], and depth array does not have those exact values.
z = np.arange(0,50) # I want uniform spacing
pos = ([0,2,5,10,15,20,30,40,48]) # I want some yticks (not all of them)
ax=plt.contourf(lat,z,temp) # temp is a variable with dimensions (lat,depth)
plt.colorbar()
plt.gca().yaxis.set_ticks(pos) # Set some yticks, not all of them
plt.yticks(z[pos],depth[pos].astype(int)) # Replace the dummy values of z-array by something meaningful
plt.gca().invert_yaxis()
plt.grid(linestyle=':')
plt.gca().set(ylabel='depth (m)',xlabel='Latitude')'''
Potential Temperature of the Atlantic Ocean:
Per the matplotlib docs on yticks, you can specify the labels you want to use. In your case, if you want to show the labels [10,100,500,1000,1500,2000,3000,4000,5000] you can simply pass that list as the second argument in plt.yticks(), like so
plt.yticks(z[pos], [10,100,500,1000,1500,2000,3000,4000,5000])
and it will display the yticks accordingly. The issue arises in the specification of the positions - since the depth array does not have points corresponding exactly to the desired ytick values you will need to interpolate in order to find the exact position at which to place the labels. Unless the approximate positions specified in pos are already sufficient, in which case the above suffices.
If the depth data are not uniformly spaced then you can use numpy.interp to perform the interpolation, as shown below
import matplotlib.pyplot as plt
import numpy as np
# Create some depth data that is not uniformly spaced over [0, 5500]
depth = [(np.random.random() - 0.5)*25 + ii for ii in np.linspace(0, 5500, 50)]
lat = np.linspace(-75, 75, 50)
z = np.linspace(0,50, 50)
yticks = [10,100,500,1000,1500,2000,3000,4000,5000]
# Interpolate depths to get z-positions
pos = np.interp(yticks, depth, z)
temp = np.outer(lat, z) # Arbitrarily populate temp for demonstration
ax = plt.contourf(lat,z,temp)
plt.colorbar()
plt.gca().yaxis.set_ticks(pos)
plt.yticks(pos,yticks) # Place yticks at interpolated z-positions
plt.gca().invert_yaxis()
plt.grid(linestyle=':')
plt.gca().set(ylabel='Depth (m)',xlabel='Latitude')
plt.show()
This will find the exact positions where the yticks would fall if the depth array had data at those positions and place them accordingly as shown below.

Custom Axis Scale for Pyplot

I have a plotting program that currently takes an array that goes from 12-2000 on both the x and y axis.
img2 = plt.imshow(data1,interpolation='nearest',cmap = cmap2, norm=MidpointNormalize(midpoint=p50)
,extent=[12,2000,12,2000], origin='lower')
This method works, but it gives me poor control over my axes. For instance, I want to convert my x and y points into a new coordinate system. The value at each location will stay the same, but the actual x and y will change by a factor of 2e-8*x^(4.3192). The problem is, this isn't in true log/log space.
ax = plt.subplot(111)
ax.set_yscale('log')
ax.set_xscale('log')
I tried changing the axes of the graph into log/log space, but they don't line up. What can I do?

Matplotlib: Adjust legend location/position

I'm creating a figure with multiple subplots. One of these subplots is giving me some trouble, as none of the axes corners or centers are free (or can be freed up) for placing the legend. What I'd like to do is to have the legend placed somewhere in between the 'upper left' and 'center left' locations, while keeping the padding between it and the y-axis equal to the legends in the other subplots (that are placed using one of the predefined legend location keywords).
I know I can specify a custom position by using loc=(x,y), but then I can't figure out how to get the padding between the legend and the y-axis to be equal to that used by the other legends. Would it be possible to somehow use the borderaxespad property of the first legend? Though I'm not succeeding at getting that to work.
Any suggestions would be most welcome!
Edit: Here is a (very simplified) illustration of the problem:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2, sharex=False, sharey=False)
ax[0].axhline(y=1, label='one')
ax[0].axhline(y=2, label='two')
ax[0].set_ylim([0.8,3.2])
ax[0].legend(loc=2)
ax[1].axhline(y=1, label='one')
ax[1].axhline(y=2, label='two')
ax[1].axhline(y=3, label='three')
ax[1].set_ylim([0.8,3.2])
ax[1].legend(loc=2)
plt.show()
What I'd like is that the legend in the right plot is moved down somewhat so it no longer overlaps with the line.
As a last resort I could change the axis limits, but I would very much like to avoid that.
I saw the answer you posted and tried it out. The problem however is that it is also depended on the figure size.
Here's a new try:
import numpy
import matplotlib.pyplot as plt
x = numpy.linspace(0, 10, 10000)
y = numpy.cos(x) + 2.
x_value = .014 #Offset by eye
y_value = .55
fig, ax = plt.subplots(1, 2, sharex = False, sharey = False)
fig.set_size_inches(50,30)
ax[0].plot(x, y, label = "cos")
ax[0].set_ylim([0.8,3.2])
ax[0].legend(loc=2)
line1 ,= ax[1].plot(x,y)
ax[1].set_ylim([0.8,3.2])
axbox = ax[1].get_position()
fig.legend([line1], ["cos"], loc = (axbox.x0 + x_value, axbox.y0 + y_value))
plt.show()
So what I am now doing is basically getting the coordinates from the subplot. I then create the legend based on the dimensions of the entire figure. Hence, the figure size does not change anything to the legend positioning anymore.
With the values for x_value and y_value the legend can be positioned in the subplot. x_value has been eyeballed for a good correspondence with the "normal" legend. This value can be changed at your desire. y_value determines the height of the legend.
Good luck!
After spending way too much time on this, I've come up with the following satisfactory solution (the Transformations Tutorial definitely helped):
bapad = plt.rcParams['legend.borderaxespad']
fontsize = plt.rcParams['font.size']
axline = plt.rcParams['axes.linewidth'] #need this, otherwise the result will be off by a few pixels
pad_points = bapad*fontsize + axline #padding is defined in relative to font size
pad_inches = pad_points/72.0 #convert from points to inches
pad_pixels = pad_inches*fig.dpi #convert from inches to pixels using the figure's dpi
Then, I found that both of the following work and give the same value for the padding:
# Define inverse transform, transforms display coordinates (pixels) to axes coordinates
inv = ax[1].transAxes.inverted()
# Inverse transform two points on the display and find the relative distance
pad_axes = inv.transform((pad_pixels, 0)) - inv.transform((0,0))
pad_xaxis = pad_axes[0]
or
# Find how may pixels there are on the x-axis
x_pixels = ax[1].transAxes.transform((1,0)) - ax[1].transAxes.transform((0,0))
# Compute the ratio between the pixel offset and the total amount of pixels
pad_xaxis = pad_pixels/x_pixels[0]
And then set the legend with:
ax[1].legend(loc=(pad_xaxis,0.6))
Plot:

Plotting a 2D mesh grid with matplotlib

I would like to plot a 2D discretization rectangular mesh with non-regular
x y axes values, e.g. the typical discretization meshes used in CFD.
An example of the code may be:
fig = plt.figure(1,figsize=(12,8))
axes = fig.add_subplot(111)
matplotlib.rcParams.update({'font.size':17})
axes.set_xticks(self.xPoints)
axes.set_yticks(self.yPoints)
plt.grid(color='black', linestyle='-', linewidth=1)
myName = "2D.jpg"
fig.savefig(myName)
where self.xPoints and self.yPoints are 1D non-regular vectors.
This piece of code produce a good discretization mesh, the problem are the
xtics and ytics labels because they appear for all values of xPoints and yPoints (they overlap).
How can I easily redefine the printed values in the axes?
Let's say I only want to show the minimum and maximum value for x and y and not all values from the discretization mesh.
I cann't post a example-figure because it is the first time I ask something here (I can send it by mail if requested)
the problem is that you explicitly told matplotlib to label each point when you wrote:
axes.set_xticks(self.xPoints)
axes.set_yticks(self.yPoints)
comment out those lines and see what the result looks like.
Of course, if you only want the first and last point labelled, it becomes:
axes.set_xticks([self.xPoints[0], self.xPoints[-1]])
...
If the gridline was specified by axes.set_xticks(), I don't think it would be possible to show ticks without overlap in your case.
I may have a solution for you:
...
ax = plt.gca()
#Arr_y: y-direction data, 1D numpy array or list.
for j in range(len(Arr_y)):
plt.hline(y = Arr_y[j], xmin = Arr_x.min(), xmax = Arr_x.max(), color = 'black')
#Arr_x: x-direction data, 1D numpy array or list.
for i in range(len(Arr_x)):
plt.vline(x = Arr_x[i], ymin = Arr_y.min(), ymax = Arr_y.max(), color = 'black')
#Custom your ticks here, 1D numpy array or list.
ax.set_xticks(Arr_xticks)
ax.set_yticks(Arr_yticks)
plt.xlim(Arr_x.min(), Arr_x.max())
plt.ylim(Arr_y.min(), Arr_y.max())
plt.show()
...
hlines and vlines are horizontal and vertical lines, you can specify those lines with boundary data in both x and y directions.
I tried it with 60×182 non uniform mesh grid which cost me 1.2s, hope I can post a picture here.

Changing axis values on a plot

How can I change the data on one axis?
I'm making some spectrum analysis on some data and my x-axis is the index of some matrix. I'd like to change it so that the x-axis becomes the data itself.
I'm using the imshow() to plot the data (I have a matrix whose elements are some intensity, the y axes are their detector-source correspondent pair and the x-axis should be their frequency).
The code for it is written down here:
def pltspec(dOD, self):
idx = 0
b = plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B = np.zeros((2*len(self.Chan),len(b[0])))
for idx in range(2*len(self.Chan)):
b = plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B[idx,:] = 20*log10(b[0])
fig = plt.figure()
ax = fig.add_subplot(111)
plt.imshow(B, origin = 'lower')
plt.colorbar()
locs, labels = xticks(find(b[1]), b[1])
plt.axis('tight')
ax.xaxis.set_major_locator(MaxNLocator(5))
I think if there's a way of interchanging the index of some array with its value, my problem would be solved.
I've managed to use the line locs, labels = xticks(find(b[1]), b[1]). But with it on my graph my axis interval just isn't right... I think it has something to do with the MaxNLocator (which I used to decrease the number of ticks).
And if I use the xlim, I can set the figure to be what I want, but the x axis is still the same (on that xlim I had to use the original data to set it right).
What am I doing wrong?
Yes, you can use the xticks method exemplified in this example.
There are also more sophisticated ways of doing it. See ticker.

Categories

Resources