Hypothesis testing for brushing experiments: Overview

Imports

In [1]:
import numpy as np
import pymc3 as pm
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
from scipy.stats import norm
from sklearn.preprocessing import LabelEncoder
from theano import tensor as tt
from sys import exit
import pickle
import arviz 
In [2]:
inpath = '../Output/SingleParameters/'
outpath = '../Output/Overview/'

Prepare some variables for later use

In [3]:
df = pd.read_excel("../Data/brushing_v2.xlsx").dropna("columns")
In [4]:
factorNames = ['Material','Brush', 'Dirt', 'Before.after','Area'] 
In [5]:
x1contrast_dict = {'No_Brush_Is_Dirt_vs_No_Brush_No_Dirt': [0, 0,1,-1],
                  'Is_Brush_No_Dirt_vs_No_Brush_No_Dirt': [0, 1,0,-1],
                  'Is_Brush_Is_Dirt_vs_No_Brush_No_Dirt': [1, 0,0,-1]} 
x2contrast_dict = {} 
x1x2contrast_dict = {}
In [6]:
quantityNames = ['epLsar', 'Asfc', 'Smfc', 'HAsfc9',
       'HAsfc81', 'Sq.SL', 'Ssk.SL', 'Sku.SL', 'Sp.SL', 'Sv.SL', 'Sz.SL',
       'Sa.SL', 'Smr.SL', 'Smc.SL', 'Sxp.SL', 'Sal.SL', 'Str.SL', 'Std.SL',
       'Sdq.SL', 'Sdr.SL', 'Vm.SL', 'Vv.SL', 'Vmp.SL', 'Vmc.SL', 'Vvc.SL',
       'Vvv.SL', 'Maximum.depth.of.furrows.SL', 'Mean.depth.of.furrows.SL',
       'Mean.density.of.furrows.SL', 'Isotropy.SL', 'First.Direction.SL',
       'Second.Direction.SL', 'Third.Direction.SL', 'Isotropy.SL.1', 'Sq.SF',
       'Ssk.SF', 'Sku.SF', 'Sp.SF', 'Sv.SF', 'Sz.SF', 'Sa.SF', 'Smr.SF',
       'Smc.SF', 'Sxp.SF', 'Sal.SF', 'Str.SF', 'Std.SF', 'Sdq.SF', 'Sdr.SF',
       'Vm.SF', 'Vv.SF', 'Vmp.SF', 'Vmc.SF', 'Vvc.SF', 'Vvv.SF',
       'Maximum.depth.of.furrows.SF', 'Mean.depth.of.furrows.SF',
       'Mean.density.of.furrows.SF', 'Isotropy.SF', 'First.Direction.SF',
       'Second.Direction.SF', 'Third.Direction.SF', 'Isotropy.SF.1']
In [7]:
idDatasetsDict = dict(enumerate(quantityNames))
In [8]:
dictQuantitiy = {}
dictHPD1 = {}
dictSig1 = {}
dictHPD2 = {}
dictSig2 = {}
dictHPD3 = {}
dictSig3 = {}

Load results to get overview over all significance tests

