I have a set of coordinates, say [(2,3),(45,4),(3,65)]
I need to plot them as a matrix is there anyway I can do this in matplotlib so I want it to have this sort of look http://imgur.com/Q6LLhmk
Edit: My original answer used ax.scatter. There is a problem with this: If two points are side-by-side, ax.scatter may draw them with a bit of space in between, depending on the scale:
For example, with
data = np.array([(2,3),(3,3)])
Here is a zoomed-in detail:
So here is a alternative solution that fixes this problem:
import matplotlib.pyplot as plt
import numpy as np
data = np.array([(2,3),(3,3),(45,4),(3,65)])
N = data.max() + 5
# color the background white (1 is white)
arr = np.ones((N,N), dtype = 'bool')
# color the dots black (0)
arr[data[:,1], data[:,0]] = 0
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.imshow(arr, interpolation='nearest', cmap = 'gray')
ax.invert_yaxis()
# ax.axis('off')
plt.show()
No matter how much you zoom in, the adjacent squares at (2,3) and (3,3) will remain side-by-side.
Unfortunately, unlike ax.scatter, using ax.imshow requires building an N x N array, so it could be more memory-intensive than using ax.scatter. That should not be a problem unless data contains very large numbers, however.
Related
An advantage of plt.pcolormesh over plt.imshow is the possibility to have unequal axis spacing.
On the other hand, plt.imshow's advantage over plt.pcolormesh is that it can display RGB-triplets.
Now, the predicament I am in is that I need to plot RGB-triplets with uneven axis spacing....
Below is a MWE:
import numpy as np
import matplotlib.pyplot as plt
from colorsys import hsv_to_rgb
square_x_axis = np.linspace(0,1,100)**2
cube_y_axis = np.linspace(0,1,200)**3
X,Y = np.meshgrid(cube_y_axis,square_x_axis); print(f'meshgrid has shape: {X.shape}')
rgb_array = np.zeros((square_x_axis.size, cube_y_axis.size,3)); print(f'rgb_array has shape: {rgb_array.shape}')
""" Now we populate the rgb array (initially in hsv color space for clarity)"""
for i,row in enumerate(rgb_array):
for j,col in enumerate(row):
rgb_array[i,j,:] = np.array(hsv_to_rgb(0,square_x_axis[i],cube_y_axis[j]))
fig = plt.figure(figsize=(15,10))
imshow_ax = plt.subplot(1,2,1)
imshow_ax.imshow(rgb_array, aspect='auto', extent=[0,1,0,1])
pcolor_R_ax = plt.subplot(3,2,2)
pcolor_R_ax.pcolormesh(X,Y,rgb_array[:,:,0], cmap='Reds')
pcolor_G_ax = plt.subplot(3,2,4)
pcolor_G_ax.pcolormesh(X,Y,rgb_array[:,:,1], cmap='Greens')
pcolor_B_ax = plt.subplot(3,2,6)
pcolor_B_ax.pcolormesh(X,Y,rgb_array[:,:,2], cmap='Blues')
Which produces the following figure:
The problem becomes immediately obvious: imshow (on the left) is capable of representing the 3D array, but its axis are scaled wrong, leading to a distorted representation. pcolormesh (on the right), on the other hand, can not represent the 3D array (hence why I plot all three channels separately), but is capable of applying the axis correctly, leading to no distortion.
How can I combine these properties?
I found another answer here that seems to work on your example, with a small tweak for some new pcolorbesh behaviour (the shading='auto' bit). Try this plot on your data:
fig = plt.figure(figsize=(15,10))
placeholder = rgb_array[..., 0]
colors = rgb_array.reshape(-1, 3)
mesh = plt.pcolormesh(X, Y, placeholder, facecolors=colors, shading='auto')
mesh.set_array(None)
It produces:
#kwinkunks answer is the method that solved my problem:
The original data, using imshow, looked like this, where both the x- and y-axis of the data plot and the colorbar are wrong. Of all 4 axes, only the data y-axis is linear, the 3 other axes are non-linear, and so using imshows's extent option is no good:
Now... taking #kwinkunks answer directly produced the following plot:
...where the axes tickmarks are now as they should be! Amazing!
So i noticed for me and a few of my colleagues that when we display a binary array using matplotlib.pyplot's imshow function the edges of the displayed image seems altered. For a while i thought it was just a visual artifact, but ran into further trouble with it today.
By the way i am running with matplotlib: 3.2.2 and numpy: 1.19.1
If i create a small binary array and plot it it you can see a small "halo" to the binary box in the image. It is not very obvious but it is there:
import matplotlib.pyplot as plt
import numpy as np
img=np.zeros((100,100))
img[25:60,25:60]=50
plt.imshow(img)
It will become more apparent if i change the cmap for the plot.
my_cmap = plt.cm.get_cmap('prism')
my_cmap.set_under('black')
plt.imshow(img,cmap=my_cmap, vmin=1)
The displayed array should only have 0's as background and 1's in the box, but the box is displayed as a green box with a red/yellow border.
With previous versions of pyplot i have not had this issue and it does become a problem when i do object detection and i want to display them and my other wise binary objects end up like this:
I hope you can help me with this
imshow doesn't know about your data being discrete or even binary. Default it adds some interpolation. You can suppress the smooth interpolation using imshow(...., interpolation='none') (or interpolation='nearest').
Note that the default mode is 'antialiased' for which the effect is different depending on the number of screen pixels occupied by an image pixel.
See the official matplotlib documentation for more details.
Here is some test code comparing the default and the 'none' interpolation mode for different image sizes:
from matplotlib import pyplot as plt
import numpy as np
x = np.round(10 + np.random.uniform(-.1, .1, (100, 100)).cumsum(axis=0).cumsum(axis=1))
x[x % 2 == 0] = x.max() + 1
fig, axes = plt.subplots(2, 6, figsize=(14, 5))
for i, axrow in enumerate(axes):
for j, ax in enumerate(axrow):
k = 10 * (j + 5)
ax.imshow(x[-k:, -k:], cmap='Dark2', interpolation=None if i == 0 else 'none')
ax.set_title("size={}\ninterpolation={}".format(k, 'None' if i == 0 else "'none'"))
plt.tight_layout()
plt.show()
Here is another example, using the 'seismic' colormap and only two data values. This colormap has dark blue and red at the extremes and white near the center, which shows the interpolation much more pronunciated:
I am working on a project in which I need to put together a plot grid of 10 rows and 3 columns. Although I have been able to make the plots and arrange the subplots, I was not able to produce a nice plot without white space such as this one below from gridspec documentatation..
I tried the following posts, but still not able to completely remove the white space as in the example image. Can someone please give me some guidance? Thanks!
Matplotlib different size subplots
how to remove “empty” space
between subplots?
Here's my image:
Below is my code. The full script is here on GitHub.
Note: images_2 and images_fool are both numpy arrays of flattened images with shape (1032, 10), while delta is an image array of shape (28, 28).
def plot_im(array=None, ind=0):
"""A function to plot the image given a images matrix, type of the matrix: \
either original or fool, and the order of images in the matrix"""
img_reshaped = array[ind, :].reshape((28, 28))
imgplot = plt.imshow(img_reshaped)
# Output as a grid of 10 rows and 3 cols with first column being original, second being
# delta and third column being adversaril
nrow = 10
ncol = 3
n = 0
from matplotlib import gridspec
fig = plt.figure(figsize=(30, 30))
gs = gridspec.GridSpec(nrow, ncol, width_ratios=[1, 1, 1])
for row in range(nrow):
for col in range(ncol):
plt.subplot(gs[n])
if col == 0:
#plt.subplot(nrow, ncol, n)
plot_im(array=images_2, ind=row)
elif col == 1:
#plt.subplot(nrow, ncol, n)
plt.imshow(w_delta)
else:
#plt.subplot(nrow, ncol, n)
plot_im(array=images_fool, ind=row)
n += 1
plt.tight_layout()
#plt.show()
plt.savefig('grid_figure.pdf')
A note at the beginning: If you want to have full control over spacing, avoid using plt.tight_layout() as it will try to arange the plots in your figure to be equally and nicely distributed. This is mostly fine and produces pleasant results, but adjusts the spacing at its will.
The reason the GridSpec example you're quoting from the Matplotlib example gallery works so well is because the subplots' aspect is not predefined. That is, the subplots will simply expand on the grid and leave the set spacing (in this case wspace=0.0, hspace=0.0) independent of the figure size.
In contrast to that you are plotting images with imshow and the image's aspect is set equal by default (equivalent to ax.set_aspect("equal")). That said, you could of course put set_aspect("auto") to every plot (and additionally add wspace=0.0, hspace=0.0 as arguments to GridSpec as in the gallery example), which would produce a plot without spacings.
However when using images it makes a lot of sense to keep an equal aspect ratio such that every pixel is as wide as high and a square array is shown as a square image.
What you will need to do then is to play with the image size and the figure margins to obtain the expected result. The figsize argument to figure is the figure (width, height) in inch and here the ratio of the two numbers can be played with. And the subplot parameters wspace, hspace, top, bottom, left can be manually adjusted to give the desired result.
Below is an example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
nrow = 10
ncol = 3
fig = plt.figure(figsize=(4, 10))
gs = gridspec.GridSpec(nrow, ncol, width_ratios=[1, 1, 1],
wspace=0.0, hspace=0.0, top=0.95, bottom=0.05, left=0.17, right=0.845)
for i in range(10):
for j in range(3):
im = np.random.rand(28,28)
ax= plt.subplot(gs[i,j])
ax.imshow(im)
ax.set_xticklabels([])
ax.set_yticklabels([])
#plt.tight_layout() # do not use this!!
plt.show()
Edit:
It is of course desireable not having to tweak the parameters manually. So one could calculate some optimal ones according to the number of rows and columns.
nrow = 7
ncol = 7
fig = plt.figure(figsize=(ncol+1, nrow+1))
gs = gridspec.GridSpec(nrow, ncol,
wspace=0.0, hspace=0.0,
top=1.-0.5/(nrow+1), bottom=0.5/(nrow+1),
left=0.5/(ncol+1), right=1-0.5/(ncol+1))
for i in range(nrow):
for j in range(ncol):
im = np.random.rand(28,28)
ax= plt.subplot(gs[i,j])
ax.imshow(im)
ax.set_xticklabels([])
ax.set_yticklabels([])
plt.show()
Try to add to your code this line:
fig.subplots_adjust(wspace=0, hspace=0)
And for every an axis object set:
ax.set_xticklabels([])
ax.set_yticklabels([])
Following the answer by ImportanceOfBeingErnest, but if you want to use plt.subplots and its features:
fig, axes = plt.subplots(
nrow, ncol,
gridspec_kw=dict(wspace=0.0, hspace=0.0,
top=1. - 0.5 / (nrow + 1), bottom=0.5 / (nrow + 1),
left=0.5 / (ncol + 1), right=1 - 0.5 / (ncol + 1)),
figsize=(ncol + 1, nrow + 1),
sharey='row', sharex='col', # optionally
)
If you are using matplotlib.pyplot.subplots you can display as many images as you want using Axes arrays. You can remove the spaces between images by making some adjustments to the matplotlib.pyplot.subplots configuration.
import matplotlib.pyplot as plt
def show_dataset_overview(self, img_list):
"""show each image in img_list without space"""
img_number = len(img_list)
img_number_at_a_row = 3
row_number = int(img_number /img_number_at_a_row)
fig_size = (15*(img_number_at_a_row/row_number), 15)
_, axs = plt.subplots(row_number,
img_number_at_a_row,
figsize=fig_size ,
gridspec_kw=dict(
top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0
)
)
axs = axs.flatten()
for i in range(img_number):
axs[i].imshow(img_list[i])
axs[i].set_xticks([])
axs[i].set_yticks([])
Since we create subplots here first, we can give some parameters for grid_spec using the gridspec_kw parameter(source).
Among these parameters are the "top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0" parameters that will prevent inter-image spacing. To see other parameters, please visit here.
I usually use a figure size like (30,15) when setting the figure_size above. I generalized this a bit and added it to the code. If you wish, you can enter a manual size here.
Here's another simple approach using the ImageGrid class (adapted from this answer).
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
nrow = 5
ncol = 3
fig = plt.figure(figsize=(4, 10))
grid = ImageGrid(fig,
111, # as in plt.subplot(111)
nrows_ncols=(nrow,ncol),
axes_pad=0,
share_all=True,)
for row in grid.axes_column:
for ax in row:
im = np.random.rand(28,28)
ax.imshow(im)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
I am creating a scatterplot with a colorbar
plt.scatter(X, Y, c=Z)
plt.colorbar()
plt.show()
plt.close()
where X and Y are float arrays and Z is an integer array.
Even though Z is an integer array (here 1-14), the colorbar displays floats.
How can I display a discrete colorbar 1-14?
I found something attempting to answer a similar question here, but I don't understand the answer (containing some complications to make 0 be gray) well enough to apply it.
Check out the second answer to your linked question. If you discretize your colourmap before calling scatter, it will automatically work as you want it to:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
n = 14
X = np.random.rand(20)
Y = np.random.rand(20)
Z = np.random.randint(low=0,high=n,size=X.shape)
plt.figure()
plt.scatter(X,Y,c=Z,cmap=cm.hot)
plt.colorbar()
plt.figure()
plt.scatter(X,Y,c=Z,cmap=cm.get_cmap('hot',n))
plt.colorbar()
Results for comparison:
Note that the default colourmap is jet. But only until viridis kicks in starting from version 2.0 as the new (and wonderful) default.
If what's bothering you is that the numbers are floating-point on the colourbar, you can set manual ticks in it, irrespective of the discretization of colours:
plt.figure()
plt.scatter(X,Y,c=Z,cmap=cm.jet)
plt.colorbar(ticks=np.unique(Z))
#or
#plt.colorbar(ticks=range(Z.min(),Z.max()+1))
Result:
Note that since I used a few random-generated points, not every number is present in Z, so unique might not be the best approach (see the missing ticks in the above figure). This is why I also added a solution based on min/max. You can tailor the limits to your needs depending on your actual application.
Here is my discrete colorbar for land use type, it seems like your work,because the Z value is also an interger array from 1-14.
My method
creat the colormap and colorbar label manually learned from here
My Code
cMap = ListedColormap(['white', '#8dd3c7','#ffffb3','#bebada', \
'#b2182b','#80b1d3','#fdb462','#b3de69','#6a3d9a',\
'#b2df8a', '#1f78b4', '#ccebc5','#ffed6f'])
## If you want to use the colormap from plt.cm..., you can use(take 'jet' for example)
cMap = plt.cm.get_cmap("jet",lut=13)
### here you can change your data in
lulc = plt.pcolormesh(lulc,cmap = cMap,alpha = 0.7)
z_range = np.linspace(1,14,14)
list = z_range.astype('S10')
k = -0.05
for i in range(0,13,1):
k = k + 1/13.0
ax.annotate(list[i],xycoords='axes fraction',xy=(1.12,k),fontsize = 14, \
fontstyle = 'italic',zorder =3)
cbar = plt.colorbar(lulc,ticks = [ ])
for label in cbar.ax.yaxis.get_ticklabels()[::-1]:
label.set_visible(False)
My result
(source: tietuku.com)
Wish it can help!
Here is my question.
When I want use a lot of colormap, I could use
CMAP = ["summer_r", "brg_r", "Dark2", "prism", "PuOr_r", "afmhot_r", "terrain_r", "PuBuGn_r", "RdPu", \
"gist_ncar_r", "gist_yarg_r", "Dark2_r", "YlGnBu", "RdYlBu", "hot_r"]
## value was a 3-d array, the first dimension represent the amount of 2-d array with the value (0, 1).
## I just plot the value 1 for each value[i,:,:]
for i in range(0,len(CMAP),1):
plt.pcolor(xx,yy,value[i,:,:], cmap = CMAP[i])
And I can get this:
http://i8.tietuku.com/cdcdcd5f539c124b.png
But I can't clearly realize the each grid's color befor generating the figure.
Because some colormap which I add in CMAP may have the same start color. SO, some value[ i, :, :] grids will be hard to distinguish.
My idea
Using one colormap instead and split into single color for each value[ i, :, :]. So, each value grid has a different color.
For example:
## 1. cut the colormap, take "jet" for example
cMap = plt.cm.get_cmap("jet",lut=6)
http://i4.tietuku.com/be127c44e87a03fc.png
## 2. I havn't figured it out
## This is the fake code
CMAP = Func[one color -> colormap](cMap)
Update -2016-01-18
This is my code to set different cmap and loop, but it was a bit of rigid.
cmap1 = colors.ListedColormap(["w",'red'])
cmap2 = colors.ListedColormap(["w",'blue'])
cmap3 = colors.ListedColormap(["w",'yellow'])
CMAP = [cmap1,cmap2,cmap3]
Then, I can cope with my original attempt.
But I was wondering is there a smart way to generate the cmap1,cmap2,......?
The hard part of this is coming up with N distinctive colors. In practice, it's usually easiest to just grab random colors as long as N is small. If you'd prefer a bit nicer way of getting N distinct colors, have a look at how seaborn's husl_palette and hsl_palette are implemented. They choose N evenly spaced colors in HSL/HUSL space and convert it back to RGB.
At any rate, there are two parts to tying specific values to specific colors in matplotlib. One is the colormap and the other is the norm. The Normalize instance (the norm) handles transforming the data ranges into a 0-1 space for the colormap.
There's a function to make this use-case easier:matplotlib.colors.from_levels_and_colors. It returns a cmap and norm instance that you can pass in to imshow/pcolormesh/scatter/etc.
As a stand-alone example, let's generate data with a random number of unique integer values. We'll use random pastel colors instead of trying to do something fancy.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
nvals = np.random.randint(2, 20)
data = np.random.randint(0, nvals, (10, 10))
colors = np.random.random((nvals, 3))
# Make the colors pastels...
colors = colors / 2.5 + 0.55
levels = np.arange(nvals + 1) - 0.5
cmap, norm = from_levels_and_colors(levels, colors)
fig, ax = plt.subplots()
im = ax.imshow(data, interpolation='nearest', cmap=cmap, norm=norm)
fig.colorbar(im, ticks=np.arange(nvals))
plt.show()
Not the nicest looking color palette, but it's not awful. Here's another run:
Even with 17 values, we're still getting fairly distinct colors by choosing random values.