Different scatterplot markers with the same label - matplotlib

I am having 'similar' issues to Matplotlib, legend with multiple different markers with one label. I was able to achieve the following thanks to this question Combine two Pyplot patches for legend.
fig = pylab.figure()
figlegend = pylab.figure(figsize=(3,2))
ax = fig.add_subplot(111)
point1 = ax.scatter(range(3), range(1,4), 250, marker=ur'$\u2640$', label = 'S', edgecolor = 'green')
point2 = ax.scatter(range(3), range(2,5), 250, marker=ur'$\u2640$', label = 'I', edgecolor = 'red')
point3 = ax.scatter(range(1,4), range(3), 250, marker=ur'$\u2642$', label = 'S', edgecolor = 'green')
point4 = ax.scatter(range(2,5), range(3), 250, marker=ur'$\u2642$', label = 'I', edgecolor = 'red')
figlegend.legend(((point1, point3), (point2, point4)), ('S','I'), 'center', scatterpoints = 1, handlelength = 1)
figlegend.show()
pylab.show()
However, my two (venus and mars) markers overlap in the legend. I tried playing with handlelength, but that doesn't seem to help. Any suggestions or comments would be helpful.

A possible workaround is to create a two column legend with blank labels in the first column:
figlegend.legend((point1, point2, point3, point4), (' ', ' ', 'S', 'I'),
'center', scatterpoints = 1, ncol = 2)

Here's my work-around MWE. I actually plot two extra "plots", point_g and point_r which have the legend handles we will use. I then cover them up by using a white squre marker. Plot the remaining plots as desired.
import matplotlib.pyplot as plt
plt.rc('text', usetex=True)
plt.rc('text', **{'latex.preamble': '\\usepackage{wasysym}'})
plt.rc('lines', **{'markersize':20})
fig = plt.figure()
point_g, = plt.plot((0,), (0,), ls='none', marker='$\\male\\female$', mec='g')
point_r, = plt.plot((0,), (0,), ls='none', marker='$\\male\\female$', mec='r')
plt.plot((0,), (0,), marker='s', mec='w', mfc='w')
plt.plot(range(3), range(1,4), ls='none', marker='$\\male$', mec='g')
plt.plot(range(3), range(2,5), ls='none', marker='$\\male$', mec='r')
plt.plot(range(1,4), range(3), ls='none', marker='$\\female$', mec='g')
plt.plot(range(2,5), range(3), ls='none', marker='$\\female$', mec='r')
plt.axis([-0.1, 4.1, -0.1, 4.1])
plt.legend((point_g, point_r), ('Green', 'Red'), markerscale=1.6, numpoints=1,
borderpad=0.8, handlelength=3, labelspacing=1)
plt.show()
Note: You do not need the LaTeX preamble if you use unicode symbols. I couldn't get them working on my system (Linux) so I used the LaTeX symbols. This method will work with all symbols, just remove the plt.rc commands and change \\male and \\female to your unicode characters.

Related

Grid lines not aligned with cells when defining minor ticks in Matplotlib Heatmap

I am using the code below to generate this heatmap:
dim = np.arange(1, 32, 1)
fig, ax = plt.subplots(figsize=(7,9))
heatmap = ax.imshow(h.T, cmap=plt.cm.get_cmap('Blues', 4), clim=[1,144])
cbaxes = fig.add_axes([.8, .35, .04, .3])
cbar = fig.colorbar(heatmap, ticks = [1, 36, 72, 108, 144], label = 'Number of valid records per day', cax = cbaxes)
ax.set_ylabel("Days", fontsize=15)
ax.set_xlabel("Months", fontsize=15)
ax.set_title("Number of valid records per day", fontsize=20)
ax.set_yticks(range(0,31))
ax.set_yticklabels(dim, ha='center', minor=False, fontsize=12)
ax.set_xticks(range(0,13,1))
ax.set_xticklabels(ylabel, rotation = 45, ha = 'right')
ax.set_facecolor('gray')
cbar.set_label('Number of valid records')
ax.xaxis.set_minor_locator(MultipleLocator(0.5))
ax.yaxis.set_minor_locator(MultipleLocator(0.5))
ax.tick_params(axis='y', which='major', pad=10)
ax.grid(which = 'minor', color = 'w')
fig.show()
As you can see there is a slight offset of the gridlines with respect to the heat map cells. Why is that? How can I fix it?
Thanks to the comment left by Jody Klymak, I added the following line of code at the beginning of my notebook and it solved the problem:
matplotlib.rcParams['figure.dpi'] = 300

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

