Grouped Bar Plot with Pattern Fill using Python and Matplotlib - python

I found the following barplot on the following website: http://ndaratha.blogspot.com/2015/03/grouped-bar-plot
According to the website, it corresponds to the following code
import matplotlib.pyplot as plt
# Input data; groupwise
green_data = [16, 23, 22, 21, 13, 11, 18, 15]
blue_data = [ 3, 3, 0, 0, 5, 5, 3, 3]
red_data = [ 6, 6, 6, 0, 0, 0, 0, 0]
black_data = [25, 32, 28, 21, 18, 16, 21, 18]
labels = ['XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII']
# Setting the positions and width for the bars
pos = list(range(len(green_data)))
width = 0.15 # the width of a bar
# Plotting the bars
fig, ax = plt.subplots(figsize=(10,6))
bar1=plt.bar(pos, green_data, width,
alpha=0.5,
color='w',
hatch='x', # this one defines the fill pattern
label=labels[0])
plt.bar([p + width for p in pos], blue_data, width,
alpha=0.5,
color='w',
hatch='o',
label=labels[1])
plt.bar([p + width*2 for p in pos], red_data, width,
alpha=0.5,
color='k',
hatch='',
label=labels[2])
plt.bar([p + width*3 for p in pos], black_data, width,
alpha=0.5,
color='w',hatch='*',
label=labels[3])
# Setting axis labels and ticks
ax.set_ylabel('Number of Switching')
ax.set_xlabel('Strategy')
ax.set_title('Grouped bar plot')
ax.set_xticks([p + 1.5 * width for p in pos])
ax.set_xticklabels(labels)
# Setting the x-axis and y-axis limits
plt.xlim(min(pos)-width, max(pos)+width*5)
plt.ylim([0, max(green_data + blue_data + red_data) * 1.5])
# Adding the legend and showing the plot
plt.legend(['OLTC', 'SVC', 'SC', 'OLTC+SC+SVC'], loc='upper right')
plt.grid()
plt.show()
But when I try running the code, I get the following output
Does anyone know what I'm doing wrong or what I should do to get the desired output?

You need to add edgecolor = "k" in your plt.bar() code which gives black colors to the bar edges, and you can get the barplot you want.

When you add edgecolor = "k", code is as follows,
import matplotlib.pyplot as plt
# Input data; groupwise
green_data = [16, 23, 22, 21, 13, 11, 18, 15]
blue_data = [ 3, 3, 0, 0, 5, 5, 3, 3]
red_data = [ 6, 6, 6, 0, 0, 0, 0, 0]
black_data = [25, 32, 28, 21, 18, 16, 21, 18]
labels = ['XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII']
# Setting the positions and width for the bars
pos = list(range(len(green_data)))
width = 0.15 # the width of a bar
# Plotting the bars
fig, ax = plt.subplots(figsize=(10,6))
bar1=plt.bar(pos, green_data, width,
alpha=0.5,
color='w',
hatch='x', # this one defines the fill pattern
label=labels[0],edgecolor='black')
plt.bar([p + width for p in pos], blue_data, width,
alpha=0.5,
color='w',
hatch='o',
label=labels[1],edgecolor='black')
plt.bar([p + width*2 for p in pos], red_data, width,
alpha=0.5,
color='k',
hatch='',
label=labels[2],edgecolor='black')
plt.bar([p + width*3 for p in pos], black_data, width,
alpha=0.5,
color='w',hatch='*',
label=labels[3],edgecolor='black')
# Setting axis labels and ticks
ax.set_ylabel('Number of Switching')
ax.set_xlabel('Strategy')
ax.set_title('Grouped bar plot')
ax.set_xticks([p + 1.5 * width for p in pos])
ax.set_xticklabels(labels)
# Setting the x-axis and y-axis limits
plt.xlim(min(pos)-width, max(pos)+width*5)
plt.ylim([0, max(green_data + blue_data + red_data) * 1.5])
# Adding the legend and showing the plot
plt.legend(['OLTC', 'SVC', 'SC', 'OLTC+SC+SVC'], loc='upper right')
plt.grid()
plt.show()

Related

How to smooth graph curves

