matplotlib histogram with equal bars width - matplotlib

I use a histogram to display the distribution. Everything works fine if the spacing of the bins is uniform. But if the interval is different, then the bar width is appropriate (as expected). Is there a way to set the width of the bar independent of the size of the bins ?
This is what i have
This what i trying to draw
from matplotlib import pyplot as plt
my_bins = [10, 20, 30, 40, 50, 120]
my_data = [5, 5, 6, 8, 9, 15, 25, 27, 33, 45, 46, 48, 49, 111, 113]
fig1 = plt.figure()
ax1 = fig1.add_subplot(121)
ax1.set_xticks(my_bins)
ax1.hist(my_data, my_bins, histtype='bar', rwidth=0.9,)
fig1.show()

I cannot mark your question as a duplicate, but I think my answer to this question might be what you are looking for?
I'm not sure how you'll make sense of the result, but you can use numpy.histogram to calculate the height of your bars, then plot those directly against an arbitrary x-scale.
x = np.random.normal(loc=50, scale=200, size=(2000,))
bins = [0,1,10,20,30,40,50,75,100]
fig = plt.figure()
ax = fig.add_subplot(211)
ax.hist(x, bins=bins, edgecolor='k')
ax = fig.add_subplot(212)
h,e = np.histogram(x, bins=bins)
ax.bar(range(len(bins)-1),h, width=1, edgecolor='k')
EDIT Here's with the adjustment to the x-tick labels so that the correspondence is easier to see.
my_bins = [10, 20, 30, 40, 50, 120]
my_data = [5, 5, 6, 8, 9, 15, 25, 27, 33, 45, 46, 48, 49, 111, 113]
fig = plt.figure()
ax = fig.add_subplot(211)
ax.hist(my_data, bins=my_bins, edgecolor='k')
ax = fig.add_subplot(212)
h,e = np.histogram(my_data, bins=my_bins)
ax.bar(range(len(my_bins)-1),h, width=1, edgecolor='k')
ax.set_xticks(range(len(my_bins)-1))
ax.set_xticklabels(my_bins[:-1])

Related

matplotlib grouped bar color change

I am trying to compare group A and group B using grouped bar, but group A must have the same colors and group B must have different colors and legends. I somehow created graph, but not sure how to change the color of each group B bar graphs..
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
labels = ['M1', 'A1', 'M2', 'A2', 'M3', 'A3', 'M4', 'A4', 'M5', 'A5']
A_group = [20, 34, 30, 35, 27, 17, 64, 23, 47, 52]
B_group = [25, 32, 34, 20, 25, 76, 33, 54, 16, 21]
x = np.arange(len(labels)) # the label locations
width = 0.35 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, A_group, width, label='A group')
rects2 = ax.bar(x + width/2, B_group, width, label='B group')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Accuracy')
ax.set_title('Test')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
plt.xticks(rotation=30, ha='right')
plt.ylim(0, 100)
fig.tight_layout()
plt.show()
Now my graph looks like this:
I want to make my graph like this. Below is an example using powerpoint.
Any helps will be appreciated. Thank you in advance.
Try drawing the bars in B_group one by one:
fig, ax = plt.subplots()
ax.bar(x-width/2, A_group, width=width,label='A Group')
cmap = plt.get_cmap('tab20')
ax.set_prop_cycle(color=[cmap(k) for k in x+1])
for i in x:
ax.bar(i+width/2, B_group[i], width=width)
Output:

Select appropriate colors in stacked Seaborn barplot

