Overlapping matplot Bar Graphs? - python

I'm learning matplot in python and I want to make a bar graph with side-by-side bars. My plot is currently overlapping for some reason.
Graph
import numpy as np
import matplotlib.pyplot as plt
n_groups = 7
means_frank = (82, 75, 86, 63, 90, 73 ,88)
means_alex = (91, 92, 80, 73, 83, 91, 71)
means_joe = (72, 42, 50, 33, 63, 34, 54)
fig = plt.figure()
ax = fig.add_subplot(111)
index = np.arange(n_groups)
bar_width = 0.27
opacity = 0.8
rects1 = ax.bar(index,means_frank,bar_width,color='b', label="Frank")
rects2 = ax.bar(index,means_alex,bar_width,color='g', label="Alex")
rects3 = ax.bar(index,means_joe,bar_width,color='r', label="Joe")
plt.ylabel('Scores')
plt.title('Test Scores')
plt.xticks([0, 5, 6], ["Assignments -->", "<-- Midterm", "Final"])
plt.legend()
plt.tight_layout()
plt.show()
How can I make these 3 different plots appear side by side instead of overlapped?
Thanks!

#ImportanceofBeingErnest helped with this link:
https://matplotlib.org/gallery/lines_bars_and_markers/barchart.html
The index argument must be modified to prevent overlap.

Related

Cartopy fancy box

Hello I have been trying to plot data in a Orthographic projection. The data is plotted but I want the box to follow the data limits. Like in this example I am sharing form M_map[enter image description here
Do you have any suggestion?
In the comments under the question, you have several links to the existing answers that match the question. So that, this question is likely a duplicate question. However, none of the answers offers the zebra-pattern border as shown in the sample plot.
I take this opportunity to offer a distinct answer that also plots the map border with zebra pattern line similar to the sample plot.
import cartopy.crs as ccrs
import cartopy
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import matplotlib.patches as patches
# The lat-long projection
noProj = ccrs.PlateCarree(central_longitude=0)
# The projection of the map:
myProj = ccrs.Orthographic(central_longitude=-25, central_latitude=58)
myProj._threshold = myProj._threshold/40. #for higher precision plot
fig = plt.figure(figsize=(8,12))
ax = fig.add_subplot(1, 1, 1, projection=myProj)
# Zebra-border-line segments ...
# four edges on separate lines of code
# 1: lower edge: Left - Right
# 2: Right edge: Bottom - Top
# 3: Upper edge: Right - Left
# 4: Left edge: Top - Bottom
[ax_hdl] = ax.plot(
[
-45, -40, -35, -30, -25, -20, -15, -10, -5,
-5,-5,-5,-5,-5,
-10,-15,-20,-25,-30,-35,-40,-45,
-45, -45, -45, -45, -45
],
[
45, 45, 45, 45, 45, 45, 45, 45, 45,
50, 55, 60, 65, 70,
70,70,70,70,70,70,70,70,
65, 60, 55, 50, 45
],
color='black', linewidth=0.5,
transform=noProj)
tx_path = ax_hdl._get_transformed_path()
path_in_data_coords, _ = tx_path.get_transformed_path_and_affine()
polygon1s = mpath.Path( path_in_data_coords.vertices)
vcode = [1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1] #Path-code
polygon1v = mpath.Path( path_in_data_coords.vertices, vcode)
ax.set_boundary(polygon1s) #masks-out unwanted part of the plot
# Zebra-pattern creation
# The pattern line is created from 2 layers
# lower layer: thicker, black solid line
# top layer: thinner, dashed white line
patch1s = patches.PathPatch(polygon1s, facecolor='none', ec="black", lw=7, zorder=100)
patch1v = patches.PathPatch(polygon1v, facecolor='none', ec="white", lw=6, zorder=101)
ax.add_patch(patch1s)
ax.add_patch(patch1v)
ax.gridlines(draw_labels=True, x_inline=False, y_inline=False)
ax.add_feature(cartopy.feature.OCEAN, linewidth=.3, color='lightblue')
ax.add_feature(cartopy.feature.LAND, zorder=1, edgecolor='black')
ax.title.set_text("Map with zebra border line")
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)

Plotting Log-normal scale in matplotlib

I've got these two lists which are x,y points to be plotted:
microns = [38, 45, 53, 63, 75, 90, 106, 125, 150, 180]
cumulative_dist = [25.037, 32.577, 38.34, 43.427, 51.57,56.99, 62.41,69.537,74.85, 81.927]
The thing is I need to plot them following the scale showed in the image below (more info here), which is a log-normal plot.
How can I get this scale using matplotlib?
I guess I'll need to use matplotlib.scale.FuncScale, but I'm not quite sure how to get there.
After David's insightful comment I've read this page and managed to plot the Figure the way I wanted.
from matplotlib.ticker import ScalarFormatter, AutoLocator
from matplotlib import pyplot
import pandas as pd
import probscale
fig, ax = pyplot.subplots(figsize=(9, 6))
microns = [38, 45, 53, 63, 75, 90, 106, 125, 150, 180]
cumulative_dist = [25.037, 32.577, 38.34, 43.427, 51.57,56.99, 62.41,69.537,74.85, 81.927]
probscale.probplot(pd.Series(microns, index=cumulative_dist), ax=ax, plottype='prob', probax='y', datascale='log',
problabel='Cumulative Distribution (%)',datalabel='Particle Size (μm)',
scatter_kws=dict(marker='.', linestyle='none', markersize=15))
ax.set_xlim(left=28, right=210)
ax.set_ylim(bottom=1, top=99)
ax.set_title('Log Normal Plot')
ax.grid(True, axis='both', which='major')
formatter = ScalarFormatter()
formatter.set_scientific(False)
ax.xaxis.set_major_formatter(formatter)
ax.xaxis.set_minor_formatter(formatter)
ax.xaxis.set_major_locator(AutoLocator())
ax.set_xticks([]) # for major ticks
ax.set_xticks([], minor=True) # for minor ticks
ax.set_xticks(microns)
fig.show()

