How to draw vertical lines on a given plot - python
Given a plot of a signal in time representation, how can I draw lines marking the corresponding time index?
Specifically, given a signal plot with a time index ranging from 0 to 2.6 (seconds), I want to draw vertical red lines indicating the corresponding time index for the list [0.22058956, 0.33088437, 2.20589566]. How can I do it?
The standard way to add vertical lines that will cover your entire plot window without you having to specify their actual height is plt.axvline
import matplotlib.pyplot as plt
plt.axvline(x=0.22058956)
plt.axvline(x=0.33088437)
plt.axvline(x=2.20589566)
OR
xcoords = [0.22058956, 0.33088437, 2.20589566]
for xc in xcoords:
plt.axvline(x=xc)
You can use many of the keywords available for other plot commands (e.g. color, linestyle, linewidth ...). You can pass in keyword arguments ymin and ymax if you like in axes corrdinates (e.g. ymin=0.25, ymax=0.75 will cover the middle half of the plot). There are corresponding functions for horizontal lines (axhline) and rectangles (axvspan).
matplotlib.pyplot.vlines vs. matplotlib.pyplot.axvline
These methods are applicable to plots generated with seaborn and pandas.DataFrame.plot, which both use matplotlib.
The difference is that vlines accepts one or more locations for x, while axvline permits one location.
Single location: x=37.
Multiple locations: x=[37, 38, 39].
vlines takes ymin and ymax as a position on the y-axis, while axvline takes ymin and ymax as a percentage of the y-axis range.
When passing multiple lines to vlines, pass a list to ymin and ymax.
Also matplotlib.axes.Axes.vlines and matplotlib.axes.Axes.axvline for the object-oriented API.
If you're plotting a figure with something like fig, ax = plt.subplots(), then replace plt.vlines or plt.axvline with ax.vlines or ax.axvline, respectively.
See this answer for horizontal lines with .hlines.
import numpy as np
import matplotlib.pyplot as plt
xs = np.linspace(1, 21, 200)
plt.figure(figsize=(10, 7))
# only one line may be specified; full height
plt.axvline(x=36, color='b', label='axvline - full height')
# only one line may be specified; ymin & ymax specified as a percentage of y-range
plt.axvline(x=36.25, ymin=0.05, ymax=0.95, color='b', label='axvline - % of full height')
# multiple lines all full height
plt.vlines(x=[37, 37.25, 37.5], ymin=0, ymax=len(xs), colors='purple', ls='--', lw=2, label='vline_multiple - full height')
# multiple lines with varying ymin and ymax
plt.vlines(x=[38, 38.25, 38.5], ymin=[0, 25, 75], ymax=[200, 175, 150], colors='teal', ls='--', lw=2, label='vline_multiple - partial height')
# single vline with full ymin and ymax
plt.vlines(x=39, ymin=0, ymax=len(xs), colors='green', ls=':', lw=2, label='vline_single - full height')
# single vline with specific ymin and ymax
plt.vlines(x=39.25, ymin=25, ymax=150, colors='green', ls=':', lw=2, label='vline_single - partial height')
# place the legend outside
plt.legend(bbox_to_anchor=(1.0, 1), loc='upper left')
plt.show()
Seaborn axes-level plot
import seaborn as sns
# sample data
fmri = sns.load_dataset("fmri")
# x index for max y values for stim and cue
c_max, s_max = fmri.pivot_table(index='timepoint', columns='event', values='signal', aggfunc='mean').idxmax()
# plot
g = sns.lineplot(data=fmri, x="timepoint", y="signal", hue="event")
# y min and max
ymin, ymax = g.get_ylim()
# vertical lines
g.vlines(x=[c_max, s_max], ymin=ymin, ymax=ymax, colors=['tab:orange', 'tab:blue'], ls='--', lw=2)
Seaborn figure-level plot
Each axes must be iterated through.
import seaborn as sns
# sample data
fmri = sns.load_dataset("fmri")
# used to get the index values (x) for max y for each event in each region
fpt = fmri.pivot_table(index=['region', 'timepoint'], columns='event', values='signal', aggfunc='mean')
# plot
g = sns.relplot(data=fmri, x="timepoint", y="signal", col="region", hue="event", kind="line")
# iterate through the axes
for ax in g.axes.flat:
# get y min and max
ymin, ymax = ax.get_ylim()
# extract the region from the title for use in selecting the index of fpt
region = ax.get_title().split(' = ')[1]
# get x values for max event
c_max, s_max = fpt.loc[region].idxmax()
# add vertical lines
ax.vlines(x=[c_max, s_max], ymin=ymin, ymax=ymax, colors=['tab:orange', 'tab:blue'], ls='--', lw=2, alpha=0.5)
For 'region = frontal' the maximum value of both events occurs at 5.
Barplot and Histograms
Note that bar plot tick locations have a zero-based index, regardless of the axis tick labels, so select x based on the bar index, not the tick label.
ax.get_xticklabels() will show the locations and labels.
import pandas as pd
import seaborn as sns
# load data
tips = sns.load_dataset('tips')
# histogram
ax = tips.plot(kind='hist', y='total_bill', bins=30, ec='k', title='Histogram with Vertical Line')
_ = ax.vlines(x=16.5, ymin=0, ymax=30, colors='r')
# barplot
ax = tips.loc[5:25, ['total_bill', 'tip']].plot(kind='bar', figsize=(15, 4), title='Barplot with Vertical Lines', rot=0)
_ = ax.vlines(x=[0, 17], ymin=0, ymax=45, colors='r')
Time Series Axis
The dates in the dataframe to be the x-axis must be a datetime dtype. If the column or index is not the correct type, it must be converted with pd.to_datetime.
If an array or list of dates is being used, refer to Converting numpy array of strings to datetime or Convert datetime list into date python, respectively.
x will accept a date like '2020-09-24' or datetime(2020, 9, 2).
import pandas_datareader as web # conda or pip install this; not part of pandas
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
# get test data; this data is downloaded with the Date column in the index as a datetime dtype
df = web.DataReader('^gspc', data_source='yahoo', start='2020-09-01', end='2020-09-28').iloc[:, :2]
# display(df.head(2))
High Low
Date
2020-09-01 3528.030029 3494.600098
2020-09-02 3588.110107 3535.229980
# plot dataframe; the index is a datetime index
ax = df.plot(figsize=(9, 6), title='S&P 500', ylabel='Price')
# add vertical lines
ax.vlines(x=[datetime(2020, 9, 2), '2020-09-24'], ymin=3200, ymax=3600, color='r', label='test lines')
ax.legend(bbox_to_anchor=(1, 1), loc='upper left')
plt.show()
For multiple lines
xposition = [0.3, 0.4, 0.45]
for xc in xposition:
plt.axvline(x=xc, color='k', linestyle='--')
To add a legend and/or colors to some vertical lines, then use this:
import matplotlib.pyplot as plt
# x coordinates for the lines
xcoords = [0.1, 0.3, 0.5]
# colors for the lines
colors = ['r','k','b']
for xc,c in zip(xcoords,colors):
plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c)
plt.legend()
plt.show()
Results
Calling axvline in a loop, as others have suggested, works, but it can be inconvenient because
Each line is a separate plot object, which causes things to be very slow when you have many lines.
When you create the legend each line has a new entry, which may not be what you want.
Instead, you can use the following convenience functions which create all the lines as a single plot object:
import matplotlib.pyplot as plt
import numpy as np
def axhlines(ys, ax=None, lims=None, **plot_kwargs):
"""
Draw horizontal lines across plot
:param ys: A scalar, list, or 1D array of vertical offsets
:param ax: The axis (or none to use gca)
:param lims: Optionally the (xmin, xmax) of the lines
:param plot_kwargs: Keyword arguments to be passed to plot
:return: The plot object corresponding to the lines.
"""
if ax is None:
ax = plt.gca()
ys = np.array((ys, ) if np.isscalar(ys) else ys, copy=False)
if lims is None:
lims = ax.get_xlim()
y_points = np.repeat(ys[:, None], repeats=3, axis=1).flatten()
x_points = np.repeat(np.array(lims + (np.nan, ))[None, :], repeats=len(ys), axis=0).flatten()
plot = ax.plot(x_points, y_points, scalex = False, **plot_kwargs)
return plot
def axvlines(xs, ax=None, lims=None, **plot_kwargs):
"""
Draw vertical lines on plot
:param xs: A scalar, list, or 1D array of horizontal offsets
:param ax: The axis (or none to use gca)
:param lims: Optionally the (ymin, ymax) of the lines
:param plot_kwargs: Keyword arguments to be passed to plot
:return: The plot object corresponding to the lines.
"""
if ax is None:
ax = plt.gca()
xs = np.array((xs, ) if np.isscalar(xs) else xs, copy=False)
if lims is None:
lims = ax.get_ylim()
x_points = np.repeat(xs[:, None], repeats=3, axis=1).flatten()
y_points = np.repeat(np.array(lims + (np.nan, ))[None, :], repeats=len(xs), axis=0).flatten()
plot = ax.plot(x_points, y_points, scaley = False, **plot_kwargs)
return plot
In addition to the plt.axvline and plt.plot((x1, x2), (y1, y2)) or plt.plot([x1, x2], [y1, y2]) as provided in the answers above, one can also use
plt.vlines(x_pos, ymin=y1, ymax=y2)
to plot a vertical line at x_pos spanning from y1 to y2 where the values y1 and y2 are in absolute data coordinates.
Related
python code to place x and y label in the middle of tick positions in matplotlib.pyplot
I am trying to create a heatmap by putting gridlines to some particular positions which I have done. Suppose, I tried to make gridlines in positions 358 and 589 in a matrix of length 640,640. After that, I wanted to change the label from 358 to a defined value of 999 and 589 to a specified value of 1023. However, I cannot change the x and y labels in the center position of two gridlines. For example, I have tried the following: data = np.random.rand(640, 640) fig, ax = plt.subplots() im = ax.imshow(data,cmap='coolwarm') ax.set_xticks([358,589]) ax.set_yticks([358,589]) ax.set_xticklabels([999,1023]) ax.set_yticklabels([999,1023]) ax.grid(which='major',color='black',linestyle='--',linewidth=1,alpha=0.5) plt.show() That create a image as follows: Heatmap with customized labelling But I want the labeling in the middle of two gridlines instead of the gridline positions. How can that be done?
By default, both the tick labels and the grid lines are decided via the major ticks. To change this, you could use the minor ticks to position the grid lines and the major ticks for the tick labels: from matplotlib import pyplot as plt import numpy as np data = np.random.randn(640, 640).cumsum(axis=0).cumsum(axis=1) fig, ax = plt.subplots() im = ax.imshow(data, cmap='coolwarm') positions = np.array([358, 589]) ax.set_xticks(positions, minor=True) ax.set_yticks(positions, minor=True) borders = np.append(0, positions) mids = (borders[:-1] + borders[1:]) / 2 ax.set_xticks(mids, [999, 1023], minor=False) ax.set_yticks(mids, [999, 1023], minor=False) ax.grid(which='minor', color='black', linestyle='--', linewidth=1, alpha=0.9) plt.show()
Graphing multiple lines for axvline [duplicate]
Given a plot of a signal in time representation, how can I draw lines marking the corresponding time index? Specifically, given a signal plot with a time index ranging from 0 to 2.6 (seconds), I want to draw vertical red lines indicating the corresponding time index for the list [0.22058956, 0.33088437, 2.20589566]. How can I do it?
The standard way to add vertical lines that will cover your entire plot window without you having to specify their actual height is plt.axvline import matplotlib.pyplot as plt plt.axvline(x=0.22058956) plt.axvline(x=0.33088437) plt.axvline(x=2.20589566) OR xcoords = [0.22058956, 0.33088437, 2.20589566] for xc in xcoords: plt.axvline(x=xc) You can use many of the keywords available for other plot commands (e.g. color, linestyle, linewidth ...). You can pass in keyword arguments ymin and ymax if you like in axes corrdinates (e.g. ymin=0.25, ymax=0.75 will cover the middle half of the plot). There are corresponding functions for horizontal lines (axhline) and rectangles (axvspan).
matplotlib.pyplot.vlines vs. matplotlib.pyplot.axvline These methods are applicable to plots generated with seaborn and pandas.DataFrame.plot, which both use matplotlib. The difference is that vlines accepts one or more locations for x, while axvline permits one location. Single location: x=37. Multiple locations: x=[37, 38, 39]. vlines takes ymin and ymax as a position on the y-axis, while axvline takes ymin and ymax as a percentage of the y-axis range. When passing multiple lines to vlines, pass a list to ymin and ymax. Also matplotlib.axes.Axes.vlines and matplotlib.axes.Axes.axvline for the object-oriented API. If you're plotting a figure with something like fig, ax = plt.subplots(), then replace plt.vlines or plt.axvline with ax.vlines or ax.axvline, respectively. See this answer for horizontal lines with .hlines. import numpy as np import matplotlib.pyplot as plt xs = np.linspace(1, 21, 200) plt.figure(figsize=(10, 7)) # only one line may be specified; full height plt.axvline(x=36, color='b', label='axvline - full height') # only one line may be specified; ymin & ymax specified as a percentage of y-range plt.axvline(x=36.25, ymin=0.05, ymax=0.95, color='b', label='axvline - % of full height') # multiple lines all full height plt.vlines(x=[37, 37.25, 37.5], ymin=0, ymax=len(xs), colors='purple', ls='--', lw=2, label='vline_multiple - full height') # multiple lines with varying ymin and ymax plt.vlines(x=[38, 38.25, 38.5], ymin=[0, 25, 75], ymax=[200, 175, 150], colors='teal', ls='--', lw=2, label='vline_multiple - partial height') # single vline with full ymin and ymax plt.vlines(x=39, ymin=0, ymax=len(xs), colors='green', ls=':', lw=2, label='vline_single - full height') # single vline with specific ymin and ymax plt.vlines(x=39.25, ymin=25, ymax=150, colors='green', ls=':', lw=2, label='vline_single - partial height') # place the legend outside plt.legend(bbox_to_anchor=(1.0, 1), loc='upper left') plt.show() Seaborn axes-level plot import seaborn as sns # sample data fmri = sns.load_dataset("fmri") # x index for max y values for stim and cue c_max, s_max = fmri.pivot_table(index='timepoint', columns='event', values='signal', aggfunc='mean').idxmax() # plot g = sns.lineplot(data=fmri, x="timepoint", y="signal", hue="event") # y min and max ymin, ymax = g.get_ylim() # vertical lines g.vlines(x=[c_max, s_max], ymin=ymin, ymax=ymax, colors=['tab:orange', 'tab:blue'], ls='--', lw=2) Seaborn figure-level plot Each axes must be iterated through. import seaborn as sns # sample data fmri = sns.load_dataset("fmri") # used to get the index values (x) for max y for each event in each region fpt = fmri.pivot_table(index=['region', 'timepoint'], columns='event', values='signal', aggfunc='mean') # plot g = sns.relplot(data=fmri, x="timepoint", y="signal", col="region", hue="event", kind="line") # iterate through the axes for ax in g.axes.flat: # get y min and max ymin, ymax = ax.get_ylim() # extract the region from the title for use in selecting the index of fpt region = ax.get_title().split(' = ')[1] # get x values for max event c_max, s_max = fpt.loc[region].idxmax() # add vertical lines ax.vlines(x=[c_max, s_max], ymin=ymin, ymax=ymax, colors=['tab:orange', 'tab:blue'], ls='--', lw=2, alpha=0.5) For 'region = frontal' the maximum value of both events occurs at 5. Barplot and Histograms Note that bar plot tick locations have a zero-based index, regardless of the axis tick labels, so select x based on the bar index, not the tick label. ax.get_xticklabels() will show the locations and labels. import pandas as pd import seaborn as sns # load data tips = sns.load_dataset('tips') # histogram ax = tips.plot(kind='hist', y='total_bill', bins=30, ec='k', title='Histogram with Vertical Line') _ = ax.vlines(x=16.5, ymin=0, ymax=30, colors='r') # barplot ax = tips.loc[5:25, ['total_bill', 'tip']].plot(kind='bar', figsize=(15, 4), title='Barplot with Vertical Lines', rot=0) _ = ax.vlines(x=[0, 17], ymin=0, ymax=45, colors='r') Time Series Axis The dates in the dataframe to be the x-axis must be a datetime dtype. If the column or index is not the correct type, it must be converted with pd.to_datetime. If an array or list of dates is being used, refer to Converting numpy array of strings to datetime or Convert datetime list into date python, respectively. x will accept a date like '2020-09-24' or datetime(2020, 9, 2). import pandas_datareader as web # conda or pip install this; not part of pandas import pandas as pd import matplotlib.pyplot as plt from datetime import datetime # get test data; this data is downloaded with the Date column in the index as a datetime dtype df = web.DataReader('^gspc', data_source='yahoo', start='2020-09-01', end='2020-09-28').iloc[:, :2] # display(df.head(2)) High Low Date 2020-09-01 3528.030029 3494.600098 2020-09-02 3588.110107 3535.229980 # plot dataframe; the index is a datetime index ax = df.plot(figsize=(9, 6), title='S&P 500', ylabel='Price') # add vertical lines ax.vlines(x=[datetime(2020, 9, 2), '2020-09-24'], ymin=3200, ymax=3600, color='r', label='test lines') ax.legend(bbox_to_anchor=(1, 1), loc='upper left') plt.show()
For multiple lines xposition = [0.3, 0.4, 0.45] for xc in xposition: plt.axvline(x=xc, color='k', linestyle='--')
To add a legend and/or colors to some vertical lines, then use this: import matplotlib.pyplot as plt # x coordinates for the lines xcoords = [0.1, 0.3, 0.5] # colors for the lines colors = ['r','k','b'] for xc,c in zip(xcoords,colors): plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c) plt.legend() plt.show() Results
Calling axvline in a loop, as others have suggested, works, but it can be inconvenient because Each line is a separate plot object, which causes things to be very slow when you have many lines. When you create the legend each line has a new entry, which may not be what you want. Instead, you can use the following convenience functions which create all the lines as a single plot object: import matplotlib.pyplot as plt import numpy as np def axhlines(ys, ax=None, lims=None, **plot_kwargs): """ Draw horizontal lines across plot :param ys: A scalar, list, or 1D array of vertical offsets :param ax: The axis (or none to use gca) :param lims: Optionally the (xmin, xmax) of the lines :param plot_kwargs: Keyword arguments to be passed to plot :return: The plot object corresponding to the lines. """ if ax is None: ax = plt.gca() ys = np.array((ys, ) if np.isscalar(ys) else ys, copy=False) if lims is None: lims = ax.get_xlim() y_points = np.repeat(ys[:, None], repeats=3, axis=1).flatten() x_points = np.repeat(np.array(lims + (np.nan, ))[None, :], repeats=len(ys), axis=0).flatten() plot = ax.plot(x_points, y_points, scalex = False, **plot_kwargs) return plot def axvlines(xs, ax=None, lims=None, **plot_kwargs): """ Draw vertical lines on plot :param xs: A scalar, list, or 1D array of horizontal offsets :param ax: The axis (or none to use gca) :param lims: Optionally the (ymin, ymax) of the lines :param plot_kwargs: Keyword arguments to be passed to plot :return: The plot object corresponding to the lines. """ if ax is None: ax = plt.gca() xs = np.array((xs, ) if np.isscalar(xs) else xs, copy=False) if lims is None: lims = ax.get_ylim() x_points = np.repeat(xs[:, None], repeats=3, axis=1).flatten() y_points = np.repeat(np.array(lims + (np.nan, ))[None, :], repeats=len(xs), axis=0).flatten() plot = ax.plot(x_points, y_points, scaley = False, **plot_kwargs) return plot
In addition to the plt.axvline and plt.plot((x1, x2), (y1, y2)) or plt.plot([x1, x2], [y1, y2]) as provided in the answers above, one can also use plt.vlines(x_pos, ymin=y1, ymax=y2) to plot a vertical line at x_pos spanning from y1 to y2 where the values y1 and y2 are in absolute data coordinates.
Matplotlib - Reversing label and line in legend
I'm trying to reverse the label and key columns in a matplotlib legend and I'm really struggling to even know where to start. In a normal matplotlib legend the pattern is key, then label, like in the example below where it goes key (blue line), then label (First Line): To match our company plotting style we plot things the reverse, i.e., label first then key (see the legend below). So the plot above would be First line, then the key (blue line). The additional complication is that the keys should be in one column (so the align in one vertical column) regardless of the length of the label.
Well, there is the keyword markerfirst for this. from matplotlib import pyplot as plt import numpy as np np.random.seed(1234) n=7 fig, ax = plt.subplots() ax.plot(np.arange(n), np.random.random(n), label="ABCDEF") ax.plot(np.arange(n), np.random.random(n), label="G") ax.legend(markerfirst=False) plt.show() Sample output
I would be tempted to write a standalone function that ignores ax.legend() entirely and instead draws a white box, the labels, and the markers where you need them. All the coordinates would be expressed in ax coordinates via transform=ax.transAxes to ensure a proper positioning and replace the locator keyword of ax.legend(). The following code will automatically cram all the artists found on the ax in the legend box boundaries that you defined. You might need to adjust the "padding" a bit. Note that for some reason it does not work with lines of width 0 that only use a marker, but it shouldn't be an issue considering your question. import matplotlib.pyplot as plt import numpy as np fig, ax = plt.subplots() # Dummy data. X = np.linspace(-5, +5, 100) Y1 = np.sin(X) Y2 = np.cos(X/3) Y3 = Y2-Y1 Y4 = Y3*Y1 ax.plot(Y1, label="Y1") ax.plot(Y2, label="Y2") ax.plot(Y3, label="Y3", linestyle="--") ax.plot(Y4, label="Y4", marker="d", markersize=4, linewidth=0) fig.show() def custom_legend(ax): """Adds a custom legend to the provided ax. Its labels are aligned on the left and the markers on the right. Both are taken automatically from the ax.""" handles, labels = ax.get_legend_handles_labels() # Boundaries of your custom legend. xmin, xmax = 0.7, 0.9 ymin, ymax = 0.5, 0.9 N = len(handles) width = xmax-xmin height = ymax-ymin dy = height/N r = plt.Rectangle((xmin, ymin), width=width, height=height, transform=ax.transAxes, fill=True, facecolor="white", edgecolor="black", zorder=1000) ax.add_artist(r) # Grab the tiny lines that would be created by a call to `ax.legend()` so # that we don't have to retrieve all the attributes ourselves. legend = ax.legend() handles = legend.legendHandles.copy() legend.remove() for n, (handle, label) in enumerate(zip(handles, labels)): # Place the labels on the left of the legend box. x = xmin + 0.01 y = ymax - n*dy - 0.05 ax.text(x, y, label, transform=ax.transAxes, va="center", ha="left", zorder=1001) # Move a bit to the right and place the line artists. x0 = (xmax - 1/2*width) x1 = (xmax - 1/8*width) y0, y1 = (y, y) handle.set_data(((x0, x1), (y0, y1))) handle.set_transform(ax.transAxes) handle.set_zorder(1002) ax.add_artist(handle) custom_legend(ax) fig.canvas.draw()
how to set the grid when using pcolormesh
I am using pcolormesh to create a grid that overlaps a 2dhistogram. import matplotlib.pyplot as plt import numpy as np import random x = [random.randrange(1,161,1) for _ in range (10)] y = [random.randrange(1,121,1) for _ in range (10)] fig, ax = plt.subplots() ax.set_xlim(0,160) ax.set_ylim(0,120) zi, yi, xi = np.histogram2d(y, x, bins=(50,120)) zi = np.ma.masked_equal(zi, 0) ax.pcolormesh(xi, yi, zi, edgecolors='black') scat = ax.scatter(x, y, s=2) Although, this code only produces a grid that covers the outermost xy data points. I'd like the grid to be constant with the set axes limits (x = 0,160), (y = 0,120). So The grid is constantly covering the plotted area. From 0,0 to 160,120. I have tried to use the vmin, vmax function in pcolormesh. But this just produces a blank figure. I don't get an error code though? ax.pcolormesh(xi, yi, zi, edgecolors='black', vmin = (0,0), vmax = (120,160)) Is there another way to extend the grid to the desired axes limits?
One problem is that the histogram2d function determines the bins itself if you use it like you do. This means that both the offset and the width of your bins is unclear until runtime because they depend on your random points rather than on your axis limits. Now once the bins are found you could read back their shape and set an axis grid accordingly. But it's easier to create your own bins so you get a grid that spans the whole axis ranges. Then you can set the edges of your bins as minor ticks and enable a grid on them. Using the lines created by pcolormesh would work too but when using it you will get some lines that are thicker than others (this has to do with line positions falling between pixels). With axis grid this doesn't happen but some lines appear to cut through your bins. In the end it's a matter of taste which one you prefer. You can always play around with edgecolor and linewidth until pcolormesh shows a decent result. import matplotlib.pyplot as plt import numpy as np import random x = [random.randrange(1,161,1) for _ in range (10)] y = [random.randrange(1,121,1) for _ in range (10)] fig, ax = plt.subplots() ax.set_xlim(0,160) ax.set_ylim(0,120) bins = [ np.linspace(*ax.get_xlim(), 120), np.linspace(*ax.get_ylim(), 50) ] # Note that I switched back to x, y and used zi.T later which I find # more readable zi, xi, yi = np.histogram2d(x, y, bins=bins) zi = np.ma.masked_equal(zi, 0) # Either use the next four lines for axis grid ax.pcolormesh(xi, yi, zi.T) ax.set_xticks(bins[0], minor=True) ax.set_yticks(bins[1], minor=True) ax.grid(True, which='minor') # or use the next line to stick with edges drawn by pcolormesh # ax.pcolormesh(xi, yi, zi.T, edgecolor='black') scat = ax.scatter(x, y, s=2)
Matplotlib conversion from data units to axis units
I'm trying to plot some data with Matplotlib (Python library) and to add an horizontal line, that would not cover the full axis range but start around the middle and finish on the right axis. I am using: plt.axhline(y=1.75,xmin=0.5) where y specifies the height of the line in data units, but xmin (as well as xmax) need to be defined in axis units (=0 for the beginning of axis and =1 at the end). Though I only know the point I want my line to start in data units, is there a method/function to convert from one to the other?
Just draw a line with plt import numpy as np import matplotlib.pyplot as plt x = np.linspace(0.2,10,100) fig, ax = plt.subplots() ax.plot(x, 1/x) ax.plot(x, np.log(x)) ax.set_aspect('equal') ax.grid(True, which='both') y = 1.25 xmin = 2 xmax = ax.get_xlim()[1] ax.plot([xmin, xmax], [y, y], color='k') which gives me: