bismo
bismo

Reputation: 1439

Plot an axes artist on a negative coordinate in matplotlib

I've been struggling with this for an hour and am still stumped... I would like to plot a series of 15 images (one for each row of a heatmap) and have code to do so:

from matplotlib.offsetbox import OffsetImage, AnnotationBbox

def getImage(path): 
    return OffsetImage(plt.imread(path),zoom=0.085)

for x0, y0, path in zip([0]*15,np.arange(0.5,15.5,1),paths):
    ab = AnnotationBbox(getImage(path), (x0, y0), frameon=False, fontsize=4)
    ax.add_artist(ab)

Since my x coordinates is list of 15 0s, my plot generates as such (not all 15 logos shown):

enter image description here

I would like to move the images to the left a bit more so they don't overlap the actual heatmap and are nicely aligned like a text label would be. However, if I change the x coordinate to something less than 0 (or anything outside of the axes bound it appears), the plot generates but all the images are absent. Are you not allowed to add axes artists to negative coordinates? I found out there is an add_artist() for the figure class, but when I try to use that instead of ax I get an error:

AttributeError: 'NoneType' object has no attribute 'transData'

So, does anyone know how I could plot these logos just a bit to the left using ax.add_artist()?

EDIT: After looking through the code of the accepted answer, I realized the xybox param of my annotation box will solve my problem. Thus, the following loop works correclty:

for x0, y0, path in zip([0]*15,np.arange(0.5,15.5,1),paths):
    ab = AnnotationBbox(getImage(path), (x0, y0), xybox=(-.4,y0), frameon=False, fontsize=4)
    ax.add_artist(ab)

But the accepted answer's way of going about it also works correctly.

Upvotes: 0

Views: 454

Answers (1)

r-beginners
r-beginners

Reputation: 35265

I adapted @JohanC's excellent response to your assignment, which you commented on. He should be the one to respond, but I will do so out of respect. Align the axis of the graph with the object from which the image was loaded. Set the position of the annotation box to a negative value based on the graph data. Add the annotation box to the artist. The heatmap data is obtained from the official reference and the team logos from the Premier League.

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox

vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
              "potato", "wheat", "barley"]
farmers = ["Farmer Joe", "Upland Bros.", "Smith Gardening",
           "Agrifun", "Organiculture", "BioGoods Ltd.", "Cornylee Corp."]

teams = ['manchester-city','liverpool','chelsea','arsenal','manchester-united',
         'west-ham-united','tottenham-hotspur-fc']

harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
                    [2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
                    [1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
                    [0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
                    [0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
                    [1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
                    [0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])

def add_image(x, y, teams, ax):
    print('./data/premier_logos/{}-logo-vector.png'.format(teams))
    img = plt.imread('./data/premier_logos/{}-logo-vector.png'.format(teams))
    im = OffsetImage(img, zoom=0.085)
    im.image.axes = ax
    x_offset = -25
    ab = AnnotationBbox(im, (x,y), xybox=(x_offset,0), frameon=False, 
                        xycoords='data', boxcoords='offset points', pad=0)
    ax.add_artist(ab)
    
fig, ax = plt.subplots()
im = ax.imshow(harvest)

# Show all ticks and label them with the respective list entries
ax.set_xticks(np.arange(len(farmers)), labels=farmers)
ax.set_yticks(np.arange(len(vegetables)), labels='')

# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
         rotation_mode="anchor")

# Loop over data dimensions and create text annotations.
for i in range(len(vegetables)):
    for j in range(len(farmers)):
        text = ax.text(j, i, harvest[i, j],
                       ha="center", va="center", color="w")

for i,t in enumerate(teams):
    add_image(0, i, t, ax=plt.gca())

ax.set_title("Harvest of local farmers (in tons/year)")
fig.tight_layout()
plt.show()

enter image description here

Upvotes: 1

Related Questions