Two y-axis on the left side of the figure - matplotlib

I want to plot curves with different y-axis that share the same x-axis. I have used the twinx function before, but it plot them on different side of the figure. Is there a way to plot both of them on the left hand side. I am looking for something like the following
but with both the axis on the same side. The code for the above example is here.
On a different not, can one plot the curves in some particular order, as z-order do not work for twinx

Whats shown in red is the default twinx() behavior. The extra modification in the example applies to whats shown in green.
You can modify both new axes similar as the green one, but select the left spine and apply a negative offset. So add/change the example with:
par1.spines["left"].set_position(("axes", -0.4)) # red one
par2.spines["left"].set_position(("axes", -0.2)) # green one
make_patch_spines_invisible(par1)
make_patch_spines_invisible(par2)
par1.spines["left"].set_visible(True)
par1.yaxis.set_label_position('left')
par1.yaxis.set_ticks_position('left')
par2.spines["left"].set_visible(True)
par2.yaxis.set_label_position('left')
par2.yaxis.set_ticks_position('left')
The zorder from lines is only taken into account within the axes (or so it appears?), since you have separate axes on top of each other, you should modify the zorder of the axes:
host.set_zorder(1)
par1.set_zorder(2)
par2.set_zorder(3)
Note that the host has a white background, placing it on top will hide the other lines unless you set the background to be transparent.

Here a function to make it automatically for any of the sides in case someone need it.
import matplotlib.pyplot as plt
import numpy as np
def plotting_several_axis(variables, positions, colors, ylabels, xlabel, yaxislabels,
fontsize=12, y_axis_dist = 0.2, figsize=(7,5)):
"""
plotting_several_axis(variables, positions, colors, ylabels, xlabel, yaxislabels,
fontsize=12, y_axis_dist = 0.2, figsize=(7,5))
Example:
a1 = np.arange(1, 100, 1)
a2 = np.arange(1, 100, 1)
a = [a1, a2]
b = [i**2 for i in a]
c = [i/5 for i in b]
d = [i*8 for i in c]
e = [i+5 for i in d]
variables = [a, b, c, d, e]
positions = ['right', 'left', 'right', 'left', 'right']
colors = ['green', 'blue', 'red', 'magenta', 'brown']
ylabels = ['potatoes', 'rice', 'tomatoes', 'juice', 'cotton']
xlabel = 'price'
yaxislabels = ['item', 'kg', 'bunch', 'Liters', 'cm3']
"""
def make_patch_spines_invisible(ax):
ax.set_frame_on(True)
ax.patch.set_visible(False)
for sp in ax.spines.values():
sp.set_visible(False)
fig, host = plt.subplots(figsize=figsize)
fig.subplots_adjust(right=0.75)
###### HOST PLOTTING
tkw = dict(size=4, width=1.5, labelsize=fontsize)
p1, = host.plot(variables[0][0], variables[0][1], colors[0], label=ylabels[0])
host.set_xlabel(xlabel, fontsize=fontsize)
host.set_ylabel(yaxislabels[0], fontsize=fontsize)
host.yaxis.label.set_color(p1.get_color())
host.tick_params(axis='y', colors=p1.get_color(), **tkw)
host.tick_params(axis='x', **tkw)
# host.set_xlim(0, 2)
lines = [p1]
# y_axis_dist = 0.2
inc_r = 1
inc_l = -y_axis_dist
for ix, i in enumerate(variables):
if ix != 0:
par = host.twinx()
if positions[ix] == 'right':
par.spines[positions[ix]].set_position(("axes", inc_r))
inc_r += y_axis_dist
elif positions[ix] == 'left':
par.spines[positions[ix]].set_position(("axes", inc_l))
inc_l -= y_axis_dist
make_patch_spines_invisible(par)
par.spines[positions[ix]].set_visible(True)
par.yaxis.set_label_position(positions[ix])
par.yaxis.set_ticks_position(positions[ix])
p, = par.plot(variables[ix][0], variables[ix][1], colors[ix], label=ylabels[ix])
par.set_ylabel(yaxislabels[ix], fontsize=fontsize)
par.yaxis.label.set_color(p.get_color())
par.tick_params(axis='y', colors=p.get_color(), **tkw)
lines.append(p)
host.legend(lines, [l.get_label() for l in lines], fontsize=fontsize, loc='lower right')
plt.savefig("example.png", dpi=300, bbox_inches="tight")
plt.show()
a1 = np.arange(1, 100, 1)
a2 = np.arange(1, 100, 1)
a = [a1, a2]
b = [i**2 for i in a]
c = [i/5 for i in b]
d = [i*8 for i in c]
e = [i+5 for i in d]
variables = [a, b, c, d, e]
positions = ['right', 'left', 'right', 'left', 'right']
colors = ['green', 'blue', 'red', 'magenta', 'brown']
ylabels = ['potatoes', 'rice', 'tomatoes', 'juice', 'cotton']
xlabel = 'price'
yaxislabels = ['item', 'kg', 'bunch', 'Liters', 'cm3']
plotting_several_axis(variables, positions, colors, ylabels, xlabel, yaxislabels, y_axis_dist=0.2)

