Center specified tick labels for matplotlib's pcolomesh at the boxes - python

I do not understand, how to properly plot my heatmap (pcolormesh) with matplotlib. I want the tick's labels be centered below/beside the corresponding boxes - and only my given data, not some artificially extended ranges.
In the docs I found an example, which works slightly modified to floats just fine for me.
Z = []
for i in range(1, 7):
Z.append([j*i for j in range(1, 11)])
Z = np.asarray(Z)
x = np.linspace(0.1, 1.0, num=10)
y = np.linspace(0.1, 0.6, num=6)
fig, ax = plt.subplots()
ax.pcolormesh(x, y, Z, vmin=np.min(Z), edgecolors='w', linewidths=0.5, vmax=np.max(Z), shading='auto')
plt.show()
The result prints the ticks centered at the boxes, which is exactly what I want.
But as soon as I use my own data it ends up with some weird behaviour
data = pd.DataFrame(index=[0, 0.25, 0.5], data={0: [31.40455938, 101.43291831, 101.67128077], 0.25: [31.40455938, 89.81448724, 99.65066293], 0.5: [31.40455938, 57.01406046, 101.47536496]})
x = data.columns.astype(np.float64).to_numpy()
y = data.index.astype(np.float64).to_numpy()
z = data.to_numpy()
cmap = LinearSegmentedColormap.from_list('G2R', ["green", "red"])
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z, shading='auto', cmap=cmap, edgecolors='w', linewidths=0.5, vmin=0, vmax=100) # shading='gouraud'
ax.set_title('not what i want')
ax.set_xlabel('X')
ax.set_ylabel('Y')
plt.show()
How do I get my heatmap to simply plot the given floats as centered tick labels without those unwanted floats? I literally want to specify my tick labels (floats or strings) to be shown centered to the boxes. I would assume there must be a way, to specify a list or array as tick labels. How do I accomplish that?

After plotting the pcolormesh you can set x and y tick with matplotlib.axes.Axes.set_xticks and matplotlib.axes.Axes.set_yticks respectively:
ax.pcolormesh(x, y, z, shading='auto', cmap=cmap, edgecolors='w', linewidths=0.5, vmin=0, vmax=100) # shading='gouraud'
ax.set_xticks(data.columns)
ax.set_yticks(data.index)

Related

Increasing size of 3d surface plot with matplotlib

Picture of Plot
This should really not be this difficult. I am plotting a 3d surface plot from an array. The code looks like this:
z = arr
y = np.arange(len(z))
x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
plt.figure(figsize=(100,100))
ax.plot_surface(x,y,z, cmap=cm.coolwarm)
ax.set_xlabel("Bonus to AC")
ax.set_ylabel("Current AC")
ax.set_zlabel("Reduction in Damage")
plt.show()
It does not matter if I set the fig size to 10,10 or 1000,1000, the image still shows up the same size.
What kind of works is adding subplots,
ax = fig.add_subplot(211, projection='3d')
but this splits it up into one okay plot and one empty plot. Not sure how to use the subplots function.
you are referencing ax from a different figure than the one produced by plt.figure
you should instead use ax= fig.add_subplot after you assign fig= plt.figure as follows.
z = np.ones((100,100))
y = np.arange(len(z))
x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,z)
ax.set_xlabel("Bonus to AC")
ax.set_ylabel("Current AC")
ax.set_zlabel("Reduction in Damage")
plt.show()
note i just swapped the z=np.ones((100,100)) in the first line so viewers can get this working.

Plotting Points on Matplotlib Colored Grid

I am working with the matplotlib library to generate colored graphs which need to have specific points overlayed on top of them. After messing around with matplotlib, I came up with a method to properly color my grid, however I am unable to plot points manually.
def generate_grid(x, y, data):
fig, ax = plt.subplots(1, 1, tight_layout=True)
my_cmap = matplotlib.colors.ListedColormap(['grey'])
my_cmap.set_bad(color='w', alpha=0)
for x in range(x + 1):
ax.axhline(x, lw=2, color='k', zorder=5)
for y in range(y+1):
ax.axvline(y, lw=2, color='k', zorder=5)
ax.imshow(data, interpolation='none', cmap=my_cmap,
extent=[0, y, 0, x], zorder=0)
plt.locator_params(axis="x", nbins=x+1)
plt.locator_params(axis="y", nbins=y+1)
locs, labels = plt.xticks()
labels = [int(item)+1 for item in locs]
plt.xticks(locs, labels)
locs, labels = plt.yticks()
z = len(locs)
labels = [z-int(item) for item in locs]
plt.yticks(locs, labels)
ax.xaxis.tick_top()
plt.show()
How would I go about plotting a point at any given location ie at (4,2) or (2,1)?
You may simply use the scatter method from within your generate_grid function, for instance, immediately before plt.show().
However, note that if you simply use ax.scatter(2,1, s=50) the symbol will end up under your grid.
You need to play with the zorder parameter to ensure that it appears over the grid. For instance ax.scatter(2,1, s=50, zorder=50) did the trick for me:

