Getting and setting maximum and minimum values from a Cartopy GeoAxesSubplot object - python

I have an array of subplots that I would like to share a colorbar through a post-processing step. When plotting 1-d data, I can do this by iterating over the axes after creating the data and using get_ylims() and set_ylims() to find, and then set the correct global minimum and maximum values.
When working with Cartopy GeoAxesSubplot objects, however, I haven't been able to find functions to retrieve or set the z-axis limits. The function get_ylims corresponds to the plot rather than the data now.
I am trying to avoid taking the extra step to calculate vmin and vmax beforehand, because the processing for each subplot takes quite a long time and I would not like to do it twice. I would much rather adjust the geoaxes in a post-processing step.
Simply, how do I get from the first figure to the second figure if I am only given the first figure?
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
# Create random data
data=[]
for i in range(4):
data.append(i + np.random.random((10,10)))
# Plot with individual colorbars
fig,ax = plt.subplots(nrows=2, ncols=2, subplot_kw={'projection':ccrs.NorthPolarStereo()})
for _ax,_dat in zip(ax.flat,data):
im = _ax.imshow(_dat)
plt.colorbar(im,ax=_ax)
fig.suptitle('Before.')
plt.show()
# Plot with a shared colorbar
fig2,ax2 = plt.subplots(nrows=2, ncols=2, subplot_kw={'projection':ccrs.NorthPolarStereo()})
for _ax,_dat in zip(ax2.flat,data):
im = _ax.imshow(_dat, vmin=0, vmax=4)
fig2.colorbar(im, ax=ax2.ravel().tolist())
fig2.suptitle('After.')
plt.show()

I ended up solving this by using the get_clim() and set_clim() functions of the matplotlib.collections.QuadMesh object.
I iterate over the axes, and then iterate over the components using get_children(). When I identify a QuadMesh object, I save it to a list. Finally, I iterate over that list twice, first to calculate the global minimum and maximum values, and then to set each subplot to those values.
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy as cpy
import matplotlib as mpl
geoaxes = figure.axes
qms = [] # to store QuadMesh object
for i in geoaxes: # iterate over axes and find QuadMesh objects
for j in i.get_children(): # breaks down a single axis (?) into components
if isinstance(j, mpl.collections.QuadMesh):
qms.append(j)
# Calculate global min and max values
min,max = qms[0].get_clim() # initialize min/max
for _qm in qms:
_clim = _qm.get_clim()
if _clim[0] < min:
min = _clim[0]
if _clim[1] > max:
max = _clim[1]
print(_clim)
# Set common bounds for each QuadMesh:
for _qm in qms:
_qm.set_clim((min, max))

Related

Edit marker shape in python

I'm using diamond pointer on the x-axis of a CDF plot to show the distribution of some data. As the number of data is high, these points are close together and not distinguishable. I was wondering if there is a way to make the diamond marker for scatter plot more pointy.
While I like #Stef's answer of creating new marker symbols, you can also just adjust the size of existing symbols with regard to their distance to other points:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.neighbors import NearestNeighbors
# create random data
x = np.random.rand(10)
y = np.ones(len(x))
# open figure + axes
fig,axs = plt.subplots(1,2)
# standard scatter-plot
MarkerSize = 40
axs[0].scatter(x,y,s=MarkerSize)
# re-arrange data
xy = []
for x1,y1 in zip(x,y):
xy.append([x1,y1])
# find nearest neighbors to itself (skip the first column because it finds the exact same element, i.e. with zero distance)
dst,idx = NearestNeighbors(n_neighbors=2).fit(xy).kneighbors(xy)
dst = dst[:,1]
# create a vector for the marker-size
S = dst/dst.max()*MarkerSize
# scatter plot with adjusted marker-size
axs[1].scatter(x,y,s=S)
I used scikit-learn's sklearn.neighbors.NearestNeighbors() to calculate the smallest distance between points and pass this as a scaling factor to the size-argument s= of matplotlib.pyplot.scatter(). There is a little tutorial for the marker-size argument in scatter().
You can define your own markers from a path, see the Marker Path Example.
import matplotlib.pyplot as plt
import matplotlib.path as mpath
pointed_diamond = mpath.Path([[0,-.5],[-.1,0],[0,.5],[.1,0],[0,-.5]], [1,2,2,2,79])
plt.plot([1,2,3], marker=pointed_diamond, markersize=10)

