Keeping subplot bar height constant when subplots are different height - matplotlib

Is it possible to have a subplot taller than other subplots in order to make space for the X axis tick labels, but the height of the bar chart inside to be the same as the bar height in the shorter subplots? When I add height parameter to df.plot() I get "TypeError: () got multiple values for keyword argument 'height'". Here is my code:
from collections import OrderedDict
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
data = OrderedDict()
data['Test Break'] = [0.1, 0.5, np.nan]
data['No Break'] = [0.9, 0.5, np.nan]
data['Not Tested'] = [0.0, 0.0, 1.0000]
index = ['Very very long name ' + str(x+1) for x in range(len(data))]
df = pd.DataFrame(data=data, index=index)
num_plots = 2
rows = num_plots + 1
cols = 1
layout = (rows, cols)
red, green, grey = '#FF0000', '#00FF00', '#888888'
light_grey = '#AAAAAA'
fig = plt.figure()
fig.set_size_inches(6, 3)
for z in range(num_plots):
is_last = z == num_plots - 1
rowspan = 2 if is_last else 1
ax = plt.subplot2grid(layout, (z, 0), rowspan=rowspan)
df.plot(ax=ax, kind='bar', stacked=True, yticks=[0,1], legend=False, color=[red, green, grey])
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.02, top=0.98, hspace=0.5)
ax.grid(True, which='major', axis='y')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_edgecolor(light_grey)
ax.spines['left'].set_edgecolor(light_grey)
if not is_last:
for tick in ax.xaxis.get_major_ticks():
tick.set_visible(False)

Related

How do I invert matplotlib bars at a specific point instead of when negative?

I'd like to invert the bars in this diagram when they are below 1, not when they are negative. Additionally I'd like to have even spacing between the ticks/steps on the y-axis
Here is my current code
import matplotlib.pyplot as plt
import numpy as np
labels = ['A','B','C']
Vals1 = [28.3232, 12.232, 9.6132]
Vals2 = [0.00456, 17.868, 13.453]
Vals3 = [0.0032, 1.234, 0.08214]
x = np.arange(len(labels))
width = 0.2
fig, ax = plt.subplots()
rects1 = ax.bar(x - width, Vals1, width, label='V1')
rects2 = ax.bar(x, Vals2, width, label='V2')
rects3 = ax.bar(x + width, Vals3, width, label='V3')
ax.set_xticks(x)
ax.set_xticklabels(labels)
plt.xticks(rotation=90)
ax.legend()
yScale = [0.0019531,0.0039063,0.0078125,0.015625,0.03125,0.0625,0.125,0.25,0.5,1,2,4,8,16,32]
ax.set_yticks(yScale)
plt.show()
I believe I've stumbled upon the answer, here it is for anyone else looking for the solution. Add the argument bottom='1' to ax.bar instantiation, and then flip the values in the array.
for i in range(len(Vals1)):
Vals1[i] = (1 - Vals1[i]) * -1
As you mentioned, the key is the bottom param of Axes.bar:
bottom (default: 0): The y coordinate(s) of the bars bases.
But beyond that, you can simplify your plotting code using pandas:
Put your data into a DataFrame:
import pandas as pd
df = pd.DataFrame({'V1': Vals1, 'V2': Vals2, 'V3': Vals3}, index=labels)
# V1 V2 V3
# A 28.3232 0.00456 0.00320
# B 12.2320 17.86800 1.23400
# C 9.6132 13.45300 0.08214
Then use DataFrame.sub to subtract the offset and DataFrame.plot.bar with the bottom param:
bottom = 1
ax = df.sub(bottom).plot.bar(bottom=bottom)

Create multiple stacked bar-lots in one figure

The first image is the figure I'm trying to reproduce, and the second image is the data I have. Does anyone have a clean way to do this with pandas or matplotlib?
Just transpose the DataFrame and use df.plot with the stacked flag set to true:
import pandas as pd
from matplotlib import pyplot as plt
df = pd.DataFrame({'squad': [0.6616, 0.1245, 0.0950],
'quac': [0.83, 0.065, 0.0176],
'quoref': [0.504, 0.340364, 0.1067]})
# Transpose
plot_df = df.T
# plot
ax = plot_df.plot(kind='bar', stacked=True, rot='horizontal')
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.)
ax.set_ylabel("% of Questions")
plt.tight_layout()
plt.show()
You can try this:
data = {'squad':[0.661669, 0.127516, 0.095005],
'quac':[0.930514, 0.065951, 0.017680],
'quoref': [0.504963, 0.340364, 0.106700]}
df = pd.DataFrame(data)
bars_1 = df.iloc[0]
bars_2 = df.iloc[1]
bars_3 = df.iloc[2]
# Heights of bars_1 + bars_2
bars_1_to_2 = np.add(bars_1, bars_2).tolist()
# The position of the bars on the x-axis
r = [0, 1, 2]
plt.figure(figsize = (7, 7))
plt.bar(r, bars_1, color = 'lightgrey', edgecolor = 'white')
plt.bar(r, bars_2, bottom = bars_1, color = 'darkgrey', edgecolor = 'white')
plt.bar(r, bars_3, bottom = bars_1_to_2, color = 'dimgrey', edgecolor = 'white')
plt.yticks(np.arange(0, 1.1, 0.1))
plt.xticks(ticks = r, labels = df.columns)
plt.ylabel('% of Questions')
plt.show()

