Python: "Squeeze" a particular plot in subplot - python

Below, I plot the following Figure in Python:
As you can see the plot on the right is much more "smooth" than the one on the left. That's because the scaling of x-axis on both plot is different. More observations on the left than on the right (about three times more). Hence how can I "squeeze" horizontally the right plot such that I get somewhat an approximative look to the one of the left? Below is my code (I use Pandas):
fig, axes = plt.subplots(1, 2, sharey=True, figsize=(30, 15))
# plot the same data on both axes
#gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1])
ax1 = df1.plot(ax=axes[0], grid='off', legend=False,
xticks=[-250, -200, -150, -100, -50,
0, 25], lw=2, colormap='jet',
fontsize=20)
ax2 = df2.plot(ax=axes[1], grid='off', legend=False,
xticks=[-5, 0, 20, 40, 60, 80], lw=2,
colormap='jet', fontsize=20)
# zoom-in / limit the view to different portions of the data
# hide the spines between ax and ax2
ax1.set_ylabel('Treatment-Control Ratio', fontsize=20)
ax1.axhline(y=1, color='r', linewidth=1.5)
ax2.axhline(y=1, color='r', linewidth=1.5)
ax1.axvline(x=0, color='r', linewidth=1.5, linestyle='--')
ax2.axvline(x=0, color='r', linewidth=1.5, linestyle='--')
ax1.set_xlabel('Event Time - 1 Minute', fontsize=20)
ax2.set_xlabel('Event Time - 1 Minute', fontsize=20)
ax1.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax1.yaxis.tick_left()
ax2.yaxis.set_major_locator(plt.NullLocator())
ax1.tick_params(labeltop='off') # don't put tick labels at the top
plt.subplots_adjust(wspace=0.11)
plt.tight_layout()

With the help of #cphlewis and #gboffi I fixed the issue with the code below:
fig, axes = plt.subplots(figsize=(30, 15))
# plot the same data on both axes
gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1.2])
ax1 = plt.subplot(gs[0])
ax2 = plt.subplot(gs[1], sharey=ax1)
df_wpc.loc[-260:25].plot(ax=ax1, grid='off', legend=False,
xticks=[-250, -200, -150, -100, -50,
0, 25], lw=2, colormap='jet',
fontsize=20)
df_pc_et.loc[-5:91].plot(ax=ax2, grid='off', legend=False,
xticks=[-5, 0, 20, 40, 60, 80], lw=2,
colormap='jet', fontsize=20)
ax1.set_ylabel('Treatment-Control Ratio', fontsize=20)
ax1.axhline(y=1, color='r', linewidth=1.8)
ax2.axhline(y=1, color='r', linewidth=1.8)
ax1.axvline(x=0, color='r', linewidth=1.8, linestyle='--')
ax2.axvline(x=0, color='r', linewidth=1.8, linestyle='--')
ax1.set_xlabel('Event Time - 1 Minute', fontsize=20)
ax2.set_xlabel('Event Time - 1 Minute', fontsize=20)
ax1.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax1.yaxis.tick_left()
ax2.yaxis.set_major_locator(plt.NullLocator())
ax1.tick_params(labeltop='off') # don't put tick labels at the top
plt.subplots_adjust(wspace=0.7)
plt.tight_layout()

Related

How to remove area under the curve in matplotlib