How to invert axes of a function with matplotlib

I have the following function, what I am looking for is to invert the x and y axes.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
major_ticks = np.arange(-2, 10+2, 1)
minor_ticks = np.arange(-2, 10+2, 0.25)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, minor=True)
ax.set_yticks(major_ticks)
ax.set_yticks(minor_ticks, minor=True)
ax.grid(which='both')
ax.grid(which='minor', alpha=0.2)
ax.grid(which='major', alpha=0.5)
plt.title('Image')
plt.xlabel('Height (m)')
plt.ylabel('Length (m)')
plt.axis('equal')
function = np.array([0, 0.52, 0.92, 0.8])
plt.plot(function)
The following graph illustrates how it should look, best regards.
The plot command lets you specify both x and y but when you call it with a single vector, it assumes the first vector is a list of ints. So to invert the plot, create the default x and then swap x and y.
plt.plot(function, np.arange(4), '.-') # function is now treated as x, and I've created the default x and am using it for y.
Also, I renamed the axes and removed most of the formatting so this looked right, but the only interesting change is the plot command.

Matplotlib - Move text label right by 'x' points

I have the following code which produces a bubble chart, and then adds the labels as text to the plot:
fig, ax = plt.subplots(figsize = (5,10))
# create data
x = [1,1,1,1,1,1,1,1,1,1]
y = ['A','B','C','D',
'E','F','G','H','I','']
z = [10,20,80,210,390,1050,2180,4690,13040,0]
labels = [1,2,8,21,39,105,218,469,1304]
plt.xlim(0.9,1.1)
for i, txt in enumerate(labels):
ax.annotate(txt, (x[i], y[i]), ha='center', va='center', )
plt.scatter(x, y, s=z*4000, c="#8C4799", alpha=0.3)
I have the text labels centered vertically and horizontally (i.e. the 1304,469 etc), but ideally I want it shifted to the right so it is away from the bubble. I have tried ha=right, but it only nudges it a tiny bit.
Is there anything I can use to move it completely away from the bubble? I.e. code I can put it the following for loop:
for i, txt in enumerate(labels):
ax.annotate(txt, (x[i], y[i]), ha='center', va='center', )
Since the size s of the bubbles is s=z*4000, a bubble's radius is np.sqrt(z*4000)/2. (For an explanation see scatter plot marker size).
You would hence create an annotation which is positionned at the center of the bubbles in data coordinates and offset it by np.sqrt(z*4000)/2 in units of points (or possibly 2 or 3 points more to have it look nicely).
This would be done using
annotate("text", xy=(x[i],y[i]),
xytext=(np.sqrt(z[i]*4000)/2+2, 0), textcoords="offset points")
Complete example:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize = (5,10))
# create data
x = [1,1,1,1,1,1,1,1,1,1]
y = ['A','B','C','D',
'E','F','G','H','I','']
z = [10,20,80,210,390,1050,2180,4690,13040,0]
labels = [1,2,8,21,39,105,218,469,1304]
plt.xlim(0.9,1.1)
sc = plt.scatter(x, y, s=z*4000, c="#8C4799", alpha=0.3)
for txt, size, xi, yi in zip(labels, sc.get_sizes(), x,y):
ax.annotate(txt, xy=(xi,yi), xytext=(np.sqrt(size)/2+2, 0),
textcoords="offset points",
ha='left', va='center', )
plt.show()
I would simply use an offset percentage (20% for example) to reposition the x-coordinate of the text. Additionally you can turn off the manual setting of x-limits.
fig, ax = plt.subplots(figsize=(4, 10))
x = [1,1,1,1,1,1,1,1,1,1]
y = ['A','B','C','D',
'E','F','G','H','I','']
z = [10,20,80,210,390,1050,2180,4690,13040,0]
labels = [1,2,8,21,39,105,218,469,1304]
for i, txt in enumerate(labels):
ax.annotate(txt, (x[i]*1.2, y[i]), ha='center', va='center', )
plt.scatter(x, y, s=z*4000, c="#8C4799", alpha=0.3)
the parameter xytext of ax.annotate lets you do this:
fig, ax = plt.subplots(figsize = (5,10))
# create data
x = [1,1,1,1,1,1,1,1,1,1]
y = ['A','B','C','D',
'E','F','G','H','I','']
z = [10,20,80,210,390,1050,2180,4690,13040,0]
labels = [1,2,8,21,39,105,218,469,1304]
plt.xlim(0.9,1.1)
for i, txt in enumerate(labels):
ax.annotate(txt, (x[i], y[i]), ha='center', va='center', xytext=(1.05,y[i]) )
plt.scatter(x, y, s=z*4000, c="#8C4799", alpha=0.3)
Brings this:
Edit: if you want the labels to be just to the right of every circle, you'll have to create an array of positions and then loop through it