How to show ranges of values with a color assigned in the legend?

With this code i'm creating colorbar scales with the function make_colormap. Source:Create own colormap using matplotlib and plot color scale
import matplotlib.colors as mcolors
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
"""
seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
cdict = {'red': [], 'green': [], 'blue': []}
for i, item in enumerate(seq):
if isinstance(item, float):
r1, g1, b1 = seq[i - 1]
r2, g2, b2 = seq[i + 1]
cdict['red'].append([item, r1, r2])
cdict['green'].append([item, g1, g2])
cdict['blue'].append([item, b1, b2])
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
c = mcolors.ColorConverter().to_rgb
rvb = make_colormap([c('grey'), c('grey'), norm(3), c('sandybrown'), c('sandybrown'),
norm(5), c('yellow'), c('yellow'), norm(10), c('navajowhite'),
c('navajowhite'), norm(15),c('lightgreen'), c('lightgreen'),norm(20),c('lime'), c('lime'),
norm(50),c('limegreen'), c('limegreen'),norm(80),c('forestgreen'), c('forestgreen'),norm(120),
c('green'), c('green'),norm(160),c('darkgreen'), c('darkgreen'),norm(200),c('teal'), c('teal'),norm(300),
c('mediumaquamarine'), c('mediumaquamarine'),norm(500),c('lightseagreen'), c('lightseagreen'),norm(700),
c('lightskyblue'), c('lightskyblue')])
So in variable rvb i'm asssing a color to ranges of values. How can i assing a color to an specific ranges of values? For example: Grey to 0-3, sandybrown to 4-5, yellow to 6-10, etc.
The map is this:
Also i want to the legend show those values assigned. For example Grey color 0-3, sandybrown 4-5, etc.
Something similar to this image (no need to be equal to the image, just need to show ranges with colors):
I also will show you part of my code when i create the map:
fig = plt.figure('map', figsize=(7,7), dpi=200)
ax = fig.add_axes([0.1, 0.12, 0.80, 0.75], projection=ccrs.PlateCarree())
plt.title('xxx')
plt.xlabel('LONGITUD')
plt.ylabel('LATITUD')
ax.outline_patch.set_linewidth(0.3)
l = NaturalEarthFeature(category='cultural', name='admin_0_countries', scale='50m', facecolor='none')
ax.add_feature(l, edgecolor='black', linewidth=0.25)
img = ax.scatter(lons, lats, s=7, c=ppvalues, cmap=rvb,norm=norm,
marker='o', transform=ccrs.PlateCarree())
handles, labels = img.legend_elements(alpha=0.2)
plt.legend(handles, labels,prop={'weight':'bold','size':10}, title='Meteorological\nStations',title_fontsize=9, scatterpoints=2);
cb = plt.colorbar(img, extend='both',
spacing='proportional', orientation='horizontal',
cax=fig.add_axes([0.12, 0.12, 0.76, 0.02]))
ax.set_extent([-90.0, -60.0, -20.0, 0.0], crs=ccrs.PlateCarree())
I don't understand the function in the question, but I have coded how to create a legend with a specified color, specified label, and specified ticks, and how to give a color bar a specified tick. Please correct the addition of colors and the tick spacing in the color bar.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.colors import LinearSegmentedColormap
list_color = ['grey','sandybrown','sandybrown','yellow',
'navajowhite','lightgreen','lime','limegreen',
'forestgreen','green','darkgreen','teal',
'mediumaquamarine','lightseagreen','lightskyblue']
list_label = ['0-3', '4-5', '6-10', '11-15',
'16-20', '21-50', '51-80', '81-120',
'121-160', '161-200','201-300','301-500',
'501-700','701-900','901-1200']
list_ticks = np.linspace(0, 1, 15)
vmin,vmax = 0, 1
cm = LinearSegmentedColormap.from_list('custom_cmap', list_color, N=len(list_color))
plt.imshow(np.linspace(0, 1, 25).reshape(5,5), cmap=cm, interpolation='nearest', vmin=vmin, vmax=vmax)
cbar = plt.colorbar( orientation='horizontal', extend='neither', ticks=list_ticks)
cbar.ax.set_xticklabels(list_label, rotation=45, fontsize=14)
all_patches = []
for h,l in zip(list_color, list_label):
patch = mpatches.Patch(color=h, label=l)
all_patches.append(patch)
plt.legend(handles=all_patches, loc='upper right', ncol=3, bbox_to_anchor=(3, 1))
plt.show()

jupyter notebook matplotlib show plot and then plot on the origin figure

I want to plot a white plot with two axes, show it to the user, then add a line to the white plot with two axes, show it to the user, then add some dot to the line, then show it to the user. How can I do this without copying the code again and again?
What I'm doing now is in the first code chunk
import math
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
plt.show()
then in the second code chunk
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
x = np.linspace(0, 1.0, 1000)
ax.plot(x, 1.0-x,zorder = 0)
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
plt.show()
then in the third code chunk
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
x = np.linspace(0, 1.0, 1000)
ax.plot(x, 1.0-x,zorder = 0)
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
p0 = 0.5
p1 = 0.5
color = "blue"
textd =0.05
ax.scatter([p0],[p1], color = color,zorder=1)
ax.text(p0+textd, p1+textd, 'tiger',color = color,zorder =2)
plt.show()
What I'm looking for is things like in the first code chunk
import math
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
plt.show()
then in the second code chunk
add line directly without duplicating the code for making axes
plt.show()
then in the third code chunk
add point directly without duplicating the code for making axes and lines
plt.show()
Update: I actually figured out the answer.
def plot(step):
fig = plt.figure(figsize=(5,5))
ax = plt.axes()
ax.set_xlabel('cat')
ax.set_ylabel('dog')
plt.title("Set of 2 animals")
if step>=1:
x = np.linspace(0, 1.0, 1000)
ax.plot(x, 1.0-x,zorder = 0)
if step>=2:
p0 = 0.5
p1 = 0.5
color = "blue"
textd =0.05
ax.scatter([p0],[p1], color = color,zorder=1)
ax.text(p0+textd, p1+textd, 'tiger',color = color,zorder =2)
plot.show()
should be able to solve the problem.

Adjust space between tick labels a in matplotlib

I based my heatmap off of: Heatmap in matplotlib with pcolor?
I checked out How to change separation between tick labels and axis labels in Matplotlib but it wasn't what I needed
How do I fix the positions of the labels so they align with the ticks?
#!/usr/bin/python
import matplotlib.pyplot as plt
import numpy as np
import random
in_path = '/path/to/data'
in_file = open(in_path,'r').read().split('\r')
wd = '/'.join(in_path.split('/')[:-1]) + '/'
column_labels = [str(random.random()) + '_dsiu' for i in in_file[0].split('\t')[2:]]
row_labels = []
#Organize data for matrix population
D_cyano_counts = {}
for line in in_file[2:]:
t = line.split('\t')
D_cyano_counts[(t[0],t[1])] = [int(x) for x in t[2:]]
#Populate matrix
matrix = []
for entry in sorted(D_cyano_counts.items(), key = lambda x: (np.mean([int(bool(y)) for y in x[-1]]), np.mean(x[-1]))):#, np.mean(x[-1]))):
(taxon_id,cyano), counts = entry
normalized_counts = []
for i,j in zip([int(bool(y)) for y in counts], counts):
if i > 0:
normalized_counts.append(i * (5 + np.log(j)))
else:
normalized_counts.append(0)
#Labels
label_type = 'species'
if label_type == 'species': label = cyano
if label_type == 'taxon_id': label = taxon_id
row_labels.append(str(random.random()))
#Fill in matrix
matrix.append(normalized_counts)
matrix = np.array(matrix)
#Fig
fig, ax = plt.subplots()
heatmap = ax.pcolor(matrix, cmap=plt.cm.Greens, alpha = 0.7)
#Format
fig = plt.gcf()
#
ax.set_frame_on(False)
#
font = {'size':3}
ax.xaxis.tick_top()
ax.set_xticks([i + 0.5 for i in range(len(column_labels))])
ax.set_yticks([i + 0.5 for i in range(len(row_labels))])
ax.set_xticklabels(column_labels, rotation = (45), fontsize = 10, va='bottom')#, fontweight = 'demi')
ax.set_yticklabels(row_labels, fontsize = 9, fontstyle='italic')
cbar = plt.colorbar(heatmap)
help(ax.set_xticklabels)
ax.margins(x=0.01,y=0.01)
fig.set_size_inches(20, 13)
plt.savefig('figure.png')
you have to set the horizontal alignment of the labels to left in your case. They are centered by default.
The link from #Jean-Sébastien contains your answer
ax.set_xticklabels(column_labels, rotation = (45), fontsize = 10, va='bottom', ha='left')