Adding a second label to colorbar - python-3.x

I have an imshow plot with a colorbar. I want two labels in the colorbar, one on the left side and the other one on the right side.
This is the mve:
V = np.array([[1, 2, 3], [4, 5, 6]]) # Just a sample array
plt.imshow(V, cmap = "hot", interpolation = 'none')
clb = plt.colorbar()
clb.set_label("Firstlabel", fontsize=10, labelpad=-40, y=0.5, rotation=90)
#clb.set_label("SECONDLABEL") # This is the label I want to add
plt.savefig("Example")
This produces:
I want a second label on the right side of the colorbar. If I use the commented line a second colorbar is added to my plot, and that is not what I want. How can I do this?

You can't have two label objects, but you could add a second label using clb.ax.text.
Also, note that to move the first label to the left hand side, you could use clb.ax.yaxis.set_label_position('left') rather than labelpad=-40
So, using lines:
clb = plt.colorbar()
clb.set_label("Firstlabel", fontsize=10, y=0.5, rotation=90)
clb.ax.yaxis.set_label_position('left')
clb.ax.text(2.5, 0.5, "SECONDLABEL", fontsize=10, rotation=90, va='center')
Produces this figure:

Related

Adjust spacing between 2 markers of same line displayed in the legend of matplotlib plot

I am plotting 2 markers of the same line using the following code and
I want to adjust the spacing between two markers in the legend.
Code : ref.
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple
fig, ax1 = plt.subplots(1, 1)
# First plot: two legend keys for a single entry
p1, = ax1.plot([1, 2], [5, 6], '-', marker='o', markersize=2, mfc="gray", mec="gray")
# `plot` returns a list, but we want the handle - thus the comma on the left
p2, = ax1.plot([1], [5], "-k", marker='s', markersize=10)
p3, = ax1.plot([3, 4], [2, 3], 'o', mfc="white", mec="k")
p4, = ax1.plot([3], [2], '-k', mfc="white", mec="k")
# Assign two of the handles to the same legend entry by putting them in a tuple
# and using a generic handler map (which would be used for any additional
# tuples of handles like (p1, p3)).
handles = [(p1, p2), (p3, p4)]
l = ax1.legend(
handles, ['data', 'models'],
handler_map={tuple: HandlerTuple(ndivide=None)},
handletextpad=1,
columnspacing=2.0, ncol=1,
)
# plt.savefig("demo.png")
plt.show()
Results in the following plot
I could use handletextpad to adjust the spacing between the marker and text but I am not sure how to adjust the spacing between 2 markers (i.e. please see the position pointed by the red arrow below).
Suggestions will be of great help.

How to add numbers to top of histogram bars?

I want to add the frequency number above each bar and want to keep the bars separated. I can't figure out how to keep the bars separated.
My code is:
df_dm2["resolution"].plot(kind='hist', color='blue', edgecolor='black', linewidth=1.2 )
plt.ylabel('Frequency')
plt.xlabel('Resolution (m)')
plt.title('Region DM2 Frame Resolution Frequency')
#plt.text( .92, 3.5, r'$\mu=1.092531 ,\ \sigma=0.091098$')
#plt.xticks(np.arange(0, 2, 0.2))
plt.yticks(np.arange(0, 200,25))
plt.savefig('histogram_dm2.png',dpi=72, bbox_inches='tight')
plt.show()
My output thus far is as follows: enter image description here
Use the rwidth option in plot:
df_dm2["resolution"].plot(kind='hist',
color='blue',
edgecolor='black',
linewidth=1.2,
rwidth=0.75)

Modify position of colorbar so that extend triangle is above plot

