Draw multiple set of barplot + lineplot using seaborn - python

I'm trying to draw multiple sets of barplot + lineplot from a dataframe using seaborn like the one below (I've already succeeded in drawing one):
Here you can find the dataframe used to produce it => dataframe
What I'd like to achieve is this kind of graph (which I have already succeeded in creating) but repeated for all the possibile product families inside a speicifc product basket ('family' is a drill down level of 'product basket')
Thanks a lot for anyone who will help me. I've tried some looping but probably I'm just doing it wrong.
Here is the code that I've used to draw the above graph:
#select a family
df_perc2=df_perc.query('family=="WORKWEAR & PROTECTIVE CLOTHING"')
#set common axis
fig, ax = plt.subplots()
ax_twin = ax.twinx()
#set boxplot general aspect
fig = plt.gcf()
fig.set_size_inches(30, 10)
sns.set_style("white")
#ship_to_count bars
barplot = sns.barplot(data=df_perc2,
x = 'orders_count',
hue = 'SF_type',
y = 'ship_to_perc',
palette = "Set2",
ax = ax)
#cumulative % line
lineplot = sns.pointplot(data = df_perc2,
x = 'orders_count',
hue = 'SF_type',
y = 'running_perc',
palette = "Set2",
marker ='o',
ax = ax_twin,
legend = False)
#set tick stiles for x and y axis
barplot.set_xticklabels(barplot.get_xmajorticklabels(), fontsize = 18)
barplot.set_yticklabels(barplot.get_yticks().round(2), size = 18)
lineplot.set_yticklabels(lineplot.get_yticks().round(2), size = 18)
#set dynamic title
barplot.set_title('% Ship Tos by # orders for '+''.join(df_perc2['product_basket'].unique())
+
' - '
+
''.join(df_perc2['family'].unique()), fontdict = { 'fontsize': 30}, y = 1.05)
barplot.get_legend().remove()
#set constant line at 90%
plt.axhline(y=0.9, color='g', ls=':', lw=4, label='90th percentile')
lineplot.legend(loc='center right',fontsize='22')

Related

Subplots with shared x-axis, hspace between pairs of axes, and legend visible

I'm having trouble with a plot I'm trying to make in matplotlib. I'm including the current version below (note the partially visible legend in the top right). I have two questions:
How do I get all of the subplots to shrink inward (i.e. their right boundaries to move leftward) to make room for the legend?
I would like group these axes into pairs by adding vertical space between each pair, i.e. if we are counting axes from 0 then I would like to have extra space between axes 1 and 2 as well as between 3 and 4. It seems like there should be a way to do this using subfigures or nested gridspecs, but subplots was the only way I could see to implement a shared x axis.
Thanks in advance.
Here is the code that I am using to generate it:
fig, axs = plt.subplots(6, 1, sharex = True, figsize = (8,8))
ts = [9, 38, 47]
for i in range(len(ts)):
t = ts[i]
ax = axs[2*i]
twin = axs[2*i+1]
bed_profile = dye_test.bed_profile_vs_t[t]
head_top_BC = head_top_BC_vs_t[t]
x_interp = np.linspace(x_axis.min(), x_axis.max(), 1000)
head_fn = interp1d(x_axis, head_top_BC)
h_interp = head_fn(x_interp)
peak_locs, peak_props = find_peaks(h_interp, prominence = 0.01)
trough_locs, trough_props = find_peaks(-h_interp, prominence = 0.01)
ax.plot(x_interp, h_interp)
ax.scatter(x_interp[peak_locs], h_interp[peak_locs], fc = 'limegreen', ec = 'black', marker = 'o', zorder = 3)
ax.scatter(x_interp[trough_locs], h_interp[trough_locs], fc = 'magenta', ec = 'black', marker = 's', zorder = 3)
ax.set_ylim(-0.072, 0.1)
twin.plot(bed_profile[:,0], bed_profile[:,1], color = 'darkgray')
twin.set_ylim(16, 19.25)
l = fig.axes[0].legend(artists, ["Head", "Head Peaks", "Head Troughs", "Bed Profile"], loc = 'upper left', bbox_to_anchor = (1.1, 1))