I am drawing graphs based on an excel data and I have average value, minimum value, and maximum value. The problem is that when I draw a graph the curves are not smooth and it is very hard to understand. I am using matplotlib to draw a graph
GoogleDrive: data.csv
Columns in Excel data Like
Code
# libraries
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
plt.style.use('seaborn-darkgrid')
# Data
df_Data = pd.read_csv('/home/khawar/Downloads/wandb_export_2021-10-09T14_05_20.127+09_00.csv')
print(df_Data.head())
# df = pd.DataFrame(
# {'x_values': df_Data['Step'], 'y1_values': df_Data['VIT - loss__MIN'], 'y2_values': df_Data['VIT+OverLP (Ours) - loss__MIN']})
df = pd.DataFrame(
{'x_values': df_Data['Step'], 'y1_values': df_Data['VIT - acc'], 'y2_values': df_Data['VIT+OverLP (Ours) - acc']})
font = {'family': 'serif',
'color': 'black',
'weight': 'normal',
'size': 11,
}
# multiple line plots
# plt.plot('x_values', 'y3_values', data=df, color='green', linewidth=1.5, label="ResNet-18")
plt.plot('x_values', 'y2_values', data=df, color='blue', linewidth=1.5, label="hello")
plt.plot('x_values', 'y1_values', data=df, color='red', linewidth=1.5, label="hi")
# plt.rcParams["font.weight"] = "bold"
# plt.rcParams["axes.labelweight"] = "bold"
# plt.xticks(weight='bold')
# plt.yticks(weight='bold')
# Display y axis values
# ax = plt.gca()
# ax.set_ylim([0.0, 10.0])
plt.xlabel('Number of iteration', fontdict=font)
plt.ylabel('Training loss', fontdict=font)
# plt.ylabel('Average accuracy (%)', fontdict=font)
plt.savefig('/media/khawar/HDD_Khawar/Thesis/deeplearning_acc.png')
plt.legend()
# show graph
plt.show()
Result
If you will see the graph curves are not smooth and I just want to smooth it
You can plot a smooth curve by first determining the spline curve’s coefficients using the scipy.interpolate.make_interp_spline()
# Dataset
x1 = np.array([1, 2, 3, 4, 5, 6, 7, 8])
y1 = np.array([20, 30, 5, 12, 39, 48, 50, 3])
x2 = np.array([10, 20, 30, 40, 50, 60, 70, 80])
y2 = np.array([2, 3, 5, 1, 3, 4, 5, 3])
X_Y_Spline1 = make_interp_spline(x1, y1)
X_Y_Spline2 = make_interp_spline(x2, y2)
# Returns evenly spaced numbers
# over a specified interval.
X_1 = np.linspace(x1.min(), x1.max(), 500)
Y_1 = X_Y_Spline1(X_1)
X_2 = np.linspace(x2.min(), x2.max(), 500)
Y_2 = X_Y_Spline2(X_2)
# Plotting the Graph
plt.plot(X_1, Y_1)
plt.plot(X_2, Y_2)
plt.title("Plot Smooth Curve Using the scipy.interpolate.make_interp_spline() Class")
plt.xlabel("X")
plt.ylabel("Y")
plt.show()

How to create a figure of subplots of grouped bar charts in python