So, I have to make a bunch of contourf plots for different days that need to share colorbar ranges. That was easily made but sometimes it happens that the maximum value for a given date is above the colorbar range and that changes the look of the plot in a way I dont need. The way I want it to treat it when that happens is to add the extend triangle above the "original colorbar". It's clear in the attached picture.
I need the code to run things automatically, right now I only feed the data and the color bar range and it outputs the images, so the fitting of the colorbar in the code needs to be automatic, I can't add padding in numbers because the figure sizes changes depending on the area that is being asked to be plotted.
The reason why I need this behavior is because eventually I would want to make a .gif and I can't have the colorbar to move in that short video. I need for the triangle to be added, when needed, to the top (and below) without messing with the "main" colorbar.
Thanks!
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize, BoundaryNorm
from matplotlib import cm
###############
## Finds the appropriate option for variable "extend" in fig colorbar
def find_extend(vmin, vmax, datamin, datamax):
#extend{'neither', 'both', 'min', 'max'}
if datamin >= vmin:
if datamax <= vmax:
extend="neither"
else:
extend="max"
else:
if datamax <= vmax:
extend="min"
else:
extend="both"
return extend
###########
vmin=0
vmax=30
nlevels=8
colormap=cm.get_cmap("rainbow")
### Creating data
z_1=30*abs(np.random.rand(5, 5))
z_2=37*abs(np.random.rand(5, 5))
data={1:z_1, 2:z_2}
x=range(5)
y=range(5)
## Plot
for day in [1, 2]:
fig = plt.figure(figsize=(4,4))
## Normally figsize=get_figsize(bounds) and bounds is retrieved from gdf.total_bounds
## The function creates the figure size based on the x/y ratio of the bounds
ax = fig.add_subplot(1, 1, 1)
norm=BoundaryNorm(np.linspace(vmin, vmax, nlevels+1), ncolors=colormap.N)
z=data[day]
cs=ax.contourf(x, y, z, cmap=cmap, norm=norm, vmin=vmin, vmax=vmax)
extend=find_extend(vmin, vmax, np.nanmin(z), np.nanmax(z))
fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax, extend=extend)
plt.close(fig)
You can do something like this: putting a triangle on top of the colorbar manually:
fig, ax = plt.subplots()
pc = ax.pcolormesh(np.random.randn(20, 20))
cb = fig.colorbar(pc)
trixy = np.array([[0, 1], [1, 1], [0.5, 1.05]])
p = mpatches.Polygon(trixy, transform=cb.ax.transAxes,
clip_on=False, edgecolor='k', linewidth=0.7,
facecolor='m', zorder=4, snap=True)
cb.ax.add_patch(p)
plt.show()

Matplotlib - Scatter Plot, How to fill in the space between each individual point?

I am plotting the following data:
fig, ax = plt.subplots()
im = ax.scatter(std_sorted[:, [1]], std_sorted[:, [2]], s=5, c=std_sorted[:, [0]])
With the following result:
My question is: can I fill the space between each point in the plot by extrapolating and then coloring that extrapolated space accordingly, so I get a uniform plot without any points?
So basically I'm looking for this result (This is simply me "squeezing" the above picture to show the desired result and not dealing with the space between the points):
The simplest thing to do in this case, is to use a short vertical line a marker, and set the markersize large enough such that there is no white space left.
Another option is to use tricontourf to create a filled image of x, y and z.
Note that neither a scatter plot nor tricontourf need the points to be sorted in any order.
If you do have your points sorted into an orderded grid, plt.imshow should give the best result.
Here is some code to show how it could look like. First some dummy data slightly similar to the example are generated. As the x,y are random, they don't fill the complete space. This might leave some blank spots in the scatter plot. The spots are nicely interpolated for the contourf, except possibly in the corners.
import numpy as np
import matplotlib.pyplot as plt
N = 50000
xmin = 0
xmax = 0.20
ymin = -0.01
ymax = 0.01
std_sorted = np.zeros((N, 3))
std_sorted[:,1] = np.random.uniform(xmin, xmax, N)
std_sorted[:,2] = np.random.choice(np.linspace(ymin, ymax, 80), N)
std_sorted[:,0] = np.cos(3*(std_sorted[:,1] - 0.04 - 100*std_sorted[:,2]**2))**10
fig, ax = plt.subplots(ncols=2)
# im = ax[0].scatter(std_sorted[:, 1], std_sorted[:, 2], s=20, c=std_sorted[:, 0], marker='|')
im = ax[0].scatter(std_sorted[:, 1], std_sorted[:, 2], s=5, c=std_sorted[:, 0], marker='.')
ax[0].set_xlim(xmin, xmax)
ax[0].set_ylim(ymin, ymax)
ax[0].set_title("scatter plot")
ax[1].tricontourf(std_sorted[:, 1], std_sorted[:, 2], std_sorted[:, 0], 256)
ax[1].set_title("tricontourf")
plt.tight_layout()
plt.show()

