aligning axes of different plots in matplotlib - python

I am trying to align these plots so the x axis of the top plot perfectly aligns with the x axis values of the imshow. I'm able to do this by setting the aspect to auto, but then my image is warped. is there a way to do this?
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10, 10, 1200)
y = np.linspace(-20, 20, 1600)
xv, yv = np.meshgrid(x, y)
w = 3
xpos = 0
ypos = 5
z = np.exp(-((xv - xpos)**2 + (yv - ypos)**2) / w**2)
xh = np.linspace(0, 2)
yh = np.sin(xh)
sumvertical = np.sum(z, 0)
xvert = range(np.shape(z)[1])
sumhoriz = np.sum(z, 1)
yhoriz = range(np.shape(z)[0])
# definitions for the axes
left, width = 0.1, 0.65
bottom, height = 0.1, 0.65
bottom_h = left_h = left + width + 0.02
rect_scatter = [left, bottom, width, height]
rect_x = [left, bottom_h, width, 0.2]
rect_y = [left_h, bottom, 0.2, height]
plt.figure(1, figsize=(8, 8))
axCenter = plt.axes(rect_scatter)
axhoriz = plt.axes(rect_x)
axvert = plt.axes(rect_y)
axCenter.imshow(z, origin='lower', cmap='jet') #aspect='auto')
axhoriz.plot(xvert, sumvertical)
axvert.plot(sumhoriz, yhoriz)
plt.show()

I would recommend using the tools from mpl_toolkits.axes_grid1, namely make_axes_locatable to divide the center axes to leave room for the marginal axes.
You should then also set the margins to 0 along the shared direction to have the ranges match up.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
x = np.linspace(-10, 10, 1200)
y = np.linspace(-20, 20, 1600)
xv, yv = np.meshgrid(x, y)
w = 3
xpos = 0
ypos = 5
z = np.exp(-((xv - xpos)**2 + (yv - ypos)**2) / w**2)
xh = np.linspace(0, 2)
yh = np.sin(xh)
sumvertical = np.sum(z, 0)
xvert = range(np.shape(z)[1])
sumhoriz = np.sum(z, 1)
yhoriz = range(np.shape(z)[0])
fig, axCenter = plt.subplots(figsize=(8, 8))
fig.subplots_adjust(.05,.1,.95,.95)
divider = make_axes_locatable(axCenter)
axvert = divider.append_axes('right', size='30%', pad=0.5)
axhoriz = divider.append_axes('top', size='20%', pad=0.25)
axCenter.imshow(z, origin='lower', cmap='jet')
axhoriz.plot(xvert, sumvertical)
axvert.plot(sumhoriz, yhoriz)
axhoriz.margins(x=0)
axvert.margins(y=0)
plt.show()

Related

Matplotlib cmap - custom color definition

