Need Assistance: Unable to get datetime as x ticks with bar chart - python

I am trying to make a combined graph for one of my systems. I am trying to visualize time series data using bar chart along with line chart(with step).
This is a time series data therefore, I want to show datetime in the x label.
When I plot bar chart with df2['datetime'].index I get plots as expected but I do not get datetime as x-ticks. I believe that this is because I am using df2['datetime'].index.
When I plot bar chart with df2['datetime'] I get datetime in x-ticks however, the plot does not look realistic.
I am also attaching a snapshot of my data for better understanding. Any suggestions will be useful. Thank you.
fig,(ax0,ax1) = plt.subplots(1,2, figsize = (24,12))
ax0.bar(df2['datetime'].index, df2['T1'], width = 1, color='blue', align = 'edge', alpha = 0.5, label ='T1')
ax0.bar(df2['datetime'].index, -1*df2['T2'], width = 1, color='red', align = 'edge',alpha = 0.5, label ='T2')
ax0.step(df2['datetime'].index, df2['T3'], color='blue', linewidth=2, where = 'post', label ='T3')
ax0.step(df2['datetime'].index, -1*df2['T4'], color='red', linewidth=2, where = 'post', label ='T4')
ax00 = ax0.twinx()
ax00.step(df2['datetime'].index, df2['A1'], color='b', linestyle = '--', linewidth=1, where = 'post', label ='A1')
ax00.step(df2['datetime'].index, df2['A2'], color='r', linestyle = '--', linewidth=1, where = 'post', label ='A2')
ax00.set_ylabel('L2', fontsize=12, color='black')
ax0.set_ylabel("L1", fontsize=12, color='black')
ax0.set_xlabel("Datetime", fontsize=12, color='black')
ax0.set_title('Zone 2', fontsize=16, color='black')
ax0.grid(True)
ax0.legend(loc='upper left',fontsize = 12)
ax00.legend(loc='upper right',fontsize = 12)
ax1.bar(df2['datetime'], df2['T1'], width = 1, color='blue', align = 'edge', alpha = 0.5, label ='T1')
ax1.bar(df2['datetime'], -1*df2['T2'], width = 1, color='red', align = 'edge',alpha = 0.5, label ='T2')
ax1.step(df2['datetime'], df2['T3'], color='blue', linewidth=2, where = 'post', label ='T3')
ax1.step(df2['datetime'], -1*df2['T4'], color='red', linewidth=2, where = 'post', label ='T4')
ax01 = ax1.twinx()
ax01.step(df2['datetime'], df2['A1'], color='b', linestyle = '--', linewidth=1, where = 'post', label ='A1')
ax01.step(df2['datetime'], df2['A2'], color='r', linestyle = '--', linewidth=1, where = 'post', label ='A2')
ax01.set_ylabel('L2', fontsize=12, color='black')
ax1.set_ylabel("L1", fontsize=12, color='black')
ax1.set_xlabel("Datetime", fontsize=12, color='black')
ax1.set_title('Zone 1', fontsize=16, color='black')
ax1.grid(True)
ax1.legend(loc='upper left',fontsize = 12)[![enter image description here][1]][1]
ax01.legend(loc='upper right',fontsize = 12)
plt.show()

The problem probably comes from width = 1, because using datetime, a width of 1 is not equal to the size between two datapoints.
Try to use a smaller width in
ax1.bar(df2['datetime'], df2['T1'], width = 1, color='blue', align = 'edge', alpha = 0.5, label ='T1')
ax1.bar(df2['datetime'], -1*df2['T2'], width = 1, color='red', align = 'edge',alpha = 0.5, label ='T2')
You can also compute what exact width you need, but I'll let you search how to do that ;)

Related

matplotlib: Twinx() hides the minor grid of primary axis

I want to:
A. See two y-axes (of two different graphs) with one x-axis.
B. See major gridlines for x-axis
c. See major and minor gridlines for y-axis of the primary plot
I can plot the graph perfectly with only one y-axis, BUT as soon as I uncomment "ax2 = ax1.twinx()", the minor gridlines of the primary axis disappear.
Picture: The correct format with single plot, and the minor_grid-problem with two plots.
Thank you in advance!
def plot_graph(x, y1, label1, y2, label2, title):
fig, ax1 = plt.subplots()
# Plotting y-axis 1
ax1.set_xlabel('Time (s)')
ax1.set_ylabel(label1, color = "red")
ax1.grid(which='major',axis='both', color='black', linewidth=1)
ax1.grid(which='minor',axis='y', color='gray', linewidth=0.3)
ax1.tick_params(axis = 'y')
ax1.plot(x, y1, color = "red")
# Plotting secondary y-axis with the same x-axis
ax2 = ax1.twinx() # PROBLEM: this alone hides the ax1 minor grid
ax2.set_ylabel(label2, color = 'blue')
ax2.plot(x,y2,color = 'blue')
ax2.tick_params(axis = 'y')
plt.minorticks_on()
plt.legend(loc='best')
plt.title(title)
plt.show()
return
Problem solved.
"plt.minorticks_on()" needs to be called before "ax2 = ax1.twinx()".
"axis='both'" in ax1.grid() does not work. => call separately for x and y axes.
'''
plot_graph(x, y1, label1, y2, label2, title):
fig, ax1 = plt.subplots()
#Plotting y-axis 1
ax1.set_xlabel('Time (s)')
ax1.set_ylabel(label1, color="red")
ax1.grid(which='major',axis='x', color='black', linewidth=1) # x major black
ax1.grid(which='minor',axis='x', color='gray', linewidth=0.3) # x minor gray
ax1.grid(which='major',axis='y', color = 'k', linewidth=1) # y major black
ax1.grid(which='minor',axis='y', color = 'gray',linewidth=0.3) # y minor gray (this was not showing)
ax1.plot(x, y1, color = "red")
plt.minorticks_on() # NEW PLACE - SOLUTION
#Plotting secondary y-axis with the same x-axis
ax2 = ax1.twinx()
ax2.set_ylabel(label2, color = 'blue')
ax2.plot(x,y2,color = 'blue')
ax2.tick_params(axis = 'y')
#plt.minorticks_on() # OLD PLACE
plt.legend(loc='best')
plt.title(title)
plt.show(block=False)
return
'''
Image: Correct output
The problem appears to be because of the axis='both' in the ax.grid lines. If you give it as both, it doesn't appear to like it and I am not sure why. I played around this appears to give what you need. Note that 3 of the grid lines are with ax1 and the other two are with ax2
Note:
I have some random numbers for x, y1, y2
I have given separate colors for X, y1/y2 major/minor lines so you
can see. Use the colors as you need.
Code
x = []
y1 = []
y2 = []
for i in range(0,10):
x.append(round(random.random()*160,2))
for i in range(0,10):
y1.append(round(random.random()*3000,2))
for i in range(0,10):
y2.append(round(random.random()*90,2))
fig, ax1 = plt.subplots()
# Plotting y-axis 1
ax1.set_xlabel('Time (s)')
ax1.set_ylabel('label1', color = "red")
ax1.grid(which='major', axis = 'x', color='g', linewidth=1) #Green Vertical major
ax1.grid(which='major', axis = 'y', color='m', linewidth=1) #Magenta Horizontal major
ax1.grid(which='minor', axis = 'x', color='y', linewidth=0.3) #Yellow Vertical minor
ax1.tick_params(axis = 'y')
ax1.plot(x, y1, color = "red")
# Plotting secondary y-axis with the same x-axis
ax2 = ax1.twinx() # PROBLEM STILL?
ax2.set_ylabel('label2', color = 'blue')
ax2.plot(x,y2,color = 'blue')
ax2.tick_params(axis = 'y')
ax2.grid(which='major', axis = 'y', color='k', linewidth=1) #Black Horizontal major
ax2.grid(which='minor', axis = 'y', color='grey', linewidth=0.3) #Grey Horizontal minor
plt.minorticks_on()
plt.legend(loc='best')
plt.title('title')
plt.show()
Graph

How to add multiple data labels in a bar chart