Single stacked bar chart with custom gradient coloring

Here's what I came up with by plotting thick line segments.
The coloration is blue, with varying alpha, 0 < alpha < 1.
My workaround doens't work as I'd like because I don't have a legend (I want a legend that shows a gradient of the blue at varying alpha).
Additionally, I've found that matplotlib scales funny. There should be no overlap of the bars, but if I adjust the window size, the gap between the line segments will change.This is the same figure as the earlier one, just after I've resized the figure window with my mouse.
I'm not sure if there's a better way to go about accomplishing this, or if there's a different package I can use.
Here's the snippet of code that I'm using.
import matplotlib.pyplot as plt
x1 =[0, 19, 39, 46, 60, 79]
x2 = [19, 39, 46, 60, 79, 90]
alpha_list = [-0.8402, -0.6652, 0.0, -0.5106, -0.8074, 0.0]
plt.figure()
for idx,x in enumerate(x1):
plt.plot([x1[idx],x2[idx]],[0,0],color = 'blue',alpha=alpha_list[idx],linewidth =20)
plt.show()
I suppose alpha is just a workaround for using different shades of blue? In that case the Blues colormap can be used instead.
Several lines can be plotted using a LineCollection.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x1 =[0, 19, 39, 46, 60, 79]
x2 = [19, 39, 46, 60, 79, 90]
alpha_list = [-0.8402, -0.6652, 0.0, -0.5106, -0.8074, 0.0]
verts = np.dstack((np.c_[x1, x2], np.zeros((len(x1), 2))))
fig, ax = plt.subplots()
lc = LineCollection(verts, linewidth=40, cmap="Blues_r", array=np.array(alpha_list))
ax.add_collection(lc)
ax.autoscale()
ax.set_ylim(-1,1)
fig.colorbar(lc)
plt.show()
I think a workaround would be to use plt.barh. Here is an example using normalized color maps. Each color gets converted to RGBA before it can be passed to plt.barh.
import matplotlib.pyplot as plt
from matplotlib import colors
import matplotlib.cm as cmx
x1 =[0, 19, 39, 46, 60, 79]
x2 = [19, 39, 46, 60, 79, 90]
values = range(len(x1))
jet = cm = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
fig, ax = plt.subplots()
for idx, x, y in zip(values,x1, x2):
colorVal = scalarMap.to_rgba(values[idx])
start = x
end = y
width=end-start
ax.barh(y = 0, width = width, left=start, height = 0.1, label = str(idx), color=colorVal)
ax.set_ylim(-.5,0.5)
ax.legend()
which returns:
If you really want to just change the alpha transparency of a single color, you would just have to input alpha_list[idx] for the last element to the RGBA tuple colorVal. For some reason, RGBA did not like negative alpha values, so notice I changed them all to positive
fig, ax = plt.subplots()
alpha_list = [0.8402, 0.6652, 0.01, 0.5106, 0.8074, 0.0]
for idx, x, y in zip(values,x1, x2):
colorVal = (0.0, 0.3, 1.0, alpha_list[idx])
start = x
end = y
width=end-start
ax.barh(y = 0, width = width, left=start, height = 0.1, label = str(idx), color=colorVal)
ax.set_ylim(-.5,0.5)
ax.legend()

How do I plot stacked histograms side by side in matplotlib?

I'm looking to plot two side-by-side stacked histograms (similar to the example image below) in matplotlib.
I've tried several variations on
bins = np.arange(10)
a1,b1,c1 =plt.hist([arr1,arr2,arr3],bins,stacked=True)
a2,b2,c2 =plt.hist([arr4,arr5,arr6],bins,stacked=True)
But can't seem to avoid getting the second plot to directly overlay the first.
Any ideas on how this could be resolved?
The picture shows a bar chart and not a histogram. I am pointing this out, not only because I am an obnoxious pedant, but also because I believe it could help you find the right tool :-)
Indeed, for your purpose plt.bar is probably a better pick than plt.hist.
Based on Scironic's suggestion, I modified this demonstration example to make stacked bars, like the ones on your figure.
Adding an offset to the position index (first argument in plt.bar()) is what prevents the bars from overlapping each other.
import numpy as np
import matplotlib.pyplot as plt
N = 5
men1 = (130, 90, 70, 64, 55)
men2 = (120, 85, 62, 50, 53)
men3 = (100, 70, 60, 45, 50)
ind = np.arange(N) + .15 # the x locations for the groups
width = 0.35 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men1, width, color='g')
rects2 = ax.bar(ind, men2, width, color='r')
rects3 = ax.bar(ind, men3, width, color='b')
women4 = (140, 90, 78, 65, 50)
women5 = (130, 80, 70, 60, 45)
women6 = (120, 60, 60, 55, 44)
xtra_space = 0.05
rects2 = ax.bar(ind + width + xtra_space , women1, width, color='orange')
rects2 = ax.bar(ind + width + xtra_space, women2, width, color='cyan')
rects2 = ax.bar(ind + width + xtra_space, women3, width, color='purple')
# add some text for labels, title and axes ticks
ax.set_ylabel('Population, millions')
ax.set_title('Population: Age Structure')
ax.set_xticks(ind+width+xtra_space)
ax.set_xticklabels( ('USA', 'Brazil', 'Russia', 'Japan', 'Mexico') )
plt.show()

Categories