How to draw a dashed line across two subplots with Python? - python

import matplotlib.pyplot as plt
import numpy as np
time = np.linspace(-1,5, 100)
fig, (ax1, ax2, ax3) = plt.subplots(3,1, sharex=True, figsize=(5,5))
ax1.plot([-1,0,0,5], [1,1,0,0], color='navy')
ax1.set_ylim(-0.1,1.2)
ax2.plot(time, np.clip(np.exp(-time),0,1), color='darkolivegreen')
ax2.set_ylim(-0.1,1.2)
ax3.plot(time, np.clip(np.exp(-time),0,0.4), color='darksalmon')
ax3.set_ylim(-0.1,0.8)
I have made three subplots and arranged them vertically (x-axis shared). Is there a convenient way to add two vertical lines x=0 and x=-log(0.4) that go through all three subplots?

You can use axvline():
import matplotlib.pyplot as plt
import numpy as np
time = np.linspace(-1.5, 100)
fig, (ax1, ax2, ax3) = plt.subplots(3,1, sharex=True, figsize=(5,5))
ax1.plot([-1,0,0,5], [1,1,0,0], color='navy')
ax1.set_ylim(-0.1,1.2)
ax2.plot(time, np.clip(np.exp(-time),0,1), color='darkolivegreen')
ax2.set_ylim(-0.1,1.2)
ax3.plot(time, np.clip(np.exp(-time),0,0.4), color='darksalmon')
ax3.set_ylim(-0.1,0.8)
# add vertical lines to all subplots
for ax in [ax1, ax2, ax3]:
ax.axvline(20, c='r')
This gives:
If you want the vertical line to go through the whole figure, you need to set up an additional subplot as big as the three subplots taken together and make its background transparent. Then you can draw the line in the background subplot:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
time = np.linspace(-1.5, 100)
fig = plt.figure()
gs = fig.add_gridspec(3, 2)
ax1 = fig.add_subplot(gs[0, :])
ax1.plot([-1,0,0,5], [1,1,0,0], color='navy')
ax1.set_ylim(-0.1,1.2)
ax2 = fig.add_subplot(gs[1, :], sharex = ax1)
ax2.plot(time, np.clip(np.exp(-time),0,1), color='darkolivegreen')
ax2.set_ylim(-0.1,1.2)
ax3 = fig.add_subplot(gs[2, :], sharex = ax1)
ax3.plot(time, np.clip(np.exp(-time),0,0.4), color='darksalmon')
ax3.set_ylim(-0.1,0.8)
# background axes object for plotting the vertical line
ax = fig.add_subplot(gs[:, :], sharex = ax1)
# set background color to transparent and turn off the frame
ax.patch.set_alpha(0)
ax.axis("off")
# plot the vertical line
ax.axvline(20, c='r')
plt.show()
This gives:

Related

How to set same width on shared x axis of heatmap and line plot with seaborn and Matplotlib? [duplicate]

How do you align the axes of different subplots when some have a colorbar and the others don't?
import numpy as np
import matplotlib.pyplot as plt
data1 = np.random.random([15,15])
data2 = np.random.random(15)
fig, [[ax1, ax2], [ax3, ax4], [ax5, ax6]] = plt.subplots(3,2)
for ax in [ax1, ax2, ax4, ax5, ax6]:
plt.sca(ax)
plt.pcolormesh(data1)
plt.colorbar()
plt.sca(ax3)
plt.plot(data2)
I would like to add white space to the left of ax3 so it is aligned with the other figures.
This was the best way so far of how to do this (based on https://stackoverflow.com/a/54473867/2383070).
I still wonder if there is an easier way, like simply adding whitespace to the left side of an axes.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
data1 = np.random.random([15,15])
data2 = np.random.random(15)
fig, [[ax1, ax2], [ax3, ax4], [ax5, ax6]] = plt.subplots(3,2)
for ax in [ax1, ax2, ax4, ax5, ax6]:
im1 = ax.pcolormesh(data1, cmap='magma')
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=.05)
plt.colorbar(im1, cax=cax)
im2 = ax3.plot(data2)
divider2 = make_axes_locatable(ax3)
cax2 = divider2.append_axes("right", size="5%", pad=.05)
cax2.remove()
This is on of the things constrained_layout is designed to do: https://matplotlib.org/tutorials/intermediate/constrainedlayout_guide.html
fig, [[ax1, ax2], [ax3, ax4], [ax5, ax6]] = plt.subplots(3,2,
constrained_layout=True)

How to customize the location of color bar in Seaborn heatmap?