I have the following code for a Matplotlib plot:
import pandas as pd
from matplotlib import pyplot as plt
columns = ['Price']
price_values = [[4.2],
[4.1],
[4],
[3.8],
[3.9],
[4.2],
[4.5],
[4.8],
[5.2],
[5.2],
[5.2],
[5.6],
[5.2],
[5.1],
[5.3],
[6],
[6.2],
[6.3],
[6.2],
[6],
[5.5] ,
[5.2],
[4.8],
[4.6]]
price_data = pd.DataFrame(price_values, index=range(0, 24), columns=columns)
fig = plt.figure(linewidth=1, figsize=(9, 5))
ax=plt.gca()
for column,color in zip(price_data.columns,['gold']):
ax.fill_between(
x=price_data.index,
y1=price_data[column],
y2=0,
label=column,
color=color,
alpha=.5,
step='post',
linewidth=5,
)
ax.set_facecolor("white")
ax.set_xlabel("Time of day", fontsize = 14, labelpad=8)
ax.set_ylabel("Price [Cent/kWh]", fontsize = 14,labelpad=8)
ax.set_xlim(0, 23)
ax.set_ylim(0, 8)
plt.xticks(price_data.index, labels=[f'{h:02d}:00' for h in price_data.index], rotation=90)
plt.tight_layout()
hours = list(range(25))
labels = [f'{h:02d}:00' for h in hours]
ax.tick_params(axis='both', which='major', labelsize=14)
ax.legend(loc='center left', bbox_to_anchor=(0.15, 1.07), fontsize = 14, ncol=3)
plt.savefig('Diagramm.png', edgecolor='black', dpi=400, bbox_inches='tight')
plt.show()
Now I would like to remove the area under the curve, sucht that I can only see the curve. I tried to use
plt.bar(fill=False)
but I get the error "TypeError: bar() missing 2 required positional arguments: 'x' and 'height'". Any suggestions how I can do that
Using fill_between and later remove the area under the curve seems like a pretty convoluted way to plot your data. But you could just set y2=price_data[column]:
price_data = pd.DataFrame(price_values, index=range(0, 24), columns=columns)
fig = plt.figure(linewidth=1, figsize=(9, 5))
ax=plt.gca()
for column,color in zip(price_data.columns,['gold']):
ax.fill_between(
x=price_data.index,
y1=price_data[column],
y2=price_data[column],
label=column,
color=color,
alpha=.5,
step='post',
linewidth=5,
)
ax.set_facecolor("white")
ax.set_xlabel("Time of day", fontsize = 14, labelpad=8)
ax.set_ylabel("Price [Cent/kWh]", fontsize = 14,labelpad=8)
ax.set_xlim(0, 23)
ax.set_ylim(0, 8)
plt.xticks(price_data.index, labels=[f'{h:02d}:00' for h in price_data.index], rotation=90)
plt.tight_layout()
hours = list(range(25))
labels = [f'{h:02d}:00' for h in hours]
ax.tick_params(axis='both', which='major', labelsize=14)
ax.legend(loc='center left', bbox_to_anchor=(0.15, 1.07), fontsize = 14, ncol=3)
plt.savefig('Diagramm.png', edgecolor='black', dpi=400, bbox_inches='tight')
plt.show()
Output:
Edit: #JohanC rightfully noted that the last value barely appears on the plot. One way to avoid this would be to replace your loop with the following:
price_data.plot(ax=ax, color="gold", drawstyle="steps-mid", linewidth=2)
Note that your solution is missing the last price value, the one between 23 and 24 h. You'll need to repeat the last value for this to work. To draw a step plot, the easiest way is ax.step.
The following example code changes the values for the first and the last value to make them stand out more.
from matplotlib import pyplot as plt
import pandas as pd
columns = ['Price']
price_values = [[1.2], [4.1], [4], [3.8], [3.9], [4.2], [4.5], [4.8], [5.2], [5.2], [5.2], [5.6], [5.2], [5.1], [5.3], [6], [6.2], [6.3], [6.2], [6], [5.5], [5.2], [4.8], [1.6]]
price_data = pd.DataFrame(price_values, index=range(0, 24), columns=columns)
fig, ax = plt.subplots(figsize=(9, 5))
for column, color in zip(price_data.columns, ['gold']):
ax.step(x=range(len(price_data) + 1), y=list(price_data[column]) + list(price_data[column][-1:]),
where='post', color=color, linewidth=5, label=column)
ax.set_xlabel("Time of day", fontsize=14, labelpad=8)
ax.set_ylabel("Price [Cent/kWh]", fontsize=14, labelpad=8)
ax.set_xlim(0, 24)
ax.set_ylim(0, 8)
xs = range(len(price_data) + 1)
ax.set_xticks(xs, labels=[f'{h:02d}:00' for h in xs], rotation=90)
ax.tick_params(axis='both', which='major', labelsize=14)
ax.legend(loc='lower left', bbox_to_anchor=(0.15, 1.01), fontsize=14, ncol=3)
plt.tight_layout()
plt.savefig('Diagramm.png', edgecolor='black', dpi=400, bbox_inches='tight')
plt.show()
Alternatively, you could use Seaborn's histplot, which has a step option (element='step', fill=False), but that works easiest if you'd let seaborn do the counting for the histogram. You could use sns.histplot's weights= parameter to fill in the values, e.g.
sns.histplot(x=price_data.index, weights=price_data[column].values, bins=len(price_data), binrange=(0, 24),
element='step', fill=False, color=color, linewidth=5, label=column, ax=ax)

