Annotating matplotlib heatmap - python

I created a simple heatmap on Matplotlib on a already existing image, now i'm trying to show the values on the cells, but the problem is that the values won't go inside the heatmap, but all around the image, here is a screenshot.
I think this happens because i'm generating the heatmap on top of an image, but i don't know how to fix that. Here is my code:
fig,ax = plt.subplots(1)
ax.imshow(im)
a = [[0.0233188 0.0232844 0.0233099 0.0242786 ]
[0.0233158 0.023217 0.02370096 0.02434176]
[0.02328474 0.02319508 0.02433976 0.02290478]
[0.02320107 0.02345002 0.02484117 0.02355316]
[0.02317872 0.02374418 0.02374605 0.02157998]]
ax1 = fig.add_subplot(111)
bounds1 = sorted([0.023, np.amin(a), np.amax(a)])
norm1 = matplotlib.colors.TwoSlopeNorm(vcenter=bounds1[1], vmin=bounds1[0], vmax=bounds1[2])
Map = ax1.imshow(a, interpolation='none', norm=norm1, extent=[0, 1.15, 0, 0.85])
x1 = [1, 2, 3, 4]
y1 = [1, 2, 3, 4, 5]
for i in range(len(y1)):
for j in range(len(x1)):
text = ax1.text(j, i, a[i, j],
ha="center", va="center", color="r")

extent=[x0, x1, y0, y1] changes the x and y coordinates of the image. When there are N cells between x0 and x1, the cell centers can be found by splitting the distance into 2N+1 parts and taking the 1st, 3rd, 5th, ... position of that list.
Note that as imshow(a, ...) didn't use origin='lower', the values are reversed. So, for the y-positions need to be traversed in reverse order.
from matplotlib import pyplot as plt
import matplotlib
import numpy as np
fig, ax = plt.subplots()
ax.axis('off')
a = np.array([[0.0233188, 0.0232844, 0.0233099, 0.0242786],
[0.0233158, 0.023217, 0.02370096, 0.02434176],
[0.02328474, 0.02319508, 0.02433976, 0.02290478],
[0.02320107, 0.02345002, 0.02484117, 0.02355316],
[0.02317872, 0.02374418, 0.02374605, 0.02157998]])
ax1 = fig.add_subplot(111)
bounds1 = sorted([0.023, np.amin(a), np.amax(a)])
norm1 = matplotlib.colors.TwoSlopeNorm(vcenter=bounds1[1], vmin=bounds1[0], vmax=bounds1[2])
x0, x1, y0, y1 = 0, 1.15, 0, 0.85
Map = ax1.imshow(a, interpolation='none', norm=norm1, extent=[x0, x1, y0, y1])
for i, yi in enumerate(np.linspace(y0, y1, 2 * a.shape[0] + 1)[-2::-2]):
for j, xj in enumerate(np.linspace(x0, x1, 2 * a.shape[1] + 1)[1::2]):
text = ax1.text(xj, yi, f'{a[i, j]:.6f}',
ha="center", va="center", color='darkred' if a[i, j] > bounds1[1] else 'white', fontsize=10)
plt.show()

Related

Matploilib 3D How to Fill Color between Polygons

