How to add labels to the axes of subplots - python

I am plotting 8 subplots into a figure as follows:
import matplotlib.pyplot as plt
fig, axs = plt.subplots(8)
label = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
data = [0.6, 0.4, 1.3, 0.8, 0.9, 1.0, 1.6, 0.2]
plt.xlim(0,2)
for i in range(8):
axs[i].get_yaxis().set_visible(False)
axs[i].get_xaxis().set_visible(False)
axs[i].set_xlim([0, 2])
axs[i].axvline(data[i],linestyle='--')
axs[i].get_yaxis().set_visible(False)
axs[7].get_xaxis().set_visible(True)
plt.show()
This looks like:
In order to label the subplots I would like to write label[i] (see code above) to the left of subplot i. How can you do that?

(As a quick fix), you might just be able to use Axes.text, for example:
axs[i].text(-0.1,0.2,label[i])
Adjust the x and y arguments as needed depending on the length of the labels.
As mentioned in the comments, another (much better) option is to keep the y-axis visible, but then set the ticks to nothing:
axs[i].set_yticks(())
axs[i].set_ylabel(label[i], rotation=0, ha='right', va='center')

As I mentioned in the comments, the proper approach would be to not set the y axis off, and remove the ticks.
The trick is to remove the two lines with axs[i].get_yaxis().set_visible(False) and add the following two lines:
axs[i].tick_params(left=False, labelleft=False)
axs[i].set_ylabel(label[i])
Please, consider the following code as a full answer (edited to include bnaecker's suggestion):
import matplotlib.pyplot as plt
plt.close('all')
fig, axs = plt.subplots(8, sharex="col")
label = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
data = [0.6, 0.4, 1.3, 0.8, 0.9, 1.0, 1.6, 0.2]
plt.xlim(0, 2)
for i in range(8):
axs[i].set_xlim([0, 2])
axs[i].tick_params(left=False, labelleft=False)
axs[i].axvline(data[i], linestyle='--')
axs[i].set_ylabel(label[i])
plt.show()
The figure should look like this:

Related

Custom categorized legend matplotlib

I made a mock-up version of my dataset to illustrate my problem.
I have a graph like this:
import pandas as pd
import matplotlib.pyplot as plt
data = {'x': [0, 1, 2, 3],
'y': [3, 2, 1, 0],
'cat1': ['A', 'B', 'A', 'B'],
'cat2': ['f', 'g', 'h', 'i']}
df = pd.DataFrame(data)
colors = {'A':'tab:red',
'B':'tab:blue'}
markers = {'f':"o",
'g':"v",
'h':"s",
'i':"+"}
fig, ax = plt.subplots()
for i in range(df.shape[0]):
ax.scatter(df.loc[i,'x'],
df.loc[i,'y'],
color=colors[df.loc[i,'cat1']],
marker=markers[df.loc[i,'cat2']],
label = df.loc[i, 'cat2'])
ax.legend()
But I'm looking for a legend like this:
Could anyone give me some tips on how to solve this? Also, it would be better if the legend in the final plot were in a box outside the plot, on the right side.
To add additional lines (A,B), rearrange the order and move the legend outside the graph, these are the steps you can follow after plotting.
Create a custom legend entries for the new entries and existing one using Line2D
Plot the legend and use bbox_to_anchor to move the legend to where you need it. You can adjust the coordinates within bbox if you want to move the position
Adjust the labels for A, B as these are only text (no marker) so that align horizontally to the middle (you can adjust set_position() if you want to move it further to the left/right
Code
import pandas as pd
import matplotlib.pyplot as plt
data = {'x': [0, 1, 2, 3],
'y': [3, 2, 1, 0],
'cat1': ['A', 'B', 'A', 'B'],
'cat2': ['f', 'g', 'h', 'i']}
df = pd.DataFrame(data)
colors = {'A':'tab:red',
'B':'tab:blue'}
markers = {'f':"o",
'h':"s",
'g':"v",
'i':"+"}
fig, ax = plt.subplots()
for i in range(df.shape[0]):
ax.scatter(df.loc[i,'x'], df.loc[i,'y'],
color=colors[df.loc[i,'cat1']],
marker=markers[df.loc[i,'cat2']],
label = df.loc[i, 'cat2']
## Create legend handle entries for each of the items
from matplotlib.lines import Line2D
title = Line2D([0], [0], linestyle="none", marker="")
f = Line2D([0], [0], linestyle="none", marker="o", markersize=10, markeredgecolor='tab:red', markerfacecolor="tab:red")
g = Line2D([0], [0], linestyle="none", marker="v", markersize=10, markeredgecolor='tab:blue', markerfacecolor="tab:blue")
h = Line2D([0], [0], linestyle="none", marker="s", markersize=10, markeredgecolor='tab:red', markerfacecolor="tab:red")
i = Line2D([0], [0], linestyle="none", marker="+", markersize=10, markeredgecolor='tab:blue', markerfacecolor="tab:blue")
## Plot in order you want, bbox to set legend box outside
leg=ax.legend((title, f, h, title, g, i), ('A', 'f', 'h', 'B', 'g','i'), bbox_to_anchor=(1.16, 1.03))
## Adjust position of A and B so that they are in middle
for item, label in zip(leg.legendHandles, leg.texts):
if label._text in ['A', 'B']:
width=item.get_window_extent(fig.canvas.get_renderer()).width
label.set_ha('left')
label.set_position((-width/2,0)) ## Adjust here to move left/right
Plot

How to generate proper legends for scatter plot in python

I am trying to prepare a box and scatter plot for 8 data points in python. I use the following code:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
x = [24.4, 6.7, 19.7, 16.0, 25.1, 19.5, 10, 22.1]
f, ax = plt.subplots()
ax.boxplot(x, vert=False, showmeans=True, showfliers=False)
x0 = np.random.normal(1, 0.05, len(x))
c = ['r', 'b', 'c', 'm', 'y', 'g', 'm', 'k']
lab = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
ax.scatter(x, x0, c=c, s=60, alpha=0.2)
ax.legend(labels=lab, loc="upper left", ncol=8)
It generate a image like the following:
It looks that the legend doesn't have the proper sphere symbols with different colors, which I expected. Beside the colors for the symbols are shallow and light.
So how to generate proper legends with correct symbols and how to make the colors of the symbols brighter and sharper?
I will deeply appreciate it if anyone can help.
Best regards
To make the colours brighter, just raise the alpha value.
For the legend, the order of the plotting matters here, it is better that the boxplot is plotted after the scatter plots. Also, to get for each point a place in the legend, it should b considered as a different graph, for that I used a loop to loop over the values of x, x0 and c. Here's the outcome:
import numpy as np
import matplotlib.pyplot as plt
# init figure
f, ax = plt.subplots()
# values
x = [24.4, 6.7, 19.7, 16.0, 25.1, 19.5, 10, 22.1]
x0 = np.random.normal(1, 0.05, len(x))
# labels and colours
c = ['r', 'b', 'c', 'm', 'y', 'g', 'm', 'k']
lab = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
# put the plots into a list
plots = []
for i in range(len(x)):
p = ax.scatter(x[i], x0[i], c=c[i], s=60, alpha=0.5) # raised the alpha to get sharper colors
plots.append(p)
# plot legends
plt.legend(plots,
labels=lab,
scatterpoints=1,
loc='upper left',
ncol=8,
fontsize=8)
# plot the box plot (the order here matters!)
ax.boxplot(x, vert=False, showmeans=True, showfliers=False)
# save the desired figure
plt.savefig('tt.png')
Output:

Matplotlib Scatter Plot Legend Creation Mystery

I have the following snipped of code (values for c, s, x, y are mockups, but the real lists follow the same format, just much bigger. Only two colors are used - red and green though. All lists are of the same size)
The issue is that the color legend fails to materialize. I am completely at loss as to why. Code snippets for legend generation is basically a cut-n-paste from docs, i.e. (https://matplotlib.org/3.1.1/gallery/lines_bars_and_markers/scatter_with_legend.html#sphx-glr-gallery-lines-bars-and-markers-scatter-with-legend-py)
Anyone has any idea??
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
c = [ 'g', 'r', 'r', 'g', 'g', 'r', 'r', 'r', 'g', 'r']
s = [ 10, 20, 10, 40, 60, 90, 90, 50, 60, 40]
x = [ 2.4, 3.0, 3.5, 3.5, 3.5, 3.5, 3.5, 2.4, 3.5, 3.5]
y = [24.0, 26.0, 20.0, 19.0, 19.0, 21.0, 20.0, 23.0, 20.0, 20.0]
fig, ax = plt.subplots()
scatter = plt.scatter(x, y, s=s, c=c, alpha=0.5)
# produce a legend with the unique colors from the scatter
handles, lables = scatter.legend_elements()
legend1 = ax.legend(handles, labels, loc="lower left", title="Colors")
ax.add_artist(legend1)
# produce a legend with a cross section of sizes from the scatter
handles, labels = scatter.legend_elements(prop="sizes", alpha=0.5)
legend2 = ax.legend(handles, labels, loc="upper right", ncol=2, title="Sizes")
plt.show()
Plot output:
It seems that legend_elements() is only meant to be used when c= is passed a numeric array to be mapped against a colormap.
You can test by replacing c=c by c=s in your code, and you will get the desired output.
Personally, I would have expected your code to work, and maybe it is worth bringing it up either as a bug or a feature request at matplotlib's github. EDIT: actually, there is already a discussion about this very issue on the issue tracker
One way to circumvent this limitation is to replace your array of colors names with a numeric array and creating a custom colormap that maps each value in your array to the desired color:
#c = [ 'g', 'r', 'r', 'g', 'g', 'r', 'r', 'r', 'g', 'r']
c = [0, 1, 1, 0, 0, 1, 1, 1, 0, 1]
cmap = matplotlib.colors.ListedColormap(['g','r'])
s = [ 10, 20, 10, 40, 60, 90, 90, 50, 60, 40]
x = [ 2.4, 3.0, 3.5, 3.5, 3.5, 3.5, 3.5, 2.4, 3.5, 3.5]
y = [24.0, 26.0, 20.0, 19.0, 19.0, 21.0, 20.0, 23.0, 20.0, 20.0]
fig, ax = plt.subplots()
scatter = plt.scatter(x, y, s=s, c=c, alpha=0.5, cmap=cmap)
# produce a legend with the unique colors from the scatter
handles, labels = scatter.legend_elements()
legend1 = ax.legend(handles, labels, loc="lower left", title="Colors")
ax.add_artist(legend1)
# produce a legend with a cross section of sizes from the scatter
handles, labels = scatter.legend_elements(prop="sizes", alpha=0.5)
legend2 = ax.legend(handles, labels, loc="upper right", ncol=2, title="Sizes")
plt.show()

How to split y axis labels and color each part separately?

ax.yaxis.get_major_ticks() allows me to colour-code each label differently. But I'm failing to split the label and mark each part with a different colour.
Example Image: A_B => A in Blue and _B in Red, similarly C_D => C in Blue and D in Red etc.
While looping through all the ticks the text is available with get_text(), but color coding each part separately is not possible with the same.
This a sample graphical representation of a horizontally stacked bar chart:
Borrowing some code from this excellent post, text can be put together via offset boxes.
import matplotlib.pyplot as plt
from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, HPacker
fig, ax = plt.subplots()
vals1 = [0.3, 0.5, 0.4, 0.2, 0.5]
vals2 = [0.2, 0.3, 0.2, 0.2, 0.1]
labels1 = ['A', 'B', 'CCCC', 'DDDDDD', 'E']
labels2 = ['B', 'CCCC', 'DDDDDD', 'E', 'F']
color1 = 'dodgerblue'
color2 = 'crimson'
ax.barh(range(len(vals1)), vals1, color=color1)
ax.barh(range(len(vals2)), vals2, left=vals1, color=color2)
ax.set_yticklabels([])
for i in range(len(labels1)):
boxes = [TextArea(text, textprops=dict(color=color))
for text, color in zip([labels1[i], '_', labels2[i]], [color1, 'black', color2])]
xbox = HPacker(children=boxes, align="right", pad=1, sep=1)
anchored_xbox = AnchoredOffsetbox(loc='center right', child=xbox, pad=0, frameon=False, bbox_to_anchor=(0, i),
bbox_transform=ax.transData, borderpad=1)
ax.add_artist(anchored_xbox)
plt.tight_layout()
plt.show()

Scatter plot with multiple Y values with line for each category X label

I am trying to create a scatter plot, where each x-axis category label contains two data points (from different conditions), and displayed in a way that shows the trend between the two. With the following code, I have managed to match each x-axis category labels with its designated data points.
import numpy as np
import matplotlib.pyplot as plt
import csv
# x-axis labels
IDs = ['a', 'b', 'c', 'd', 'e', 'f']
# y-axis data
lowCont = [-0.31, 0.71, 0.37, 0.05, 0.15, 1.33]
highCont = [-0.38, -0.16, 0.02, -0.55, -0.02, -0.51]
# Standard Errors for each data point
lowContErr = [0.03,0.13,0.02,0.10,0.09,0.04]
highContErr = [0.07, 0.09, 0.03, 0.09, 0.06, 0.03]
# plotting
plt.scatter(range(len(lowCont)), lowCont, color = 'r', label = 'label1')
plt.scatter(range(len(highCont)), highCont, color = 'k', label = 'label2')
plt.xticks(range(len(lowCont)), IDs, size='small')
plt.errorbar(range(len(lowCont)), lowCont,yerr=lowContErr, linestyle="None", color = 'r')
plt.errorbar(range(len(highCont)), highCont,yerr = highContErr,linestyle = "None", color = 'k')
plt.xlabel('x')
plt.ylabel('y')
plt.title('graph title')
plt.legend()
plt.show()
However, what I am trying to establish here is to highlight the trend between the two data points for each x-axis label (increasing or decreasing). For this, I need the coupling of data points to be displayed side by side (rather than on top of each on a single vertical axis). Here is a sample of the desired plot for one x-axis label:
I guess my thought patterns direct me to create dummy sub x-axis categories for x-axis category (e.g., 0, 1), and assign the data points to them, but my skills in python and matplotlib are not enough for what I am trying to make.
You can achieve this by shifting the highCont along the x axis by a certain amount, then using plt.plot() to draw lines between them.
I have used the variable shift to move the highCont values along the x axis by 0.2 in the example below.
You can add caps to your error bars (which you included in the desired image) by using the capsize argument of plt.errorbar(), which defaults to None if not provided.
import matplotlib.pyplot as plt
import numpy as np
IDs = ['a', 'b', 'c', 'd', 'e', 'f']
lowCont = [-0.31, 0.71, 0.37, 0.05, 0.15, 1.33]
highCont = [-0.38, -0.16, 0.02, -0.55, -0.02, -0.51]
lowContErr = [0.03,0.13,0.02,0.10,0.09,0.04]
highContErr = [0.07, 0.09, 0.03, 0.09, 0.06, 0.03]
shift = 0.2 # Change this to increase distance between pairs of points
x_vals = np.arange(0,len(lowCont),1)
shifted_x_vals = np.arange(0+shift,len(highCont)+shift,1)
# loop through the data and plot the pairs of points to join them by a line
for x,x1,y,y1 in zip(x_vals,shifted_x_vals,lowCont,highCont):
plt.plot([x,x1], [y,y1], color="k")
plt.scatter(x_vals, lowCont, color = 'r', label = 'label1')
plt.scatter(shifted_x_vals, highCont, color = 'k', label = 'label2')
# set ticks to between the two points
plt.xticks(x_vals + (shift/2), IDs, size='small')
plt.errorbar(x_vals, lowCont,yerr=lowContErr, linestyle="None", color = 'r', capsize=3)
plt.errorbar(shifted_x_vals, highCont,yerr = highContErr,linestyle = "None", color = 'k', capsize=3)
plt.xlabel('x')
plt.ylabel('y')
plt.title('graph title')
plt.legend()
plt.show()
Which gives

Categories

Resources