Matplotlib Draw a Constant y Axis

I want to use matpoltlib to make a plot that with a constant y axis(always from 0 to 14 and the gap is 1), since I want to make labels for them and my dot values will be(x, y) where y is from 0 to 14 gap 1, and a changing x axis. I already tried to play with y ticks. And here is my code for that:
fig, ax = plt.subplots()
fig.canvas.draw()
plt.yticks(np.arange(0, 14, 1))
labels = [item.get_text() for item in ax.get_yticklabels()]
labels[1] = 'Not Detected'
labels[2] = 'A/G'
labels[3] = 'G/G'
labels[4] = 'C/T'
labels[5] = 'C/C'
labels[6] = 'A/A'
labels[7] = '-1'
labels[8] = 'ε3/ε3'
labels[9] = 'A/C'
labels[10] = 'T/T'
labels[11] = 'C/G'
labels[12] = 'ε2/ε3'
labels[13] = 'G/T'
ax.set_yticklabels(labels)
what I'm thinking about is to use some values or lines with white color so those y axis will appear. But I'm looking for a more efficient way of doing it. And here is the diagram I generated with the current code. It only shows C/C right now and I want all labels to appear in the diagram.
I tried draw white points with:
x1 = np.arange(n)
y1 = np.arange(1,15,1)
plt.scatter(x1,y1,color = 'white')
Which did give me what I want: But I was wondering whether there is a lib setting that can do this.
I would recommend just using a fixed locator and fixed formatter for your y axis. The function, ax.set_yticklabels() is simply a convenience wrapper for these tick methods.
I would also recommend having your y_labels in a list or using a loop structure as this is a more generalizable and modifiable implementation.
If I'm understanding the goals of your plot correctly, something like this may work well for you.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
#make some data
x = np.arange(25)
y = np.random.randint(1, 14, size=25)
#convert y labels to a list
y_labels = [
'Not Detected','A/G','G/G','C/T','C/C','A/A',
'-1','ε3/ε3', 'A/C','T/T','C/G','ε2/ε3','G/T'
]
#define figure/ax and set figsize
fig, ax = plt.subplots(figsize=(12,8))
#plot data, s is marker size, it's points squared
ax.scatter(x, y, marker='x', s=10**2, color='#5d2287', linewidth=2)
#set major locator and formatter to fixed, add grid, hide top/right spines
locator = ax.yaxis.set_major_locator(mpl.ticker.FixedLocator(np.arange(1, 14)))
formatter = ax.yaxis.set_major_formatter(mpl.ticker.FixedFormatter(y_labels))
grid = ax.grid(axis='y', dashes=(8,3), alpha=0.3, color='gray')
spines = [ax.spines[x].set_visible(False) for x in ['top','right']]
params = ax.tick_params(labelsize=12) #increase label font size

How to have a secondary y axis in a nested GridSpec?

