How do I make the text appear after 3barplot in Matplotlib - python

Why doesn't zorder work in this case? I've tried using it but the text still ends up being covered by the bar plot towers.
import numpy as np
from matplotlib import pyplot as plt
Percentage_Differences_1 = np.array([ [7.94*(10**-10),7.94*(10**-9),7.94*(10**-8),7.94*(10**-7),7.94*(10**-6),7.94*(10**-5)],
[7.92*(10**-12),7.92*(10**-11),7.92*(10**-10),7.92*(10**-9),7.92*(10**-8),7.92*(10**-7)],
[7.72*(10**-14),7.72*(10**-13),7.72*(10**-12),7.72*(10**-11),7.72*(10**-10),7.72*(10**-9)],
[5.66*(10**-16),5.66*(10**-15),5.66*(10**-14),5.66*(10**-13),5.66*(10**-12),5.66*(10**-11)],
[1.49*(10**-17),1.49*(10**-16),1.49*(10**-15),1.49*(10**-14),1.49*(10**-13),1.49*(10**-12)],
[2.21*(10**-18),2.21*(10**-17),2.21*(10**-16),2.21*(10**-15),2.21*(10**-14),2.21*(10**-13)] ]) # Layer 1, 12
fig1 = plt.figure(dpi = 120, tight_layout = True)
fig1.set_size_inches(10, 7)
ax1 = fig1.add_subplot(111, projection='3d')
width = depth = 0.3
column_names = ['$10^{-6} m$','$10^{-5} m$','$10^{-4} m$','$10^{-3} m$','$10^{-2} m$','$10^{-1} m$']
row_names = ['$10^{-6} g$','$10^{-5} g$','$10^{-4} g$','$10^{-3} g$','$10^{-2} g$','$10^{-1} g$']
height_names = ['$10^{-2}$','$10^{-4}$','$10^{-6}$','$10^{-8}$','$10^{-10}$','$10^{-12}$','$10^{-14}$','$10^{-16}$','$10^{-18}$']
for x in range(0,6):
for y in range(0,6):
plot1 = ax1.bar3d(x, y, 0, width, depth, np.log10(Percentage_Differences_1[x][y]), color = "#0040bf", alpha=0.3, zorder = 1)
txt1 = ax1.text(x,y,1.15*np.log10(Percentage_Differences_1[x][y]),'{:.2e}'.format(Percentage_Differences_1[y][x]), verticalalignment='top', bbox=dict(facecolor='grey', alpha=0.5), zorder = 2)
ax1.view_init(-140, -30)
ax1.set_xticks(np.linspace(0, 6, num = 6))
ax1.set_yticks(np.linspace(0, 6, num = 6))
ax1.set_xticklabels(column_names)
ax1.set_yticklabels(row_names)
ax1.set_zticklabels(height_names)
ax1.set_xlabel("Mass", labelpad = 13, rotation = 45)
ax1.set_ylabel("Radius", labelpad = 10, rotation = 45)
ax1.set_zlabel("Deviation $\Delta$")
ax1.set_title("1st Initial Condition: $r(0)$ and $r'(0)$ of $\Theta(12) = 2.18 \\times 10^{7} m$", pad = 40)
plt.show()
I've tried using both set_zorder and zorder but the plot still ends up covering the majority of the text labels.

Change your zorder for a number larger than the number of bar objects, 100 for example:

Related

Align colorbar with GeoAxes subplot edges