In [9]:
for index,idMeasure in idDatasetsDict.items():   
    
            identifier = idMeasure
            try:                
                with open(inpath + 'model_{}.pkl'.format(identifier), 'rb') as buff:
                    data = pickle.load(buff) 
                    
                
                filtered = df[factorNames +  [idMeasure]]
                
                filtered = pd.get_dummies(filtered, columns=['Before.after'])
                
                filtered['Weighted_'+idMeasure] = filtered[idMeasure]*filtered['Before.after_After']-filtered[idMeasure]*filtered['Before.after_Before']
                
                filtered = filtered.groupby(['Material','Brush','Dirt','Area']).sum().reset_index()[['Material','Brush','Dirt','Area','Weighted_'+idMeasure]]
                
                filtered = filtered.replace({'Yes': 'Is'})
                
                filtered = filtered.rename(columns={'Weighted_'+idMeasure : 'Delta_' +idMeasure})
                              
                melt = pd.DataFrame()
                melt["Factor1"] = filtered['Brush']+"_Brush_" + filtered['Dirt'] + "_Dirt"
                melt["Factor2"] = filtered["Material"]
                melt['Value'] = filtered['Delta_'+idMeasure]
                mu_Val = melt['Value'].mean()
                sig_Val = melt['Value'].std(ddof=1)
                melt['z'] = (melt['Value'] - mu_Val)/sig_Val

                basic_model, trace = data['model'], data['trace']
                
                b1_sample = sig_Val*trace['b1']+mu_Val
                b2_sample = sig_Val*trace['b2']+mu_Val
                b1b2_sample = sig_Val*trace['M']+mu_Val
                                
                dictQuantitiy[index] = idMeasure                
                
                for indexContrast, (key, value) in enumerate(x1contrast_dict.items()):
                        contrast = np.dot(b1_sample, value)
                        
                        hpd = arviz.hpd(contrast)                    
                        significance = 0.0 <= hpd[0] or 0.0 >= hpd[-1]  
                        
                        if indexContrast == 0: 
                            dictHPD1[index] = hpd
                            dictSig1[index] = significance
                        if indexContrast == 1: 
                            dictHPD2[index] = hpd
                            dictSig2[index] = significance
                            
                        if indexContrast == 2: 
                            dictHPD3[index] = hpd
                            dictSig3[index] = significance
                        
            except:
                print("Error for {}".format(idMeasure))
                pass
                    
In [10]:
dictConverted = {'measuredQuantity':list(dictQuantitiy.values()),
                 'hpd1':list(dictHPD1.values()),
                 'No_Brush_Is_Dirt_Significant':list(dictSig1.values()),
                 'hpd2':list(dictHPD2.values()),
                 'Is_Brush_No_Dirt_Significant':list(dictSig2.values()),
                 'hpd3':list(dictHPD3.values()),
                 'Is_Brush_Is_Dirt_Significant':list(dictSig3.values())
                }