matplotlib: controlling position of y axis label with multiple twinx subplots

I wrote a Python script based on matplotlib that generates curves based on a common timeline. The number of curves sharing the same x axis in my plot can vary from 1 to 6 depending on user options.
Each of the data plotted use different y scales and require a different axis for drawing. As a result, I may need to draw up to 5 different Y axes on the right of my plot. I found the way in some other post to offset the position of the axes as I add new ones, but I still have two issues:
How to control the position of the multiple axes so that the tick labels don't overlap?
How to control the position of each axis label so that it is placed vertically at the bottom of each axis? And how to preserve this alignment as the display window is resized, zoomed-in etc...
I probably need to write some code that will first query the position of the axis and then a directive that will place the label relative to that position but I really have no idea how to do that.
I cannot share my entire code because it is too big, but I derived it from the code in this example. I modified that example by adding one extra plot and one extra axis to more closely match what intend to do in my script.
import matplotlib.pyplot as plt
def make_patch_spines_invisible(ax):
ax.set_frame_on(True)
ax.patch.set_visible(False)
for sp in ax.spines.values():
sp.set_visible(False)
fig, host = plt.subplots()
fig.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
par3 = host.twinx()
# Offset the right spine of par2. The ticks and label have already been
# placed on the right by twinx above.
par2.spines["right"].set_position(("axes", 1.2))
# Having been created by twinx, par2 has its frame off, so the line of its
# detached spine is invisible. First, activate the frame but make the patch
# and spines invisible.
make_patch_spines_invisible(par2)
# Second, show the right spine.
par2.spines["right"].set_visible(True)
par3.spines["right"].set_position(("axes", 1.4))
make_patch_spines_invisible(par3)
par3.spines["right"].set_visible(True)
p1, = host.plot([0, 1, 2], [0, 1, 2], "b-", label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], "r-", label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], "g-", label="Velocity")
p4, = par3.plot([0,0.5,1,1.44,2],[100, 102, 104, 108, 110], "m-", label="Acceleration")
host.set_xlim(0, 2)
host.set_ylim(0, 2)
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
par3.set_ylabel("Acceleration")
host.yaxis.label.set_color(p1.get_color())
par1.yaxis.label.set_color(p2.get_color())
par2.yaxis.label.set_color(p3.get_color())
par3.yaxis.label.set_color(p4.get_color())
tkw = dict(size=4, width=1.5)
host.tick_params(axis='y', colors=p1.get_color(), **tkw)
par1.tick_params(axis='y', colors=p2.get_color(), **tkw)
par2.tick_params(axis='y', colors=p3.get_color(), **tkw)
par3.tick_params(axis='y', colors=p4.get_color(), **tkw)
host.tick_params(axis='x', **tkw)
lines = [p1, p2, p3, p4]
host.legend(lines, [l.get_label() for l in lines])
# fourth y axis is not shown unless I add this line
plt.tight_layout()
plt.show()
When I run this, I obtain the following plot:
output from above script
In this image, question 2 above means that I would want the y-axis labels 'Temperature', 'Velocity', 'Acceleration' to be drawn directly below each of the corresponding axis.
Thanks in advance for any help.
Regards,
L.
What worked for me was ImportanceOfBeingErnest's suggestion of using text (with a line like
host.text(1.2, 0, "Velocity" , ha="left", va="top", rotation=90,
transform=host.transAxes))
instead of trying to control the label position.

Resources