Matplotlib Pyplot logo/image in Plot - python

I'm struggling to achieve a simple goal in matplotlib... I want to put a small logo or indicator in the bottom right of my graph, without altering the axis or the real data that is being displayed. Here is my code now:
fig = plt.figure()
plt.rcParams.update({'font.size': 15})
img = plt.imread('./path/to/image.png')
ax1 = fig.add_subplot(111)
ax1.yaxis.tick_left()
ax1.tick_params(axis='y', colors='black', labelsize=15)
ax1.tick_params(axis='x', colors='black', labelsize=15)
plt.grid(b=True, which='major', color='#D3D3D3', linestyle='-')
plt.scatter([1,2,3,4,5],[5,4,3,2,1], alpha=1.0)
plt.autoscale(enable=True, axis=u'both')
fig.savefig('figure.png')
My output from this is below.
This is now laying the photo over the whole graph -- I'd like it scaled to 20% of width & height (if possible) and anchored to the bottom right. This also ruins my axis, because in this output I should be in the 0-100 range on both x & y. Any ideas to solve this, the scaling is the big issue.
Edit1: I've tried the solution below and linked questions here on SO. The problem is relying on the extent variable being passed to imshow() then doesn't work well when introducing new data. For example plotting a scatter plot coming from a data frame, could be from 0..1000 and 50..100 but using extent won't show the label or the position will be off.
Edit2: There seems to be some progress with getting the figure length with fig.get_size_inches() and passing the variable to extent. Apparently all of matplotlib graph calculations are done through inches, so this may be a promising lead.

import matplotlib.image as image
import matplotlib.pyplot as plt
im = image.imread('debian-swirl.png')
fig, ax = plt.subplots()
ax.imshow(im, aspect='auto', extent=(0.4, 0.6, .5, .7), zorder=-1)
ax.yaxis.tick_left()
ax.tick_params(axis='y', colors='black', labelsize=15)
ax.tick_params(axis='x', colors='black', labelsize=15)
ax.grid(b=True, which='major', color='#D3D3D3', linestyle='-')
ax.scatter([1,2,3,4,5],[5,4,3,2,1], alpha=1.0)
plt.show()
I added a png file to bottom left. Adjust the extent parameter to set the logo position.
Similar to : Scale image in matplotlib without changing the axis

The following is an adaptation of the answer by Kirubaharan J, but adapting the position of the logo to the extent of the graph (but the aspect ratio of the logo itself is not preserved)
import matplotlib.image as image
import matplotlib.pyplot as plt
im =image.imread('debian-swirl.png')
fig, ax = plt.subplots()
ax.yaxis.tick_left()
ax.tick_params(axis='y', colors='black', labelsize=15)
ax.tick_params(axis='x', colors='black', labelsize=15)
ax.grid(b=True, which='major', color='#D3D3D3', linestyle='-')
ax.scatter( [100,90,89,70], [55, 23,76,29], alpha=1.0)
plt.autoscale(enable=True, axis=u'both')
xrng=plt.xlim()
yrng=plt.ylim()
scale=.2 #the image takes this fraction of the graph
ax.imshow(im,aspect='auto',extent=(xrng[0],xrng[0] + scale*(xrng[1]-xrng[0]), yrng[0], yrng[0] + scale*(yrng[1]-yrng[0]) ), zorder=-1)
plt.xlim(xrng)
plt.ylim(yrng)
plt.show()

I've worked on a similar problem to print several pdf with a fix logo on every pages independant of the graph size. The best solution I found was using GridSpec.
fig = plt.figure(figsize = (11,8.5)) # 8.5" x 11" : letter format
G = plt.GridSpec(14,21)
I my case I'v build a grid of 14 square by 21 over an 8.5 x 11 inch template.
Then I just have to allocate a section of the grid for the logo and import it using matplotlib.image
ax = fig.add_subplot(G[2:5,5:14])
logo = mpimg.imread("logo.png")
imagebox = OffsetImage(logo, zoom=0.08)
ab = AnnotationBbox(imagebox, (0.4, 0.6), frameon = False)
ax.add_artist(ab)
You can control the scale using the zoom arg in OffsetImage
You can find the detail at the following link :
https://www.science-emergence.com/Articles/How-to-insert-an-image-a-picture-or-a-photo-in-a-matplotlib-figure/