How to prevent spans from hiding bar charts?

I'm plotting a bar graphic and horizontal spans with this code:
fig = plt.figure('Graphic', figsize=(20,15), dpi=400)
ax1 = fig.add_axes([0.1, 0.1, 0.85, 0.75])
data.plot('DATE',["PP"],kind='bar',color='black', fontsize = 15.0,ax=ax1,alpha=1)
data.plot('DATE',['PP'],kind='line',marker='*',style=['--'],linewidth=1,color='gray', ms=5,ax=ax1)
ax1.axhspan(0, 1, facecolor='lightyellow', alpha=1)
ax1.axhspan(1, 1.5, facecolor='yellow', alpha=1)
ax1.axhspan(1.5, 2, facecolor='lime', alpha=1)
ax1.axhspan(2, 3.5, facecolor='green', alpha=1)
ax1.axhspan(0, -1, facecolor='bisque', alpha=1)
ax1.axhspan(-1, -1.5, facecolor='orange', alpha=1)
ax1.axhspan(-1.5, -2, facecolor='pink', alpha=1)
ax1.axhspan(-2, -3.5, facecolor='red', alpha=1)
The issue is that spans are hiding the Bar graphic. I would like to be able to visualize the spans with the bar graphs. Both with alpha=1. I don't want to reduce the alpha values.
Is this possible?
Thanks in advance.
I am displaying the image with axhspans with alpha=1 covering the bar charts.
I noticed two things that needed to change.
When you use pandas line and bar plots with X-axis being dates, there was/is a bug. Refer to this link. The workaround used here is what was there. Using matplotlib plot instead of pandas helped resolve this.
Refer to zorder. You can specify the order of the various components (line plot, bar, spans) to tell it what will come on top of what. Higher the zorder, the higher the plot will be. I have used 1 for the spans zorder, 2 for the bar plot zorder and 2 for line plot.
Updated code is below. See if this helps.
fig = plt.figure('Graphic', figsize=(20,15), dpi=400)
ax1 = fig.add_axes([0.1, 0.1, 0.85, 0.75])
data.plot('DATE',["PP"],kind='bar',color='black', fontsize = 15.0,ax=ax1,alpha=1, zorder=2) ## Added zorder
# Changed to matplotlib, increased linewidth to 3 so you can see it and zorder=3
ax1.plot(data[['PP']], marker='*',ls='--',linewidth=3,color='gray', ms=5, zorder=3)
## All zorder = 0
ax1.axhspan(0, 1, facecolor='lightyellow', alpha=1, zorder=1)
ax1.axhspan(1, 1.5, facecolor='yellow', alpha=1, zorder=1)
ax1.axhspan(1.5, 2, facecolor='lime', alpha=1, zorder=1)
ax1.axhspan(2, 3.5, facecolor='green', alpha=1, zorder=1)
ax1.axhspan(0, -1, facecolor='bisque', alpha=1, zorder=1)
ax1.axhspan(-1, -1.5, facecolor='orange', alpha=1, zorder=1)
ax1.axhspan(-1.5, -2, facecolor='pink', alpha=1, zorder=1)
ax1.axhspan(-2, -3.5, facecolor='red', alpha=1, zorder=1)
Plot
The order of display in the pandas plot is not adjustable, so I guess we have to deal with it in matplotlib. ax is set up with a line chart and horizontal fill, and a bar chart is added as a second axis. Then I get the order of the line chart, add 1 to the value of the line chart, and set the display order to the bar chart. Since no data was provided, stock price data was used as a sample.
import yfinance as yf
import pandas as pd
data = yf.download("AAPL", start="2022-06-01", end="2022-09-01")
data.index = pd.to_datetime(data.index)
import matplotlib.pyplot as plt
fig = plt.figure('Graphic', figsize=(10,7.5), dpi=100)
ax1 = fig.add_axes([0.1, 0.1, 0.85, 0.75])
ax1.plot(data.index, data['Close'], marker='*', linestyle='--', linewidth=1, color='gray', ms=5)
ax1.axhspan(170, 180, facecolor='lightyellow', alpha=1)
ax1.axhspan(160, 170, facecolor='yellow', alpha=1)
ax1.axhspan(150, 160, facecolor='lime', alpha=1)
ax1.axhspan(145, 150, facecolor='green', alpha=1)
ax1.axhspan(140, 145, facecolor='bisque', alpha=1)
ax1.axhspan(135, 140, facecolor='orange', alpha=1)
ax1.axhspan(130, 135, facecolor='pink', alpha=1)
ax1.axhspan(120, 130, facecolor='red', alpha=1)
ax2 = ax1.twinx()
ax2.bar(x=data.index, height=data['Volume'], color='black')
ax2.set_zorder(ax1.get_zorder()+1)
ax2.set_frame_on(False)
ax1.set_ylim(120, 180)
plt.show()