I want to combine multiple grouped bar charts into one figure, as the image below shows.
grouped bar charts in a single figure
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
Firstly I created each grouped bar chart by plt.subplots()
x = np.arange(len(labels))
width = 0.3
fig1, ax = plt.subplots()
rects1 = ax.bar(x-width/2, yesterday_test1_mean, width)
rects2 = ax.bar(x+width/2, yesterday_test2_mean, width)
fig2, ax = plt.subplots()
rects3 = ax.bar(x-width/2, today_test1_mean, width)
rects4 = ax.bar(x+width/2, today_test2_mean, width)
Then, I used add_subplot in an attempt to treat fig1 and fig2 as new axes in a new figure.
fig_all = plt.figure()
fig1 = fig_all.add_subplot(1,2,1)
fig2 = fig_all.add_subplot(1,2,2)
fig_all.tight_layout()
plt.show()
But it didn't work. How can I combined several grouped bar charts into a single figure?
Thanks in advance.
Well, I tried something. Here's a rough result. Only thing I changed is that rather using axes, I am just using subplot as I learned over time. So with fig and axes as output, there must be a way too. But this is all I've ever used. I've not added the legend and title yet, but I guess you can try it on your own too.
Here's the code with just small change:
import matplotlib.pyplot as plt
import numpy as np
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
x = np.arange(len(labels))
width = 0.3
plt.figure(figsize=(12,5))
plt.subplot(121)
plt.bar(x-width/2, yesterday_test1_mean, width)
plt.bar(x+width/2, yesterday_test2_mean, width)
plt.subplot(122)
plt.bar(x-width/2, today_test1_mean, width)
plt.bar(x+width/2, today_test2_mean, width)
plt.show()
And here's your initial result:
While you see the result and try some stuff on your own, let me try to add the labels and legend to it as well as you've provided in the sample image.
Edit: The final output
So here it is, the exact thing you're looking for:
Code:
import matplotlib.pyplot as plt
import numpy as np
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
x = np.arange(len(labels))
width = 0.3
plt.figure(figsize=(12,5))
plt.subplot(121)
plt.title('Yesterday', fontsize=18)
plt.bar(x-width/2, yesterday_test1_mean, width, label='test1', hatch='//', color=np.array((199, 66, 92))/255)
plt.bar(x+width/2, yesterday_test2_mean, width, label='test2', color=np.array((240, 140, 58))/255)
plt.xticks([0,1,2], labels, fontsize=15)
plt.subplot(122)
plt.title('Today', fontsize=18)
plt.bar(x-width/2, today_test1_mean, width, hatch='//', color=np.array((199, 66, 92))/255)
plt.bar(x+width/2, today_test2_mean, width, color=np.array((240, 140, 58))/255)
plt.xticks([0,1,2], labels, fontsize=15)
plt.figlegend(loc='upper right', ncol=1, labelspacing=0.5, fontsize=14, bbox_to_anchor=(1.11, 0.9))
plt.tight_layout(w_pad=6)
plt.show()
There is two method for doing subplots that you might try to combine accidentally: plt.subplot and plt.subplots.
Here is example how you can use plt.subplots to create two bar charts.
import numpy as np
import matplotlib.pyplot as plt
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
x = np.arange(len(labels))
width = 0.3
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.bar(x-width/2, yesterday_test1_mean, width)
ax1.bar(x+width/2, yesterday_test2_mean, width)
ax2.bar(x-width/2, today_test1_mean, width)
ax2.bar(x+width/2, today_test2_mean, width)
plt.show()
There is only little changes on your code. You can add labels with:
ax1.set_title('Yesterday')
ax2.set_title('Today')
ax1.set_xticks(x)
ax1.set_xticklabels(labels)
ax2.set_xticks(x)
ax2.set_xticklabels(labels)

Increasing pie chart size with matplotlib, radius parameter appears to do nothing

Trying to make the pie larger. Looking at the docs, and other places, it says to set the radius. It seems no matter which value I put in the radius there's no increase. I'm posting the full code and the image it generates.
import matplotlib.pyplot as plt
def autopct_generator(limit):
"""Remove percent on small slices."""
def inner_autopct(pct):
return ('%.2f%%' % pct) if pct > limit else ''
return inner_autopct
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs', 'Test', 'Test2', 'Test3', \
'Test4', 'Test5', 'Test6', 'Test7', 'Test8', 'Test9', 'Test10', \
'Test11', 'Test12', 'Test13', 'Test14'
sizes = [15, 30, 45, 10, 10, 24, 13, 18, 28, 20, 13, 15, 5, 1, 18, 10,
10, 10]
NUM_COLORS = len(sizes)
fig1, ax1 = plt.subplots(figsize=(6, 5))
# set color theme
# https://matplotlib.org/api/pyplot_summary.html#colors-in-matplotlib
theme = plt.get_cmap('bwr')
ax1.set_color_cycle([theme(
1. * i / NUM_COLORS) for i in range(NUM_COLORS)])
box = ax1.get_position()
ax1.set_position([box.x0, box.y0, box.width * 1.3, box.height])
_, _, autotexts = ax1.pie(
sizes, autopct=autopct_generator(7), startangle=90, radius=1.8 * 1000)
for autotext in autotexts:
autotext.set_weight('bold')
ax1.axis('equal')
total = sum(sizes)
plt.legend(
loc='upper left',
labels=['%s, %1.1f%%' % (
l, (float(s) / total) * 100) for l, s in zip(labels, sizes)],
prop={'size': 12},
bbox_to_anchor=(0.0, 1),
bbox_transform=fig1.transFigure
)
# fig1.set_size_inches(18.5, 10.5)
fig1.savefig('chart.png')
If you turn on the axes of the pie chart,
ax.pie(..., radius=1800, frame=True)
you'll see that the radius is indeed applied correctly.
If you want to let the axes appear larger in the plot, you may use the subplot parameters.
fig.subplots_adjust(left,bottom,right,top)
Example code:
import matplotlib.pyplot as plt
sizes = [15, 30, 45, 10, 10, 24, 13, 18, 28, 20, 13, 15, 5, 1, 18, 10,
10, 10]
labels = ["Frogs %s" % i for i in sizes]
fig1, ax1 = plt.subplots(figsize=(6, 5))
fig1.subplots_adjust(0.3,0,1,1)
theme = plt.get_cmap('bwr')
ax1.set_prop_cycle("color", [theme(1. * i / len(sizes)) for i in range(len(sizes))])
_, _ = ax1.pie(sizes, startangle=90)
ax1.axis('equal')
total = sum(sizes)
plt.legend(
loc='upper left',
labels=['%s, %1.1f%%' % (
l, (float(s) / total) * 100) for l, s in zip(labels, sizes)],
prop={'size': 11},
bbox_to_anchor=(0.0, 1),
bbox_transform=fig1.transFigure
)
plt.show()