In [11]:
dfSig = pd.DataFrame.from_dict(dictConverted)
dfSig['lowHPD1'] = dfSig.apply(lambda r: r['hpd1'][0],axis=1)
dfSig['highHPD1'] = dfSig.apply(lambda r: r['hpd1'][-1],axis=1)
dfSig = dfSig.drop('hpd1', 1)
dfSig['lowHPD2'] = dfSig.apply(lambda r: r['hpd2'][0],axis=1)
dfSig['highHPD2'] = dfSig.apply(lambda r: r['hpd2'][-1],axis=1)
dfSig = dfSig.drop('hpd2', 1)
dfSig['lowHPD3'] = dfSig.apply(lambda r: r['hpd3'][0],axis=1)
dfSig['highHPD3'] = dfSig.apply(lambda r: r['hpd3'][-1],axis=1)
dfSig = dfSig.drop('hpd3', 1)
dfSig['anySig'] = dfSig.apply(lambda r: r['No_Brush_Is_Dirt_Significant'] or r['Is_Brush_No_Dirt_Significant'] or r['Is_Brush_Is_Dirt_Significant'],axis=1)
dfSig
Out[11]:
measuredQuantity No_Brush_Is_Dirt_Significant Is_Brush_No_Dirt_Significant Is_Brush_Is_Dirt_Significant lowHPD1 highHPD1 lowHPD2 highHPD2 lowHPD3 highHPD3 anySig
0 epLsar False True True -0.000479 0.000016 -0.000677 -0.000107 -0.000690 -0.000130 True
1 Asfc False False False -3.144006 12.387835 -3.370301 11.936648 -5.530938 8.454338 False
2 Smfc False False False -7.991613 121.428134 -5.720968 134.487161 -8.276030 126.684908 False
3 HAsfc9 False True False -0.035967 0.465312 0.056452 0.665604 -0.021494 0.487958 True
4 HAsfc81 False False False -0.040201 0.605657 -0.003064 0.719795 -0.007711 0.650600 False
5 Sq.SL False False False -325.847177 1035.070897 -421.703348 953.494348 -754.375251 562.432978 False
6 Ssk.SL False False False -0.393692 0.688395 -0.685515 0.355393 -0.484713 0.581271 False
7 Sku.SL False True False -0.956137 0.715376 0.293037 2.012304 -1.588012 0.049136 True
8 Sp.SL False False False -1774.756542 4456.756443 -1760.988940 4106.140574 -2884.171456 2828.659948 False
9 Sv.SL False False False -2594.206823 4094.054931 -1746.367688 5089.329752 -4588.013483 2183.535245 False
10 Sz.SL False False False -2551.506514 8131.628052 -2332.133688 8515.624185 -5741.627649 4194.793455 False
11 Sa.SL False False False -260.906399 621.058454 -368.530720 496.218745 -470.977082 394.733170 False
12 Smr.SL True False False 0.009736 0.331283 -0.159055 0.127101 -0.195928 0.090206 True
13 Smc.SL False False False -644.483045 1011.229093 -1065.602747 593.107562 -689.218075 961.174563 False
14 Sxp.SL False False False -1487.509661 4095.409203 -728.216660 5435.832972 -3260.535929 2263.146450 False
15 Sal.SL False False False -1.619218 1.142412 -2.117712 0.852832 -2.200910 0.757532 False
16 Str.SL False False False -0.117270 0.172689 -0.089158 0.213735 -0.168020 0.124464 False
17 Std.SL False False False -28.195205 61.820148 -22.493012 69.681270 -16.163748 78.482641 False
18 Sdq.SL False False False -0.185528 1.183338 -0.063378 1.569166 -0.443641 0.895380 False
19 Sdr.SL False False False -7.136652 11.573374 -5.887337 13.581937 -9.783634 8.022601 False
20 Vm.SL False False False -0.042627 0.146329 -0.107273 0.064534 -0.138253 0.046188 False
21 Vv.SL False False False -0.628569 1.120733 -1.148369 0.629365 -0.758307 1.009142 False
22 Vmp.SL False False False -0.038216 0.145730 -0.102542 0.080404 -0.139582 0.051603 False
23 Vmc.SL False False False -0.351024 0.445069 -0.408162 0.393795 -0.379502 0.420005 False
24 Vvc.SL False False False -0.775247 1.106130 -1.279438 0.585206 -0.680259 1.207173 False
25 Vvv.SL False False False -0.123061 0.280069 -0.062158 0.386216 -0.233205 0.162142 False
26 Maximum.depth.of.furrows.SL False False False -1569.961428 5403.294014 -573.948607 7285.722583 -2553.648069 3950.339347 False
27 Mean.depth.of.furrows.SL False False False -370.245452 1126.397567 -213.277317 1458.185855 -423.637264 1048.799168 False
28 Mean.density.of.furrows.SL False False False -397.044764 340.879772 -240.400385 484.502073 -319.730300 397.693938 False
29 Isotropy.SL False False False -11.268780 18.459808 -10.991930 19.454649 -15.905495 13.360756 False
... ... ... ... ... ... ... ... ... ... ... ...
33 Isotropy.SL.1 False False False -19.226140 13.460167 -31.122050 6.371314 -19.471795 13.449864 False
34 Sq.SF False False False -260.330445 433.858068 -453.958383 284.030311 -311.000432 376.471812 False
35 Ssk.SF False False False -0.120335 0.608019 -0.104615 0.623294 -0.222537 0.456017 False
36 Sku.SF False False False -0.214055 0.408618 -0.453918 0.163264 -0.671599 0.010666 False
37 Sp.SF False False False -642.719080 2030.353176 -1163.397713 1445.395521 -1093.622633 1497.141894 False
38 Sv.SF False False False -880.489236 642.551924 -1455.948427 182.969439 -1466.080686 187.363967 False
39 Sz.SF False False False -1423.577142 2320.099357 -2203.810211 1384.936330 -2187.617220 1413.558532 False
40 Sa.SF False False False -249.196597 332.644543 -390.388528 207.852587 -258.307579 305.356957 False
41 Smr.SF False False False -10.618282 22.364140 -15.976074 16.867425 -26.012278 8.980751 False
42 Smc.SF False False False -309.624758 846.064813 -561.863869 570.414458 -417.129487 723.713855 False
43 Sxp.SF False True False -1140.309510 59.367195 -1480.342773 -79.200178 -846.684902 332.446258 True
44 Sal.SF False False False -4.458439 1.931876 -4.151544 2.261065 -3.045501 2.896131 False
45 Str.SF False True False -0.324711 0.023357 0.236730 0.603433 -0.105132 0.248463 True
46 Std.SF False False False -41.062990 26.116249 -59.846692 14.748099 -58.610559 14.952719 False
47 Sdq.SF False False False -0.008667 0.017678 -0.015191 0.011856 -0.011148 0.014645 False
48 Sdr.SF False False False -0.082482 0.141657 -0.132129 0.082171 -0.111339 0.104010 False
49 Vm.SF False False False -0.013585 0.049005 -0.028445 0.027560 -0.030907 0.029140 False
50 Vv.SF False False False -0.340637 0.853134 -0.560543 0.566110 -0.557011 0.627423 False
51 Vmp.SF False False False -0.010978 0.051857 -0.030144 0.027672 -0.032202 0.028944 False
52 Vmc.SF False False False -0.273540 0.376074 -0.331493 0.305298 -0.262163 0.402933 False
53 Vvc.SF False False False -0.283649 0.914482 -0.515675 0.691432 -0.417705 0.775765 False
54 Vvv.SF True True False -0.099113 -0.019024 -0.121133 -0.035998 -0.070542 0.005308 True
55 Maximum.depth.of.furrows.SF False False False -163.496569 327.226810 -148.391381 314.489156 -115.260858 362.018409 False
56 Mean.depth.of.furrows.SF False False False -85.266028 198.400694 -91.383736 199.696311 -54.271561 268.312713 False
57 Mean.density.of.furrows.SF False False False -4.759447 17.118410 -8.516278 12.833441 -7.451044 13.490752 False
58 Isotropy.SF False True False -33.362462 2.723042 22.193711 58.361448 -11.786072 24.525002 True
59 First.Direction.SF False False False -43.939526 27.590497 -16.610605 57.262991 -25.454533 42.683465 False
60 Second.Direction.SF False False False -3.042626 107.092195 -68.126598 30.536122 -27.395579 72.869765 False
61 Third.Direction.SF False False False -4.098905 141.343732 -53.587714 66.320503 -17.192689 110.670642 False
62 Isotropy.SF.1 False True False -23.941132 13.655180 4.088759 46.506207 -1.825278 37.043055 True

