This question already has answers here:
How can I make the xtick labels of a plot be simple drawings using matplotlib?
(2 answers)
Closed 5 years ago.
I have a series of small, fixed width images and I want to replace the tick labels with them. For example, consider the following minimal working example:
import numpy as np
import pylab as plt
A = np.random.random(size=(5,5))
fig, ax = plt.subplots(1, 1)
ax.matshow(A)
plt.show()
I would like to replace the "0" with a custom image. I can turn off the labels, load an image into an array and display it just fine. However, I'm unsure of
Where the locations of the tick labels are, since they lie outside the plot.
Use imshow to display that image when it it will be "clipped" if put into an axis.
My thought were to use set_clip_on somehow or a custom artist, but I haven't made much progress.
Interesting question, and potentially has many possible solutions. Here is my approach, basically first calculate where the label '0' is, then draw a new axis there using absolute coordinates, and finally put the image there:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import pylab as pl
A = np.random.random(size=(5,5))
fig, ax = plt.subplots(1, 1)
xl, yl, xh, yh=np.array(ax.get_position()).ravel()
w=xh-xl
h=yh-yl
xp=xl+w*0.1 #if replace '0' label, can also be calculated systematically using xlim()
size=0.05
img=mpimg.imread('microblog.png')
ax.matshow(A)
ax1=fig.add_axes([xp-size*0.5, yh, size, size])
ax1.axison = False
imgplot = ax1.imshow(img,transform=ax.transAxes)
plt.savefig('temp.png')
Related
I'm experimenting with seaborn and have a question about specifying axes properties. In my code below, I've taken two approaches to creating a heatmap of a matrix and placing the results on two sets of axes in a figure.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
A=np.random.randn(4,4)
labels=['a','b','c','d']
fig, ax = plt.subplots(2)
sns.heatmap(ax =ax[0], data = A)
ax[0].set_xticks(range(len(labels)))
ax[0].set_xticklabels(labels,fontsize=10,rotation=45)
ax[0].set_yticks(range(len(labels)))
ax[0].set_yticklabels(labels,fontsize=10,rotation=45)
ax[1].set_xticks(range(len(labels)))
ax[1].set_xticklabels(labels,fontsize=10,rotation=45)
ax[1].set_yticks(range(len(labels)))
ax[1].set_yticklabels(labels,fontsize=10,rotation=45)
sns.heatmap(ax =ax[1], data = A,xticklabels=labels, yticklabels=labels)
plt.show()
The resulting figure looks like this:
Normally, I would always take the first approach of creating the heatmap and then specifying axis properties. However, when creating an animation (to be embedded on a tkinter canvas), which is what I'm ultimately interested in doing, I found such an ordering in my update function leads to "flickering" of axis labels. The second approach will eliminate this effect, and it also centers the tickmarks within squares along the axes.
However, the second approach does not rotate the y-axis tickmark labels as desired. Is there a simple fix to this?
I'm not sure this is what you're looking for. It looks like you create your figure after you change the yticklabels. so the figure is overwriting your yticklabels.
Below would fix your issue.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
A=np.random.randn(4,4)
labels=['a','b','c','d']
fig, ax = plt.subplots(2)
sns.heatmap(ax =ax[0], data = A)
ax[0].set_xticks(range(len(labels)))
ax[0].set_xticklabels(labels,fontsize=10,rotation=45)
ax[0].set_yticks(range(len(labels)))
ax[0].set_yticklabels(labels,fontsize=10,rotation=45)
ax[1].set_xticks(range(len(labels)))
ax[1].set_xticklabels(labels,fontsize=10,rotation=45)
ax[1].set_yticks(range(len(labels)))
sns.heatmap(ax =ax[1], data = A,xticklabels=labels, yticklabels=labels)
ax[1].set_yticklabels(labels,fontsize=10,rotation=45)
plt.show()
This question already has answers here:
plot several image files in matplotlib subplots
(2 answers)
Closed 1 year ago.
I am producing a lot of figures with Matplotlib.pyplot (spatial data) and saving them as png's. I would like to be able to first make the figures (in loops), and then choose a few to put together in a multiple-panel figure, using Matplotlib.
I suppose this would mean re-opening the existing png's, and then putting them together using pyplot.subplots(), but I can't figure out a way to do it.
Does anybody have an idea?
Thanks!
Here's an example of what I think you mean:
import matplotlib.pyplot as plt
from matplotlib import image
import numpy as np
# initialise grid of axes
fig, axes = plt.subplots(2,2)
axes = axes.ravel()
# create fake data
img = [
'01-img.png',
'02-img.png',
'03-img.png',
'04-img.png',
]
# iterate over axes
for i, ax in enumerate(axes):
im = image.imread(img[i])
ax.imshow(im)
plt.show()
Use image.imread to load the image into a plottable form, then use ax.imshow to plot the pixels on the axis
This question already has answers here:
Modify tick label text
(13 answers)
Closed 3 years ago.
I have the following piece of code which creates a simple plot with matplotlib (python 3.6.9, matplotlib 3.1.2, Mac Mojave):
import numpy as np
import matplotlib.pyplot as plt
plt.imshow(np.random.random((50,50)))
plt.show()
The created plot is as expected:
Now, in order to relabel the xtick/ytick labels I am using the following code
import numpy as np
import matplotlib.pyplot as plt
plt.imshow(np.random.random((50,50)));
ticks, labels = plt.xticks()
labels[1] = '22'
plt.xticks(ticks, labels)
plt.show()
where I expect the second label to be replaced by '22', but everything else stays the same. However, I get the following plot instead:
There is some unexpected white area in the left part of the plot
All the other labels have vanished.
How to do it correctly?
Just as a reminder: I want to get the exact same result as the original image (first plot), ONLY with one of the lables changed.
This question has been asked before, one example is here. But the answer does not seem to work. Here is the code
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
plt.imshow(np.random.random((50,50)))
labels = [item.get_text() for item in ax.get_xticklabels()]
labels[1] = 'Test'
ax.set_xticklabels(labels)
plt.show()
which creates an image as follows:
which does not show the white area anymore, but still the other labels are not shown.
Create axes using subplots, so that you can have set_xticklabels method, so you can update the labels.
You need to use, canvas.draw() to get the values.
import numpy as np
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.imshow(np.random.random((50,50)));
fig.canvas.draw()
#labels = ['-10','0','22','20','30','40'] or
labels[2]=22
ax.set_xticklabels(labels)
plt.show()
Output:
Hope this is what you need!
I frequently find myself working in log units for my plots, for example taking np.log10(x) of data before binning it or creating contour plots. The problem is, when I then want to make the plots presentable, the axes are in ugly log units, and the tick marks are evenly spaced.
If I let matplotlib do all the conversions, i.e. by setting ax.set_xaxis('log') then I get very nice looking axes, however I can't do that to my data since it is e.g. already binned in log units. I could manually change the tick labels, but that wouldn't make the tick spacing logarithmic. I suppose I could also go and manually specify the position of every minor tick such it had log spacing, but is that the only way to achieve this? That is a bit tedious so it would be nice if there is a better way.
For concreteness, here is a plot:
I want to have the tick labels as 10^x and 10^y (so '1' is '10', 2 is '100' etc.), and I want the minor ticks to be drawn as ax.set_xaxis('log') would draw them.
Edit: For further concreteness, suppose the plot is generated from an image, like this:
import matplotlib.pyplot as plt
import scipy.misc
img = scipy.misc.face()
x_range = [-5,3] # log10 units
y_range = [-55, -45] # log10 units
p = plt.imshow(img,extent=x_range+y_range)
plt.show()
and all we want to do is change the axes appearance as I have described.
Edit 2: Ok, ImportanceOfBeingErnest's answer is very clever but it is a bit more specific to images than I wanted. I have another example, of binned data this time. Perhaps their technique still works on this, though it is not clear to me if that is the case.
import numpy as np
import pandas as pd
import datashader as ds
from matplotlib import pyplot as plt
import scipy.stats as sps
v1 = sps.lognorm(loc=0, scale=3, s=0.8)
v2 = sps.lognorm(loc=0, scale=1, s=0.8)
x = np.log10(v1.rvs(100000))
y = np.log10(v2.rvs(100000))
x_range=[np.min(x),np.max(x)]
y_range=[np.min(y),np.max(y)]
df = pd.DataFrame.from_dict({"x": x, "y": y})
#------ Aggregate the data ------
cvs = ds.Canvas(plot_width=30, plot_height=30, x_range=x_range, y_range=y_range)
agg = cvs.points(df, 'x', 'y')
# Create contour plot
fig = plt.figure()
ax = fig.add_subplot(111)
ax.contourf(agg, extent=x_range+y_range)
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.show()
The general answer to this question is probably given in this post:
Can I mimic a log scale of an axis in matplotlib without transforming the associated data?
However here an easy option might be to scale the content of the axes and then set the axes to a log scale.
A. image
You may plot your image on a logarithmic scale but make all pixels the same size in log units. Unfortunately imshow does not allow for such kind of image (any more), but one may use pcolormesh for that purpose.
import numpy as np
import matplotlib.pyplot as plt
import scipy.misc
img = scipy.misc.face()
extx = [-5,3] # log10 units
exty = [-45, -55] # log10 units
x = np.logspace(extx[0],extx[-1],img.shape[1]+1)
y = np.logspace(exty[0],exty[-1],img.shape[0]+1)
X,Y = np.meshgrid(x,y)
c = img.reshape((img.shape[0]*img.shape[1],img.shape[2]))/255.0
m = plt.pcolormesh(X,Y,X[:-1,:-1], color=c, linewidth=0)
m.set_array(None)
plt.gca().set_xscale("log")
plt.gca().set_yscale("log")
plt.show()
B. contour
The same concept can be used for a contour plot.
import numpy as np
from matplotlib import pyplot as plt
x = np.linspace(-1.1,1.9)
y = np.linspace(-1.4,1.55)
X,Y = np.meshgrid(x,y)
agg = np.exp(-(X**2+Y**2)*2)
fig, ax = plt.subplots()
plt.gca().set_xscale("log")
plt.gca().set_yscale("log")
exp = lambda x: 10.**(np.array(x))
cf = ax.contourf(exp(X), exp(Y),agg, extent=exp([x.min(),x.max(),y.min(),y.max()]))
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.show()
I would like to plot in a single line ticks according to an array (up to 1000 elements). I would rather not to use something like:
plt.xticks(energies[i][j])
because each sample value is written up below tick. I have looked extensively at Matplotlib documentation but didn't find nothing besides hist(). If you guys know other way to visualize 1D arrays into a single line I would very much appreciate, especially if it involves colors representing density of values.
I'm using Spyder 2.2.5, Python 2.7.6 | 64-bit in OSX 10.7.4
Edit
As #tcaswell mentions in comments, eventplot is a good way to do this. Here is an example:
from matplotlib import pyplot as plt
import numpy as np
plt.figure()
a = [1,2,5,6,9,11,15,17,18]
plt.hlines(1,1,20) # Draw a horizontal line
plt.eventplot(a, orientation='horizontal', colors='b')
plt.axis('off')
plt.show()
Or you can use vertical line markers? The example below has the basic idea. You could change the color of the markers to represent density.
from matplotlib import pyplot as plt
import numpy as np
a = [1,2,5,6,9,11,15,17,18]
plt.hlines(1,1,20) # Draw a horizontal line
plt.xlim(0,21)
plt.ylim(0.5,1.5)
y = np.ones(np.shape(a)) # Make all y values the same
plt.plot(a,y,'|',ms = 40) # Plot a line at each location specified in a
plt.axis('off')
plt.show()