Add vertical line to pandas df.plot of timeseries

I have a time series plot and I would like to add a vertical line to it at event time. If I use this code:
event_time = pd.to_datetime('10/12/2016 06:21:00')
ax = df_stats_t.plot(x = 't', y='t_TI_var_pwr', linestyle='none',...
color = 'black', marker = 'o')
ax1 = ax.twinx()
ax1.axvline(event_time, color='red', linestyle='-')
df_stats_t.plot(x='t',y='t_TI_var_ws',ax=ax1, linestyle='none', ...
color = 'green', marker = 'o')
It takes a subset of the time series starting at event_time and doesn't produce a vertical line.
If I move ax1.axvline(event_time, color='red', linestyle='-') to the bottom, I get the plot I want but the vertical line is still missing.
event_time = pd.to_datetime('10/12/2016 06:21:00')
ax = df_stats_t.plot(x = 't', y='t_TI_var_pwr', linestyle='none',...
color = 'black', marker = 'o')
ax1 = ax.twinx()
df_stats_t.plot(x='t',y='t_TI_var_ws',ax=ax1, linestyle='none',...
color = 'green', marker = 'o')
ax1.axvline(event_time, color='red', linestyle='-')
How can I get the vertical line to discplay at x = event_time for all y values?
works with plt
ax = df_stats_t.plot(x = 't', y='t_TI_var_pwr', linestyle='none', color = 'black', marker = 'o')
ax1 = ax.twinx()
df_stats_t.plot(x='t',y='t_TI_var_ws',ax=ax1, linestyle='none', color = 'green', marker = 'o')
plt.axvline(event_time, color='red', linestyle='-')

How to create a single legend for a group of bar charts generated from a dataframe