63 rows × 11 columns

All rows with at least one significant result

In [12]:
dfSig[dfSig.anySig]
Out[12]:
measuredQuantity No_Brush_Is_Dirt_Significant Is_Brush_No_Dirt_Significant Is_Brush_Is_Dirt_Significant lowHPD1 highHPD1 lowHPD2 highHPD2 lowHPD3 highHPD3 anySig
0 epLsar False True True -0.000479 0.000016 -0.000677 -0.000107 -0.000690 -0.000130 True
3 HAsfc9 False True False -0.035967 0.465312 0.056452 0.665604 -0.021494 0.487958 True
7 Sku.SL False True False -0.956137 0.715376 0.293037 2.012304 -1.588012 0.049136 True
12 Smr.SL True False False 0.009736 0.331283 -0.159055 0.127101 -0.195928 0.090206 True
43 Sxp.SF False True False -1140.309510 59.367195 -1480.342773 -79.200178 -846.684902 332.446258 True
45 Str.SF False True False -0.324711 0.023357 0.236730 0.603433 -0.105132 0.248463 True
54 Vvv.SF True True False -0.099113 -0.019024 -0.121133 -0.035998 -0.070542 0.005308 True
58 Isotropy.SF False True False -33.362462 2.723042 22.193711 58.361448 -11.786072 24.525002 True
62 Isotropy.SF.1 False True False -23.941132 13.655180 4.088759 46.506207 -1.825278 37.043055 True
In [13]:
dfSig[dfSig.anySig].to_csv(outpath+'tableSignificants.csv')