I have two grouped bar charts of value changes between cases for two systems among 3 groups as below. Here I was able to add data labels to the bars using the code below (figure produced attached)
What I want to do is on top (or bottom for the negative change in value cases), add an extra data label that captures the % of the value changes as shown in the second figure with the 33% in red (I edited it in by hands). How do I achieve that from this code? Thank you in advance.
import matplotlib.pyplot as plt
import numpy as np
value_case0_system1 = np.array([30, 20, 40])
value_case1_system1 = np.array([20, 25, 50])
value_case2_system1 = np.array([10, 35, 45])
value_case1_system2 = np.array([60, 50, 40])
value_case2_system2 = np.array([50, 40, 55])
change_case0_to_case1_system1 = np.subtract(value_case1_system1,value_case0_system1)
change_case1_to_case2_system1 = np.subtract(value_case2_system1,value_case1_system1)
change_case1_to_case2_system2 = np.subtract(value_case2_system2,value_case1_system2)
fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(18,10))
labels = ['Group 1', 'Group 2', 'Group 3']
x = np.arange(len(labels))
ax0.set_ylabel('Change in Values', va='center', rotation='vertical',
fontsize=17, fontweight='bold')
width = 0.28
ax0.set_title('System 1', fontsize=17, fontweight='bold')
ax0.axhline(y=0, color='black', ls=':', lw=2)
ax0.set_xticks(x)
ax0.set_xticklabels(labels,fontsize=15)
rects1 = ax0.bar(x-width/2, change_case0_to_case1_system1, width, label='Case 0 to Case 1',
color='#292929', edgecolor='black', linewidth=1)
rects2 = ax0.bar(x+width/2, change_case1_to_case2_system1, width, label='Case 1 to Case 2',
color='#7f6d5f', edgecolor='black', linewidth=1)
ax0.bar_label(rects1, padding=3, fontsize=11)
ax0.bar_label(rects2, padding=3, fontsize=11)
leg = ax0.legend(loc="upper left", bbox_to_anchor=[0, 1],
ncol=1, fancybox=True)
ax0.legend(fontsize=15)
ax1.set_title('System 2', fontsize=17, fontweight='bold')
ax1.axhline(y=0, color='black', ls=':', lw=2)
ax1.set_xticks(x)
ax1.set_xticklabels(labels,fontsize=15)
rects3 = ax1.bar(x, change_case1_to_case2_system2, width, label='Case 1 to Case 2',
color='#7f6d5f', edgecolor='black', linewidth=1)
ax1.legend(shadow=True, fancybox=True)
ax1.bar_label(rects3, padding=3, fontsize=11)
leg = ax1.legend(loc="upper left", bbox_to_anchor=[0, 1],
ncol=1, fancybox=True)
ax1.legend(fontsize=15)
plt.tight_layout()
plt.show()
The code for the extra plot formatting has been left out, because it's not relevant for the answer. It can be added back, as per your requirements.
Each .bar_label colors the label globally, so unlike this answer, a second .bar_label needs to be added for the percent change, with a different color and padding
For each case-to-case, calculate the percent change, and set the string format in a list comprehension.
Set the list of string formatted calculations to the labels parameter in .bar_label.
Given the code in the OP, 6 lines of code need to be added, 3 for creating the list of labels, and 3 for adding the labels to the plot.
Additional resources:
matplotlib: Bar Label Demo
Adding value labels on a matplotlib bar chart
Tested in python 3.8.11, matplotlib 3.4.3
change_case0_to_case1_system1 = np.subtract(value_case1_system1, value_case0_system1)
# add list of string formatted percent change calculation
per_change_case0_to_case1_system1 = [f'({v}%)' for v in (change_case0_to_case1_system1 / value_case0_system1).round(2)*100]
change_case1_to_case2_system1 = np.subtract(value_case2_system1, value_case1_system1)
# add list of string formatted percent change calculation
per_change_case1_to_case2_system1 = [f'({v}%)' for v in (change_case1_to_case2_system1 / value_case1_system1).round(2)*100]
change_case1_to_case2_system2 = np.subtract(value_case2_system2, value_case1_system2)
# add list of string formatted percent change calculation
per_case1_to_case2_system2 = [f'({v}%)' for v in (change_case1_to_case2_system2 / value_case1_system2).round(2)*100]
fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(18,10))
labels = ['Group 1', 'Group 2', 'Group 3']
x = np.arange(len(labels))
width = 0.28
ax0.set_xticks(x)
ax0.set_xticklabels(labels, fontsize=15)
rects1 = ax0.bar(x-width/2, change_case0_to_case1_system1, width, label='Case 0 to Case 1', color='#292929', edgecolor='black', linewidth=1)
rects2 = ax0.bar(x+width/2, change_case1_to_case2_system1, width, label='Case 1 to Case 2', color='#7f6d5f', edgecolor='black', linewidth=1)
ax0.bar_label(rects1, padding=3, fontsize=11)
# add a second annotation with the string formatted labels
ax0.bar_label(rects1, labels=per_change_case0_to_case1_system1, padding=15, fontsize=11, color='red')
ax0.bar_label(rects2, padding=3, fontsize=11)
# add a second annotation with the string formatted labels
ax0.bar_label(rects2, labels=per_change_case1_to_case2_system1, padding=15, fontsize=11, color='red')
rects3 = ax1.bar(x, change_case1_to_case2_system2, width, label='Case 1 to Case 2', color='#7f6d5f', edgecolor='black', linewidth=1)
ax1.set_xticks(x)
ax1.set_xticklabels(labels,fontsize=15)
ax1.bar_label(rects3, padding=3, fontsize=11)
# add a second annotation with the string formatted labels
ax1.bar_label(rects3, labels=per_case1_to_case2_system2, padding=15, fontsize=11, color='red')
plt.tight_layout()
plt.show()

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))

Removing axes and lining up a subplot