i think it's best to simply put the image on a new axis...
in this way you have full control on where to put it without having to bother with existing axes
import matplotlib.image as image
import matplotlib.pyplot as plt
# create a plot
f, ax = plt.subplots()
im = image.imread("path-to-logo.png")
# put a new axes where you want the image to appear
# (x, y, width, height)
imax = f.add_axes([0.8, 0.75, 0.1, 0.1])
# remove ticks & the box from imax
imax.set_axis_off()
# print the logo with aspect="equal" to avoid distorting the logo
imax.imshow(im, aspect="equal")

Related

Change coordinates for origin in scatter plot with centred axes

I have some datasets that I'm visualizing in a scatter plot. I have a bunch of mean values, and a global mean. What I'm after, but cant really achieve,is to have a scatter plot that is centered in the plot, while also placing the origin at the global mean.
This is the code that defines the layout of the plot:
plt.figure(1)
plt.suptitle('Example')
plt.xlabel('x (pixels)')
plt.ylabel('y (pixels)')
ax = plt.gca()
ax.spines['left'].set_position('center')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_color('none')
ax.scatter(x_data, y_data, color=color, alpha=0.08, label=csv_file_name)
ax.plot(global_mean[0], global_mean[1], color='green',
marker='x', label='Global mean')
This produces the following plot (the ax.scatter() is called multiple times for each dataset, but it's not in the code above):
I've tried playing around with the ax.set_position() parameters but nothing have worked well so far. Is there a way to do what I'm after with matplotlib, or do I need to use some other plot library?
You can use the ax.spines() method to move them around.
import numpy as np
import random
import matplotlib.pyplot as plt
#generate some random data
x = np.linspace(1,2, 100)
y = [random.random() for _ in range(100)]
fig = plt.figure(figsize=(10,5))
# original plot
ax = fig.add_subplot(1,2,1)
ax.scatter(x, y)
# same plot, but with the spines moved
ax2 = fig.add_subplot(1,2,2)
ax2.scatter(x, y)
# move the left spine (y axis) to the right
ax2.spines['left'].set_position(('axes', 0.5))
# move the bottom spine (x axis) up
ax2.spines['bottom'].set_position(('axes', 0.5))
# turn off the right and top spines
ax2.spines['right'].set_visible(False)
ax2.spines['top'].set_visible(False)
plt.show()

matplotlib: axes border and tick mark/label locations

The end result I'm attempting to achieve is to have a "thicker" black boarder around my plot, along xmin, xmax, ymin, & ymax. I've tried a couple of different things (such as just drawing a rectangle on the plot, see below), but I have not been able to achieve the desired results for a few reasons.
Because I cannot just use the spines (I've set 2 of them to always be at 0), I need to add some other line or rectangle to create the desired border.
By default the first and last tick labels overhang the axes. I "overcame" this by changing the horizontal or vertical alignment, but they could still use some more padding. I know this is possible, but requires a transform and is a bit clunky.
Now I'd like to remove the first and last tick marks on both axis. This is because given the way the rectangle is drawn it is always inside the plot area, but the first and last tick mark are always outside it, regardless of how thick the rectangle is. Making the rectangle thicker only causes it to overlap the first and last tick label more, which the actual tick mark remains outside the rectangle.
Any other suggestions on how to achieve this kind of border while always maintaining an axis at 0, 0 would be welcomed. That is the overall desired result.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.patches import Rectangle
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
patPlotBorder = ax.add_artist(Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=2))
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
Without changing much of your code, you can set the clip_on to False, such that the complete rectangle is shown.
border = Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=3, clip_on=False)
ax.add_artist(border)
Since the gridlines are shown above the axes content, you have some grey line within the rectangle border.
Alternatively, you can use two axes. One with all the content and the modified spine positions etc., and one where you just make the spines bold and remove all the rest.
import numpy as np
import matplotlib.pyplot as plt
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax2 = fig.add_subplot(111)
ax2.patch.set_visible(False)
ax2.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
for _, sp in ax2.spines.items():
sp.set_linewidth(3)
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
You can access the individual grid lines by calling get_{x|y}gridlines(). Each grid line is an object of type Line2D, and you can change any of their properties, such as thickness, color, etc.
ax.get_xgridlines()[0].set_linewidth(5)
ax.get_xgridlines()[-1].set_linewidth(5)
ax.get_ygridlines()[0].set_linewidth(5)
ax.get_ygridlines()[-1].set_linewidth(5)

Z-order across axes when using matplotlib's twinx [duplicate]