I have a figure with 3 subplots, two of which share a colorbar and the third has has it's own colorbar.
I would like the colorbars to align with the vertical limits of their respective plots, and for the top two plots to have the same vertical limits.
Googling, I have found ways to do this with a single plot, but am stuck trying to make it work for my fig. My figure currently looks like this:
The code for which is as follows:
import cartopy.io.shapereader as shpreader
import cartopy.crs as ccrs
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
shpfilename = shpreader.natural_earth(resolution='50m',
category='cultural',
name='admin_0_countries')
reader = shpreader.Reader(shpfilename)
countries = reader.records()
projection = ccrs.PlateCarree()
fig = plt.figure()
axs = [plt.subplot(2, 2, x + 1, projection = projection) for x in range(2)]\
+ [plt.subplot(2, 2, (3, 4), projection = projection)]
def cmap_seg(cmap, value, k):
cmaplist = [cmap(i) for i in range(cmap.N)]
cmap = mpl.colors.LinearSegmentedColormap.from_list(
'Custom cmap', cmaplist, cmap.N)
bounds = np.linspace(0, k, k + 1)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
color = cmap(norm(value))
return color, cmap
for country in countries:
c_name = country.attributes["SOVEREIGNT"]
country_dat = df.loc[c_name]
cmap = matplotlib.cm.get_cmap("plasma")
cmap_blues = matplotlib.cm.get_cmap("Blues")
ax_extent = [-170, 180, -65, 85]
alpha = 1.0
edgecolor = "k"
linewidth = 0.5
ax = axs[0]
value = country_dat.loc["wgi_bin"]
ax.add_geometries([country.geometry],
projection,
facecolor = cmap_seg(cmap, value, 5)[0],
alpha = alpha,
edgecolor = edgecolor,
linewidth = linewidth)
ax.set_xlabel("WGI group")
ax.set_extent(ax_extent)
ax = axs[1]
value = country_dat.loc["epi_bin"]
ax.add_geometries([country.geometry],
projection,
facecolor = cmap_seg(cmap, value, 5)[0],
alpha = alpha,
edgecolor = edgecolor,
linewidth = linewidth)
ax.set_xlabel("EPI group")
ax.set_extent(ax_extent)
ax = axs[2]
value = country_dat.loc["diff"]
ax.add_geometries([country.geometry],
projection,
facecolor = cmap_seg(cmap_blues, value, 4)[0],
alpha = alpha,
edgecolor = edgecolor,
linewidth = linewidth)
ax.set_xlabel("difference")
ax.set_extent(ax_extent)
subplot_labels = ["WGI group", "EPI group", "Metric difference"]
for i, ax in enumerate(axs):
ax.text(0.5, -0.07, subplot_labels[i], va='bottom', ha='center',
rotation='horizontal', rotation_mode='anchor',
transform=ax.transAxes)
sm = plt.cm.ScalarMappable(cmap=cmap_seg(cmap, 5, 5)[1], norm = plt.Normalize(0, 5))
sm._A = []
cb = plt.colorbar(sm, ax = axs[1], values = [1,2,3,4, 5], ticks = [1,2,3,4,5])
sm2 = plt.cm.ScalarMappable(cmap=cmap_seg(cmap_blues, 5, 4)[1], norm = plt.Normalize(0, 4))
sm2._A = []
cb2 = plt.colorbar(sm2, ax = axs[2], values = [0,1,2,3], ticks = [0,1,2,3])
Try this:
# update your code for this specific line (added shrink option)
cb = plt.colorbar(sm, ax=axs[1], values=[1,2,3,4,5], ticks=[1,2,3,4,5], shrink=0.6)
And add these lines of code towards the end:
p00 = axs[0].get_position()
p01 = axs[1].get_position()
p00_new = [p00.x0, p01.y0, p00.width, p01.height]
axs[0].set_position(p00_new)
The plot should be similar to this:

How to limit lower error of bar plot to 0?