I have a 3D Beam Section with a void (starting with 2D Polygons codes for both external polygon and hollow polygon) and I need to draw that in Matplotlib 3D space. I am adding z cordinate based on the legnth of the beam and filtering all ploygons involved first and drawn them in Matplotlib 3D space with follwing code. At the momnent I was able to fill voids(see attached pic) but that's not what I wanted.
How I can chnage the verts to fill in between voids and external polygons (I meant solid part)
import numpy as np
from sympy import Line, Polygon as Polyg
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
shell=[(-500,0), (500,0), (400,500), (-400,500), (-500,0)]
hole1= [(-200,100), (200,100), (300,400), (-300,400), (-200,100)]
length =20000
ex = np.array(shell)
int_=np.array(hole1)
#hole2=[(-400,50), (-200,50), (-200,100), (-400,100), (-400,50)]
#holes=(hole1,hole2)
# insert Z cordinate for external polygon
polye1 = np.insert(ex, 2, 0, axis=1)
polye2 = np.insert(ex, 2, length, axis=1)
polyi1 = np.insert(int_, 2, 0, axis=1)
polyi2 = np.insert(int_, 2, length, axis=1)
vertices_e = np.dstack((polye1, polye2))
vertices_i = np.dstack((polyi1, polyi2))
polygons = []
polygonsi = []
for i in np.arange(vertices_e.shape[0]) - 1:
polygons.append(np.array([vertices_e[i, :, 1],
vertices_e[i + 1, :, 1],
vertices_e[i + 1, :, 0],
vertices_e[i, :, 0]]))
polygons.append(polye1)
polygons.append(polye2)
for i in np.arange(vertices_i.shape[0]) - 1:
polygonsi.append(np.array([vertices_i[i, :, 1],
vertices_i[i + 1, :, 1],
vertices_i[i + 1, :, 0],
vertices_i[i, :, 0]]))
polygonsi.append(polyi1)
polygonsi.append(polyi2)
A=polygons
B=polygonsi
fig = plt.figure()
ax = Axes3D(fig, auto_add_to_figure=False)
ax.set_box_aspect([1, 1, 1])
fig.add_axes(ax)
for i in range(0, len(A)):
x, y, z = zip(*A[i])
x1 = [*(x)]
y1 = [*(y)]
z1 = [*(z)]
ax.plot(x1, z1, y1, 'b') # interchange the axis to get correct oriantation
for i in range(0, len(B)):
xi, yi, zi = zip(*B[i])
x2 = [*(xi)]
y2 = [*(yi)]
z2 = [*(zi)]
ax.plot(x2, z2, y2, 'r') # interchange the axis to get correct oriantation
verts = [list(zip(x2, z2, y2))] # this is important variable to fill in between later
ax.add_collection3d(Poly3DCollection(verts, facecolors='cyan', linewidths=1, edgecolors='b', alpha=.25))
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.view_init(30, 120)
plt.show()

Matplotlib, plot a vector of numbers as a rectangle filled with numbers