Count the number of significant results

In [14]:
dfSig[dfSig.anySig].index.size
Out[14]:
9

Get fraction of significant results

In [15]:
1.0*dfSig[dfSig.anySig].index.size / len(quantityNames)
Out[15]:
0.14285714285714285

Get significance plots

In [16]:
SMALL_SIZE = 8
MEDIUM_SIZE = 10
BIGGER_SIZE = 12

plt.rc('font', size=SMALL_SIZE)          # controls default text sizes
plt.rc('axes', titlesize=SMALL_SIZE)     # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE)    # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE)    # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE)  # fontsize of the figure title
sns.set_style("ticks")
In [17]:
def plotEmpty(ax):
    ax.plot()
    ax.spines['right'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.set_yticks([])
    ax.set_xticks([])
In [18]:
for index, row in dfSig[dfSig.anySig].iterrows():
    idMeasure = row['measuredQuantity']
    
    # renaming 
    dictTreatment = {'No_No':'Control','Is_No':'Brush_NoDirt','No_Is':'RubDirt','Is_Is':'BrushDirt'}  
    
    # get sample data 
    raw = df[factorNames +  [idMeasure]].replace({'Yes': 'Is'})
    raw['Brush_Dirt'] = raw.Brush +"_" + raw.Dirt    
    raw.rename(columns={"Before.after": "Processing"},inplace=True)
    raw=raw.assign(Treatment=raw.Brush_Dirt)
    raw=raw.replace({"Treatment": dictTreatment})
    
    filtered = pd.get_dummies(df[factorNames +  [idMeasure]], columns=['Before.after'])
    filtered['Weighted_'+idMeasure] = filtered[idMeasure]*filtered['Before.after_After']-filtered[idMeasure]*filtered['Before.after_Before']
    filtered = filtered.groupby(['Material','Brush','Dirt','Area']).sum().reset_index()[['Material','Brush','Dirt','Area','Weighted_'+idMeasure]]
    filtered = filtered.replace({'Yes': 'Is'})
    filtered = filtered.rename(columns={'Weighted_'+idMeasure : 'Delta_' +idMeasure})    
    boxDF = filtered
    boxDF['Brush_Dirt'] = boxDF.Brush +"_" + boxDF.Dirt    
      
    boxDF=boxDF.assign(Treatment=boxDF.Brush_Dirt)
    boxDF=boxDF.replace({"Treatment": dictTreatment})
    
    melt = pd.DataFrame()
    melt["Factor1"] = filtered['Brush']+"_Brush_" + filtered['Dirt'] + "_Dirt"
    melt["Factor2"] = filtered["Material"]
    melt['Value'] = filtered['Delta_'+idMeasure]
    mu_Val = melt['Value'].mean()
    sig_Val = melt['Value'].std(ddof=1)
    
    with open(inpath + 'model_{}.pkl'.format(idMeasure), 'rb') as buff:
        data = pickle.load(buff) 
        
    basic_model, trace = data['model'], data['trace']
    b1_sample = sig_Val*trace['b1']+mu_Val
    
    # dict for column filtering 
    column_dict = {'No_Brush_Is_Dirt_vs_No_Brush_No_Dirt':'No_Is',
                  'Is_Brush_No_Dirt_vs_No_Brush_No_Dirt': 'Is_No',
                  'Is_Brush_Is_Dirt_vs_No_Brush_No_Dirt': 'Is_Is'
                  } 
           
    for indexContrast, (key, value) in enumerate(x1contrast_dict.items()):
            contrast = np.dot(b1_sample, value)

            hpd = arviz.hpd(contrast)                    
            significance = 0.0 <= hpd[0] or 0.0 >= hpd[-1]  
            
            if significance:
                # short version label
                colKey = column_dict[key] 
                
                # short version renamed
                colKeyRenamed = dictTreatment[colKey] 
                                
                # compute figsize
                widthMM = 190 
                widthInch = widthMM / 25.4
                ratio = 0.66
                heigthIch = ratio*widthInch
                
                # create figure 
                fig = plt.figure(figsize=(widthInch,heigthIch), dpi= 300, facecolor='w');
                axisWidths = np.array([10,1,10,7,10,2,12])
                numCols = sum(axisWidths)
                axisWidths,numCols,np.cumsum(axisWidths)

                ax =  [None] * len(axisWidths)
                for index,width in enumerate(axisWidths):

                    # get start point 
                    start = 0 if index == 0 else np.cumsum(axisWidths)[index-1]

                    ax[index] = plt.subplot2grid((1,numCols), (0, start), colspan=width)
                
                                
                # compute common y axis for point plots 
                ylims = (0.99*raw[idMeasure].min(),raw[idMeasure].max()*1.01)
                    
                dodgeVal = 0.6
                
                # ============ Point plot ===============
                    
                mat1 = "Flint"
                ax[0].set_ylim(ylims)
                g = sns.pointplot(data = raw[raw.Material==mat1], x="Processing", y=idMeasure,
                            hue="Treatment",
                            capsize=.2,  dodge=dodgeVal,
                            hue_order=['Control','Brush_NoDirt','RubDirt','BrushDirt'],
                                  order=['Before','After'],
                           ax=ax[0])
                ax[0].set_title('{}'.format(mat1))
                ax[0].spines['right'].set_visible(False)
                legend = g.legend_
                for text in legend.get_texts():
                    text.set_fontsize(6)
                #g.legend_.remove()
                
                # =========== Placeholder ==============
                plotEmpty(ax[1])

                
                # ============ Point plot ===============
                currentAxis = ax[2]
                mat2 = "Quartzite"
                currentAxis.set_ylim(ylims)
                g = sns.pointplot(data = raw[raw.Material==mat2], x="Processing", y=idMeasure,hue="Treatment",
                            capsize=.2,  dodge=dodgeVal,
                            hue_order=['Control','Brush_NoDirt','RubDirt','BrushDirt'],
                                  order=['Before','After'],
                           ax=currentAxis)
                currentAxis.set_title('{}'.format(mat2))
                currentAxis.set_yticks([])
                currentAxis.set_ylabel('')
                currentAxis.spines['left'].set_visible(False)                
                g.legend_.remove()
                
                # =========== Placeholder ==============
                plotEmpty(ax[3])

                # ============ Slope plot ===============
                currentAxis = ax[4]
                g = sns.swarmplot(x="Treatment", y="Delta_"+idMeasure,
                            #data = boxDF[(boxDF.Brush_Dirt == colKey) | (boxDF.Brush_Dirt == 'No_No')],
                            data = boxDF,
                            #order=['No_No','Is_No','No_Is','Is_Is'],
                            order=['Control','Brush_NoDirt','RubDirt','BrushDirt'],
                            #inner="sticks",                                  
                            ax=currentAxis)
                currentAxis.set_title('Slopes')
                currentAxis.set_xticks([])
                
                
                # =========== Placeholder ==============
                plotEmpty(ax[5])
                
                # ============ Posterior plot ===============  
                currentAxis = ax[6]
                g = pm.plot_posterior(contrast, ref_val=0.0, ax=currentAxis,bins=50,credible_interval=0.95)
                currentAxis.set_xlabel("Delta_{}".format(idMeasure))
                currentAxis.set_title('Contrast {}'.format(colKeyRenamed))
                currentAxis.spines['right'].set_visible(True)
                currentAxis.spines['left'].set_visible(True)
                currentAxis.spines['top'].set_visible(True)
                currentAxis.spines['bottom'].set_visible(True)
                
                # set color of frames            
                for ax in ax:
                    ax.tick_params(color='black', labelcolor='black')
                    for spine in ax.spines.values():
                        spine.set_edgecolor('gray')
                
                
                # finalize figure 
                fig.tight_layout()              
                plt.savefig(outpath+'/Showcase_{}_{}.pdf'.format(idMeasure,key), bbox_inches='tight')
                plt.show()
                
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
/home/bob/.local/lib/python3.7/site-packages/ipykernel_launcher.py:156: UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
In [ ]: