Matplotlib legend: how to assign multiple scatterpoints values - python

I'm using the matplotlib library in python to generate publication-quality xy scatter plots. I ran into a problem regarding the markers in the legend. I'm plotting 2 different xy-scatter series; one is a set of xy points that forms a curve, and the other is a single xy point.
I would like the legend to show 3 markers for the "curve", and 1 marker for the single point. The only way I know how to change the number of legend markers is using the "scatterpoints" argument when declaring the legend. However, this argument sets the number of markers for all series in the legend, and I'm not sure how to change each legend entry individually.
Sadly I can't post pictures as a new user, but hopefully this description is sufficient. Is there a way to set scatterpoints values individually for each legend entry using matplotlib?
EDIT: Here are links showing images with different values for scatterpoints.
scatterpoints = 3: http://imgur.com/8ONAT
scatterpoints = 1: http://imgur.com/TFcYV
Hopefully this makes the issue a bit more clear.

you can get the line in legend, and change it yourself:
import numpy as np
import pylab as pl
x = np.linspace(0, 2*np.pi, 100)
pl.plot(x, np.sin(x), "-x", label=u"sin")
pl.plot(x, np.random.standard_normal(len(x)), 'o', label=u"rand")
leg = pl.legend(numpoints=3)
l = leg.legendHandles[1]
l._legmarker.set_xdata(l._legmarker.get_xdata()[1:2])
l._legmarker.set_ydata(l._legmarker.get_ydata()[1:2])
##or
#l._legmarker.set_markevery(3)
pl.show()
Legend.legendHandles is a list of all the lines in legend, and the _legmarker attribute of the line is the marks.
You can call set_markevery(3) or set_xdata() & set_ydata() to change the number of marks.

Related

how to get different line colors depending on one variable for different plots in one single figure in python? [duplicate]

This question already has an answer here:
Drawing a colorbar aside a line plot, using Matplotlib
(1 answer)
Closed 1 year ago.
Let's say I have one figure with a certain number of plots, which resembles like this one:
where the colors of the single plots are decided automatically by matplotlib. The code to obtain this is very simple:
for i in range(len(some_list)):
x, y = some_function(dataset, some_list[i])
plt.plot(x, y)
Now suppose that all these lines depend on a third variable z. I would like to include this information plotting the given lines with a color that gives information about the magnitude of z, possibly using a colormap and a colorbar on the right side of the figure. What would you suggest me to do? I exclude to use a legend since in my figures I have many more lines that the ones I am showing. All information I can find is about how to draw one single line with different colors, but this is not what I am looking for. I thank you in advance!
Here it is some code that, in my opinion, you can easily adapt to your problem
import numpy as np
import matplotlib.pyplot as plt
from random import randint
# generate some data
N, vmin, vmax = 12, 0, 20
rd = lambda: randint(vmin, vmax)
segments_z = [((rd(),rd()),(rd(),rd()),rd()) for _ in range(N)]
# prepare for the colorization of the lines,
# first the normalization function and the colomap we want to use
norm = plt.Normalize(vmin, vmax)
cm = plt.cm.rainbow
# most important, plt.plot doesn't prepare the ScalarMappable
# that's required to draw the colorbar, so we'll do it instead
sm = plt.cm.ScalarMappable(cmap=cm, norm=norm)
# plot the segments, the segment color depends on z
for p1, p2, z in segments_z:
x, y = zip(p1,p2)
plt.plot(x, y, color=cm(norm(z)))
# draw the colorbar, note that we pass explicitly the ScalarMappable
plt.colorbar(sm)
# I'm done, I'll show the results,
# you probably want to add labels to the axes and the colorbar.
plt.show()

Using markevery kwarg with axhline

I was trying to plot a vertical line with markers on it using ax.axvline but the markers only show up on the bottom and top of the figure. I have played around with the markevery kwarg but it does not seem to have any effect when I change it even though it works for a normal line plot. Does anyone know if this is because no discrete values are specified along the axis or am I just doing something wrong?
I realize that I can plot a vertical line on my own and specify the markers, but I figure given the purpose of axvline I should use it.
Here is an example code of what I am talking about:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-10,10)
y = x**2-15.
fig = plt.figure(figsize=(4,4))
ax = plt.subplot(111)
ax.plot(y,x) #Test curve
ax.plot(2+np.zeros(len(x)),x,marker='X',markevery=1) #another way to plot what I want.
ax.axvline(0,c='r',marker='X',markevery=1) #markerevery doesn't seem to work
plt.show()
As mentioned by ImportanceofBeingErnest, the markereverykwarg does not apply for axvline or axhline because there are technically only 2 points used to draw the line at the boundaries.

Aspect ratio in semi-log plot with Matplotlib

When I plot a function in matplotlib, the plot is framed by a rectangle. I want the ratio of the length and height of this rectangle to be given by the golden mean ,i.e., dx/dy=1.618033...
If the x and y scale are linear I found this solution using google
import numpy as np
import matplotlib.pyplot as pl
golden_mean = (np.sqrt(5)-1.0)/2.0
dy=pl.gca().get_ylim()[1]-pl.gca().get_ylim()[0]
dx=pl.gca().get_xlim()[1]-pl.gca().get_xlim()[0]
pl.gca().set_aspect((dx/dy)*golden_mean,adjustable='box')
If it is a log-log plot I came up with this solution
dy=np.abs(np.log10(pl.gca().get_ylim()[1])-np.log10(pl.gca().get_ylim()[0]))
dx=np.abs(np.log10(pl.gca().get_xlim()[1])-np.log10(pl.gca().get_xlim()[0]))
pl.gca().set_aspect((dx/dy)*golden_mean,adjustable='box')
However, for a semi-log plot, when I call set_aspect, I get
UserWarning: aspect is not supported for Axes with xscale=log, yscale=linear
Can anyone think of a work-around for this?
the most simple solution would be to log your data and then use the method for lin-lin.
you can then label the axes to let it look like a normal log-plot.
ticks = np.arange(min_logx, max_logx, 1)
ticklabels = [r"$10^{}$".format(tick) for tick in ticks]
pl.yticks(ticks, ticklabels)
if you have higher values than 10e9 you will need three pairs of braces, two pairs for the LaTeX braces and one for the .format()
ticklabels = [r"$10^{{{}}}$".format(tick) for tick in ticks]
Edit:
if you want also the ticks for 0.1ex ... 0.9ex, you want to use the minor ticks as well:
they need to be located at log10(1), log10(2), log10(3) ..., log10(10), log10(20) ...
you can create and set them with:
minor_ticks = []
for i in range(min_exponent, max_exponent):
for j in range(2,10):
minor_ticks.append(i+np.log10(j))
plt.gca().set_yticks(minor_labels, minor=True)

Matplotlib connect scatterplot points with line - Python

I have two lists, dates and values. I want to plot them using matplotlib. The following creates a scatter plot of my data.
import matplotlib.pyplot as plt
plt.scatter(dates,values)
plt.show()
plt.plot(dates, values) creates a line graph.
But what I really want is a scatterplot where the points are connected by a line.
Similar to in R:
plot(dates, values)
lines(dates, value, type="l")
, which gives me a scatterplot of points overlaid with a line connecting the points.
How do I do this in python?
I think #Evert has the right answer:
plt.scatter(dates,values)
plt.plot(dates, values)
plt.show()
Which is pretty much the same as
plt.plot(dates, values, '-o')
plt.show()
You can replace -o with another suitable format string as described in the documentation.
You can also split the choices of line and marker styles using the linestyle= and marker= keyword arguments.
For red lines an points
plt.plot(dates, values, '.r-')
or for x markers and blue lines
plt.plot(dates, values, 'xb-')
In addition to what provided in the other answers, the keyword "zorder" allows one to decide the order in which different objects are plotted vertically.
E.g.:
plt.plot(x,y,zorder=1)
plt.scatter(x,y,zorder=2)
plots the scatter symbols on top of the line, while
plt.plot(x,y,zorder=2)
plt.scatter(x,y,zorder=1)
plots the line over the scatter symbols.
See, e.g., the zorder demo
They keyword argument for this is marker, and you can set the size of the marker with markersize. To generate a line with scatter symbols on top:
plt.plot(x, y, marker = '.', markersize = 10)
To plot a filled spot, you can use marker '.' or 'o' (the lower case letter oh). For a list of all markers, see:
https://matplotlib.org/stable/api/markers_api.html

Plotting point on top of filled contour plot adds lots of blank space

I have the following Python code which I am using to plot a filled contour plot:
def plot_polar_contour(values, azimuths, zeniths):
theta = np.radians(azimuths)
zeniths = np.array(zeniths)
values = np.array(values)
values = values.reshape(len(azimuths), len(zeniths))
r, theta = np.meshgrid(zeniths, np.radians(azimuths))
fig, ax = subplots(subplot_kw=dict(projection='polar'))
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
cax = ax.contourf(theta, r, values, 30)
autumn()
cb = fig.colorbar(cax)
cb.set_label("Pixel reflectance")
show()
This gives me a plot like:
However, when I add the line ax.plot(0, 30, 'p') just before show() I get the following:
It seems that just adding that one point (which is well within the original axis range) screws up the axis range on the radius axis.
Is this by design, or is this a bug? What would you suggest doing to fix it? Do I need to manually adjust the axis ranges, or is there a way to stop the extra plot command doing this?
If the axis auto-scaling mode isn't explicitly specified, plot will use "loose" autoscaling and contourf will use "tight" autoscaling.
The same things happens for non-polar axes. E.g.
import matplotlib.pyplot as plt
import numpy as np
plt.imshow(np.random.random((10,10)))
plt.plot([7], [7], 'ro')
plt.show()
You have a number of options.
Explicitly call ax.axis('image') or ax.axis('tight') at some point in the code.
Pass in scalex=False and scaley=False as keyword arguments to plot.
Manually set the axis limits.
The easiest and most readable is to just explicitly call ax.axis('tight'), i.m.o.

Categories