So let's say I have a vector of numbers.
np.random.randn(5).round(2).tolist()
[2.05, -1.57, 1.07, 1.37, 0.32]
I want a draw a rectangle that shows this elements as numbers in a rectangle.
Something like this:
Is there an easy way to do this in matplotlib?
A bit convoluted but you could take advantage of seaborn.heatmap, creating a white colormap:
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
data = np.random.randn(5).round(2).tolist()
linewidth = 2
ax = sns.heatmap([data], annot=True, cmap=LinearSegmentedColormap.from_list('', ['w', 'w'], N=1),
linewidths=linewidth, linecolor='black', square=True,
cbar=False, xticklabels=False, yticklabels=False)
plt.tight_layout()
plt.show()
In this case, the external lines won't be as thick as the internal ones. If needed, this can be fixed with:
ax.axhline(y=0, color='black', lw=linewidth*2)
ax.axhline(y=1, color='black', lw=linewidth*2)
ax.axvline(x=0, color='black', lw=linewidth*2)
ax.axvline(x=len(data), color='black', lw=linewidth*2)
Edit: avoid these lines and add clip_on=False to sns.heatmap (thanks/credit #JohanC)
Output:
We can add rectangles , and annotate them in a for loop.
from matplotlib import pyplot as plt
import numpy as np
# Our numbers
nums = np.random.randn(5).round(2).tolist()
# rectangle_size
rectangle_size = 2
# We want rectangles look squared, you can change if you want
plt.rcParams["figure.figsize"] = [rectangle_size * len(nums), rectangle_size]
plt.rcParams["figure.autolayout"] = True
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(len(nums)):
# We are adding rectangles
# You can change colors as you wish
plt.broken_barh([(rectangle_size * i, rectangle_size)], (0, rectangle_size), facecolors='white', edgecolor='black'
,linewidth = 1)
# We are calculating where to annotate numbers
cy = rectangle_size / 2.0
cx = rectangle_size * i + cy
# Annotation You can change color,font, etc ..
ax.annotate(str(nums[i]), (cx, cy), color='black', weight='bold', fontsize=20, ha='center', va='center')
# For squared look
plt.xlim([0, rectangle_size*len(nums)])
plt.ylim([0, rectangle_size])
# We dont want to show ticks
plt.axis('off')
plt.show()
One way using the Rectangle patch is:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.patches import Rectangle
x = np.random.randn(5).round(2).tolist()
fig, ax = plt.subplots(figsize=(9, 2)) # make figure
dx = 0.15 # edge size of box
buf = dx / 10 # buffer around edges
# set x and y limits
ax.set_xlim([0 - buf, len(x) * dx + buf])
ax.set_ylim([0 - buf, dx + buf])
# set axes as equal and turn off axis lines
ax.set_aspect("equal")
ax.axis("off")
# draw plot
for i in range(len(x)):
# create rectangle with linewidth=4
rect = Rectangle((dx * i, 0), dx, dx, facecolor="none", edgecolor="black", lw=4)
ax.add_patch(rect)
# get text position
x0, y0 = dx * i + dx / 2, dx / 2
# add text
ax.text(
x0, y0, f"{x[i]}", color="black", ha="center", va="center", fontsize=28, fontweight="bold"
)
fig.tight_layout()
fig.show()
which gives:

plotting data on a hexagonal figure

I want to build a graph that will look like this, - for each point I have a single value and there is a maximum that reaches the border.
All I can find is how to have hexbin in a scatterplot with seaborn or similar - any ideas, is there some ready solution maybe or I would need to code my way through it?
You could use tripcolor to show 6 shaded triangles. Scaling the outer vectors can adapt the triangles to show the desired proportions.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as tri
proportions = [0.6, 0.75, 0.8, 0.9, 0.7, 0.8]
labels = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta']
N = len(proportions)
proportions = np.append(proportions, 1)
theta = np.linspace(0, 2 * np.pi, N, endpoint=False)
x = np.append(np.sin(theta), 0)
y = np.append(np.cos(theta), 0)
triangles = [[N, i, (i + 1) % N] for i in range(N)]
triang_backgr = tri.Triangulation(x, y, triangles)
triang_foregr = tri.Triangulation(x * proportions, y * proportions, triangles)
cmap = plt.cm.rainbow_r # or plt.cm.hsv ?
colors = np.linspace(0, 1, N + 1)
plt.tripcolor(triang_backgr, colors, cmap=cmap, shading='gouraud', alpha=0.4)
plt.tripcolor(triang_foregr, colors, cmap=cmap, shading='gouraud', alpha=0.8)
plt.triplot(triang_backgr, color='white', lw=2)
for label, color, xi, yi in zip(labels, colors, x, y):
plt.text(xi * 1.05, yi * 1.05, label, # color=cmap(color),
ha='left' if xi > 0.1 else 'right' if xi < -0.1 else 'center',
va='bottom' if yi > 0.1 else 'top' if yi < -0.1 else 'center')
plt.axis('off')
plt.gca().set_aspect('equal')
plt.show()
The code allows for different numbers of triangles. Here are examples with 5 or 6 triangles:

Advanced horizontal bar chart with Python?

I want to make a graph like the two below.
How can I achieve that with python? I am sorry that I can´t provide any implementation because I don´t have any idea at all. I think my question is something different to this.
https://matplotlib.org/gallery/lines_bars_and_markers/barh.html#sphx-glr-gallery-lines-bars-and-markers-barh-py
Could someone give me some suggestions with just some simple numbers?
The tutorial for vertical gradient bars can be adapted to draw horizontal bars with the darkest spot in the middle:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.colors as mcolors
import numpy as np
def hor_gradient_image(ax, extent, darkest, **kwargs):
'''
puts a horizontal gradient in the rectangle defined by extent (x0, x1, y0, y1)
darkest is a number between 0 (left) and 1 (right) setting the spot where the gradient will be darkest
'''
ax = ax or plt.gca()
img = np.interp(np.linspace(0, 1, 100), [0, darkest, 1], [0, 1, 0]).reshape(1, -1)
return ax.imshow(img, extent=extent, interpolation='bilinear', vmin=0, vmax=1, **kwargs)
def gradient_hbar(y, x0, x1, ax=None, height=0.8, darkest=0.5, cmap=plt.cm.PuBu):
hor_gradient_image(ax, extent=(x0, x1, y - height / 2, y + height / 2), cmap=cmap, darkest=darkest)
rect = mpatches.Rectangle((x0, y - height / 2), x1 - x0, height, edgecolor='black', facecolor='none')
ax.add_patch(rect)
# cmap = mcolors.LinearSegmentedColormap.from_list('turq', ['paleturquoise', 'darkturquoise'])
cmap = mcolors.LinearSegmentedColormap.from_list('turq', ['#ACFAFA', '#3C9E9E'])
fig, ax = plt.subplots()
for y in range(1, 11):
x0, x1 = np.sort(np.random.uniform(1, 9, 2))
gradient_hbar(y, x0, x1, ax=ax, height=0.7, darkest=0.5, cmap=cmap)
ax.set_aspect('auto')
ax.use_sticky_edges = False
ax.autoscale(enable=True, tight=False)
ax.grid(axis='x')
plt.show()

Plot a 3D Boundary Decision in Python

I'm trying to plot a 3D Decision Boundary, but it does not seem to be working the way it looks, see how it is:
I want it to appear as in this example here:
I do not know how to explain, but in the example above it literally looks like a "wall". And this is what I want to do in my code.
Then follow my code:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_title('Hello World')
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
w = [3,2,1]
x = 1
y = 1
z = 1
x_plan = (- w[1] * y - w[2] * z) / w[0]
y_plan = (- w[0] * x - w[2] * z) / w[1]
z_plan = (- w[0] * x - w[1] * y) / w[2]
ax.plot3D([x_plan, 1, 1], [1, y_plan, 1], [1, 1, z_plan], "lightblue")
plt.show()
P.S.: I'm using:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
I believe that the problem should be in the calculation, or else in the:
ax.plot3D([x_plan, 1, 1], [1, y_plan, 1], [1, 1, z_plan], "lightblue")
P.S.2: I know that my Boundary Decision is not separating the data correctly, but at the moment this is a detail for me, later I will fix it.
To plot a 3d surface you actually need to use plt3d.plot_surface, see reference.
As an example, this piece of code will generate the following image (Notice the comment on plt3d.plot_surface line):
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
def randrange(n, vmin, vmax):
'''
Helper function to make an array of random numbers having shape (n, )
with each number distributed Uniform(vmin, vmax).
'''
return (vmax - vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 10
for c, m, zlow, zhigh in [('r', 'o', 0, 100)]:
xs = randrange(n, 0, 50)
ys = randrange(n, 0, 50)
zs = randrange(n, zlow, zhigh)
ax.scatter(xs, ys, zs, c=c, marker=m)
for c, m, zlow, zhigh in [('b', '^', 0, 100)]:
xs = randrange(n, 60, 100)
ys = randrange(n, 60, 100)
zs = randrange(n, zlow, zhigh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
xm,ym = np.meshgrid(xs, ys)
ax.plot_surface(xm, ym, xm, color='green', alpha=0.5) # Data values as 2D arrays as stated in reference - The first 3 arguments is what you need to change in order to turn your plane into a boundary decision plane.
plt.show()

Categories

Resources