I calculated the rttMeans and rttStds arrays. However, the value of rttStds makes the lower error less than 0.
rttStds = [3.330311915835426, 3.3189677330174883, 3.3319538853150386, 3.325173772304221, 3.3374145232695813]
How to set lower error to 0 instead of -#?
The python bar plot code is bellow.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(rc={'figure.figsize':(18,16)},style='ticks',font_scale = 1.5,font='serif')
N = 5
ind = ['RSU1', 'RSU2', 'RSU3', 'RSU4', 'RSU5'] # the x locations for the groups
width = 0.4 # the width of the bars: can also be len(x) sequence
fig = plt.figure(figsize=(10,6))
ax = fig.add_subplot(111)
p1 = plt.bar(ind, rttMeans, width, yerr=rttStds, log=False, capsize = 16, color='green', hatch="/", error_kw=dict(elinewidth=3,ecolor='black'))
plt.margins(0.01, 0)
#Optional code - Make plot look nicer
plt.xticks(rotation=0)
i=0.18
for row in rttMeans:
plt.text(i, row, "{0:.1f}".format(row), color='black', ha="center")
i = i + 1
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
params = {'axes.titlesize':24,
'axes.labelsize':24,
'xtick.labelsize':28,
'ytick.labelsize':28,
'legend.fontsize': 24,
'axes.spines.right':False,
'axes.spines.top':False}
plt.rcParams.update(params)
plt.tick_params(axis="y", labelsize=28, labelrotation=20, labelcolor="black")
plt.tick_params(axis="x", labelsize=28, labelrotation=20, labelcolor="black")
plt.ylabel('RT Time (millisecond)', fontsize=24)
plt.title('# Participating RSUs', fontsize=24)
# plt.savefig('RSUs.pdf', bbox_inches='tight')
plt.show()
You can pass yerr as a pair [lower_errors, upper_errors] where you can control lower_errors :
lowers = np.minimum(rttStds,rttMeans)
p1 = plt.bar(ind, rttMeans, width, yerr=[lowers,rttStds], log=False, capsize = 16, color='green', hatch="/", error_kw=dict(elinewidth=3,ecolor='black'))
Output:

How to plot cross-sections of imshow?

I am doing numerical simulations in python 3.6 and trying to inspect cross-sections of 2d imshow. I made the horizontal inspection and would like to have vertical, but got into some difficulties. The blue inspection lines correspond to 'bottom' (horizontal) and 'left' (vertical) subplots. Example code (I haven't been allowed to attach a matplotlib image):
from mpl_toolkits.axes_grid1 import make_axes_locatable
import matplotlib.pyplot as plt
import numpy as np
Array = np.random.rand(100, 100)
grid_points = 100
fig_mpl, ax = plt.subplots(figsize = (10, 10), facecolor = 'white')
line = ax.imshow(Array, cmap = 'hot')
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size = "5%", pad = 0.05)
caxb = divider.append_axes("bottom", size = "10%", pad = 0.05)
caxl = divider.append_axes("left", size = "10%", pad = 0.05)
bar = fig_mpl.colorbar(line, cax = cax, orientation = 'vertical')
ax.axhline(grid_points/2)
ax.axvline(grid_points/2)
X = np.linspace(0, grid_points - 1, grid_points)
projb, = caxb.plot(X, Array[int(grid_points/2)], color = 'red')
projl, = caxl.plot(X, Array[:, int(grid_points/2)], color = 'red')
caxb.set_ylim(-0.1*np.max(Array), 1.1*np.max(Array))
caxb.set_xlim(0, grid_points - 1)
caxl.set_xlim(-0.1*np.max(Array), 1.1*np.max(Array))
caxl.set_ylim(0, grid_points - 1)
ax.set_xticks([])
ax.set_yticks([])
caxb.set_xticks([])
caxl.set_yticks([])
caxb.set_yticks([np.min(Array), np.max(Array)])
caxl.set_xticks([np.min(Array), np.max(Array)])
caxb.yaxis.tick_right()
for tick in caxl.get_xticklabels():
tick.set_rotation(-90)
caxb.grid(color = 'black', marker = 8)
caxl.grid(color = 'black', marker = 8)
fig_mpl.subplots_adjust(wspace = 0)
fig_mpl.tight_layout()
I want projl to plot the cross-section of Array in vertical caxl.
Is there any proper way to do the thing?
Instead of
projl, = caxl.plot(X, Array[:, int(grid_points/2)], color = 'red')
you need
projl, = caxl.plot(Array[:, int(grid_points/2)], X, color = 'red')
because the amplitude should be shown along the horizontal (x-) axis and the grid index (X) along the vertical (y-) axis.

How to efficiently animate multiple patches from a list of lists