2D Color coded scatter plot with user defined color range and static colormap

I have 3 vectors - x,y,vel each having some 8k values. I also have quite a few files containing these 3 vectors. All the files have different x,y,vel. I want to get multiple scatter plots with the following conditions:
Color coded according to the 3rd variable i.e vel.
Once the ranges have been set for the colors (for the data from the 1st file), they should remain constant for all the remaining files. i don't want a dynamically changing (color code changing with each new file).
Want to plot a colorbar.
I greatly appreciate all your thoughts!!
I have attached the code for a single file.
import numpy as np
import matplotlib.pyplot as plt
# Create Map
cm = plt.cm.get_cmap('RdYlBu')
x,y,vel = np.loadtxt('finaldata_temp.txt', skiprows=0, unpack=True)
vel = [cm(float(i)/(8000)) for i in xrange(8000)] # 8000 is the no. of values in each of x,y,vel vectors.
# 2D Plot
plt.scatter(x, y, s=27, c=vel, marker='o')
plt.axis('equal')
plt.savefig('testfig.png', dpi=300)
plt.show()
quit()
You will have to iterate over all your data files to get the maximum value for vel, I have added a few lines of code (that need to be adjusted to fit your case) that will do that.
Therefore, your colorbar line has been changed to use the max_vel, allowing you to get rid of that code using the fixed value of 8000.
Additionally, I took the liberty to remove the black edges around the points, because I find that they 'obfuscate' the color of the point.
Lastly, I have added adjusted your plot code to use an axis object, which is required to have a colorbar.
import numpy as np
import matplotlib.pyplot as plt
# This is needed to iterate over your data files
import glob
# Loop over all your data files to get the maximum value for 'vel'.
# You will have to adjust this for your code
"""max_vel = 0
for i in glob.glob(<your files>,'r') as fr:
# Iterate over all lines
if <vel value> > max_vel:
max_vel = <vel_value>"""
# Create Map
cm = plt.cm.get_cmap('RdYlBu')
x,y,vel = np.loadtxt('finaldata_temp.txt', skiprows=0, unpack=True)
# Plot the data
fig=plt.figure()
fig.patch.set_facecolor('white')
# Here we switch to an axis object
# Additionally, you can plot several of your files in the same figure using
# the subplot option.
ax=fig.add_subplot(111)
s = ax.scatter(x,y,c=vel,edgecolor=''))
# Here we assign the color bar to the axis object
cb = plt.colorbar(mappable=s,ax=ax,cmap=cm)
# Here we set the range of the color bar based on the maximum observed value
# NOTE: This line only changes the calculated color and not the display
# 'range' of the legend next to the plot, for that we need to switch to
# ColorbarBase (see second code snippet).
cb.setlim(0,max_vel)
cb.set_label('Value of \'vel\'')
plt.show()
Snippet, demonstrating ColorbarBase
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
cm = plt.cm.get_cmap('RdYlBu')
x = [1,5,10]
y = [2,6,9]
vel = [7,2,1]
# Plot the data
fig=plt.figure()
fig.patch.set_facecolor('white')
ax=fig.add_subplot(111)
s = ax.scatter(x,y,c=vel,edgecolor=''))
norm = mpl.colors.Normalize(vmin=0, vmax=10)
ax1 = fig.add_axes([0.95, 0.1, 0.01, 0.8])
cb = mpl.colorbar.ColorbarBase(ax1,norm=norm,cmap=cm,orientation='vertical')
cb.set_clim(vmin = 0, vmax = 10)
cb.set_label('Value of \'vel\'')
plt.show()
This produces the following plot
For more examples of what you can do with the colorbar, specifically the more flexible ColorbarBase, I would suggest that you check the documentation -> http://matplotlib.org/examples/api/colorbar_only.html

Custom colour maps Matplotlib, make one value a prescribed colour