I have a dataframe as shown in the code and I generated a barh chart for each row using a loop. I am trying to plot a single legend for all the charts on the bottom-right corner but without success. I tried the lines of codes such as the ones below - and many others found online - inside the plotting loop and outside without success. Kindly suggest a solution that could work.
#handles, labels = bars.get_legend_handles_labels()
#bars.legend(handles, labels)
#bars.legend(loc='bottom right', ncol=9)
import pandas as pd
import matplotlib.pyplot as plt
import io
lines=['Business Category,Not_sure!,Performance_of_certain_applications,DNS_Resolution_Time,User’s_perception_of_your_network_(QoE),Routing_stability,Packet_loss/Jitter,Network_utilisation,Reachability,Latency,Bandwidth/Throughput\n'
'Academic
Institution,0.0,18.0,16.0,19.0,17.0,17.0,22.0,24.0,26.0,33.0\n'
'Civil society,0.0,5.0,2.0,2.0,1.0,1.0,2.0,4.0,4.0,6.0\n'
'End-user (Home/Mobile broadband),0.0,5.0,7.0,5.0,5.0,6.0,7.0,6.0,9.0,9.0\n'
'Internet Service Provider (ISP),1.0,20.0,22.0,22.0,27.0,31.0,20.0,25.0,32.0,32.0\n'
'Internet eXchange Point (IXP),0.0,2.0,3.0,2.0,7.0,6.0,5.0,5.0,8.0,7.0\n'
'Other,1.0,7.0,8.0,9.0,10.0,9.0,17.0,13.0,16.0,19.0\n'
'Regulator/Government Agency,0.0,1.0,1.0,2.0,1.0,0.0,2.0,1.0,4.0,5.0\n'
'Total,2.0,58.0,59.0,61.0,68.0,70.0,75.0,78.0,99.0,111.0\n']
df3 = pd.read_csv(pd.compat.StringIO("\n".join(lines)), sep=",").set_index('Business Category')
i = j = roundcounter = 0
patterns = ['\\', '|', '/', '+', 'x', 'o', 'O', '.', '*', '-']
color=['orange', 'darkseagreen', 'maroon', 'mediumpurple', 'saddlebrown', 'orchid', 'indianred',
'tomato', 'dimgrey', 'aquamarine']
fig, axes = plt.subplots(3,3, sharex=False, sharey=True)
print("\nWhich of these performance indicators/metrics are important for your organisation/network?\n\n")
for col in df3[:-1].index:
bars = df3.loc[col].plot.barh(width=.9, figsize=(15, 10), color=color, title=df3.loc[col].name,
ax=axes[i, j])
for spine in axes[i, j].spines:
axes[i, j].spines[spine].set_visible(False)
for bar, pattern in zip(bars.patches, patterns):
bar.set_hatch(pattern)
fig.tight_layout()
if j==2:
if roundcounter==0:
roundcounter+=1
i=1
j=0
elif roundcounter==1:
roundcounter+=1
j=0
i=2
elif roundcounter==2:
i=2
j=0
elif j==1 or j==0:
j+=1
axes[2, 1].axis('off')
axes[2, 2].axis('off')
bars.legend()
plt.savefig('figures/metrics.png')
plt.show()
As a new user I cannot post image yet but it is available here: https://drive.google.com/open?id=1c46FOZnA9aBtDb62kJg8h5bxePoTThrI
I am able to fix the problem after trying a number of solutions. See code below. There is an additional 'import matplotlib as mpl' to the libraries in the original question.
#Adding 'Total' row and column to use for sorting.
df3.loc['Total',:]= df3.sum(axis=0)
df3=df3[df3.iloc[-1,:].sort_values(ascending=False).index]
df3['Total'] = df3.sum(axis=1)
df3 = df3.sort_values(by='Total', ascending=False)
i = j = roundcounter = 0
patterns = ['\\', '|', '/', '+', 'x', 'o', 'O', '.', '*', '-']
color=['orange', 'darkseagreen', 'maroon', 'mediumpurple', 'saddlebrown', 'orchid', 'indianred',
'tomato', 'dimgrey', 'aquamarine']
fig, axes = plt.subplots(3,3, sharex=False, sharey=True)
print("\nWhich of these performance indicators/metrics are important for your organisation/network?\n\n")
#Plot the graphs
for col in df3[1:].index:
bars = df3.loc[col].drop(['Total']).plot.barh(width=.9, figsize=(22, 18), color=color, ax=axes[i, j])
axes[i, j].set_title(df3.loc[col].name, fontdict={'fontsize': 25, 'fontweight': 'medium'})
axes[i, j].get_yaxis().set_ticklabels([])
for tick in axes[i, j].xaxis.get_major_ticks():
tick.label.set_fontsize(25)
for spine in axes[i, j].spines:
axes[i, j].spines[spine].set_visible(False)
for bar, pattern in zip(bars.patches, patterns):
bar.set_hatch(pattern)
if j==2:
if roundcounter==0:
roundcounter+=1
i=1
j=0
elif roundcounter==1:
roundcounter+=1
j=0
i=2
elif roundcounter==2:
i=2
j=0
elif j==1 or j==0:
j+=1
axes[0, 2].set_xticks([0, 4, 8, 12, 16, 20], minor=False)
axes[2, 1].axis('off')
axes[2, 2].axis('off')
labels = df3.loc['Academic Institution'].drop(['Total']).index.tolist()
handles = [rect for rect in bars.get_children() if isinstance(rect, mpl.patches.Rectangle)]
legend = fig.legend(handles, labels, loc=4, fontsize=25)
legend.set_title('Metric/Options Selected',prop={'size':26})
plt.savefig('figures/metrics.png', bbox_inches="tight")
fig.tight_layout()
plt.show()

matplotlib line plot dont show vertical lines in step function

I do have a plot that only consists of horizontal lines at certain values when I have a signal, otherwise none. So, I am looking for a way to plot this without the vertical lines. there may be gaps between the lines when there is no signal and I dont want the lines to connect nor do I want a line falling off to 0. Is there a way to plot this like that in matplotlib?
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
axes = self.figure.add_subplot(111)
axes.plot(df.index, df["x1"], lw=1.0, c=self.getColour('g', i), ls=ls)
The plot you are looking for is Matplotlib's plt.hlines(y, xmin, xmax).
For example:
import matplotlib.pyplot as plt
y = range(1, 11)
xmin = range(10)
xmax = range(1, 11)
colors=['blue', 'green', 'red', 'yellow', 'orange', 'purple',
'cyan', 'magenta', 'pink', 'black']
fig, ax = plt.subplots(1, 1)
ax.hlines(y, xmin, xmax, colors=colors)
plt.show()
Yields a plot like this:
See the Matplotlib documentation for more details.