matplotlib colorbar boundaries do not implemented

I am trying to create several plots all with the same colorbar limits in a loop.
I set the limits of the contour plot with map.contourf(x, y, U_10m, vmin=0, vmax=25) and this seems to give consistent colour scales for each plot. However, when I use cbar = plt.colorbar(boundaries=np.linspace(0,1,25), ticks=[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]) # sets all cbar to same limits each plot does not have the same colorbar limits (examples of two plots with different colorbars and code below).
from netCDF4 import Dataset as NetCDFFile
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
def wrf_tseries_contour_plotter (
ncfile, time_ind, lowerllat, upperrlat, lowerllon, upperrlon, output_dir):
'''
EDITED FROM http://www.atmos.washington.edu/~ovens/wrfwinds.html
'''
print 'timestep:', + time_ind
#which figure is being generated 0 = 00:00, 144 = 23:50
nc = NetCDFFile(ncfile, 'r')
#
# get the actual longitudes, latitudes, and corners
lons = nc.variables['XLONG'][time_ind]
lats = nc.variables['XLAT'][time_ind]
#get the u10 to plot as a contour instead of t2m
U10_raw = nc.variables['U10'][time_ind] #61 is the index for 10:00am
V10_raw = nc.variables['V10'][time_ind]
#bodge to calculate U from U and V (u10 = sqrt(u^2+v^2))
v2 = np.square(V10_raw)
u2 = np.square(U10_raw)
U_10m = np.sqrt(u2 + v2)
# Make map
map = Basemap(projection='cyl',llcrnrlat=lowerllat,urcrnrlat=upperrlat,
llcrnrlon=lowerllon,urcrnrlon=upperrlon,
resolution='h')
# lllat, urlat,lllon, urlon set outside of f(x) lower left and
# upper right lat/lon for basemap axis limits
x, y = map(lons[:,:], lats[:,:])
map.contourf(x, y, U_10m, vmin=0, vmax=25)
map.drawcoastlines(linewidth = 0.5, color = '0.15')
#thinner lines for larger scale map
#plt.clim(0, 25) #added
cbar = plt.colorbar(boundaries=np.linspace(0,1,25), ticks=[0, 2, 4, 6,
8, 10, 12, 14, 16, 18, 20, 22, 24]) # sets all cbar to same limits
cbar.set_label('10m U (m/s)', size=12)
cbar.ax.set_yticklabels([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24])
#cbar.set_clim(0, 25)
time_str = str(time_ind)
plt.title('gust 20070724' + '_' + time_str)
fig_name = '\gust20070724_'+ time_str + '.png'
plt.savefig(output_dir + fig_name)
plt.close()
#set inputs for wrf_tseries_contour_plotter(ncfile, time_ind, lllat, urlat,
lllon, urlon, output_dir)
ncfile = 'E:\WRFout_UK2Fino\wrfout_d03_2007-07-24_00%3A00%3A00'
tlist = np.arange(0,145)
#set the lower left/upper right lat/lon for axis limits on the maps
lowerllat=48
upperrlat=63
lowerllon=-10
upperrlon=25
#set output directory for figures
output_dir = '''C:\cbar_test'''
for time_ind in tlist:
wrf_tseries_contour_plotter(ncfile, time_ind, lowerllat, upperrlat,
lowerllon, upperrlon, output_dir)
You have to use vmin and vmax values to set boundaries of a colorbar like in this example:
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
# test data
x = np.linspace(0,15,100)
X,Y = np.meshgrid(x,x)
SPD1 = np.sqrt(X*X + Y*Y)
SPD2 = SPD1 * 1.3
fig = plt.figure()
# implement boundaries of colorbar and it ticks
vmin, vmax = 0, 26
levels = np.linspace(vmin,vmax,14)
# 1st subplot
ax1 = fig.add_subplot(221)
# Set contour levels and limits
CF1 = ax1.contourf(X, Y, SPD1, levels=levels, vmax=vmax, vmin=vmin)
cbar = plt.colorbar(CF1)
cbar.set_label('10m U (m/s)', size=12)
#2nd subplot
ax1 = fig.add_subplot(222)
CF1 = ax1.contourf(X, Y, SPD2, levels=levels, vmax=vmax, vmin=vmin)
cbar = plt.colorbar(CF1)
cbar.set_label('10m U (m/s)', size=12)
plt.tight_layout()
plt.show()
However you have to select vmin, vmax correctly because of if your values are outside boundaries of colorbar they will not shown (right upper corner of 2nd subplot).

Matplotlib - Hide error bars' label & points in legend

Here's an example of what I mean:
import matplotlib.pyplot as plt
xdata = [5, 10, 15, 20, 25, 30, 35, 40]
ydata = [1, 3, 5, 7, 9, 11, 13, 15]
yerr_dat = 0.5
plt.figure()
plt.plot(xdata, ydata, 'go--', label='Data', zorder=1)
plt.errorbar(xdata, ydata, yerr = yerr_dat, zorder=2, fmt='ko')
plt.legend()
plt.show()
which will plot this:
I don't want the error points and the None label in the legend, how can I take those out?
I'm using Canopy in its version 1.0.1.1190.
Edit
After trying Joe's solution with this code:
import matplotlib.pyplot as plt
xdata = [5, 10, 15, 20, 25, 30, 35, 40]
ydata = [1, 3, 5, 7, 9, 11, 13, 15]
yerr_dat = 0.5
value = 20
plt.figure()
scatt = plt.plot(xdata, ydata, 'go--', label='Data', zorder=1)
hline = plt.hlines(y=5, xmin=0, xmax=40)
vline = plt.vlines(x=20, ymin=0, ymax=15)
plt.errorbar(xdata, ydata, yerr = yerr_dat, zorder=2, fmt='ko')
plt.legend([scatt, vline, hline], ['Data', 'Horiz line', 'Verti line = %d' % value], fontsize=12)
plt.show()
I get this warning:
/home/gabriel/Canopy/appdata/canopy-1.0.0.1160.rh5-x86/lib/python2.7/site-packages/matplotlib/legend.py:628: UserWarning: Legend does not support [<matplotlib.lines.Line2D object at 0xa09a28c>]
Use proxy artist instead.
http://matplotlib.sourceforge.net/users/legend_guide.html#using-proxy-artist
(str(orig_handle),))
and this output:
where the first label is not showing for some reason. Ideas?
Edit 2
Turns out I was missing a comma in the line:
scatt, = plt.plot(xdata, ydata, 'go--', label='Data', zorder=1)
After adding it everything worked like a charm. Thanks Joe!
On newer versions of matplotlib, what you're wanting is the default behavior. Only artists with an explicitly assigned label will appear in the legend.
However, it's easy to control what's displayed in the legend. Just pass in only the artists you'd like to label:
import matplotlib.pyplot as plt
xdata = [5, 10, 15, 20, 25, 30, 35, 40]
ydata = [1, 3, 5, 7, 9, 11, 13, 15]
yerr_dat = 0.5
plt.figure()
dens = plt.plot(xdata, ydata, 'go--', zorder=1)
plt.errorbar(xdata, ydata, yerr = yerr_dat, zorder=2, fmt='ko')
plt.legend(dens, ['Density Profile'])
plt.show()
Alternately, you could specify label='_nolegend_' for the errorbar plot, but I don't know what versions of matplotlib support that, and passing in explicit lists of artists and labels will work for any version.
If you'd like to add other artists:
import matplotlib.pyplot as plt
xdata = [5, 10, 15, 20, 25, 30, 35, 40]
ydata = [1, 3, 5, 7, 9, 11, 13, 15]
yerr_dat = 0.5
plt.figure()
# Note the comma! We're unpacking the tuple that `plot` returns...
dens, = plt.plot(xdata, ydata, 'go--', zorder=1)
hline = plt.axhline(5)
plt.errorbar(xdata, ydata, yerr = yerr_dat, zorder=2, fmt='ko')
plt.legend([dens, hline], ['Density Profile', 'Ceiling'], loc='upper left')
plt.show()

Categories