In pyplot, you can change the order of different graphs using the zorder option or by changing the order of the plot() commands. However, when you add an alternative axis via ax2 = twinx(), the new axis will always overlay the old axis (as described in the documentation).
Is it possible to change the order of the axis to move the alternative (twinned) y-axis to background?
In the example below, I would like to display the blue line on top of the histogram:
import numpy as np
import matplotlib.pyplot as plt
import random
# Data
x = np.arange(-3.0, 3.01, 0.1)
y = np.power(x,2)
y2 = 1/np.sqrt(2*np.pi) * np.exp(-y/2)
data = [random.gauss(0.0, 1.0) for i in range(1000)]
# Plot figure
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax2.hist(data, bins=40, normed=True, color='g',zorder=0)
ax2.plot(x, y2, color='r', linewidth=2, zorder=2)
ax1.plot(x, y, color='b', linewidth=2, zorder=5)
ax1.set_ylabel("Parabola")
ax2.set_ylabel("Normal distribution")
ax1.yaxis.label.set_color('b')
ax2.yaxis.label.set_color('r')
plt.show()
Edit: For some reason, I am unable to upload the image generated by this code. I will try again later.
You can set the zorder of an axes, ax.set_zorder(). One would then need to remove the background of that axes, such that the axes below is still visible.
ax2 = ax1.twinx()
ax1.set_zorder(10)
ax1.patch.set_visible(False)

How to change color bar to align with main plot in Matplotlib?

When plotting matrix with imshow in Matplotlib, how to change colorbar legend bar size, location, font and other parameters?
Here I created an example code
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def plot_matrix(mat, title='example', cmap=plt.cm.Blues):
plt.imshow(mat, interpolation='nearest', cmap=cmap)
plt.grid(False)
plt.title(title)
plt.colorbar()
data = np.random.random((20, 20))
plt.figure(figsize=(8,8))
plt.tick_params(axis='both', which='major', labelsize=12)
plot_matrix(data)
In a real use case, I got complex labels and the legend bar becomes much higher then the matrix itself. I want to change the legend bar to make the plot more efficiently use the space.
I found a documentation for the matplotlib.pyplot.colorbar, however have not figure out a good way to set the size, location and font size for the color legend bar.
imshow enforces a 1:1 aspect (by default, but you can change it with aspect parameter), which makes things a little trickier. To always get consistent result, I might suggest manually specify the size of axes:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def plot_matrix(mat, figsize, title='example', cmap=plt.cm.Blues):
f = plt.figure(figsize=figsize)
ax = plt.axes([0, 0.05, 0.9, 0.9 ]) #left, bottom, width, height
#note that we are forcing width:height=1:1 here,
#as 0.9*8 : 0.9*8 = 1:1, the figure size is (8,8)
#if the figure size changes, the width:height ratio here also need to be changed
im = ax.imshow(mat, interpolation='nearest', cmap=cmap)
ax.grid(False)
ax.set_title(title)
cax = plt.axes([0.95, 0.05, 0.05,0.9 ])
plt.colorbar(mappable=im, cax=cax)
return ax, cax
data = np.random.random((20, 20))
ax, cax = plot_matrix(data, (8,8))
Now you have the axis where the colorbar is plotted in, cax. You can do a lot of thing with that, say, rotate the labels, using plt.setp(cax.get_yticklabels(), rotation=45)

matplotlib add rectangle to Figure not to Axes

I need to add a semi transparent skin over my matplotlib figure. I was thinking about adding a rectangle to the figure with alpha <1 and a zorder high enough so its drawn on top of everything.
I was thinking about something like that
figure.add_patch(Rectangle((0,0),1,1, alpha=0.5, zorder=1000))
But I guess rectangles are handled by Axes only. is there any turn around ?
Late answer for others who google this.
There actually is a simple way, without phantom axes, close to your original wish. The Figure object has a patches attribute, to which you can add the rectangle:
fig, ax = plt.subplots(nrows=1, ncols=1)
ax.plot(np.cumsum(np.random.randn(100)))
fig.patches.extend([plt.Rectangle((0.25,0.5),0.25,0.25,
fill=True, color='g', alpha=0.5, zorder=1000,
transform=fig.transFigure, figure=fig)])
Gives the following picture (I'm using a non-default theme):
The transform argument makes it use figure-level coordinates, which I think is what you want.
You can use a phantom axes on top of your figure and change the patch to look as you like, try this example:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
ax.set_zorder(1000)
ax.patch.set_alpha(0.5)
ax.patch.set_color('r')
ax2 = fig.add_subplot(111)
ax2.plot(range(10), range(10))
plt.show()
If you aren't using subplots, using gca() will work easily.
from matplotlib.patches import Rectangle
fig = plt.figure(figsize=(12,8))
plt.plot([0,100],[0,100])
plt.gca().add_patch(Rectangle((25,50),15,15,fill=True, color='g', alpha=0.5, zorder=100, figure=fig))

Categories