Search code examples
pythonpython-3.xindexoutofboundsexceptionsubplot

index out of bound multiple subplots with division rest python


I am creating multiple subplots from a dataframe containing reaction results (70 per dataframe) and I want to plot the reactions 12 by 12 to have a quick look before going through a more thorough analysis. Since 70/12 leaves a remainder, a simple implementation would run out of bound. I can solve this by using a "if,else" statement but it is not really elegant nor efficient. I would like to know if there was a nicer option. warDf is of size 70, meanDf is of size 130x70. time, pcmean and ncmean are of size 130. I am using the libraries pandas (pd), numpy (np) and matplotlib.pyplot (plt),

it=int(np.ceil(np.size(warDf)/12))# defining what to loop over
kk=0

for kk in np.arange(0,it): 
    #declaring the subplots 
    fig,axes=plt.subplots(nrows=3,ncols=4,sharex='col',sharey='row')
    #transforming axes in a usable list
    axe_list=[item for sublist in axes for item in sublist]

    # checking that I don't run out of bond
    if (12*kk+12<np.size(warDf)):
        k=0 
        # plotting each graph in its corresponding subplot
        for k in np.arange(0,12):                

            ax=axe_list.pop(0)
            ax.plot(time,meanDf.iloc[:,12*kk+k],label=(meanDf.columns[12*kk+k]),color='blue')
            ax.plot(time,pcmean,color='green')
            ax.plot(time,ncmean,color='red')
            ax.set_ylabel('fluorescence')
            ax.set_xlabel('time/ (s)')
            ax.legend()
    else: # better option??
        k=0
        s2=np.size(warDf)-12*kk
        for k in np.arange(0,s2):
            ax=axe_list.pop(0)
            ax.plot(time,meanDf.iloc[:,12*kk+k],label=(meanDf.columns[12*kk+k]),color='blue')
            ax.plot(time,pcmean,color='green')
            ax.plot(time,ncmean,color='red')
            ax.set_ylabel('fluorescence')
            ax.set_xlabel('time/ (s)')
            ax.legend()

plt.show()

Solution

  • You could use the min() function. Replace the entire if/else with this:

    k=0 # note: you don't have to pre-define k here
    
    s2 = min(np.size(warDf) - 12 * kk, 12) # new part
    
    for k in np.arange(0,s2): # the rest is the same as in the body of the else
        ax=axe_list.pop(0)
        ax.plot(time,meanDf.iloc[:,12*kk+k],label=(meanDf.columns[12*kk+k]),color='blue')
        ax.plot(time,pcmean,color='green')
        ax.plot(time,ncmean,color='red')
        ax.set_ylabel('fluorescence')
        ax.set_xlabel('time/ (s)')
        ax.legend()
    

    Explanation

    You currently have

    if (12 * kk + 12 < np.size(warDf)):
        s2 = 12 # define s2 as a variable here as well
        for k in np.arange(0, s2):
            # ...
    else:
        s2 = np.size(warDf) - 12 * kk 
        for k in np.arrange(0, s2):
            # ...
    

    Rearranging that first if, we can get:

    if (12 < np.size(warDf) - 12 * kk):
        s2 = 12
        # ...
    else:
        s2 = np.size(warDf) - 12 * kk
        # ...
    

    Now you can see that the right side of the if and the assignment to s2 are the same. If 12 is less, use 12. Otherwise, use np.size(warDf) - 12 * kk. This is the definition of min().