I am plotting a graph using matplotlib.
Here is the code:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_title("Grid search results - " + model_name)
ax.set_xlabel("Log10(Wight decay)")
ax.set_ylabel("Log10(Learning rate)")
ax.set_zlabel("Batch size")
ax.set_xticks(weightdecay)
ax.set_yticks(learningrate)
ax.set_zticks(trainbatchsize)
scat_plot = ax.scatter(xs=weightdecay, ys=learningrate, zs=trainbatchsize, c=f1, cmap="bwr")
ax.text(top_score[0], top_score[1], top_score[2], top_score[3], color="black")
cb = plt.colorbar(scat_plot, pad=0.2)
cb.ax.set_xlabel('F1 score')
plt.plot(top_score[0], top_score[1], top_score[2], marker="o", markersize=15, markerfacecolor="yellow")
path = Path(output_dir)
plt.savefig(str(path.absolute()) + '/grid_search_plot_' + model_name + ".pdf")
plt.show()
The graph I am getting looks like:
What I would like to do is to use a more granular color-bar. For example for my F1-score (colour-bar), show in:
color1 scores < 0.5
color2 scores 0.5 - 0.75
color3 scores 0.75 - 0.80
color4 scores 0.8 - 0.85
color5 scores 0.85-1
I was trying to re-use some code to create a custom cmap but nothing was working as expected.
One cheap/quick solution might be to create a "categorical color value", like this:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import ListedColormap
import numpy as np
N = 40
x = np.random.uniform(0, 1, N)
y = np.random.uniform(0, 1, N)
z = np.random.uniform(0, 1, N)
# color values
c = np.random.uniform(0, 1, N)
# new color values
new_col = c.copy()
new_col[c < 0.5] = 0
new_col[(c >= 0.5) & (c < 0.75)] = 1
new_col[(c >= 0.75) & (c < 0.8)] = 2
new_col[(c >= 0.8) & (c < 0.85)] = 3
new_col[c >= 0.85] = 4
new_col = new_col / new_col.max()
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
scatter = ax.scatter(x, y, z, c=new_col, cmap=cm.get_cmap("tab10", 5))
cb = fig.colorbar(scatter)
cb.ax.set_yticklabels([0, 0.5, 0.75, 0.80, 0.85, 1])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_zlim(0, 1)
EDIT to accommodate comments. The following should be able to deal with cases in which a category doesn't have any element:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import ListedColormap
import numpy as np
N = 40
x = np.random.uniform(0, 1, N)
y = np.random.uniform(0, 1, N)
z = np.random.uniform(0, 1, N)
# color values
c = np.random.uniform(0, 1, N)
# number of categories
NC = 5
# new color values
new_col = c.copy()
new_col[c < 0.5] = 0
new_col[(c >= 0.5) & (c < 0.75)] = 1
new_col[(c >= 0.75) & (c < 0.8)] = 2
new_col[(c >= 0.8) & (c < 0.85)] = 3
new_col[c >= 0.85] = 4
new_col = new_col / NC
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
cmap = ListedColormap(["red", "green", "blue", "magenta", "cyan"])
scatter = ax.scatter(x, y, z, c=cmap(new_col))
cb = fig.colorbar(cm.ScalarMappable(cmap=cmap))
cb.ax.set_yticks(np.linspace(0, 1, NC+1), [0, 0.5, 0.75, 0.80, 0.85, 1])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_zlim(0, 1)

Make a sphere from a circle of dots

I have a circle a circle from dots
Now I need make a sphere.
Could someone help me with it.
I think i have to use x2 + y2 + z2 <= R2 and use Axes3D module.
import numpy
import matplotlib.pyplot as plt
X = list(range(1, 101))
Y = list(range(1, 101))
x = numpy.array(X)
y = numpy.array(Y)
xgrid, ygrid = numpy.meshgrid(x, y)
plt.style.use('seaborn')
fig, ax = plt.subplots()
filter = (xgrid-50)**2 + (ygrid-50)**2 <= 25**2
ax.scatter(xgrid[filter], ygrid[filter], s= 1, color='green')
ax.set_title('сетка из точек 100х100',
fontfamily = 'monospace',
fontstyle = 'normal',
fontweight = 'bold',
fontsize = 10)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
ax.tick_params(axis='both', which='major', labelsize=14)
ax.axis([0, 101, 0, 101])
plt.show()
Is this what you're looking for? I don't recommend plotting that much points though, it is quite heavy, try plotting less points (like in the code comments)...
Also try not using 'filter', it is a reserved keyword in Python
import numpy as np
import matplotlib.pyplot as plt
# define sphere parameters
cX, cY, cZ = 50, 50, 50
radius = 25
x = np.array(range(0, 101))
y = np.array(range(0, 101))
z = np.array(range(0, 101))
# try that instead, it is less heavy
# x = np.array(range(0, 101, 5))
# y = np.array(range(0, 101, 5))
# z = np.array(range(0, 101, 5))
xgrid, ygrid, zgrid = np.meshgrid(x, y, z)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
check = (xgrid - cX)**2 + (ygrid - cY)**2 + (zgrid - cZ)**2 <= radius**2
ax.scatter(xgrid[check], ygrid[check], zgrid[check], color='green')
ax.set_title('сетка из точек 100х100',
fontfamily = 'monospace',
fontstyle = 'normal',
fontweight = 'bold',
fontsize = 10)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
ax.set_xlim3d(0, 101)
ax.set_ylim3d(0, 101)
ax.set_zlim3d(0, 101)
plt.show()