I have the following code to create a heatmap. However, it creates an overlap of the color bar and the right axis text. The text has no problems, I want it to be in that length.
How can I locate the colorbar on the right/left side of the heatmap with no overlap?
I tried with "pad" parameter in cbar_kws but it didn't help.enter image description here
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT=pd.DataFrame(np.random.randn(300,3), columns=list('ABC'))
miniPT=PT.iloc[:,:-1]
SMALL_SIZE = 8
MEDIUM_SIZE = 80
BIGGER_SIZE = 120
plt.rc('font', size=MEDIUM_SIZE) # controls default text sizes
plt.rc('axes', titlesize=MEDIUM_SIZE) # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
plt.rc('xtick', labelsize=MEDIUM_SIZE) # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc('legend', fontsize=MEDIUM_SIZE) # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
plt.figure(figsize=(10, miniPT.shape[0]/5.2))
ax =sns.heatmap(miniPT, annot=False, cmap='RdYlGn')
for _, spine in ax.spines.items():
spine.set_visible(True)
# second axis
asset_list=np.asarray(PT['C'])
asset_list=asset_list[::-1]
ax3 = ax.twinx()
ax3.set_ylim([0,ax.get_ylim()[1]])
ax3.set_yticks(ax.get_yticks())
ax3.set_yticklabels(asset_list, fontsize=MEDIUM_SIZE*0.6)
# colorbar
cbar = ax.collections[0].colorbar
cbar.ax.tick_params(labelsize=MEDIUM_SIZE)
One way to get the overlap automatically adjusted by matplotlib, is to explicitly create subplots: one for the heatmap and another for the colorbar. sns.heatmap's cbar_ax= parameter can be set to point to this subplot. gridspec_kws= is needed to set the relative sizes. At the end, plt.tight_layout() will adjust all the paddings to make everything fit nicely.
The question's code contains some strange settings (e.g. a fontsize of 80 is immense). Also, 300 rows will inevitably lead to overlapping text (the fontsize needs to be so small that non-overlapping text wouldn't be readable). Here is some more simplified example code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))
fig, (ax, cbar_ax) = plt.subplots(ncols=2, figsize=(10, len(PT) / 5.2), gridspec_kw={'width_ratios': [10, 1]})
sns.heatmap(PT.iloc[:, :-1], annot=False, cmap='RdYlGn', cbar_ax=cbar_ax, ax=ax)
for _, spine in ax.spines.items():
spine.set_visible(True)
# second axis
asset_list = np.asarray(PT['C'])
ax3 = ax.twinx()
ax3.set_ylim(ax.get_ylim())
ax3.set_yticks(np.arange(len(PT)))
ax3.set_yticklabels(asset_list, fontsize=80)
# colorbar
cbar_ax.tick_params(labelsize=80)
plt.tight_layout()
plt.show()
As the plot is quite large, here only the bottom part is pasted, with a link to the full plot.
This is how it would look like with:
fontsize 80 (Note that font sizes are measured in "points per inch", standard 72 points per inch);
figure width of 20 inches (instead of 10);
300 rows
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT = pd.DataFrame(np.random.randn(300, 3), columns=list('ABC'))
fig, (ax, cbar_ax) = plt.subplots(ncols=2, figsize=(20, len(PT) / 5.2), gridspec_kw={'width_ratios': [15, 1]})
sns.heatmap(PT.iloc[:, :-1], annot=False, cmap='RdYlGn', cbar_ax=cbar_ax, ax=ax)
for _, spine in ax.spines.items():
spine.set_visible(True)
# second axis
asset_list = np.asarray(PT['C'])
ax3 = ax.twinx()
ax3.set_ylim(ax.get_ylim())
ax3.set_yticks(np.arange(len(PT)))
ax3.set_yticklabels(asset_list, fontsize=80)
# colorbar
cbar_ax.tick_params(labelsize=80)
plt.tight_layout()
plt.show()
My solution was eventually move the colorbar to left side. This is the code and the output:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
PT = pd.DataFrame(np.random.randn(300, 3), columns=list('ABC'))
fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(10, len(PT) / 5.2), gridspec_kw={'width_ratios': [15, 15]})
sns.heatmap(PT.iloc[:, :-1], annot=False, cmap='RdYlGn', cbar_ax=ax0, ax=ax1)
for _, spine in ax1.spines.items():
spine.set_visible(True)
# second axis
asset_list = np.asarray(PT['C'])
ax3 = ax1.twinx()
ax3.set_ylim(ax1.get_ylim())
ax3.set_yticks(np.arange(len(PT)))
ax3.set_yticklabels(asset_list, fontsize=80)
# colorbar
ax0.tick_params(labelsize=80)
plt.tight_layout()
plt.show()

How do you controle zorder across twinx in matplotlib?

I'm trying to control the zorder of different plots across twinx axes. How can I get the blue noisy plots to appear in the background and the orange smoothed plots to appear in the foreground in this plot?
from matplotlib import pyplot as plt
import numpy as np
from scipy.signal import savgol_filter
random = np.random.RandomState(0)
x1 = np.linspace(-10,10,500)**3 + random.normal(0, 100, size=500)
x2 = np.linspace(-10,10,500)**2 + random.normal(0, 100, size=500)
fig,ax1 = plt.subplots()
ax1.plot(x1, zorder=0)
ax1.plot(savgol_filter(x1,99,2), zorder=1)
ax2 = ax1.twinx()
ax2.plot(x2, zorder=0)
ax2.plot(savgol_filter(x2,99,2), zorder=1)
plt.show()
Similar to this thread, though not ideal, this is an approach using twiny along with twinx.
# set up plots
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax3 = ax1.twiny()
ax4 = ax2.twiny()
# background
ax1.plot(x1)
ax2.plot(x2)
# smoothed
ax3.plot(savgol_filter(x1,99,2), c='orange')
ax4.plot(savgol_filter(x2,99,2), c='orange')
# turn off extra ticks and labels
ax3.tick_params(axis='x', which='both', bottom=False, top=False)
ax4.tick_params(axis='x', which='both', bottom=False, top=False)
ax3.set_xticklabels([])
ax4.set_xticklabels([])
# fix zorder
ax1.set_zorder(1)
ax2.set_zorder(2)
ax3.set_zorder(3)
ax4.set_zorder(4)
plt.show()
Output:

