I want to plot a grid onto the background of 2D plot line, similar as it is for ECG presentations, i.e. at specific points in regular interval dots are shown, e.g. as in this image
In this example there are precisely 4 dots spaced between to major dots. Want I don't want is sth as this Plotting a grid with Matplotlib, i.e. just dotted grid lines
What I did so far (coming from ancient matlab knowledge) is this:
xg = np.linspace(iStart/fs, iEnd/fs, len(y))
yrange = ax.get_ylim()
yg = np.linspace(yrange[0], yrange[1], 4)
xx, yy = np.meshgrid(xg, yg)
gridpoints, = plt.plot(xx.reshape(1,-1),yy.reshape(1,-1),linewidth=0.3,color='0.75',marker=".",markersize=10)
But it gets me this:
What am I not getting right?
As said by jpnadas you can use plt.grid()
here an example on how you can put and customize grid
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
img = plt.imread(imagename)
_, ax = plt.subplots(ncols=1,nrows=1)
ax.imshow(img)
plt.gca().xaxis.set_major_locator(MultipleLocator(16))
plt.gca().yaxis.set_major_locator(MultipleLocator(16))
plt.gca().xaxis.set_minor_locator(MultipleLocator(32))
plt.gca().yaxis.set_minor_locator(MultipleLocator(32))
# Don't allow the axis to be on top of your data
ax.set_axisbelow(True)
# Turn on the minor TICKS, which are required for the minor GRID
ax.minorticks_on()
# Customize the major grid
ax.grid(which='major', linestyle='-', linewidth='4', color='yellow')
# Customize the minor grid
ax.grid(which='minor', linestyle=':', linewidth='2', color='blue')
plt.show()
I found my mistake. It was not an error in my thinking, but that my len(y) in the linspace of x was referencing the wrong vector, hencing producing a too fine grid, which looked like a line.
Related
The question
I am trying to draw grid lines from the ticks of my SecondaryAxis with
ax2.grid(color=color,linestyle='--')
nothing shows up on the figure, I believe I am in the same situation as for Format SecondaryAxis ticklabels Matplotlib, aren't I ?
However, does anybody have a workaround for the issue without reversing the scales ? I mean by reversing the scale is to have the percentages scale on the main axis and the normal scale on the secondary axis.
The full code
import matplotlib.pyplot as plt
import numpy as np
#generate dummy load duration curve
dur=2500
load = np.random.normal(60,30,dur+1)
load[::-1].sort()
x=range(0,dur+1)
perticks = np.linspace(0,1,11)
xticks = perticks*dur
# get yticks from xticks
yticks = np.interp(xticks, range(0,dur+1), load)
print(yticks)
# create figure object with axe object
fig, ax1 = plt.subplots(figsize=(16, 8))
ax1.plot(x, load)
#create second axis
ax2 = ax1.secondary_yaxis('right')
# label and color of the secondaryaxis
perlabels = ['0%', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%']
color ='tab:blue'
ax2.set_yticks(yticks)
ax2.set_yticklabels(labels=perlabels)
ax2.tick_params(axis='y', color=color, labelcolor=color)
# draw grid lines on the secondaryaxis
ax2.grid(color=color,linestyle='--')
# do the same for x axis
ax3 = ax1.secondary_xaxis('top')
ax3.set_xticks(xticks)
ax3.set_xticklabels(labels=perlabels)
ax3.tick_params(axis='x', color=color, labelcolor=color)
ax3.grid(color=color,linestyle='--')
The output
I did some digging on this topic, and opened an issue on GitHub. Here's what I found out:
The SecondaryAxis is "quite new thing", added in matplotlib 3.1.0. (May 2019). Even the v.3.3.3 docs say that the secondary_xaxis() method is experimental.
The SecondaryAxis inherits from _AxesBase, which is an "implementation detail". It is not supposed (as of v.3.3.3) to work as Axes object, and the SecondaryAxis.grid() is not supposed to draw anything (like _AxesBase.grid() does). Although, I agree it is misleading that there is a non-working method.
Therefore, at the time of writing, .grid() is only assumed to work on primaxy axes.
Making the blue axis primary
Since .grid() only works on non-secondary axis, you make the primary axis blue, and move it to top & right.
Code
# Take the x and y-ticks for transfering them to secondary axis
xticks_orig = ax1.get_xticks()
yticks_orig = ax1.get_yticks()
# Make the primary axis blue since we want to draw grid on it
ax1.xaxis.tick_top()
ax1.yaxis.tick_right()
ax1.set_xticks(xticks)
ax1.set_yticks(yticks)
ax1.set_yticklabels(labels=perlabels)
ax1.set_xticklabels(labels=perlabels)
ax1.tick_params(axis="both", color=color, labelcolor=color)
ax1.grid(color=color, linestyle="--")
# Draw the black secondary axis
ax2 = ax1.secondary_yaxis("left")
ax3 = ax1.secondary_xaxis("bottom")
ax2.set_yticks(yticks_orig)
ax3.set_xticks(xticks_orig)
Adding grid lines manually
You could add the grid lines also manually, like this
xlim = ax1.get_xlim()
for y in ax2.get_yticks():
ax1.plot(xlim, (y, y), ls="--", color=color, lw=0.5)
ax1.set_xlim(xlim)
ylim = ax1.get_ylim()
for x in ax3.get_xticks():
ax1.plot((x, x), ylim, ls="--", color=color, lw=0.5)
ax1.set_ylim(ylim)
The output would look like this:
The difference here is now we draw lines on the figure which look like the grid lines.
To add on np8's answer, you can also use axvline to draw the lines. This has the advantage that you do not need to keep track of the y limits manually:
for x in ax2.get_xticks():
ax1.axvline(x, color=color, zorder=-1, linestyle="--", linewidth=0.5)
Note also that you will need to appropriately transform the x-coordinate to match the transform you do from ax1 to ax2.
Also, in my case I first had to render the canvas in order for the tick labels to be generated:
fig1.canvas.draw()
I'm doing a bunch of work with various spherical projection plots using the Astropy WCS package, and have run into some frustrations concerning grid lines. As grid lines do not always intersect with the image bounding box or multiple intersect at the same place, they can go unlabeled or have their labels rendered illegible. I would like to be able to insert grid line labels in each line, much akin to the matplotlib.pyplot.clabel() function applied to contour plots, as in this matplotlib example. I can't embed the image as I am a new user; my apologies.
I know I can place labels using text(), figtext(), or annotate(), but since clabel() works I figure the functionality already exists, even if it hasn't been applied to grid lines. Projection plotting aside, does anyone know a way that in-line grid line labels akin to clabel() can be applied to grid lines on a plain rectangular plot?
To annotate the gridlines, you may use the positions of the major ticks (as those are the positions at which the gridlines are created).
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,10)
y = np.sin(x)*10
fig, ax = plt.subplots()
ax.plot(x,y)
ax.grid()
for xi in ax.xaxis.get_majorticklocs():
ax.text(xi,0.8, "{:.2f}".format(xi), clip_on=True, ha="center",
transform=ax.get_xaxis_transform(), rotation=90,
bbox={'facecolor':'w', 'pad':1, "edgecolor":"None"})
for yi in ax.yaxis.get_majorticklocs():
ax.text(0.86,yi, "{:.2f}".format(yi), clip_on=True, va="center",
transform=ax.get_yaxis_transform(),
bbox={'facecolor':'w', 'pad':1, "edgecolor":"None"})
plt.show()
I'm creating a plot using python 3.5.1 and matplotlib 1.5.1 that has two subplots (side by side) with a shared Y axis. A sample output image is shown below:
Notice the extra white space at the top and bottom of each set of axes. Try as I might I can't seem to get rid of it. The overall goal of the figure is to have a waterfall type plot on the left with a shared Y axes with the plot on the right.
Here's some sample code to reproduce the image above.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib inline
# create some X values
periods = np.linspace(1/1440, 1, 1000)
# create some Y values (will be datetimes, not necessarily evenly spaced
# like they are in this example)
day_ints = np.linspace(1, 100, 100)
days = pd.to_timedelta(day_ints, 'D') + pd.to_datetime('2016-01-01')
# create some fake data for the number of points
points = np.random.random(len(day_ints))
# create some fake data for the color mesh
Sxx = np.random.random((len(days), len(periods)))
# Create the plots
fig = plt.figure(figsize=(8, 6))
# create first plot
ax1 = plt.subplot2grid((1,5), (0,0), colspan=4)
im = ax1.pcolormesh(periods, days, Sxx, cmap='viridis', vmin=0, vmax=1)
ax1.invert_yaxis()
ax1.autoscale(enable=True, axis='Y', tight=True)
# create second plot and use the same y axis as the first one
ax2 = plt.subplot2grid((1,5), (0,4), sharey=ax1)
ax2.scatter(points, days)
ax2.autoscale(enable=True, axis='Y', tight=True)
# Hide the Y axis scale on the second plot
plt.setp(ax2.get_yticklabels(), visible=False)
#ax1.set_adjustable('box-forced')
#ax2.set_adjustable('box-forced')
fig.colorbar(im, ax=ax1)
As you can see in the commented out code I've tried a number of approaches, as suggested by posts like https://github.com/matplotlib/matplotlib/issues/1789/ and Matplotlib: set axis tight only to x or y axis.
As soon as I remove the sharey=ax1 part of the second subplot2grid call the problem goes away, but then I also don't have a common Y axis.
Autoscale tends to add a buffer to the data so that all of the data points are easily visible and not part-way cut off by the axes.
Change:
ax1.autoscale(enable=True, axis='Y', tight=True)
to:
ax1.set_ylim(days.min(),days.max())
and
ax2.autoscale(enable=True, axis='Y', tight=True)
to:
ax2.set_ylim(days.min(),days.max())
To get:
I'm trying to have an imshow plot inset into another in matplotlib.
I have a figure that has an plot on top and an imshow on the bottom:
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
plt.plot()
ax2 = fig.add_subplot(2,1,2)
plt.imshow()
and then I have another figure, which also is comprised of a plot on top and an imshow below. I want this second figure to be inset into the top plot on the first figure.
I tried follow this example. The code ran without an error but I had an empty axis in the position I wanted it.
My problem is just that I'm not sure where to put the plt.setp() command If that's what I am supposed to use.
First, I don't think you can put a figure into a figure in matplotlib. You will have to arrange your Axes objects (subplots) to achieve the look you want.
The example you provided uses absolute positioning to do that. setp there is not related to positioning, though — it just removes axis ticks from insets. An example of code that does what you want:
import numpy
import matplotlib.pyplot as plt
x = numpy.linspace(0, 1)
xx, yy = numpy.meshgrid(x, x)
im = numpy.sin(xx) + numpy.cos(yy)**2
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(x, x**2)
ax2 = fig.add_subplot(2,1,2)
ax2.imshow(im)
inset1 = fig.add_axes([.15, .72, .15, .15])
inset1.plot(x, x**2)
plt.setp(inset1, xticks=[], yticks=[])
inset2 = fig.add_axes([0.15, 0.55, .15, .15])
inset2.imshow(im)
plt.setp(inset2, xticks=[], yticks=[])
fig.savefig('tt.png')
Here the insets use explicit positioning with coordinates given in "figure units" (the whole figure has size 1 by 1).
Now, of course, there's plenty of room for improvement. You probably want the widths of your plots to be equal, so you'll have to:
specify the positioning of all subplots explicitly; or
play with aspect ratios; or
use two GridSpec objects (this way you'll have the least amount of magic numbers and manual adjustment)
I want to have some grid lines on a plot, but actually full-length lines are too much/distracting, even dashed light grey lines. I went and manually did some editing of the SVG output to get the effect I was looking for. Can this be done with matplotlib? I had a look at the pyplot api for grid, and the only thing I can see that might be able to get near it are the xdata and ydata Line2D kwargs.
This cannot be done through the basic API, because the grid lines are created using only two points. The grid lines would need a 'data' point at every tick mark for there to be a marker drawn. This is shown in the following example:
import matplotlib.pyplot as plt
ax = plt.subplot(111)
ax.grid(clip_on=False, marker='o', markersize=10)
plt.savefig('crosses.png')
plt.show()
This results in:
Notice how the 'o' markers are only at the beginning and the end of the Axes edges, because the grid lines only involve two points.
You could write a method to emulate what you want, creating the cross marks using a series of Artists, but it's quicker to just leverage the basic plotting capabilities to draw the cross pattern.
This is what I do in the following example:
import matplotlib.pyplot as plt
import numpy as np
NPOINTS=100
def set_grid_cross(ax, in_back=True):
xticks = ax.get_xticks()
yticks = ax.get_yticks()
xgrid, ygrid = np.meshgrid(xticks, yticks)
kywds = dict()
if in_back:
kywds['zorder'] = 0
grid_lines = ax.plot(xgrid, ygrid, 'k+', **kywds)
xvals = np.arange(NPOINTS)
yvals = np.random.random(NPOINTS) * NPOINTS
ax1 = plt.subplot(121)
ax2 = plt.subplot(122)
ax1.plot(xvals, yvals, linewidth=4)
ax1.plot(xvals, xvals, linewidth=7)
set_grid_cross(ax1)
ax2.plot(xvals, yvals, linewidth=4)
ax2.plot(xvals, xvals, linewidth=7)
set_grid_cross(ax2, in_back=False)
plt.savefig('gridpoints.png')
plt.show()
This results in the following figure:
As you can see, I take the tick marks in x and y to define a series of points where I want grid marks ('+'). I use meshgrid to take two 1D arrays and make 2 2D arrays corresponding to the double loop over each grid point. I plot this with the mark style as '+', and I'm done... almost. This plots the crosses on top, and I added an extra keyword to reorder the list of lines associated with the plot. I adjust the zorder of the grid marks if they are to be drawn behind everything.*****
The example shows the left subplot where by default the grid is placed in back, and the right subplot disables this option. You can notice the difference if you follow the green line in each plot.
If you are bothered by having grid crosses on the boarder, you can remove the first and last tick marks for both x and y before you define the grid in set_grid_cross, like so:
xticks = ax.get_xticks()[1:-1] #< notice the slicing
yticks = ax.get_yticks()[1:-1] #< notice the slicing
xgrid, ygrid = np.meshgrid(xticks, yticks)
I do this in the following example, using a larger, different marker to make my point:
***** Thanks to the answer by #fraxel for pointing this out.
You can draw on line segments at every intersection of the tickpoints. Its pretty easy to do, just grab the tick locations get_ticklocs() for both axis, then loop through all combinations, drawing short line segments using axhline and axvline, thus creating a cross hair at every intersection. I've set zorder=0 so the cross-hairs are drawn first, so that they are behind the plot data. Its easy to control the color/alpha and cross-hair size. Couple of slight 'gotchas'... do the plot before you get the tick locations.. and also the xmin and xmax parameters seem to require normalisation.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot((0,2,3,5,5,5,6,7,8,6,6,4,3,32,7,99), 'r-',linewidth=4)
x_ticks = ax.xaxis.get_ticklocs()
y_ticks = ax.yaxis.get_ticklocs()
for yy in y_ticks[1:-1]:
for xx in x_ticks[1:-1]:
plt.axhline(y=yy, xmin=xx / max(x_ticks) - 0.02,
xmax=xx / max(x_ticks) + 0.02, color='gray', alpha=0.5, zorder=0)
plt.axvline(x=xx, ymin=yy / max(y_ticks) - 0.02,
ymax=yy / max(y_ticks) + 0.02, color='gray', alpha=0.5, zorder=0)
plt.show()