I have an array in python, using matplotlib, with floats ranging between 0 and 1.
I am displaying this array with imshow, I am trying to create a custom cmap, which is identical to Greens, however when a cell becomes 0 I would like to be able to map that value to red, and leave the rest of he spectrum unchanged.
If anyone more familiar with matplotlib would be able to help me I would greatly appreciate it!
For instance how would I edit this script so that the zero value in the matrix showed as red?
import numpy as np
from matplotlib import pyplot as plt
import matplotlib
x = np.array([[0,1,2],[3,4,5],[6,7,8]])
fig = plt.figure()
cmap_custom = matplotlib.cm.Greens
plt.imshow( x, interpolation='nearest' ,cmap = cmap_custom)
plt.colorbar()
plt.show()
The colormaps in matplotlib allow you to set special colors for values that are outside of the defined range. In your case specify the color for values below the defined range with cmap_custom.set_under('r').
Then you also need to specify the lower end of the range: vmin=0.01 (just some value > 0).
Finally create the colorbar with plt.colorbar(extend='min').
import numpy as np
from matplotlib import pyplot as plt
import matplotlib
x = np.array([[0,1,2],[3,4,5],[6,7,8]])
fig = plt.figure()
cmap_custom = matplotlib.cm.Greens
cmap_custom.set_under('r')
plt.imshow( x, interpolation='nearest' ,cmap = cmap_custom, vmin=0.01)
plt.colorbar(extend='min')
plt.show()

Scale axes 3d in matplotlib

I'm facing issues in scaling axes 3d in matplotlib. I have found another questions but somehow the answer it does not seems to work. Here is a sample code:
import matplotlib as mpl
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
data=np.array([[0,0,0],[10,1,1],[2,2,2]])
fig=plt.figure()
ax=Axes3D(fig)
ax.set_xlim3d(0,15)
ax.set_ylim3d(0,15)
ax.set_zlim3d(0,15)
ax.scatter(data[:,0],data[:,1],data[:,2])
plt.show()
It seems it just ignore the ax.set commands...
In my experience, you have to set your axis limits after plotting the data, otherwise it will look at your data and adjust whatever axes settings you entered before to fit it all in-frame out to the next convenient increment along the axes in question. If, for instance, you set your x-axis limits to +/-400 but your data go out to about +/-1700 and matplotlib decides to label the x-axis in increments of 500, it's going to display the data relative to an x-axis that goes out to +/-2000.
So in your case, you just want to rearrange that last block of text as:
fig=plt.figure()
ax=Axes3D(fig)
ax.scatter(data[:,0],data[:,1],data[:,2])
ax.set_xlim3d(0,15)
ax.set_ylim3d(0,15)
ax.set_zlim3d(0,15)
plt.show()
The way of ColorOutOfSpace is good. But if you want to automate the scaling you have to search for the maximum and minimum number in the data and scale with those values.
min = np.amin(data) # lowest number in the array
max = np.amax(data) # highest number in the array
ax.set_xlim3d(min, max)
ax.set_ylim3d(min, max)
ax.set_zlim3d(min, max)

matplotlib plotting multiple lines in 3D

I am trying to plot multiple lines in a 3D plot using matplotlib. I have 6 datasets with x and y values. What I've tried so far was, to give each point in the data sets a z-value. So all points in data set 1 have z=1 all points of data set 2 have z=2 and so on.
Then I exported them into three files. "X.txt" containing all x-values, "Y.txt" containing all y-values, same for "Z.txt".
Here's the code so far:
#!/usr/bin/python
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import pylab
xdata = '/X.txt'
ydata = '/Y.txt'
zdata = '/Z.txt'
X = np.loadtxt(xdata)
Y = np.loadtxt(ydata)
Z = np.loadtxt(zdata)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X,Y,Z)
plt.show()
What I get looks pretty close to what I need. But when using wireframe, the first point and the last point of each dataset are connected. How can I change the colour of the line for each data set and how can I remove the connecting lines between the datasets?
Is there a better plotting style then wireframe?
Load the data sets individually, and then plot each one individually.
I don't know what formats you have, but you want something like this
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
datasets = [{"x":[1,2,3], "y":[1,4,9], "z":[0,0,0], "colour": "red"} for _ in range(6)]
for dataset in datasets:
ax.plot(dataset["x"], dataset["y"], dataset["z"], color=dataset["colour"])
plt.show()
Each time you call plot (or plot_wireframe but i don't know what you need that) on an axes object, it will add the data as a new series. If you leave out the color argument matplotlib will choose them for you, but it's not too smart and after you add too many series' it will loop around and start using the same colours again.
n.b. i haven't tested this - can't remember if color is the correct argument. Pretty sure it is though.

Categories