Rotating Rectangles around point with matplotlib

I wanted to rotate Rectangles with matplotlib, but the normal patch always rotates around the lower left corner of the Rectangle. Is there a way to describe more general transformations? For Example I want to rotate about the mid point on the shorter side of the rectangle, so that the result looks like the motion of a clock hand.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
height = 0.1
width = 1
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim([-width * 1.2, width * 1.2])
ax.set_ylim([-width * 1.2, width * 1.2])
ax.plot(0, 0, color='r', marker='o', markersize=10)
for deg in range(0, 360, 45):
rec = Rectangle((0, 0), width=width, height=height,
angle=deg, color=str(deg / 360), alpha=0.9)
ax.add_patch(rec)
Same as the other answer, just without subclassing and using private attributes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
height = 0.1
width = 1
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim([-width * 1.2, width * 1.2])
ax.set_ylim([-width * 1.2, width * 1.2])
ax.plot(0, 0, color='r', marker='o', markersize=10)
point_of_rotation = np.array([0, height/2]) # A
# point_of_rotation = np.array([width/2, height/2]) # B
# point_of_rotation = np.array([width/3, height/2]) # C
# point_of_rotation = np.array([width/3, 2*height]) # D
for deg in range(0, 360, 45):
rec = plt.Rectangle(-point_of_rotation, width=width, height=height,
color=str(deg / 360), alpha=0.9,
transform=Affine2D().rotate_deg_around(*(0,0), deg)+ax.transData)
ax.add_patch(rec)
plt.show()
I created a custom Rectangle class which has as additional argument the relative point of rotation - relative means measured in the frame defined by the original lower left coordinates of the rectangle (x0, y0) and the angle.
rec = RotatingRectangle((0, 0), width=1, height=0.1, rel_point_of_rot=(0, 0.05), angle=45)
creates an rectangle where the the relative point (0, 0.05) [the midpoint of the left side] is shifted to the center (0, 0) and than a rotation of 45 deg is performed around this point.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
class RotatingRectangle(Rectangle):
def __init__(self, xy, width, height, rel_point_of_rot, **kwargs):
super().__init__(xy, width, height, **kwargs)
self.rel_point_of_rot = rel_point_of_rot
self.xy_center = self.get_xy()
self.set_angle(self.angle)
def _apply_rotation(self):
angle_rad = self.angle * np.pi / 180
m_trans = np.array([[np.cos(angle_rad), -np.sin(angle_rad)],
[np.sin(angle_rad), np.cos(angle_rad)]])
shift = -m_trans # self.rel_point_of_rot
self.set_xy(self.xy_center + shift)
def set_angle(self, angle):
self.angle = angle
self._apply_rotation()
def set_rel_point_of_rot(self, rel_point_of_rot):
self.rel_point_of_rot = rel_point_of_rot
self._apply_rotation()
def set_xy_center(self, xy):
self.xy_center = xy
self._apply_rotation()
height = 0.1
width = 1
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.set_xlim([-width * 1.2, width * 1.2])
ax.set_ylim([-width * 1.2, width * 1.2])
ax.plot(0, 0, color='r', marker='o', markersize=10)
point_of_rotation = np.array([0, height/2]) # A
# point_of_rotation = np.array([width/2, height/2]) # B
# point_of_rotation = np.array([width/3, height/2]) # C
# point_of_rotation = np.array([width/3, 2*height]) # D
for deg in range(0, 360, 45):
rec = RotatingRectangle((0, 0), width=width, height=height,
rel_point_of_rot=point_of_rotation,
angle=deg, color=str(deg / 360), alpha=0.9)
ax.add_patch(rec)

Color map to shades of blue - python