I want to create a stacked barplot using Seaborn with this MiltiIndex DataFrame
header = pd.MultiIndex.from_product([['#'],
['TE', 'SS', 'M', 'MR']])
dat = ([[100, 20, 21, 35], [100, 12, 5, 15]])
df = pd.DataFrame(dat, index=['JC', 'TTo'], columns=header)
df = df.stack()
df = df.sort_values('#', ascending=False).sort_index(level=0, sort_remaining=False)
The code I'm using for the plot is:
fontP = FontProperties()
fontP.set_size('medium')
colors = {'TE': 'green', 'SS': 'blue', 'M': 'yellow', 'MR': 'red'}
kwargs = {'alpha':0.5}
plt.figure(figsize=(12, 9))
sns.barplot(x=df2.index.get_level_values(0).unique(),
y=df2.loc[pd.IndexSlice[:, df2.index[0]], '#'],
color=colors[df2.index[0][1]], **kwargs)
sns.barplot(x=df2.index.get_level_values(0).unique(),
y=df2.loc[pd.IndexSlice[:, df2.index[1]], '#'],
color=colors[df2.index[1][1]], **kwargs)
sns.barplot(x=df2.index.get_level_values(0).unique(),
y=df2.loc[pd.IndexSlice[:, df2.index[2]], '#'],
color=colors[df2.index[2][1]], **kwargs)
bottom_plot = sns.barplot(x=df2.index.get_level_values(0).unique(),
y=df2.loc[pd.IndexSlice[:, df2.index[3]], '#'],
color=colors[df2.index[3][1]], **kwargs)
bar1 = plt.Rectangle((0, 0), 1, 1, fc='green', edgecolor="None")
bar2 = plt.Rectangle((0, 0), 0, 0, fc='yellow', edgecolor="None")
bar3 = plt.Rectangle((0, 0), 2, 2, fc='red', edgecolor="None")
bar4 = plt.Rectangle((0, 0), 3, 3, fc='blue', edgecolor="None")
l = plt.legend([bar1, bar2, bar3, bar4], [
"TE", "M",
'MR', 'SS'
],
bbox_to_anchor=(0.95, 1),
loc='upper left',
prop=fontP)
l.draw_frame(False)
sns.despine()
bottom_plot.set_ylabel("#")
axes = plt.gca()
axes.yaxis.grid()
And I get:
My problem is the order of the colors in the second bar ('TTo'), I want the colors to be automatically selected based on the level 1 index value (['TE', 'SS', 'M', 'MR']) so that they are ordered correctly. Further down the one with the highest value with its corresponding color, in front the next one with the next highest value and its color and so on, as the first bar shows ('JC).
Maybe there is a simpler way to do this in Seaborn than the one I'm using...
I'm not sure how to create such a plot with seaborn. Here is a way to create it with a loop through the rows and adding one matplotlib bar at each step:
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
sns.set()
header = pd.MultiIndex.from_product([['#'],
['TE', 'SS', 'M', 'MR']])
dat = ([[100, 20, 21, 35], [100, 12, 5, 15]])
df = pd.DataFrame(dat, index=['JC', 'TTo'], columns=header)
df = df.stack()
df = df.sort_values('#', ascending=False).sort_index(level=0, sort_remaining=False)
colors = {'TE': 'green', 'SS': 'blue', 'M': 'yellow', 'MR': 'red'}
prev_index0 = None
for (index0, index1), quantity in df.itertuples():
if index0 != prev_index0:
bottom = 0
plt.bar(index0, quantity, fc=colors[index1], ec='none', bottom=bottom, label=index1)
bottom += quantity
prev_index0 = index0
legend_handles = [plt.Rectangle((0, 0), 0, 0, color=colors[c], label=c) for c in colors]
plt.legend(handles=legend_handles)
plt.show()
To plot the bars back to front without stacking, the code can be simplified:
colors = {'TE': 'forestgreen', 'SS': 'cornflowerblue', 'M': 'gold', 'MR': 'crimson'}
for (index0, index1), quantity in df.itertuples():
plt.bar(index0, quantity, fc=colors[index1], ec='none', label=index1)
legend_handles = [plt.Rectangle((0, 0), 0, 0, color=colors[c], label=c, ec='black') for c in colors]
plt.legend(handles=legend_handles, bbox_to_anchor=(1.02, 1.02), loc='upper left')
plt.tight_layout()

How to reverse colorbar values in matpotlib?

I am using the cbar.ax.tick_params matplotlib command to make a colorbar for an XY scatterplot. How do I reverse the values (not the color-ramp) so that the lowest value is at the top of the bar. This is to represent geological data where the youngest rocks are on top of the older rocks. Here the age is represented by color.
Here is my code:
plt.scatter(summary["d18O"], summary["eHf"], s=150, c = color, cmap = color_map, edgecolors='black', marker='o')
plt.errorbar(summary["d18O"], summary["eHf"], summary["xerr"], summary["yerr"], ls='none', color='lightgrey', zorder=-1)
cbar=plt.colorbar()
cbar.ax.tick_params(labelsize=14)
cbar.minorticks_on()
cbar.set_label('Age (Ma)', style='italic', fontsize=16)
plt.axvline(x=5.3, color='black', zorder=-1)
plt.axhline(y=0, color='black', zorder=-1)
plt.tick_params(labelsize=14)
ax.set_xticks([4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
ax.set_yticks([-6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16])
plt.ylabel(u'${\epsilon}$Hf$_{T}$', style='italic', fontsize=18)
plt.xlabel(u'$\delta^{18}$O$_{V-SMOW}$ ‰',style='italic', fontsize=18)
plt.text(11.5, 0.3, 'CHUR', fontsize=18)
plt.text(4.9, 5, 'mantle zircon = 5.3‰', fontsize=16, rotation=90)
plt.show()
As #r-beginners mentioned,
cbar.ax.invert_yaxis()
would solve the problem if cbar is your colorer object.

Pollution rose plot gridded

I am trying to create a pollution rose plot as described in the link Plotting Windrose: making a pollution rose with concentration set to color
Example in the reply is working but when I used my data then it is giving a weird plot. Any advice where I am going wrong? Thank you.
import matplotlib.pyplot as plt
import numpy as np
wd = [90.,297.,309.,336.,20.,2.,334.,327.,117.,125.,122.,97.,95.,97.,103.,106.,125.,148.,147.,140.,141.,145.,144.,151.,161.]
ws = [15,1.6,1.8,1.7,2.1,1.6,2.1,1.4,3,6.5,7.1,8.2,10.2,10.2,10.8,10.2,11.4,9.7,8.6,7.1,6.4,5.5,5,5,6]
oz = [10.,20.,30.,40.,50.,60.,70.,80.,90.,100.,110.,120.,90.,140.,100.,106.,125.,148.,147.,140.,141.,145.,144.,151.,161.]
pi_fac = 22/(7*180.)
wd_rad = [w * pi_fac for w in wd]
ws_r = np.linspace(min(ws),max(ws),16)
WD,WS = np.meshgrid(wd_rad,ws_r)
C = oz + np.zeros((len(ws_r),len(wd)),dtype=float)
C = np.ma.masked_less_equal(C,10)
fig, ax = plt.subplots(subplot_kw={"projection":"polar"})
ax.pcolormesh(WD,WS,C,vmin=10, vmax=170) # I tried different vmin and vmax too
plt.show()
The linked post assumes you have a regular grid for directions and for speeds, but your input seems to be quite unordered combinations.
To create a plot with colored regions depending on the oz values, you could try tricontourf. tricontourf takes in X, Y and Z values that don't need to lie on a grid and creates a contour plot. Although it is meant for rectangular layouts, it might also work for your case. It will have a discontinuity though, when crossing from 360º to 0º.
The plot of this example also draws a colorbar to show which range of oz values correspond to which color. vmin and vmax can change this mapping of colors.
import matplotlib.pyplot as plt
import numpy as np
wd = [90, 297, 309, 336, 20, 2, 334, 327, 117, 125, 122, 97, 95, 97, 103, 106, 125, 148, 147, 140, 141, 145, 144, 151, 161]
ws = [15, 1.6, 1.8, 1.7, 2.1, 1.6, 2.1, 1.4, 3, 6.5, 7.1, 8.2, 10.2, 10.2, 10.8, 10.2, 11.4, 9.7, 8.6, 7.1, 6.4, 5.5, 5, 5, 6]
oz = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 90, 140, 100, 106, 125, 148, 147, 140, 141, 145, 144, 151, 161]
fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
cont = ax.tricontourf(np.radians(np.array(wd)), ws, oz, cmap='hot')
plt.colorbar(cont)
plt.show()
With ax.scatter(np.radians(np.array(wd)), ws, c=oz, cmap='hot', vmax=250) you could create a scatter plot to get an idea how the input looks like when colored.
You might want to incorporate Python's windrose library to get polar plots to resemble a windrose.
Another approach, which might be closer to the one intended by the linked question, would be to use scipy's interpolate.griddata to map the data to a grid. To get rid of the areas without data, an 'under' color of 'none' can be used, provided that vmin is higher than zero.
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
wd = [90, 297, 309, 336, 20, 2, 334, 327, 117, 125, 122, 97, 95, 97, 103, 106, 125, 148, 147, 140, 141, 145, 144, 151, 161]
ws = [15, 1.6, 1.8, 1.7, 2.1, 1.6, 2.1, 1.4, 3, 6.5, 7.1, 8.2, 10.2, 10.2, 10.8, 10.2, 11.4, 9.7, 8.6, 7.1, 6.4, 5.5, 5, 5, 6]
oz = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 90, 140, 100, 106, 125, 148, 147, 140, 141, 145, 144, 151, 161]
wd_rad = np.radians(np.array(wd))
oz = np.array(oz, dtype=np.float)
WD, WS = np.meshgrid(np.linspace(0, 2*np.pi, 36), np.linspace(min(ws), max(ws), 16 ))
Z = interpolate.griddata((wd_rad, ws), oz, (WD, WS), method='linear')
fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
cmap = plt.get_cmap('hot')
cmap.set_under('none')
img = ax.pcolormesh(WD, WS, Z, cmap=cmap, vmin=20)
plt.colorbar(img)
plt.show()

Matplotlib: barh full width bars

I'm trying to generate a stacked horizontal bar chart in matplotlib. The issue I am facing is that the width of the bars does not fully fill the available width of the plotting area (additional space on the right).
Unfortunately I couldn't find any information on this online.
What could I do to resolve this?
Chart with additional space on the right of the bars
measures = ("A", "B", "C", "D", "A", "B", "C", "D", "A", "B")
measure_bars = y_pos = np.arange(len(measures))
yes_data = [10, 10, 10, 10, 15, 10, 10, 10, 10, 10]
number_of_answers = [20, 30, 20, 20, 20, 20, 20, 20, 20, 20]
font = {'fontname': 'Arial', 'color': '#10384f'}
yes_data = [i / j * 100 for i, j in zip(yes_data, number_of_answers)]
no_data = [100 - i for i in yes_data]
bar_width = 0.6
plt.rcParams['xtick.top'] = plt.rcParams['xtick.labeltop'] = True
plt.rcParams['xtick.bottom'] = plt.rcParams['xtick.labelbottom'] = False
fig = plt.figure()
plt.barh(measure_bars, yes_data, color='#89d329', height=bar_width, zorder=2)
plt.barh(measure_bars, no_data, left=yes_data, color='#ff3162', height=bar_width, zorder=3)
plt.grid(color=font["color"], zorder=0)
plt.yticks(measure_bars, measures, **font)
plt.title("TECHNICAL AND ORGANIZATIONAL MEASURES", fontweight="bold", size="16", x=0.5, y=1.1, **font)
ax = plt.axes()
ax.xaxis.set_major_formatter(PercentFormatter())
ax.spines['bottom'].set_color(font["color"])
ax.spines['top'].set_color(font["color"])
ax.spines['right'].set_color(font["color"])
ax.spines['left'].set_color(font["color"])
ax.xaxis.label.set_color(font["color"])
ax.tick_params(axis='x', colors=font["color"])
for tick in ax.get_xticklabels():
tick.set_fontname(font["fontname"])
ax.xaxis.set_ticks(np.arange(0.0, 100.1, 10))
plt.gca().legend(('Yes', 'No'), bbox_to_anchor=(0.7, 0), ncol=2, shadow=False)
plt.show()
Please add (somewhere in the middle)
ax.set_xlim(0, 1)