Plots a subset of images on an axis. Useful for visualizing image
  embeddings, especially when plotted over a scatterplot. Selects random points
  to annotate with their corresponding image, respecting an exclusion_radius
  around each selected point.

def embedded_images(X, images, exclusion_radius=None, ax=None, cmap=None,
                    zoom=1, seed=None, frameon=False):
  '''Plots a subset of images on an axis. Useful for visualizing image
  embeddings, especially when plotted over a scatterplot. Selects random points
  to annotate with their corresponding image, respecting an exclusion_radius
  around each selected point.'''
  assert X.shape[0] == images.shape[0], 'Unequal number of points and images'
  assert X.shape[1] == 2, 'X must be 2d'
  if ax is None:
    ax = plt.gca()
  if exclusion_radius is None:
    # TODO: make a smarter default based on image size and axis limits
    exclusion_radius = 1.
  if seed is not None:
    np.random.seed(seed)
  while X.shape[0] > 0:
    i = np.random.choice(X.shape[0])
    im = OffsetImage(images[i], zoom=zoom, cmap=cmap)
    ab = AnnotationBbox(im, X[i], xycoords='data', frameon=frameon)
    ax.add_artist(ab)
    dist = np.sqrt(np.square(X[i] - X).sum(axis=1))
    mask = (dist > exclusion_radius).ravel()
    X = X[mask]
    images = images[mask]
  return plt.show