I am trying to animate multiple patches as efficiently as possible when reading data from a list?
The code below displays an animation of the scatter plot but not the patches. Each point in scatter plot contains various sizes of circles. This example would require 6 different circles to be animated at 2 subjects each time point. But what if there were 20 subjects that each had 3 circles around them.
What is the most efficient way to animate all 60 circles for each frame?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
x_data = np.random.randint(80, size=(400, 4))
y_data = np.random.randint(80, size=(400, 4))
fig, ax = plt.subplots(figsize = (8,6))
ax.set_xlim(0,80)
ax.set_ylim(0,80)
scatter = ax.scatter(x_data[0], y_data[0], zorder = 5) #Scatter plot
Player_1 = x_data[0][0], y_data[0][0]
Player_2 = x_data[0][1], y_data[0][1]
Player_1_IR = mpl.patches.Circle(Player_1, radius = 2, color = 'black', lw = 1, alpha = 0.8, zorder = 4)
Player_1_MR = mpl.patches.Circle(Player_1, radius = 4, color = 'gray', lw = 1, alpha = 0.8, zorder = 3)
Player_1_OR = mpl.patches.Circle(Player_1, radius = 6, color = 'lightgrey', lw = 1, alpha = 0.8, zorder = 2)
Player_2_IR = mpl.patches.Circle(Player_2, radius = 2, color = 'black', lw = 1, alpha = 0.8, zorder = 4)
Player_2_MR = mpl.patches.Circle(Player_2, radius = 4, color = 'gray', lw = 1, alpha = 0.8, zorder = 3)
Player_2_OR = mpl.patches.Circle(Player_2, radius = 6, color = 'lightgrey', lw = 1, alpha = 0.8, zorder = 2)
ax.add_patch(Player_1_IR)
ax.add_patch(Player_1_MR)
ax.add_patch(Player_1_OR)
ax.add_patch(Player_2_IR)
ax.add_patch(Player_2_MR)
ax.add_patch(Player_2_OR)
def animate(i) :
scatter.set_offsets(np.c_[x_data[i,:], y_data[i,:]])
ani = animation.FuncAnimation(fig, animate, frames=len(x_data),
interval = 700, blit = False)
plt.show()
You can store all patches that you want to update in a list through which you then iterate through every iteration step. Note that the size of the Circle patches is in data units/coordinates while the scatter plot points are in points (one point = 1/72 inch), which means that the relative size between scatter points and circles depends on the figure size and axes limits and will change when you re-scale the figure.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
x_data = np.random.randint(80, size=(400, 20))
y_data = np.random.randint(80, size=(400, 20))
fig, ax = plt.subplots(figsize = (8,6))
ax.set_xlim(0,80)
ax.set_ylim(0,80)
scatter = ax.scatter(x_data[0], y_data[0], zorder = 5) #Scatter plot
##creating list of patches
players = []
for n in range(10):
##as there are always 3 circles, append all three patches as a list at once
players.append([
mpl.patches.Circle((x_data[0,n],y_data[0,n]), radius = 2, color = 'black', lw = 1, alpha = 0.8, zorder = 4),
mpl.patches.Circle((x_data[0,n],y_data[0,n]), radius = 4, color = 'gray', lw = 1, alpha = 0.8, zorder = 3),
mpl.patches.Circle((x_data[0,n],y_data[0,n]), radius = 6, color = 'lightgrey', lw = 1, alpha = 0.8, zorder = 2)
])
##adding patches to axes
for player in players:
for circle in player:
ax.add_patch(circle)
def animate(i):
scatter.set_offsets(np.c_[x_data[i,:], y_data[i,:]])
##updating players:
for n,player in enumerate(players):
for circle in player:
circle.center = (x_data[i,n],y_data[i,n])
ani = animation.FuncAnimation(fig, animate, frames=len(x_data),
interval = 700, blit = False)
plt.show()
Old Answer (slightly different visual effect, but could be tuned to look the same):
If you really just want circles around your scatter points, you can actually forget about the Circle patches and just overlay several scatter plots with different marker sizes.
In the example below I only mark part of the scatter points with circles by slicing the array of random numbers. Also remember that in scatter plots the marker size is given as points square, so if you want to increase the circle radius from, say, 5 to 6, the given marker size should change from 25 to 36.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
x_data = np.random.randint(80, size=(400, 20))
y_data = np.random.randint(80, size=(400, 20))
fig, ax = plt.subplots(figsize = (8,6))
ax.set_xlim(0,80)
ax.set_ylim(0,80)
scatter = ax.scatter(x_data[0], y_data[0], zorder = 5) #Scatter plot
scatter_IR = ax.scatter(
x_data[0,:10], y_data[0,:10], zorder = 4,
facecolor='black', edgecolor = 'black',
alpha = 0.8, s = 100
)
scatter_MR = ax.scatter(
x_data[0,:10], y_data[0,:10], zorder = 3,
facecolor='grey', edgecolor = 'grey',
alpha = 0.8, s = 225
)
scatter_OR = ax.scatter(
x_data[0,:10], y_data[0,:10], zorder = 2,
facecolor='lightgrey', edgecolor = 'lightgrey',
alpha = 0.8, s = 400
)
def animate(i) :
scatter.set_offsets(np.c_[x_data[i,:], y_data[i,:]])
scatter_IR.set_offsets(np.c_[x_data[i,:10], y_data[i,:10]])
scatter_MR.set_offsets(np.c_[x_data[i,:10], y_data[i,:10]])
scatter_OR.set_offsets(np.c_[x_data[i,:10], y_data[i,:10]])
ani = animation.FuncAnimation(fig, animate, frames=len(x_data),
interval = 700, blit = False)
plt.show()