I have the following (example) code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)
norm = mpl.colors.Normalize(vmin=c.min(), vmax=c.max())
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.jet)
cmap.set_array([])
fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap.to_rgba(i + 1))
fig.colorbar(cmap, ticks=c)
plt.show();
I would like to find a substirute for cmap.to_rgba that makes the colour of each line come out as a differnet shade of blue. Basically I want to keep the same layout as the result of this code, but using the colour map Blues.
How can I do it?
You need to change your colormap that you are using from jet to Blues.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)
norm = mpl.colors.Normalize(vmin=c.min(), vmax=c.max())
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.Blues)
cmap.set_array([])
fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap.to_rgba(i + 1))
fig.colorbar(cmap, ticks=c)
plt.show()
This produces:

Matplotlib : display array values with imshow

I'm trying to create a grid using a matplotlib function like imshow.
From this array:
[[ 1 8 13 29 17 26 10 4],
[16 25 31 5 21 30 19 15]]
I would like to plot the value as a color AND the text value itself (1,2, ...) on the same grid. This is what I have for the moment (I can only plot the color associated to each value):
from matplotlib import pyplot
import numpy as np
grid = np.array([[1,8,13,29,17,26,10,4],[16,25,31,5,21,30,19,15]])
print 'Here is the array'
print grid
fig1, (ax1, ax2)= pyplot.subplots(2, sharex = True, sharey = False)
ax1.imshow(grid, interpolation ='none', aspect = 'auto')
ax2.imshow(grid, interpolation ='bicubic', aspect = 'auto')
pyplot.show()
You want to loop over the values in grid, and use ax.text to add the label to the plot.
Fortunately, for 2D arrays, numpy has ndenumerate, which makes this quite simple:
for (j,i),label in np.ndenumerate(grid):
ax1.text(i,j,label,ha='center',va='center')
ax2.text(i,j,label,ha='center',va='center')
If for any reason you have to use a different extent from the one that is provided naturally by imshow the following method (even if more contrived) does the job:
size = 4
data = np.arange(size * size).reshape((size, size))
# Limits for the extent
x_start = 3.0
x_end = 9.0
y_start = 6.0
y_end = 12.0
extent = [x_start, x_end, y_start, y_end]
# The normal figure
fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111)
im = ax.imshow(data, extent=extent, origin='lower', interpolation='None', cmap='viridis')
# Add the text
jump_x = (x_end - x_start) / (2.0 * size)
jump_y = (y_end - y_start) / (2.0 * size)
x_positions = np.linspace(start=x_start, stop=x_end, num=size, endpoint=False)
y_positions = np.linspace(start=y_start, stop=y_end, num=size, endpoint=False)
for y_index, y in enumerate(y_positions):
for x_index, x in enumerate(x_positions):
label = data[y_index, x_index]
text_x = x + jump_x
text_y = y + jump_y
ax.text(text_x, text_y, label, color='black', ha='center', va='center')
fig.colorbar(im)
plt.show()
If you want to put other type of data and not necessarily the values that you used for the image you can modify the script above in the following way (added values after data):
size = 4
data = np.arange(size * size).reshape((size, size))
values = np.random.rand(size, size)
# Limits for the extent
x_start = 3.0
x_end = 9.0
y_start = 6.0
y_end = 12.0
extent = [x_start, x_end, y_start, y_end]
# The normal figure
fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111)
im = ax.imshow(data, extent=extent, origin='lower', interpolation='None', cmap='viridis')
# Add the text
jump_x = (x_end - x_start) / (2.0 * size)
jump_y = (y_end - y_start) / (2.0 * size)
x_positions = np.linspace(start=x_start, stop=x_end, num=size, endpoint=False)
y_positions = np.linspace(start=y_start, stop=y_end, num=size, endpoint=False)
for y_index, y in enumerate(y_positions):
for x_index, x in enumerate(x_positions):
label = values[y_index, x_index]
text_x = x + jump_x
text_y = y + jump_y
ax.text(text_x, text_y, label, color='black', ha='center', va='center')
fig.colorbar(im)
plt.show()

Categories

Resources