Matplotlib: how to remove spacing between a group of subplots

I have a series of pyplot subplots that I've created using a gridspec. They all have an hspace between them, which is fine, except that I would like to keep three of them without any space. Is there a way to do this? Currently, they look like this:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure()
grid_spec = gridspec.GridSpec(nrows=10, ncols=10)
grid_spec.update(hspace=1.5)
ax1 = plt.subplot(grid_spec[0:4, :])
ax2 = plt.subplot(grid_spec[4:7, :], sharex=ax1)
# I would like to group the next 3 together
# so that they are stacked top to bottom and side by side
ax3 = plt.subplot(grid_spec[7:8, :5])
ax4 = plt.subplot(grid_spec[8:, :5], sharex=ax3)
ax5 = plt.subplot(grid_spec[8:, 5:6], sharey=ax4)
plt.show()
I would like them to be arranged like this so I can plot the following 2-D KDE diagram and have the relevant 1-D diagrams above and to the right (roughly displaying this sort of data crudely drawn in paint):
I appreciate any help with this one. Can't seem to find documentation on this sort of thing. Thanks!
You can use mpl_toolkits.axes_grid1.make_axes_locatable to subdivide the area of a subplot of a 3 x 2 grid.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig = plt.figure()
gs = fig.add_gridspec(nrows=3, ncols=2, hspace=.5,
height_ratios=[4, 3, 3], width_ratios=[7, 4])
ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :], sharex=ax1)
ax3 = fig.add_subplot(gs[2, 0])
div = make_axes_locatable(ax3)
ax4 = div.append_axes("top", "40%", pad=0.2, sharex=ax3)
ax5 = div.append_axes("right", "25%", pad=0.2, sharey=ax3)
ax4.tick_params(labelbottom=False)
ax5.tick_params(labelleft=False)
plt.show()
Also, you can create a subgridspec, like
import matplotlib.pyplot as plt
from matplotlib import gridspec
fig = plt.figure()
gs = gridspec.GridSpec(nrows=3, ncols=2, hspace=.5,
height_ratios=[4, 3, 3], width_ratios=[7, 4])
ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :], sharex=ax1)
sub_gs = gridspec.GridSpecFromSubplotSpec(2,2, subplot_spec=gs[2,0], hspace=0.3, wspace=0.1,
height_ratios=[1,3], width_ratios=[3,1])
ax3 = fig.add_subplot(sub_gs[1,0])
ax4 = fig.add_subplot(sub_gs[0,0], sharex=ax3)
ax5 = fig.add_subplot(sub_gs[1,1], sharey=ax3)
ax4.tick_params(labelbottom=False)
ax5.tick_params(labelleft=False)
plt.show()
In both cases you will probably want to fine tune the parameters a bit. In general, the matplotlib gridspec tutorial gives a nice overview with many examples on this matter.

Adjusting space in-between subplots

I am plotting 4 subplots in one figure, and I want to adjust the space in-between evenly.
I tried grid.GridSpec.update.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.figure(figsize=(8,8))
gs2 = gridspec.GridSpec(2, 2)
gs2.update(wspace=0.01, hspace=0.01)
ax1 = plt.subplot(gs2[0,0],aspect='equal')
ax1.imshow(img)
ax1.axis('off')
ax2 = plt.subplot(gs2[0,1],aspect='equal')
ax2.imshow(img)
ax2.axis('off')
ax3 = plt.subplot(gs2[1,0],aspect='equal')
ax3.imshow(img)
ax3.axis('off')
ax4 = plt.subplot(gs2[1,1],aspect='equal')
ax4.imshow(img)
ax4.axis('off')
The vertical space in-between 2 plots is too big, and it does not change no matter how I adjust gs2.update(hspace= ), as shown below:
It's likely your aspect='equal' that's causing the problem.
Try this
import numpy as np
%matplotlib inline # if in a jupyter notebook like environment
img = np.ones((30, 30))
fig, axes = plt.subplots(2, 2, sharex=True, sharey=True, figsize=(8,8),
gridspec_kw={'wspace': 0.01, 'hspace': 0.01})
axes = axes.ravel()
for ax in axes:
# aspect : ['auto' | 'equal' | scalar], optional, default: None
ax.imshow(img, aspect='auto')
ax.axis('off')

Categories

Resources