I would like to utilize customer markers in both scatter and line charts. How can I make custom marker out of a PNG file?
I don't believe matplotlib can customize markers like that. See here for the level of customization, which falls way short of what you need.
As an alternative, I've coded up this kludge which uses matplotlib.image to place images at the line point locations.
import matplotlib.pyplot as plt
from matplotlib import image
# constant
dpi = 72
path = 'smile.png'
# read in our png file
im = image.imread(path)
image_size = im.shape[1], im.shape[0]
fig = plt.figure(dpi=dpi)
ax = fig.add_subplot(111)
# plot our line with transparent markers, and markersize the size of our image
line, = ax.plot((1,2,3,4),(1,2,3,4),"bo",mfc="None",mec="None",markersize=image_size[0] * (dpi/ 96))
# we need to make the frame transparent so the image can be seen
# only in trunk can you put the image on top of the plot, see this link:
# http://www.mail-archive.com/matplotlib-users#lists.sourceforge.net/msg14534.html
ax.patch.set_alpha(0)
ax.set_xlim((0,5))
ax.set_ylim((0,5))
# translate point positions to pixel positions
# figimage needs pixels not points
line._transform_path()
path, affine = line._transformed_path.get_transformed_points_and_affine()
path = affine.transform_path(path)
for pixelPoint in path.vertices:
# place image at point, centering it
fig.figimage(im,pixelPoint[0]-image_size[0]/2,pixelPoint[1]-image_size[1]/2,origin="upper")
plt.show()
Produces:
Following on from Mark's answer. I just thought I would add to this a bit because I tried to run this and it does what I want with the exception of actually displaying the icons on the graph. Maybe something has changed with matplotlib. It has been 4 years.
The line of code that reads:
ax.get_frame().set_alpha(0)
does not seem to work, however
ax.patch.set_alpha(0)
does work.
The other answer may lead to problems when resizing the figure. Here is a different approach, positionning the images inside annotation boxes, which are anchored in data coordinates.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
path = "https://upload.wikimedia.org/wikipedia/commons/b/b5/Tango-example_icons.png"
image = plt.imread(path)[116:116+30, 236:236+30]
x = np.arange(10)
y = np.random.rand(10)
fig, ax = plt.subplots()
ax.plot(x,y)
def plot_images(x, y, image, ax=None):
ax = ax or plt.gca()
for xi, yi in zip(x,y):
im = OffsetImage(image, zoom=72/ax.figure.dpi)
im.image.axes = ax
ab = AnnotationBbox(im, (xi,yi), frameon=False, pad=0.0,)
ax.add_artist(ab)
plot_images(x, y, image, ax=ax)
plt.show()
Related
Below code saves all RGB components as one file named as shown in picture. Is it possible that I can save each one of them to different files in one take? Also I dont understand why blue component has axes numbered and others not.
from matplotlib import pyplot as plt
from PIL import Image
import numpy as np
#Image into RGB Components
img1 = np.array(Image.open('1.jpeg'))
figure, plots = plt.subplots(ncols=3, nrows=1,figsize=(12, 4))
for i, subplot in zip(range(3), plots):
temp = np.zeros(img1.shape, dtype='uint8')
temp[:,:,i] = img1[:,:,i]
subplot.imshow(temp)
plt.savefig("RedComponent.png", bbox_inches='tight')
subplot.set_axis_off()
plt.show()
Your last savefig call is happening before the axis is removed, that's why it's there on the final axes object.
from matplotlib import pyplot as plt
from PIL import Image
import numpy as np
#Image into RGB Components
img1 = np.array(Image.open('1.jpeg'))
rgb = ("Red", "Green", "Blue") # this gives you the basis for different filenames
figure, plots = plt.subplots(ncols=3, nrows=1,figsize=(12, 4))
for i, subplot, channel in zip(range(3), plots, rgb):
# create an additional figure within the loop for the single colour
fig2, ax2 = plt.subplots(figsize=(4, 4))
temp = np.zeros(img1.shape, dtype='uint8')
temp[:,:,i] = img1[:,:,i]
subplot.imshow(temp)
ax2.imshow(temp)
# removing the axis before saving
ax2.set_axis_off()
subplot.set_axis_off()
# Save single colour panel as its own file
fig2.savefig("{}Component.png".format(channel), bbox_inches='tight')
plt.close(fig2) # remove the single-colour plot
# save the three-Axes figure separately once outside the loop
figure.savefig("AllComponents.png", bbox_inches='tight')
plt.show()
I am using python 3.5.2
I would like to make a pie chart with an png image imbedded. I have pictures of certain bulk products that I would like to insert into the slices. For example strawberries in one slice and raspberries in another. Much like the picture http://www.python-course.eu/images/pie_chart_with_raspberries.png shows.
I can produce images and even plot images instead of points as demonstrated here Matplotlib: How to plot images instead of points?
However, I could not find any approach towards what I am proposing. I suppose it could be manually done in paint, but I was trying to avoid that.
That is sure possible. We can start with a normal pie chart. Then we would need to get the images into the plot. This is done using plt.imread and by using a matplotlib.offsetbox.OffsetImage. We would need to find good coordinates and zoom levels to place the image, such that it overlapps completely with respective pie wedge. Then the Path of the pie's wedge is used as a clip path of the image, such that only the part inside the wedge is left over. Setting the zorder of the unfilled wedge to a high number ensures the borders to be placed on top of the image. This way it looks like the wedges are filled with the image.
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
total = [5,7,4]
labels = ["Raspberries", "Blueberries", "Blackberries"]
plt.title('Berries')
plt.gca().axis("equal")
wedges, texts = plt.pie(total, startangle=90, labels=labels,
wedgeprops = { 'linewidth': 2, "edgecolor" :"k","fill":False, })
def img_to_pie( fn, wedge, xy, zoom=1, ax = None):
if ax==None: ax=plt.gca()
im = plt.imread(fn, format='png')
path = wedge.get_path()
patch = PathPatch(path, facecolor='none')
ax.add_patch(patch)
imagebox = OffsetImage(im, zoom=zoom, clip_path=patch, zorder=-10)
ab = AnnotationBbox(imagebox, xy, xycoords='data', pad=0, frameon=False)
ax.add_artist(ab)
positions = [(-1,0.3),(0,-0.5),(0.5,0.5)]
zooms = [0.4,0.4,0.4]
for i in range(3):
fn = "data/{}.png".format(labels[i].lower())
img_to_pie(fn, wedges[i], xy=positions[i], zoom=zooms[i] )
wedges[i].set_zorder(10)
plt.show()
I would like to utilize customer markers in both scatter and line charts. How can I make custom marker out of a PNG file?
I don't believe matplotlib can customize markers like that. See here for the level of customization, which falls way short of what you need.
As an alternative, I've coded up this kludge which uses matplotlib.image to place images at the line point locations.
import matplotlib.pyplot as plt
from matplotlib import image
# constant
dpi = 72
path = 'smile.png'
# read in our png file
im = image.imread(path)
image_size = im.shape[1], im.shape[0]
fig = plt.figure(dpi=dpi)
ax = fig.add_subplot(111)
# plot our line with transparent markers, and markersize the size of our image
line, = ax.plot((1,2,3,4),(1,2,3,4),"bo",mfc="None",mec="None",markersize=image_size[0] * (dpi/ 96))
# we need to make the frame transparent so the image can be seen
# only in trunk can you put the image on top of the plot, see this link:
# http://www.mail-archive.com/matplotlib-users#lists.sourceforge.net/msg14534.html
ax.patch.set_alpha(0)
ax.set_xlim((0,5))
ax.set_ylim((0,5))
# translate point positions to pixel positions
# figimage needs pixels not points
line._transform_path()
path, affine = line._transformed_path.get_transformed_points_and_affine()
path = affine.transform_path(path)
for pixelPoint in path.vertices:
# place image at point, centering it
fig.figimage(im,pixelPoint[0]-image_size[0]/2,pixelPoint[1]-image_size[1]/2,origin="upper")
plt.show()
Produces:
Following on from Mark's answer. I just thought I would add to this a bit because I tried to run this and it does what I want with the exception of actually displaying the icons on the graph. Maybe something has changed with matplotlib. It has been 4 years.
The line of code that reads:
ax.get_frame().set_alpha(0)
does not seem to work, however
ax.patch.set_alpha(0)
does work.
The other answer may lead to problems when resizing the figure. Here is a different approach, positionning the images inside annotation boxes, which are anchored in data coordinates.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
path = "https://upload.wikimedia.org/wikipedia/commons/b/b5/Tango-example_icons.png"
image = plt.imread(path)[116:116+30, 236:236+30]
x = np.arange(10)
y = np.random.rand(10)
fig, ax = plt.subplots()
ax.plot(x,y)
def plot_images(x, y, image, ax=None):
ax = ax or plt.gca()
for xi, yi in zip(x,y):
im = OffsetImage(image, zoom=72/ax.figure.dpi)
im.image.axes = ax
ab = AnnotationBbox(im, (xi,yi), frameon=False, pad=0.0,)
ax.add_artist(ab)
plot_images(x, y, image, ax=ax)
plt.show()
Is it possible to save (to a png) an individual subplot in a matplotlib figure? Let's say I have
import pyplot.matplotlib as plt
ax1 = plt.subplot(121)
ax2 = plt.subplot(122)
ax1.plot([1,2,3],[4,5,6])
ax2.plot([3,4,5],[7,8,9])
Is it possible to save each of the two subplots to different files or at least copy them separately to a new figure to save them?
I am using version 1.0.0 of matplotlib on RHEL 5.
While #Eli is quite correct that there usually isn't much of a need to do it, it is possible. savefig takes a bbox_inches argument that can be used to selectively save only a portion of a figure to an image.
Here's a quick example:
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
# Make an example plot with two subplots...
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(range(10), 'b-')
ax2 = fig.add_subplot(2,1,2)
ax2.plot(range(20), 'r^')
# Save the full figure...
fig.savefig('full_figure.png')
# Save just the portion _inside_ the second axis's boundaries
extent = ax2.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
fig.savefig('ax2_figure.png', bbox_inches=extent)
# Pad the saved area by 10% in the x-direction and 20% in the y-direction
fig.savefig('ax2_figure_expanded.png', bbox_inches=extent.expanded(1.1, 1.2))
The full figure:
Area inside the second subplot:
Area around the second subplot padded by 10% in the x-direction and 20% in the y-direction:
Applying the full_extent() function in an answer by #Joe 3 years later from here, you can get exactly what the OP was looking for. Alternatively, you can use Axes.get_tightbbox() which gives a little tighter bounding box
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
from matplotlib.transforms import Bbox
def full_extent(ax, pad=0.0):
"""Get the full extent of an axes, including axes labels, tick labels, and
titles."""
# For text objects, we need to draw the figure first, otherwise the extents
# are undefined.
ax.figure.canvas.draw()
items = ax.get_xticklabels() + ax.get_yticklabels()
# items += [ax, ax.title, ax.xaxis.label, ax.yaxis.label]
items += [ax, ax.title]
bbox = Bbox.union([item.get_window_extent() for item in items])
return bbox.expanded(1.0 + pad, 1.0 + pad)
# Make an example plot with two subplots...
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(range(10), 'b-')
ax2 = fig.add_subplot(2,1,2)
ax2.plot(range(20), 'r^')
# Save the full figure...
fig.savefig('full_figure.png')
# Save just the portion _inside_ the second axis's boundaries
extent = full_extent(ax2).transformed(fig.dpi_scale_trans.inverted())
# Alternatively,
# extent = ax.get_tightbbox(fig.canvas.renderer).transformed(fig.dpi_scale_trans.inverted())
fig.savefig('ax2_figure.png', bbox_inches=extent)
I'd post a pic but I lack the reputation points
What I want is really simple: I have a small image file called "logo.png" that I want to display on the upper left corner of my plots. But you can't find any example of that in the matplotlib examples gallery.
I'm using django, and my code is something like this:
def get_bars(request)
...
fig = Figure(facecolor='#F0F0F0',figsize=(4.6,4))
...
ax1 = fig.add_subplot(111,ylabel="Valeur",xlabel="Code",autoscale_on=True)
ax1.bar(ind,values,width=width, color='#FFCC00',edgecolor='#B33600',linewidth=1)
...
canvas = FigureCanvas(fig)
response = HttpResponse(content_type='image/png')
canvas.print_png(response)
return response
If you want the image at the corner of your actual figure (rather than the corner of your axis), look into figimage.
Perhaps something like this? (using PIL to read the image):
import matplotlib.pyplot as plt
import Image
import numpy as np
im = Image.open('/home/jofer/logo.png')
height = im.size[1]
# We need a float array between 0-1, rather than
# a uint8 array between 0-255
im = np.array(im).astype(np.float) / 255
fig = plt.figure()
plt.plot(np.arange(10), 4 * np.arange(10))
# With newer (1.0) versions of matplotlib, you can
# use the "zorder" kwarg to make the image overlay
# the plot, rather than hide behind it... (e.g. zorder=10)
fig.figimage(im, 0, fig.bbox.ymax - height)
# (Saving with the same dpi as the screen default to
# avoid displacing the logo image)
fig.savefig('/home/jofer/temp.png', dpi=80)
plt.show()
Another option, if you'd like to have the image be a fixed fraction of the figure's width/height is to create a "dummy" axes and place the image in it with imshow. This way the image's size and position is independent of DPI and the figure's absolute size:
import matplotlib.pyplot as plt
from matplotlib.cbook import get_sample_data
im = plt.imread(get_sample_data('grace_hopper.jpg'))
fig, ax = plt.subplots()
ax.plot(range(10))
# Place the image in the upper-right corner of the figure
#--------------------------------------------------------
# We're specifying the position and size in _figure_ coordinates, so the image
# will shrink/grow as the figure is resized. Remove "zorder=-1" to place the
# image in front of the axes.
newax = fig.add_axes([0.8, 0.8, 0.2, 0.2], anchor='NE', zorder=-1)
newax.imshow(im)
newax.axis('off')
plt.show()
There is now a much easier way, using the new inset_axes command (matplotlib >3.0 required).
This command allows one to define a new set of axes as a child of an existing axes object. The advantage of this is that you can define your inset axes in whatever units you please, like axes fraction or data coordinates, using the appropriate transform expression.
So here's a code example:
# Imports
import matplotlib.pyplot as plt
import matplotlib as mpl
# read image file
with mpl.cbook.get_sample_data(r"C:\path\to\file\image.png") as file:
arr_image = plt.imread(file, format='png')
# Draw image
axin = ax.inset_axes([105,-145,40,40],transform=ax.transData) # create new inset axes in data coordinates
axin.imshow(arr_image)
axin.axis('off')
The advantage of this method is that your image will scale automatically as your axes get rescaled!