Force square subplots when plotting a colorbar

I'm trying to generate two subplots side by side, sharing the y axis, with a single colorbar for both.
This is a MWE of my code:
import matplotlib.pyplot as plt
import numpy as np
def rand_data(l, h):
return np.random.uniform(low=l, high=h, size=(100,))
# Generate data.
x1, x2, y, z = rand_data(0., 1.), rand_data(100., 175.), \
rand_data(150., 200.), rand_data(15., 33.)
fig = plt.figure()
cm = plt.cm.get_cmap('RdYlBu')
ax0 = plt.subplot(121)
plt.scatter(x1, y, c=z, cmap=cm)
ax1 = plt.subplot(122)
# make these y tick labels invisible
plt.setp(ax1.get_yticklabels(), visible=False)
plt.scatter(x2, y, c=z, cmap=cm)
cbar = plt.colorbar()
plt.show()
what this returns is a left subplot slightly larger horizontally than the right one since this last includes the colorbar, see below:
I've tried using ax.set_aspect('equal') but since the x axis are not in the same range the result looks awful.
I need both these plots to be displayed squared. How can I do this?
To expend my comment that one can make 3 plots, plot the colorbar() in the 3rd one, the data plots in the 1st and 2nd. This way, if necessary, we are free to do anything we want to the 1st and 2nd plots:
def rand_data(l, h):
return np.random.uniform(low=l, high=h, size=(100,))
# Generate data.
x1, x2, y, z = rand_data(0., 1.), rand_data(100., 175.), \
rand_data(150., 200.), rand_data(15., 33.)
fig = plt.figure(figsize=(12,6))
gs=gridspec.GridSpec(1,3, width_ratios=[4,4,0.2])
ax1 = plt.subplot(gs[0])
ax2 = plt.subplot(gs[1])
ax3 = plt.subplot(gs[2])
cm = plt.cm.get_cmap('RdYlBu')
ax1.scatter(x1, y, c=z, cmap=cm)
SC=ax2.scatter(x2, y, c=z, cmap=cm)
plt.setp(ax2.get_yticklabels(), visible=False)
plt.colorbar(SC, cax=ax3)
plt.tight_layout()
plt.savefig('temp.png')
Updated - here is another option without using GridSpec.
import numpy as np
import matplotlib.pyplot as plt
N = 50
x_vals = np.random.rand(N)
y_vals = np.random.rand(N)
z1_vals = np.random.rand(N)
z2_vals = np.random.rand(N)
minimum_z = min(np.min(z1_vals), np.min(z2_vals))
maximum_z = max(np.max(z1_vals), np.max(z2_vals))
fig, axis_array = plt.subplots(1,2, figsize = (20, 10), subplot_kw = {'aspect':1})
ax0 = axis_array[0].scatter(x_vals, y_vals, c = z1_vals, s = 100, cmap = 'rainbow', vmin = minimum_z, vmax = maximum_z)
ax1 = axis_array[1].scatter(x_vals, y_vals, c = z2_vals, s = 100, cmap = 'rainbow', vmin = minimum_z, vmax = maximum_z)
cax = fig.add_axes([0.95, 0.05, 0.02, 0.95]) #this locates the axis that is used for your colorbar. It is scaled 0 - 1.
fig.colorbar(ax0, cax, orientation = 'vertical') #'ax0' tells it which plot to base the colors on
plt.show()

Categories