Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to plot a Line2D in points coordinates in Matplotlib in Python?

It is very straight forward to plot a line between two points (x1, y1) and (x2, y2) in Matplotlib using Line2D:

Line2D(xdata=(x1, x2), ydata=(y1, y2))

But in my particular case I have to draw Line2D instances using Points coordinates on top of the regular plots that are all using Data coordinates. Is that possible?

like image 823
c00kiemonster Avatar asked Sep 15 '15 13:09

c00kiemonster


People also ask

How do you plot points on a graph in Python?

Use plt. scatter() to plot points Call plt. scatter(x, y) with x as a sequence of x-coordinates and y as a corresponding sequence of y-coordinates to plot the points.


2 Answers

As @tom mentioned, the key is the transform kwarg. If you want an artist's data to be interpreted as being in "pixel" coordinates, specify transform=IdentityTransform().

Using Transforms

Transforms are a key concept in matplotlib. A transform takes coordinates that the artist's data is in and converts them to display coordinates -- in other words, pixels on the screen.

If you haven't already seen it, give the matplotlib transforms tutorial a quick read. I'm going to assume a passing familiarity with the first few paragraphs of that tutorial, so if you're

For example, if we want to draw a line across the entire figure, we'd use something like:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

# The "clip_on" here specifies that we _don't_ want to clip the line
# to the extent of the axes
ax.plot([0, 1], [0, 1], lw=3, color='salmon', clip_on=False, 
        transform=fig.transFigure)

plt.show()

enter image description here


This line will always extend from the lower-left corner of the figure to the upper right corner, no matter how we interactively resize/zoom/pan the plot.


Drawing in pixels

The most common transforms you'll use are ax.transData, ax.transAxes, and fig.transFigure. However, to draw in points/pixels, you actually want no transform at all. In that case, you'll make a new transform instance that does nothing: the IdentityTransform. This specifies that the data for the artist is in "raw" pixels.


Any time you'd like to plot in "raw" pixels, specify transform=IdentityTransform() to the artist.


If you'd like to work in points, recall that there are 72 points to an inch, and that for matplotlib, fig.dpi controls the number of pixels in an "inch" (it's actually independent of the physical display). Therefore, we can convert points to pixels with a simple formula.

As an example, let's place a marker 30 points from the bottom-left edge of the figure:

import matplotlib.pyplot as plt
from matplotlib.transforms import IdentityTransform

fig, ax = plt.subplots()

points = 30
pixels = fig.dpi * points / 72.0

ax.plot([pixels], [pixels], marker='o', color='lightblue', ms=20,
        transform=IdentityTransform(), clip_on=False)
plt.show()

enter image description here


Composing Transforms

One of the more useful things about matplotlib's transforms is that they can be added to create a new transform. This makes it easy to create shifts.

For example, let's plot a line, then add another line shifted by 15 pixels in the x-direction:

import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D

fig, ax = plt.subplots()
ax.plot(range(10), color='lightblue', lw=4)

ax.plot(range(10), color='gray', lw=4,
        transform=ax.transData + Affine2D().translate(15, 0))

plt.show()

enter image description here

A key thing to keep in mind is that the order of the additions matters. If we did Affine2D().translate(15, 0) + ax.transData instead, we'd shift things by 15 data units instead of 15 pixels. The added transforms are "chained" (composed would be a more accurate term) in order.

enter image description here


This also makes it easy to define things like "20 pixels from the right hand side of the figure". For example:

import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D

fig, ax = plt.subplots()

ax.plot([1, 1], [0, 1], lw=3, clip_on=False, color='salmon',
        transform=fig.transFigure + Affine2D().translate(-20, 0))

plt.show()

enter image description here

like image 193
Joe Kington Avatar answered Oct 17 '22 13:10

Joe Kington


You can use the transform keyword to change between data coordinate (the default) and axes coordinates. For example:

import matplotlib.pyplot as plt
import matplotlib.lines as lines

plt.plot(range(10),range(10),'ro-')

myline = lines.Line2D((0,0.5,1),(0.5,0.5,0),color='b') # data coords

plt.gca().add_artist(myline)

mynewline = lines.Line2D((0,0.5,1),(0.5,0.5,0),color='g',transform=plt.gca().transAxes) # Axes coordinates

plt.gca().add_artist(mynewline)

plt.show()

enter image description here

like image 27
tmdavison Avatar answered Oct 17 '22 12:10

tmdavison