I'd like to obtain this figure:
But with two plots inside each graph, like this:
Here is a sample of the code I used for the first figure
measures = ['ACE', 'SCE', 'LZs', 'LZc']
conditions = ['dark','light','flick3','flick10','switch']
outer_grid = gridspec.GridSpec(2,2)
for measure in measures:
inner_grid = gridspec.GridSpecFromSubplotSpec(5, 1, subplot_spec=outer_grid[measures.index(measure)])
ax={}
for cond in conditions:
c=conditions.index(cond)
ax[c] = plt.Subplot(fig, inner_grid[c])
if c != 0:
ax[c].get_shared_y_axes().join(ax[0], ax[c])
ax[c].plot()
ax[c+n]=ax[c].twinx()
ax[c+n].scatter()
ax[c+n].set_ylim(0,5)
fig.add_subplot(ax[c],ax[c+n])
For the second plot, it's basically the same without the first loop and GridSpec, using ax[c]=plt.subplot('51{c}') instead of ax[c]=plt.Subplot(fig, inner_grid[c]).
As you can see, when using GridSpec I still have the secondary y axis but not the scatter plot associated.
I guess the short question would be How to write fig.add_subplot(ax[c],ax[c+n]) properly?
(fig.add_subplot(ax[c]) fig.add_subplot(ax[c+n]) in two lines doesn't work.)
It is not clear from your question exactly which data you're plotting in each subplot, plus the way you're creating your subplots seems a little convoluted, which is probably why you're having problems. Here is how I would do it:
import matplotlib.gridspec as gs
measures = ['ACE', 'SCE', 'LZs', 'LZc']
conditions = ['dark','light','flick3','flick10','switch']
colors = ['g','c','b','r','grey']
Npoints = 10
data = [np.random.random((Npoints,len(measures))) for i in range(len(conditions))]
gs00 = gs.GridSpec(len(conditions), 1)
fig = plt.figure(figsize=(5,5))
for i,condition in enumerate(conditions):
ax1 = fig.add_subplot(gs00[i])
ax2 = ax1.twinx()
ax1.plot(range(Npoints), data[i][:,0], 'o-', color=colors[i], label=measures[0])
ax2.plot(range(Npoints), data[i][:,1], 'o-.', color=colors[i], label=measures[1])
ax1.set_ylim((-0.1,1.1))
ax2.set_ylim(ax1.get_ylim())
ax1.set_title(condition)
EDIT to get the same thing repeated 4 times, the logic is exactly the same, you just have to play around with the gridspec. But the only things that matters are the lines ax1 = fig.add_subplot(gs01[j]) followed by ax2 = ax1.twinx(), which will create a second axis on top of the first
import matplotlib.gridspec as gs
measures = ['ACE', 'SCE', 'LZs', 'LZc']
conditions = ['dark','light','flick3','flick10','switch']
colors = ['g','c','b','r','grey']
Npoints = 10
data = [np.random.random((Npoints,len(measures))) for i in range(len(conditions))]
gs00 = gs.GridSpec(2,2)
plt.style.use('seaborn-paper')
fig = plt.figure(figsize=(10,10))
grid_x, grid_y = np.unravel_index(range(len(measures)),(2,2))
for i,measure in enumerate(measures):
gs01 = gs.GridSpecFromSubplotSpec(len(conditions), 1, subplot_spec=gs00[grid_x[i],grid_y[i]])
for j,condition in enumerate(conditions):
ax1 = fig.add_subplot(gs01[j])
ax2 = ax1.twinx()
ax1.plot(range(Npoints), data[j][:,0], 'o-', color=colors[j], label=measures[0])
ax2.plot(range(Npoints), data[j][:,1], 'o-.', color=colors[j], label=measures[1])
ax1.set_ylim((-0.1,1.1))
ax2.set_ylim(ax1.get_ylim())
if j==0:
ax1.set_title(measure)

How can I edit the xticks on a python twinx?

I've got two pandas series I would like to plot on the same axis, using a twinx. Here is a picture of what it looks like:
fig,(ax1,ax2,ax3,ax4,ax5) = plt.subplots(nrows = 5,ncols = 1, figsize = (8,13))
plt.subplots_adjust(hspace = 0.5)
ax1_1 = ax1.twinx()
df[["Var"]].plot(ax = ax1, label = 'Variance')
df[['w2']].plot(ax = ax1_1, color = 'g', label = '$w$')
ax1.locator_params('y',nbins = 5)
ax1_1.locator_params('y', nbins = 5)
ax1.set_ylabel('AC', labelpad = 10)
ax1_1.set_ylabel('w', labelpad = 10)
ax1.legend(loc = 'upper left')
ax1_1.legend()
I'd like to edit the x axis ticks, but using ax1.set_xticks() and ax1.set_xticklabels() doesn't seem to work. Furthermore, why are there are no x ticks after I execute the code found above? Shouldn't the ticks be the index of the Series?

Laying out several plots in matplotlib + numpy

I am pretty new to python and want to plot a dataset using a histogram and a heatmap below. However, I am a bit confused about
How to put a title above both plots and
How to insert some text into bots plots
How to reference the upper and the lower plot
For my first task I used the title instruction, which inserted a caption in between both plots instead of putting it above both plots
For my second task I used the figtext instruction. However, I could not see the text anywhere in the plot. I played a bit with the x, y and fontsize parameters without any success.
Here is my code:
def drawHeatmap(xDim, yDim, plot, threshold, verbose):
global heatmapList
stableCells = 0
print("\n[I] - Plotting Heatmaps ...")
for currentHeatmap in heatmapList:
if -1 in heatmapList[currentHeatmap]:
continue
print("[I] - Plotting heatmap for PUF instance", currentHeatmap,"(",len(heatmapList[currentHeatmap])," values)")
# Convert data to ndarray
#floatMap = list(map(float, currentHeatmap[1]))
myArray = np.array(heatmapList[currentHeatmap]).reshape(xDim,yDim)
# Setup two plots per page
fig, ax = plt.subplots(2)
# Histogram
weights = np.ones_like(heatmapList[currentHeatmap]) / len(heatmapList[currentHeatmap])
hist, bins = np.histogram(heatmapList[currentHeatmap], bins=50, weights=weights)
width = 0.7 * (bins[1] - bins[0])
center = (bins[:-1] + bins[1:]) / 2
ax[0].bar(center, hist, align='center', width=width)
stableCells = calcPercentageStable(threshold, verbose)
plt.figtext(100,100,"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", fontsize=40)
heatmap = ax[1].pcolor(myArray, cmap=plt.cm.Blues, alpha=0.8, vmin=0, vmax=1)
cbar = fig.colorbar(heatmap, shrink=0.8, aspect=10, fraction=.1,pad=.01)
#cbar.ax.tick_params(labelsize=40)
for y in range(myArray.shape[0]):
for x in range(myArray.shape[1]):
plt.text(x + 0.5, y + 0.5, '%.2f' % myArray[y, x],
horizontalalignment='center',
verticalalignment='center',
fontsize=(xDim/yDim)*5
)
#fig = plt.figure()
fig = matplotlib.pyplot.gcf()
fig.set_size_inches(60.5,55.5)
plt.savefig(dataDirectory+"/"+currentHeatmap+".pdf", dpi=800, papertype="a3", format="pdf")
#plt.title("Heatmap for PUF instance "+str(currentHeatmap[0][0])+" ("+str(numberOfMeasurements)+" measurements; "+str(sizeOfMeasurements)+" bytes)")
if plot:
plt.show()
print("\t[I] - Done ...")
And here is my current output:
Perhaps this example will make things easier to understand. Things to note are:
Use fig.suptitle to add a title to the top of a figure.
Use ax[i].text(x, y, str) to add text to an Axes object
Each Axes object, ax[i] in your case, holds all the information about a single plot. Use them instead of calling plt, which only really works well with one subplot per figure or to modify all subplots at once. For example, instead of calling plt.figtext, call ax[0].text to add text to the top plot.
Try following the example code below, or at least read through it to get a better idea how to use your ax list.
import numpy as np
import matplotlib.pyplot as plt
histogram_data = np.random.rand(1000)
heatmap_data = np.random.rand(10, 100)
# Set up figure and axes
fig = plt.figure()
fig.suptitle("These are my two plots")
top_ax = fig.add_subplot(211) #2 rows, 1 col, 1st plot
bot_ax = fig.add_subplot(212) #2 rows, 1 col, 2nd plot
# This is the same as doing 'fig, (top_ax, bot_ax) = plt.subplots(2)'
# Histogram
weights = np.ones_like(histogram_data) / histogram_data.shape[0]
hist, bins = np.histogram(histogram_data, bins=50, weights=weights)
width = 0.7 * (bins[1] - bins[0])
center = (bins[:-1] + bins[1:]) / 2
# Use top_ax to modify anything with the histogram plot
top_ax.bar(center, hist, align='center', width=width)
# ax.text(x, y, str). Make sure x,y are within your plot bounds ((0, 1), (0, .5))
top_ax.text(0.5, 0.5, "Here is text on the top plot", color='r')
# Heatmap
heatmap_params = {'cmap':plt.cm.Blues, 'alpha':0.8, 'vmin':0, 'vmax':1}
# Use bot_ax to modify anything with the heatmap plot
heatmap = bot_ax.pcolor(heatmap_data, **heatmap_params)
cbar = fig.colorbar(heatmap, shrink=0.8, aspect=10, fraction=.1,pad=.01)
# See how it looks
plt.show()

Categories

Resources