I have produced a graph with two subplots and am trying to add a histogram to the end of the residuals plot but am unable to remove the x-axis of the histogram plot and get it to line up with the end of the residual plot.
Here is a copy of my current code:
#graph with histogram and std error plot thing
fig1 = plt.figure(figsize =(9.6,7.2))
ax = fig1.add_axes((0.2,0.4,.75,.6))
ax.errorbar(xval, yval*1000, yerr=yerr*1000, xerr=xerr, marker='x', linestyle='None')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('bottom')
# Axis labels
plt.xlabel('Height (m)', fontsize = 12)
plt.ylabel('dM/dt (g $s^{-1}$) × $10^{3}$', fontsize = 12)
# Generate best fit line using model function and best fit parameters, and add to plot
fit_line=model_funct(xval, [a_soln, b_soln])
plt.plot(xval, fit_line*1000)
# Set suitable axis limits: you will probably need to change these...
#pyplot.xlim(-1, 61)
#pyplot.ylim(65, 105)
# pyplot.show()
ax2 = fig1.add_axes((0.2,0.2,.75,.2)) #start frame1 at 0.2, 0.4
plt.xlabel("Height of Water (m)", fontsize = 12)
plt.ylabel("Normalised\nResiduals", fontsize = 12) #\n is used to start a new line
ax2.plot(h,normalised_residuals,"x", color = "green")
plt.axhline(0, linewidth=1, linestyle="--", color="black")
plt.savefig("Final Graph with added parts.png", dpi = 500)
ax2.axhspan(ymin = -np.std(normalised_residuals), ymax = np.std(normalised_residuals), color = 'gray', alpha =0.5)
ax3 = fig1.add_axes((1,0.2,0.2,0.2))
ax3.hist(normalised_residuals, bins=8, orientation="horizontal")
ax3.spines['right'].set_visible(False)
ax3.spines['top'].set_visible(False)
ax3.spines['left'].set_visible(False)
ax3.yaxis.set_ticks_position('left')
ax3.xaxis.set_ticks_position('bottom')
and here is a picture of my graph currently:
An example with random data. Using tick_params and manually setting both ylim and the histogram range, did the trick.
import matplotlib.pyplot as plt
import numpy as np
fig1 = plt.figure(figsize=(20, 15))
ax = fig1.add_axes((0.2, 0.4, .75, .6))
ax2 = fig1.add_axes((0.2, 0.2, .75, .2))
ax3 = fig1.add_axes((.95, 0.2, 0.2, 0.2))
xval = (np.linspace(0.02, 0.15, 20)
+ (np.random.default_rng(0).random(20) - 0.5) / 30)
yval = 2 * xval + 0.08
xerr = (np.random.default_rng(0).random(20) * 2 - 1) / 60
yerr = (np.random.default_rng(1).random(20) * 2 - 1) / 60
ax.errorbar(xval, yval, yerr=yerr, xerr=xerr, marker='x', linestyle='None')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.tick_params(labelbottom=False)
ax.set_xlabel('Height (m)', fontsize=12)
ax.set_ylabel('dM/dt (g $s^{-1}$) × $10^{3}$', fontsize=12)
ax2.plot(xval, xerr, 'x', color='green')
ax2.axhline(0, linewidth=1, linestyle='--', color='black')
ax2.axhspan(ymin=-np.std(xerr), ymax=np.std(xerr), color='gray', alpha=0.5)
ax2.set_xlabel('Height of Water (m)', fontsize=12)
ax2.set_ylabel('Normalised\nResiduals', fontsize=12)
resLim = ax2.get_ylim()
ax3.hist(xerr, bins=8, orientation='horizontal', range=resLim, rwidth=0.9)
ax3.spines['right'].set_visible(False)
ax3.spines['top'].set_visible(False)
ax3.spines['left'].set_visible(False)
ax3.spines['bottom'].set_visible(False)
ax3.tick_params(labelbottom=False, labelleft=False, bottom=False, left=False)
ax3.set_ylim(resLim)
fig1.savefig('so.png', bbox_inches='tight')

How can i add a legend to multiple pyplot histogram?

I would like to identify the next combined histogram with a legend
import matplotlib.pyplot as plt
nbins=10
plt.title('Gaussian random numbers B-M')
plt.axis([-3, 3, 1, 25])
plotcos = plt.hist(coseno, nbins, alpha=.8, edgecolor = 'black', linewidth=1)
plotsen = plt.hist(seno, nbins, alpha=.8, edgecolor = 'black', linewidth=1)
plt.show()
Tnx
I assume by "next combined histogram" you mean individual legends for each histogram. Just use the label parameter in both your plot commands and then show the legend using plt.legend() as
plotcos = plt.hist(coseno, nbins, alpha=.8, edgecolor = 'black', linewidth=1, label='coseno')
plotsen = plt.hist(seno, nbins, alpha=.8, edgecolor = 'black', linewidth=1, label='seno')
plt.legend()

Categories

Resources