Color bar limits matplotlib

I'm trying to create a plot with 4 hist2d subplots and one color bar.
The thing is that each subplot can have different ranges of z values, so the color bar is not uniform.
I want to set the color bar to a pre-defined range.
here is the code I'm using:
def multiple_med_plot_test(file):
extent = [-8, 37, 28, 46]
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(26, 11), constrained_layout=True,
subplot_kw={'projection': ccrs.PlateCarree()})
ax0 = axes[0][0]
ax1 = axes[0][1]
ax2 = axes[1][0]
ax3 = axes[1][1]
axes_dict = {'Dec': ax0, 'Aug': ax1, 'Sep': ax2, 'Sum': ax3}
for month in axes_dict.keys():
ax = axes_dict[month]
ax.add_feature(cfeature.LAND, edgecolor='k', zorder=50)
ax.set_extent(extent)
gl = ax.gridlines(draw_labels=True, zorder=100, color='grey', linestyle='--')
gl.top_labels = False
gl.right_labels = False
gl.xlabel_style = {'size': 16}
gl.ylabel_style = {'size': 16}
if ax in [ax1, ax3]:
gl.left_labels = False
ax.set_title(month, fontsize=18, color='darkred')
if month != 'Sum':
hist0 = ax.hist2d(file.Long, file.Lat, range=[(-8, 37), (28, 46)], bins=(500, 200))
elif month == 'Sum':
hist1 = ax.hist2d(file.Long, file.Lat, range=[(-8, 37), (28, 46)], bins=(500, 200))
fig.suptitle('Lightning Density per Month', fontsize=22)
cbar = fig.colorbar(hist1[3], ax=axes, shrink=0.95)
cbar.set_label('# of lightnings', fontsize=20, rotation=-90, labelpad=30)
cbar.ax.tick_params(labelsize=16)
# plt.savefig('D:/Lightning Data/Yearly_Summary', dpi=100)
plt.show()
In previous versions of the code I used plt.clim and that was awesome, but the way my code is right now doesn't let me do it.
I would like to get some help on this!
If you want a linear scale, set vmin and vmax parameters. For log-like scale or similar, use norm. See hist2d documentation.

Change axis in matplotlib

I have the following code:
import pandas as pd
from matplotlib import pyplot as plt
%matplotlib inline
hours = list(range(25)) # [0, 1, 2, ... 22, 23, 24]
labels = [f'{h:02d}:00' for h in hours] # ["00:00", "01:00", ... "23:00", "24:00"]
load = [2000, 2000, 0, 0, 0, 0, 0, 2000, 2000, 2000, 2000, 2000,0,0, 0, 0, 0, 2000, 2000,2000, 2000, 2000, 0,0,0, 0]
temperature = [21, 21.6, 22, 21.3, 20.8, 20.4, 20.1, 20, 20.6, 21.1, 21.5, 21.8, 22, 21.4, 20.9, 20.5, 20.2, 20, 20.7, 21.2, 21.6, 21.9, 22, 21.4, 21]
plt.figure(linewidth=1, figsize=(9, 5))
ax = plt.gca()
ax.plot(hours, load[0:25], color="goldenrod",drawstyle="steps-post", linewidth=3)
ax.plot(hours, load[0:25], color="gold",drawstyle="steps-post", linewidth=3, alpha=.8, label = 'Electrical power') # <- drawstyle argument.
ax.set_xlabel("Time of day", fontsize=16, labelpad=8)
ax.set_ylabel("Electrical power in W", fontsize=14, labelpad=8)
ax.set_xlim(0, 24)
ax.set_ylim(0, 3000)
plt.xticks(hours, labels=labels, rotation=90)
plt.grid(axis='y', alpha=.4)
ax.tick_params(axis='both', which='major', labelsize=14)
ax2 = ax.twinx()
ax2.plot(hours, temperature, color="red", linewidth=3, label = 'Temperature')
ax2.set_ylabel("Temperature in °C", fontsize=14, labelpad=8)
ax2.set_ylim(20, 22.5)
ax2.tick_params(axis='both', which='major', labelsize=14)
fig = plt.gcf()
fig.legend(loc='center left', bbox_to_anchor=(0.25, 1.03), fontsize=14, ncol=3)
fig.tight_layout()
ax.patch.set_visible(False)
fig.savefig('ControlStrategy_Conventional.png', edgecolor='black', dpi=400, bbox_inches='tight')
plt.show()
I would like to change the axis. So the temperature should be displayed in the left axis and the load on the right axis. I tried to change it but the resulting plot looked weird. Can anyone tell me what to do? I'd appreciate every comment.
You can first create the twin-axis and then freely select what to plot on what axis. Further, for clarity and readability, I prefer to stick with one interface (object oriented or pyplot) and not mix them:
hours = list(range(25))
labels = [f'{h:02d}' for h in hours[::2]]
fig,ax = plt.subplots(figsize=(9, 5), linewidth=1)
ax2 = ax.twinx()
ax.plot(hours, temperature, color="red", linewidth=3, label = 'Temperature')
ax2.plot(hours, load[0:25], color="gold",drawstyle="steps-post", linewidth=3, alpha=.8, label = 'Electrical power')
ax2.fill_between(hours, load[0:25], step="post", color="yellow")
ax.set_zorder(1)
ax.patch.set_visible(False)
ax.set_xlabel("Time of day", fontsize=16, labelpad=8)
ax.set_xlim(0, 24)
ax.set_xticks(hours[::2])
ax.set_xticklabels(labels=labels)
ax.tick_params(axis='both', which='major', labelsize=14)
ax.grid(axis='y', alpha=.4)
ax.set_ylabel("Temperature in °C", fontsize=14, labelpad=8)
ax.set_ylim(20, 22.5)
ax2.set_ylabel("Electrical power in W", fontsize=14, labelpad=8)
ax2.set_ylim(0, 3000)
fig.legend(loc='center left', bbox_to_anchor=(0.25, 1.03), fontsize=14, ncol=3)
fig.tight_layout()

Issue with setting upper limit to y-xis on graph with matplotlib

The following code returns the nice graph provided here:
.
However, when I add a line code such as ax.set_ylim(ymax=14) I get the following graph:
.
Clearly there is something wrong. Could anyone help me figure out what?
Thanks!
fig, ax = plt.subplots(figsize=(15, 10))
# Set bins for histograms:
bins = np.arange(0, 40+0.5, 1)
# set interval on this bins for curve fitting:
xplot = np.linspace(min(bins), max(bins), 100)
# -- make a histogram
ax.grid(axis='y', zorder=0)
plt.axvline(x=af_farm_w_speedb.mean(), color='black', linestyle='dashed', linewidth=2, label = "Mean speed at control")
plt.axvline(x=af_farm_w_speedw.mean(), color='red', linestyle='dashed', linewidth=2, label = "Mean speed at treatment")
ax.hist([af_farm_w_speedb, af_farm_w_speedw], bins=bins, density=True, alpha = 1, align='left', zorder=1, rwidth=0.8, color=['lightsteelblue','grey'], label = ['Records at control', 'Records at treatment'])
ax.tick_params(axis='both', which='major', labelsize=30)
ax.set_yticklabels([0, 2, 4, 6, 8, 10, 12])
ax.set_title('Post-farm wind speed distribution', fontsize=35)
(scale, a, shape, c) = stats.exponweib.fit(af_farm_w_speedw, f0=1, floc=0)
ax.plot(xplot, stats.exponweib.pdf(xplot, *stats.exponweib.fit(af_farm_w_speedb, 1, 1, scale=1, loc=0)), zorder=3, color = "black", linewidth=1.6, label="Weibull fit at control")
ax.plot(xplot, stats.exponweib.pdf(xplot, *stats.exponweib.fit(af_farm_w_speedw, 1, 1, scale=1, loc=0)), zorder=3, color = "red", linewidth=1.6, label="Weibull fit at treatment")
fig.text(0.6, 0.55, "Mean speed at treatment: {:.4g}".format(af_farm_w_speedw.mean()), fontsize=18)
fig.text(0.6, 0.6, "Mean speed at control: {:.4g}".format(af_farm_w_speedb.mean()), fontsize=18)
ax.legend(prop=dict(size=18))

Categories

Resources