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)
Related
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')
I am looping through a list containing 6 col_names. I loop by taking 3 cols at a time so i can print 3 subplots per iteration later.
I have 2 dataframes with same column names so they look identical except for the histograms of each column name.
I want to plot similar column names of both dataframes on the same subplot. Right now, im plotting their histograms on 2 separate subplots.
currently, for col 'A','B','C' in df_plot:
and for col 'A','B','C' in df_plot2:
I only want 3 charts where i can combine similar column names into same chart so there is blue and yellow bars in the same chart.
Adding df_plot2 below doesnt work. i think im not defining my second axs properly but im not sure how to do that.
col_name_list = ['A','B','C','D','E','F']
chunk_list = [col_name_list[i:i + 3] for i in xrange(0, len(col_name_list), 3)]
for k,g in enumerate(chunk_list):
df_plot = df[g]
df_plot2 = df[g][df[g] != 0]
fig, axs = plt.subplots(1,len(g),figsize = (50,20))
axs = axs.ravel()
for j,x in enumerate(g):
df_plot[x].value_counts(normalize=True).head().plot(kind='bar',ax=axs[j], position=0, title = x, fontsize = 30)
# adding this doesnt work.
df_plot2[x].value_counts(normalize=True).head().plot(kind='bar',ax=axs[j], position=1, fontsize = 30)
axs[j].title.set_size(40)
fig.tight_layout()
the solution is to plot on the same ax:
change axs[j] to axs
for k,g in enumerate(chunk_list):
df_plot = df[g]
df_plot2 = df[g][df[g] != 0]
fig, axs = plt.subplots(1,len(g),figsize = (50,20))
axs = axs.ravel()
for j,x in enumerate(g):
df_plot[x].value_counts(normalize=True).head().plot(kind='bar',ax=axs, position=0, title = x, fontsize = 30)
# adding this doesnt work.
df_plot2[x].value_counts(normalize=True).head().plot(kind='bar',ax=axs, position=1, fontsize = 30)
axs[j].title.set_size(40)
fig.tight_layout()
then just call plt.plot()
Example this will plot x and y on the same subplot:
import matplotlib.pyplot as plt
x = np.arange(0, 10, 1)
y = np.arange(0, 20, 2)
ax = plt.subplot(1,1)
fig = plt.figure()
ax = fig.gca()
ax.plot(x)
ax.plot(y)
plt.show()
EDIT:
There is now a squeeze keyword argument. This makes sure the result is always a 2D numpy array.
fig, ax2d = subplots(2, 2, squeeze=False)
if needed Turning that into a 1D array is easy:
axli = ax1d.flatten()
I want to use the cursor (x,y values get displayed at the bottom left of figure) to measure the y and x distance between two points, however this only works for the data plotted on the second axis.
Is there a way to switch back an forth between the second axis and first y-axis?
Please note: I do not want a programmatic way of getting distance between points, just to use the cursor when I am viewing data in the figure plot.
Not sure if this helps but my code is literally the example for plotting two axes from the matplotlib page:
fig, ax1 = plt.subplots()
ax1.plot(sensor1, 'b-')
ax1.set_xlabel('(time)')
# Make the y-axis label and tick labels match the line color.
ax1.set_ylabel('Sensor 1', color='b')
for tl in ax1.get_yticklabels():
tl.set_color('b')
ax2 = ax1.twinx()
ax2.plot(sensor2, 'r.')
ax2.set_ylabel('Sensor 2', color='r')
for tl in ax2.get_yticklabels():
tl.set_color('r')
plt.show()
You can use the excellent answer here to get both coordinates displayed at the same time. In order to get distance between two points, you can then combine this idea with ginput to map from one to the other and add the result as a title,
import matplotlib.pyplot as plt
import numpy as np
#Provide other axis
def get_othercoords(x,y,current,other):
display_coord = current.transData.transform((x,y))
inv = other.transData.inverted()
ax_coord = inv.transform(display_coord)
return ax_coord
#Plot the data
fig, ax1 = plt.subplots()
t = np.linspace(0,2*np.pi,100)
ax1.plot(t, np.sin(t),'b-')
ax1.set_xlabel('(time)')
ax1.set_ylabel('Sensor 1', color='b')
for tl in ax1.get_yticklabels():
tl.set_color('b')
ax2 = ax1.twinx()
ax2.plot(t,3.*np.cos(t),'r-')
ax2.set_ylabel('Sensor 2', color='r')
for tl in ax2.get_yticklabels():
tl.set_color('r')
#Get user input
out = plt.ginput(2)
#2nd axis from input
x2b, x2t = out[0][0], out[1][0]
y2b, y2t = out[0][1], out[1][1]
#Draw line
ax2.plot([x2b, x2t],[y2b, y2t],'k-',lw=3)
#1st axis from transform
x1b, y1b = get_othercoords(x2b,y2b,ax2,ax1)
x1t, y1t = get_othercoords(x2t,y2t,ax2,ax1)
plt.title("Distance x1 = " + str(x1t-x1b) + " y1 = " + str(y1t-y1b) + "\n"
"Distance x2 = " + str(x2t-x2b) + " y2 = " + str(y2t-y2b))
plt.draw()
plt.show()
which gives something like,
I would like to have a window that is divided in 4 sectors: in the (0,0) a imshow image (ax1); (1,0) a subplot image that uses twinx() image that divides the window(ax2 & ax3); (1,1) a regular plot image (ax4); and an iterative section (0,1) of plots that should give "number_of_subplots" plots one above the other (ax5). Hopefully with no xticklabels but the last one.
This is how the frame should look like before the iterative subplot creation.
My problem: when iterating to create the subplots on the top right space of the window, the subplots span away from that space and eliminate the ax4
This is how the window looks after the "for" cyle for the subplot creation
Below you'll find a simplification of the code I am using, just so you can see it better. I have replaced my experimental data with random numbers so you can replicate this easily.
Could you give me a hint on what am I doing wrong? I still do not dominate all the handlers in python. I used to do similar things in matlab a few years ago.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
import pdb
pos = [1,2,3,4,5]
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
xx = np.linspace(0, 20, 1000)
fig1 = plt.figure()
number_of_subplots = len(pos) #number between 1-7
ax1 = plt.subplot2grid((number_of_subplots+1,2),(0,0),rowspan = number_of_subplots-1) # Here the idea is to "dinamically" create the division of the grid, making space at the bottom of it for the image in the bottom left.
ax1.scatter(x,y)
ax2 = plt.subplot2grid((number_of_subplots+1,2),(number_of_subplots-1,0), rowspan = 2)
ax2.plot(xx,np.sin(xx),label = 'sin(x)',color = 'b')
ax3 = ax2.twinx()
ax3.plot(xx,np.cos(xx), label = 'cos(x)', color = 'r')
ax4 = plt.subplot2grid((number_of_subplots+1,2),(number_of_subplots-1,1), rowspan = 2)
ax4.plot(xx,np.tan(xx), label = 'tan(x)', color = 'g')
for i,v in enumerate(xrange(number_of_subplots)):
v = v+1
ax5 = plt.subplot2grid((number_of_subplots+1,2),(v-1,1))
ax5.plot(np.sin(xx+3.1416*v/2)) # Grafica los perfiles, asociandoles el mismo color que para los cortes en la imagen 2D
if (i % 2 == 0): #Even
ax5.yaxis.tick_left()
else:
ax5.yaxis.tick_right()
plt.draw()
plt.show()
Solved the issue by using GridSpec as it is supposed to be used. Below is the implementation of the code that gives the following solution.
This is the correct way the image should look like and the implementation is below on the code.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
import pdb
pos = [1,2,3,4,5]
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
xx = np.linspace(0, 20, 1000)
number_of_subplots = len(pos) #number between 1-7
fig1 = plt.figure()
gs0 = gridspec.GridSpec(2,2,height_ratios=[3,1],hspace=0.1)
ax1 = plt.subplot(gs0[0,0])
ax2 = plt.subplot(gs0[-1,0])
ax4 = plt.subplot(gs0[-1,-1])
gs2 = gridspec.GridSpecFromSubplotSpec(number_of_subplots, 1, subplot_spec=gs0[1],wspace=0.0, hspace=0.0)
ax1.scatter(x,y)
ax2.plot(xx,np.sin(xx),label = 'sin(x)',color = 'b')
ax3 = ax2.twinx()
ax3.plot(xx,np.cos(xx), label = 'cos(x)', color = 'r')
ax4.plot(xx,np.tan(xx), label = 'tan(x)', color = 'g')
for i in enumerate(xrange(number_of_subplots)):
ax5 = plt.subplot(gs2[i,:])
ax5.plot(np.sin(xx+3.1416*i/2))
if (i % 2 == 0): #Even
ax5.yaxis.tick_left()
else:
ax5.yaxis.tick_right()
plt.draw()
plt.show()
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()