I'm trying to make a plot with one panel up top (colspan = 2) and two plots below, with a controlled amount of space between them. I'd like the bounds of the plots to be in alignment. Here's what I'm starting with:
import cartopy
from matplotlib import pyplot
from matplotlib.gridspec import GridSpec
gs = GridSpec(2, 2, height_ratios=[2, 1], hspace=0, wspace=0)
ax0 = pyplot.subplot(gs[0, :], projection=cartopy.crs.LambertConformal())
ax0.add_feature(cartopy.feature.COASTLINE)
ax0.set_extent([-120, -75, 20, 52], cartopy.crs.Geodetic())
ax1 = pyplot.subplot(gs[1, 0], projection=cartopy.crs.LambertConformal())
ax1.add_feature(cartopy.feature.COASTLINE)
ax1.set_extent([-90, -75, 20, 30], cartopy.crs.Geodetic())
ax2 = pyplot.subplot(gs[1, 1], projection=cartopy.crs.LambertConformal())
ax2.add_feature(cartopy.feature.COASTLINE)
ax2.set_extent([-90, -75, 20, 30], cartopy.crs.Geodetic())
pyplot.show()
First problem is that the wspace=0 parameter doesn't take.
Second problem is (at least this is my guess on how to proceed) calculating a height ratio that will make the width of the upper subplot equal the combined width of the lower subplots (plus any wspace).
Related
I have a group bar chart that I would like to scale.
I am running matplotlib in a Jupyter Notebook and the bar chart is very squashed. I would like to make the axis bigger but can't get it to work in a group bar chart. If I could make it wider it would be much more readable. But if I just increase "width" then the bars start to overlap each other.
The second problem is what to do about the labels. How can the labels be printed to three decimal places?
Note: I recognise that the the values plotted are orders of magnitude different so you cannot really read the small values. Ordinarily you would not combine these onto a single chart - but this is a class exercise to demonstrating why you would not do it so I expect that.
Here is the self-contained code to demonstrate the problem:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
labels = ['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70-79', '80-89', '90-99']
t3=[1.2833333333333332, 1.6970588235294117, 1.7189655172413794, 1.8090163934426229, 1.44140625, 1.5763157894736846, 1.3685185185185187, 1.430120481927711, 1.5352941176470587, 1.9]
tt4= [116.33333333333333, 106.0, 106.93103448275862, 109.47540983606558, 98.734375, 99.84210526315789, 96.72839506172839, 99.40963855421687, 104.94117647058823, 203.0]
tsh= [1.2833333333333332, 1.6970588235294117, 1.7189655172413794, 1.8090163934426229, 1.44140625, 1.5763157894736846, 1.3685185185185187, 1.430120481927711, 1.5352941176470587, 1.9]
hypo_count= [2, 15, 55, 58, 59, 69, 72, 74, 33, 1]
x = np.arange(len(labels)) # the label locations
width = 0.2 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(x, t3, width, label='T3 avg')
rects2 = ax.bar(x+(width), tt4, width, label='TT4 avg')
rects3 = ax.bar(x+(width*2), tsh, width, label='TSH avg')
rects4 = ax.bar(x+(width*3), hypo_count, width, label='# Hypothyroid +ve')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_title('Age Bracket')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()
# Print the value on top of each bar
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
ax.bar_label(rects3, padding=3)
ax.bar_label(rects4, padding=3)
fig.tight_layout()
plt.show()
I am making a figure with several panels ("subfigures"). I want to place a label ("(a)", "(b)", ...) in the top left corner of each of the panels.
My question: How do I place a label at relative figure coordinates (0, 1) (top-left of the panel) in such a way that:
the specification is independent of aspect ratio, data, etc.;
it is the true top-left (it does not add or leaves a margin)?
Current solution
My current solution is this:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(ncols=2, constrained_layout=True)
ax = axes[0]
ax.plot([0, 1], [0, 1])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.text(-0.2, 1, "(a)", ha="left", va="top", transform=ax.transAxes)
ax = axes[1]
ax.plot([0, 1], [0, 1])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.text(-0.2, 1, "(b)", ha="left", va="top", transform=ax.transAxes)
plt.show()
which gives
What I'm looking to improve is that the horizontal -0.2 needs to be tuned every time something like aspect ratio changes. In addition, the vertical 1 needs to be tuned for example when a title is added.
Please note, I've looked at other questions like question and my problem is different and not a duplicate!
I would like to have two plots, with the same x axis in matplotlib. I thought this should be achieved via constrained_layout, but apparently this is not the case. Here is an example code.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.gridspec as grd
x = np.arange(0, 30, 0.001)
df_line = pd.DataFrame({"x": x, "y": np.sin(x)})
df_bar = pd.DataFrame({
"x_bar": [1, 7, 10, 20, 30],
"y_bar": [0.0, 0.3, 0.4, 0.1, 0.2]
})
fig = plt.subplots(constrained_layout=True)
gs = grd.GridSpec(2, 1, height_ratios=[3, 2], wspace=0.1)
ax1 = plt.subplot(gs[0])
sns.lineplot(data=df_line, x=df_line["x"], y=df_line["y"], ax=ax1)
ax1.set_xlabel("time", fontsize="22")
ax1.set_ylabel("y values", fontsize="22")
plt.yticks(fontsize=16)
plt.xticks(fontsize=16)
plt.setp(ax1.get_legend().get_texts(), fontsize="22")
ax2 = plt.subplot(gs[1])
sns.barplot(data=df_bar, x="x_bar", y="y_bar", ax=ax2)
ax2.set_xlabel("time", fontsize="22")
ax2.set_ylabel("y values", fontsize="22")
plt.yticks(fontsize=16)
plt.xticks(fontsize=16)
this leads to the following figure.
However, I would like to see the corresponding x values of both plot aligned. How can I achieve this? Note, I've tried to use the following related question. However, this doesn't fully apply to my situation. First with the high number of x points (which I need in reality) point plots is make the picture to big and slow for loading. On top, I can't use the rank method as my categories for the barplot are not evenly distributed. They are specific points on the x axis which should be aligned with the corresponding point on the lineplot
x = np.arange(0, 30, 0.001)
df_line = pd.DataFrame({"x": x, "y": np.sin(x)})
df_bar = pd.DataFrame({
"x_bar": [1, 7, 10, 20, 30],
"y_bar": [0.0, 0.3, 0.4, 0.1, 0.2]
})
fig, (ax1, ax2) = plt.subplots(2,1)
ax1.plot(df_line['x'], df_line['y'])
for i in range(len(df_bar['x_bar'])):
ax2.axvline(x=df_bar['x_bar'][i], ymin=0, ymax=df_bar['y_bar'][i])
Output:
---edit---
I incorporated #mozway advice for linewidth:
lw = (300/ax1.get_xlim()[1])
ax2.axvline(x=df_bar['x_bar'][i], ymin=0, ymax=df_bar['y_bar'][i], solid_capstyle='butt', lw=lw)
Output:
or:
In an errorbar matplotlib plot, the main line, the markers and the errorbars of a same color overlap each other on their countour when I use the alpha parameter. Although my goal was to have a transparency between the two different colors, but not within the same color, as if same color lines, markers and errorbars were only one object. Is that possible?
import matplotlib.pyplot as plt
import numpy as np
Time = np.array([1, 2, 3])
Green = np.array([3, 5, 9])
Blue = np.array([4, 7, 13])
Green_StDev = np.array([0.6, 0.6, 0.7])
Blue_StDev = np.array([0.5, 0.5, 0.6])
plt.errorbar(Time, Green, Green_StDev, marker='o', c='green', alpha=0.5)
plt.errorbar(Time, Blue, Blue_StDev, marker='o', c='blue', alpha=0.5)
plt.show()
Like the example below, but with transparency only between different color objects, differently of the example above.
I think you cannot draw them as one single object since they (marker and error bar) are drawn individually. However, to make it more 'aesthetic', you could redraw a non-transparent marker:
import matplotlib.pyplot as plt
import numpy as np
Time = np.array([1, 2, 3])
Green = np.array([3, 5, 9])
Blue = np.array([4, 7, 13])
Green_StDev = np.array([0.6, 0.6, 0.7])
Blue_StDev = np.array([0.5, 0.5, 0.6])
plt.errorbar(Time, Green, Green_StDev, marker='o', c='green', alpha=0.5)
# Add additional marker
plt.scatter(Time, Green,marker='o', c='green')
plt.errorbar(Time, Blue, Blue_StDev, marker='o', c='blue', alpha=0.5)
# Add additional marker
plt.scatter(Time, Blue, marker='o', c='blue')
plt.show()
I'm trying to create a plot with two subplots (one row, two columns), in which a vertical distplot and a vertical barplot (both from seaborn) share the Y axis. The result should look somewhat like an asymmetric violin plot.
The data for the bar plot is of this form:
In[8]: barplot_data[0:5]
Out[8]:
[{'time': 0, 'val': 171.19374169863295},
{'time': 50, 'val': 2313.8459788903383},
{'time': 100, 'val': 1518.687964071397},
{'time': 150, 'val': 1355.8373488876694},
{'time': 200, 'val': 1558.7682098705088}]
I.e., for every time step (in steps of 50), I know the height of the bar. The data for the dist plot is of the form:
In[9]: distplot_data[0:5]
Out[9]: [605, 477, 51, 337, 332]
I.e., a series of time points of which I'd like the distribution to be drawn.
Here's how I create the bar plot in the right subplot:
barplot_df = pd.DataFrame(barplot_data)
fig, axes = plt.subplots(1, 2, sharex=False, sharey=True, squeeze=False)
left_ax = axes[0][0]
right_ax = axes[0][1]
sns.barplot(y='time', x='val',
data=barplot_df,
orient='h',
ax = right_ax)
The result is pretty much what I want on the right side:
Similarly, I can put the dist plot on the left side:
fig, axes = plt.subplots(1, 2, sharex=False, sharey=True, squeeze=False)
left_ax = axes[0][0]
right_ax = axes[0][1]
sns.distplot(distplot_data, ax=left_ax, vertical=True)
This also works. I think it's kind of strange that the direction of the Y axis is reversed, but whatever:
However, now I'm just trying to plot them both into the same figure and it wreaks havoc on the dist plot:
fig, axes = plt.subplots(1, 2, sharex=False, sharey=True, squeeze=False)
left_ax = axes[0][0]
right_ax = axes[0][1]
sns.barplot(y='time', x='val',
data=barplot_df,
orient='h',
ax = right_ax)
sns.distplot(distplot_data, ax=left_ax, vertical=True)
I can only imagine that this is because of the axis of the distplot somehow being distorted or something? Does someone know what's going on here?