Related

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

matplotlib scatter plot add legend without loop and without using seaborn

I receive the error No handles with labels found to put in legend. when running the code below. How can I add a legend to this scatter plot that shows the color definitions (a red dot for A, blue dot for B, green dot for C)?
### Dummy Dataset
x = [0,1,-1,4,0,2,2,4,2]
y = [1,5,9,2,4,2,5,6,1]
cat = ['A','B','B','B','A','C','A','B','B']
df = pd.DataFrame(list(zip(x,y,cat)), columns =['x', 'y', 'cat'])
### Build color definitions
df.loc[:, 'color'] = df.cat
df.color.replace(['A', 'B', 'C'], ['red', 'blue', 'green'], inplace=True)
display(df)
### Plotting
fig = plt.figure(figsize=(5,5), constrained_layout=True)
gs = fig.add_gridspec(2, 1)
ax1 = fig.add_subplot(gs[0, 0])
ax1.scatter(df.x, df.y, edgecolors = 'none', c = df.color)
ax1.legend(loc='upper left', facecolor='white', frameon=1,
framealpha=1, labelspacing=0.2, borderpad=0.25)
It seems like there might not be a way to do this without a simple loop. Based on the procedure here, the following code works.
x = [0,1,-1,4,0,2,2,4,2]
y = [1,5,9,2,4,2,5,6,1]
cat = ['A','B','B','B','A','C','A','B','B']
df = pd.DataFrame(list(zip(x,y,cat)), columns =['x', 'y', 'cat'])
mycolorsdict = {'A':'red', 'B':'blue', 'C':'green'}
fig = plt.figure(figsize=(5,5), constrained_layout=True)
gs = fig.add_gridspec(2, 1)
ax1 = fig.add_subplot(gs[0, 0])
grouped = df.groupby('cat')
for key, group in grouped:
group.plot(ax=ax1, kind='scatter',
x='x', y='y',
label=key, color=mycolorsdict[key])
ax1.legend(loc='upper left', facecolor='white', frameon=1,
framealpha=1, labelspacing=0.2, borderpad=0.25)

Issue adjusting figsize with matplotlib barh subplot

I've tried specifying in a few ways but have not been able to get this figure any bigger than what's shown.
category_names = ['Database', 'Frontend', 'QA', 'ML', 'Fullstack']
labels = list(final_df.index)
data = np.array(final_df.iloc[:, 1:])
data_cum = data.cumsum(axis=1)
category_colors = plt.get_cmap('RdYlGn')(np.linspace(0, 1000, data.shape[1]))
fig, ax = plt.subplots(figsize=(100,75))
ax.invert_yaxis()
# ax.xaxis.set_visible(False)
ax.set_xlim(0, 200)
for i, (colname, color) in enumerate(zip(category_names, category_colors)):
widths = data[:, i]
starts = data_cum[:, i] - widths
ax.barh(labels, widths, left=starts, height=0.5,
label=colname, color=color)
xcenters = starts + widths / 2
r, g, b, _ = color
text_color = 'white' if r * g * b < 0.5 else 'darkgrey'
for y, (x, c) in enumerate(zip(xcenters, widths)):
ax.text(x, y, str(int(c)), ha='center', va='center',
color=text_color, fontsize=15)
If I make the figsize any bigger, the kernel dies and I've tried adjusting height and np.linspace params, as well as trying to set size with fig.set_size_inches. Any ideas on what's going on here?

Keeping subplot bar height constant when subplots are different height

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)

Change markerfacecolor for some of the markers in Matplotlib

I have a plot that shows markers in a circle. I want to be able to change the colour of 3 of them. I've tried using a variable for markerfacecolor as follows but that doesn't work:
angle = 0.0
colorR = 'red'
angleUpdate = 2 * numpy.pi / (len(v.T))
for i in range(len(v.T)):
x = numpy.sin(angle)
y = numpy.cos(angle)
angle += angleUpdate
if i < 3:
colorR = 'green'
v[0, i] = x
v[1, i] = y
plt.plot(v[0], v[1], 'ko', markerfacecolor = colorR, markersize = 70, clip_on = False)
plt.show()
Is there a way of doing this?
In your plot 'ko' means put a black circle marker; k stands for black. You should try:
plt.plot(v[0], v[1], 'o', markerfacecolor = 'red')
To get the abbreviation of other symbols and colors try:
help(plt.plot)
You can either achieve your case using plot or scatter, depending on what you are doing:
import pylab as plt
x=[1,2,3,4,5,6,7,8,9,10]
plt.plot(x[:5],x[:5],'o',c='r',markersize=10)
plt.plot(x[5:],x[5:],'o',c='b',markersize=10)
plt.show()
will generate,
Similarly, you can also use
plt.scatter(x[:5],x[:5],c='r',s=100)
plt.scatter(x[5:],x[5:],c='b',s=100)