Legend combined with Text, how to find the legend width and height

I would like to set legend and text boxes locations and styles exactly same, the latter especially to make text aligned.
import matplotlib.pyplot as plt
x = np.arange(10)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(3):
ax.plot(x, i * x ** 2, label = '$y = %i x^2$'%i)
ax.set_title('example plot')
# Shrink the axis by 20% to put legend and text at the bottom
#+ of the figure
vspace = .2
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * vspace,
box.width, box.height * (1 - vspace)])
# Put a legend to the bottom left of the current axis
x, y = 0, 0
# First solution
leg = ax.legend(loc = 'lower left', bbox_to_anchor = (x, y), \
bbox_transform = plt.gcf().transFigure)
# Second solution
#leg = ax.legend(loc = (x, y)) , bbox_transform = plt.gcf().transFigure)
# getting the legend location and size properties using a code line I found
#+ somewhere in SoF
bb = leg.legendPatch.get_bbox().inverse_transformed(ax.transAxes)
ax.text(x + bb.width, y, 'some text', transform = plt.gcf().transFigure, \
bbox = dict(boxstyle = 'square', ec = (0, 0, 0), fc = (1, 1, 1)))
plt.show()
This should place the text at the right of the legend box but that's not what it does. And the two boxes are not vertically aligned.
The second solution does not actually anchoring the legend to the figure, but to the axes instead.
You can use the frame data to get the right width in order to position the Text() object correctly.
In the example below I had to apply a 1.1 factor for the width (this value I haven't found how to get, and if you don't apply the factor the text clashes with the legend).
Note also that you must plt.draw() before getting the right width value.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10)
fig = plt.figure(figsize=(3, 2))
ax = fig.add_subplot(1, 1, 1)
for i in range(3):
ax.plot(x, i*x**2, label=r'$y = %i \cdot x^2$'%i)
ax.set_title('example plot')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
x, y = 0.2, 0.5
leg = ax.legend(loc='lower left', bbox_to_anchor=(x, y),
bbox_transform=fig.transFigure, fontsize=8)
plt.draw()
f = leg.get_frame()
w0, h0 = f.get_width(), f.get_height()
inv = fig.transFigure.inverted()
w, h = inv.transform((w0, h0))
ax.text(x+w*1.1, y+h/2., 'some text', transform=fig.transFigure,
bbox=dict(boxstyle='square', ec=(0, 0, 0), fc=(1, 1, 1)),
fontsize=7)
fig.savefig('test.jpg', bbox_inches='tight')
for x, y = 0.2, 0.5:
for x, y = -0.3, -0.3:

Categories

Resources