Esempio n. 1
0
def plot_table(ax, data, float_format='{:.2f}', columns=None, bbox=None, cell_font_size=6):
    if columns is not None:
        if type(columns) is dict:
            data = data[columns.keys()]
            
            data = data.rename(columns)
        
        else:
            data = data[columns]
    
    if bbox is None:
        bbox = [0, 0, 1, 1]
    
    ax.set_axis_off()
    
    table = Table(ax, bbox=bbox)

    num_rows, num_cols = data.shape
    
    width = 1.0 / num_cols
    
    #height = 1.0 / num_rows 
    height = table._approx_text_height()

    # Add cells
    for (i,j), val in np.ndenumerate(data):
        val = float_format.format(val)
        
        table.add_cell(i, j, width, height, text=val, loc='center')
    
    cells = table.get_celld()
    
    for index, val in np.ndenumerate(data):
        cells[index].set_fontsize(cell_font_size)

    # Row Labels...
    for i, label in enumerate(data.index):
        table.add_cell(i, -1, width, height, text=label, loc='right', edgecolor='none', facecolor='none')
    
    # Column Labels...
    for j, label in enumerate(data.columns):
        table.add_cell(-1, j, width, height/2, text=label, loc='center', edgecolor='none', facecolor='none')
    
    ax.add_table(table)
    
    table.set_fontsize(cell_font_size)
    
    return ax
Esempio n. 2
0
    def plot(self, experiment, **kwargs):
        """Plot a table"""
        
        if not experiment:
            raise util.CytoflowViewError("No experiment specified")
        
        if not self.channel or self.channel not in experiment.data:
            raise util.CytoflowViewError("Must set a channel")            
        
        if not (self.row_facet or self.column_facet):
            raise util.CytoflowViewError("Must set at least one of row_facet "
                                         "or column_facet")
            
        if self.subrow_facet and not self.row_facet:
            raise util.CytoflowViewError("Must set row_facet before using "
                                         "subrow_facet")
            
        if self.subcolumn_facet and not self.column_facet:
            raise util.CytoflowViewError("Must set column_facet before using "
                                         "subcolumn_facet")
            
        if self.row_facet and self.row_facet not in experiment.conditions:
            raise util.CytoflowViewError("Row facet {0} not in the experiment"
                                    .format(self.row_facet))        
            
        if self.subrow_facet and self.subrow_facet not in experiment.conditions:
            raise util.CytoflowViewError("Subrow facet {0} not in the experiment"
                                    .format(self.subrow_facet))  
            
        if self.column_facet and self.column_facet not in experiment.conditions:
            raise util.CytoflowViewError("Column facet {0} not in the experiment"
                                    .format(self.column_facet))  
            
        if self.subcolumn_facet and self.subcolumn_facet not in experiment.conditions:
            raise util.CytoflowViewError("Subcolumn facet {0} not in the experiment"
                                    .format(self.subcolumn_facet))  
            
        if not self.function:
            raise util.CytoflowViewError("Summary function isn't set")
            
        row_groups = np.sort(pd.unique(experiment[self.row_facet])) \
                     if self.row_facet else [None]
                     
        subrow_groups = np.sort(pd.unique(experiment[self.subrow_facet])) \
                        if self.subrow_facet else [None] 
        
        col_groups = np.sort(pd.unique(experiment[self.column_facet])) \
                     if self.column_facet else [None]
                     
        subcol_groups = np.sort(pd.unique(experiment[self.subcolumn_facet])) \
                        if self.subcolumn_facet else [None]


        if self.subset:
            try:
                data = experiment.query(self.subset).data.reset_index()
            except:
                raise util.CytoflowViewError("Subset string '{0}' isn't valid"
                                        .format(self.subset))
                
            if len(data.index) == 0:
                raise util.CytoflowViewError("Subset string '{0}' returned no events"
                                        .format(self.subset))
        else:
            data = experiment.data
            
        group_vars = []
        if self.row_facet: group_vars.append(self.row_facet) 
        if self.subrow_facet: group_vars.append(self.subrow_facet)
        if self.column_facet: group_vars.append(self.column_facet)
        if self.subcolumn_facet: group_vars.append(self.subcolumn_facet)
                
        agg = data.groupby(by = group_vars)[self.channel].aggregate(self.function)

        # agg is a multi-index series; we can get a particular value from it
        # with get(idx1, idx2...)

        row_offset = (self.column_facet != "") + (self.subcolumn_facet != "")        
        col_offset = (self.row_facet != "") + (self.subrow_facet != "")
        
        num_cols = len(col_groups) * len(subcol_groups) + col_offset
        
        fig = plt.figure()
        ax = fig.add_subplot(111)
        
        # hide the plot axes that matplotlib tries to make
        ax.xaxis.set_visible(False)
        ax.yaxis.set_visible(False)
        for sp in ax.spines.itervalues():
            sp.set_color('w')
            sp.set_zorder(0)
        
        loc = 'best'
        bbox = None
        
        t = Table(ax, loc, bbox, **kwargs)
        width = [1.0 / num_cols] * num_cols
        height = t._approx_text_height() * 1.8
        
         
        # make the main table       
        for (ri, r) in enumerate(row_groups):
            for (rri, rr) in enumerate(subrow_groups):
                for (ci, c) in enumerate(col_groups):
                    for (cci, cc) in enumerate(subcol_groups):
                        row_idx = ri * len(subrow_groups) + rri + row_offset
                        col_idx = ci * len(subcol_groups) + cci + col_offset
                        agg_idx = [x for x in (r, rr, c, cc) if x is not None]
                        agg_idx = tuple(agg_idx)
                        if len(agg_idx) == 1:
                            agg_idx = agg_idx[0]
                        t.add_cell(row_idx, 
                                   col_idx,
                                   width = width[col_idx],
                                   height = height,
                                   text = agg.get(agg_idx))
                        
        # row headers
        if self.row_facet:
            for (ri, r) in enumerate(row_groups):
                row_idx = ri * len(subrow_groups) + row_offset
                text = "{0} = {1}".format(self.row_facet, r)
                t.add_cell(row_idx,
                           0,
                           width = width[0],
                           height = height,
                           text = text)
                
        # subrow headers
        if self.subrow_facet:
            for (ri, r) in enumerate(row_groups):
                for (rri, rr) in enumerate(subrow_groups):
                    row_idx = ri * len(subrow_groups) + rri + row_offset
                    text = "{0} = {1}".format(self.subrow_facet, rr)
                    t.add_cell(row_idx,
                               1,
                               width = width[1],
                               height = height,
                               text = text)
                    
        # column headers
        if self.column_facet:
            for (ci, c) in enumerate(col_groups):
                col_idx = ci * len(subcol_groups) + col_offset
                text = "{0} = {1}".format(self.column_facet, c)
                t.add_cell(0,
                           col_idx,
                           width = width[col_idx],
                           height = height,
                           text = text)

        # column headers
        if self.subcolumn_facet:
            for (ci, c) in enumerate(col_groups):
                for (cci, cc) in enumerate(subcol_groups):
                    col_idx = ci * len(subcol_groups) + cci + col_offset
                    text = "{0} = {1}".format(self.subcolumn_facet, c)
                    t.add_cell(1,
                               col_idx,
                               width = width[col_idx],
                               height = height,
                               text = text)                
                        
        ax.add_table(t)
    def label_manual(self,
                     display_elems,
                     figsize=(15, 7.5),
                     title="Sleep Stages"):
        """
        Displays dialog for manual stage labeling

        args:
            displa_elems: list of lists of parameters used for display in format 
                          ((data_struct,{'parname1':parval1,'parname2':parval2,...}),...)
                          where data_struct is of class containing plot method (such 
                          as EEGSpectralData) and 'parname1':parval1,... are
                          name-value pairs supplied to the function.
            figsize: Size of the figure
            block: Blocks code execution until label dialog is closed
        """
        self.saving = False
        sleep_stage_labels = [
            'NREM3', 'NREM2', 'REM', 'NREM1', 'WAKE', 'MASK OFF', '???'
        ]

        height_ratios = np.ones(len(display_elems)) * 3
        height_ratios = np.append(height_ratios, 1)
        fig = plt.figure(figsize=figsize)
        gs = gridspec.GridSpec(len(display_elems) + 1,
                               1,
                               height_ratios=height_ratios)
        ax_transforms = {}

        def format_time_period(val):
            m, s = divmod(int(round(val)), 60)
            h, m = divmod(m, 60)
            return "%d:%02d:%02d" % (h, m, s)

        def reset_labels(event=None):
            self.stage_times = [0]
            self.stage_labels = [5]
            if event is not None:
                redraw_labels(event)

        def reload_labels(event=None):
            self.stage_times = np.append([], self.loaded_stage_times)
            self.stage_labels = np.append([], self.loaded_stage_labels)
            if self.stage_times[0] is None or self.stage_labels[
                    0] is None or self.stage_times.size != self.stage_labels.size:
                print("data is bad, resetting labels")
                reset_labels(event)
            else:
                print("stage_times size is " + str(self.stage_times.size))
                print("stage_labels size is " + str(self.stage_labels.size))
            if event is not None:
                redraw_labels(event)

        def redraw_labels(event=None):
            line1.set_xdata(
                np.concatenate((self.stage_times, [self.sleep_length])))
            line1.set_ydata(
                np.concatenate((self.stage_labels, [self.stage_labels[-1]])))
            data = [0, 0, 0, 0, 0, 0, 0, 0]
            for x in range(0, len(self.stage_times)):
                data_offset = int(
                    len(sleep_stage_labels) - (self.stage_labels[x] + 1))
                thisval = self.stage_times[x]
                nextval = self.sleep_length if x is len(
                    self.stage_times) - 1 else self.stage_times[x + 1]
                diffval = nextval - thisval
                data[data_offset] = data[data_offset] + diffval
            data[len(data) - 1] = self.sleep_length
            for x in range(0, len(data)):
                table.get_celld()[x, 1].get_text().set_text(
                    format_time_period(data[x]))
            fig.canvas.draw()

        def on_pick(figure, mouseevent):
            #print(ax_transforms)
            #print(mouseevent)
            #print(figure)
            #print(figure.get_axes())
            if not mouseevent.inaxes in ax_transforms:
                return False, {}
            eegdata = ax_transforms[mouseevent.inaxes]
            xmouse, ymouse = mouseevent.xdata, mouseevent.ydata
            xmouse = eegdata.index_to_time(xmouse)
            #print('x, y of mouse: {:.2f},{:.2f}'.format(xmouse, ymouse))
            larger = [
                x[0] for x in enumerate(self.stage_times) if x[1] > xmouse
            ]
            if len(larger) > 0:
                idx = larger[0]
                if self.stage_labels[idx - 1] != self.stage_label:
                    self.stage_times[idx] = xmouse
                    self.stage_labels[idx] = self.stage_label
                else:
                    self.stage_times = np.delete(self.stage_times, idx)
                    self.stage_labels = np.delete(self.stage_labels, idx)
            else:
                if self.stage_labels[-1] != self.stage_label:
                    self.stage_times = np.append(self.stage_times, xmouse)
                    self.stage_labels = np.append(self.stage_labels,
                                                  self.stage_label)
            #print(stage_times)
            #print(stage_labels)
            for i in range(1, len(self.stage_labels) - 1):
                if self.stage_labels[i] == self.stage_labels[i - 1]:
                    self.stage_labels = np.delete(self.stage_labels, i)
                    self.stage_times = np.delete(self.stage_times, i)
            redraw_labels()
            return True, {}

        for did in range(len(display_elems)):
            delem = display_elems[did]
            ax = plt.subplot(gs[did])
            ax_transforms[ax] = delem[0]
            params = delem[1]
            params['axes'] = ax
            delem[0].plot(**params)
            if did == 0:
                plt.title(title)
            #TODO
            ax.set_picker(True)

            fig.canvas.mpl_connect('pick_event', on_pick)
            fig.set_picker(on_pick)

        xtickspacing = 300
        if len(np.arange(0, self.sleep_length, 300)) > 20:
            xtickspacing = 600
        if len(np.arange(0, self.sleep_length, 600)) > 20:
            xtickspacing = 1200
        if len(np.arange(0, self.sleep_length, 1200)) > 20:
            xtickspacing = 1800
        if len(np.arange(0, self.sleep_length, 1800)) > 20:
            xtickspacing = 3600
        xticks = np.arange(0, self.sleep_length, xtickspacing)
        xticklabels = [str(int(i / 60)) for i in xticks]

        reload_labels()
        ax1 = plt.subplot(gs[-1])
        line1, = ax1.plot(np.concatenate(
            (self.stage_times, [self.sleep_length])),
                          np.concatenate(
                              (self.stage_labels, [self.stage_labels[-1]])),
                          drawstyle="steps-post")
        ax1.set_xlabel("Time (min)")
        ax1.set_ylabel("Sleep Stage")
        ax1.set_xlim(0, self.sleep_length)
        ax1.set_yticks(np.arange(7))
        ax1.set_yticklabels(sleep_stage_labels)
        ax1.set_xticks(xticks)
        ax1.set_xticklabels(xticklabels)

        self.stage_label = 6
        rax = plt.axes([0.0, 0.0, 0.2, 0.16], facecolor='lightgoldenrodyellow')
        radio = RadioButtons(rax, sleep_stage_labels[::-1], active=0)

        def stagepicker(label):
            self.stage_label = sleep_stage_labels.index(label)
            #print(plot_eeg_log_hist.stage_label)
            #fig.canvas.draw_idle()
        def done(event):
            self.saving = True
            plt.close()

        if self.loaded_stage_labels is not None:
            axreload = plt.axes([0.7, 0.0, 0.1, 0.075])
            breload = Button(axreload, 'Reload')
            breload.on_clicked(reload_labels)
        axreset = plt.axes([0.8, 0.0, 0.1, 0.075])
        breset = Button(axreset, 'Reset')
        breset.on_clicked(reset_labels)
        axdone = plt.axes([0.9, 0.0, 0.1, 0.075])
        bdone = Button(axdone, 'Save &\n Quit')
        bdone.on_clicked(done)
        radio.on_clicked(stagepicker)
        tableax = plt.axes([0.2, 0.0, 0.25, 0.16], facecolor='lightblue')
        tableax.get_yaxis().set_visible(False)
        table = Table(tableax, bbox=[0, 0, 1, 1])
        height = table._approx_text_height()
        lidx = 0
        for label in sleep_stage_labels[::-1]:
            table.add_cell(lidx, 0, width=0.6, height=height, text=label)
            table.add_cell(lidx, 1, width=0.4, height=height, text='')
            lidx = lidx + 1
        table.add_cell(lidx,
                       0,
                       width=0.6,
                       height=height,
                       text='Total Sleep Time')
        table.add_cell(lidx, 1, width=0.4, height=height, text='')
        tableax.add_table(table)

        fig.canvas.callbacks.connect('pick_event', on_pick)
        fig.canvas.set_window_title('EEG Spectrogram Analysis')

        plt.subplots_adjust(left=0.15 if figsize[0] < 10 else 0.075,
                            bottom=0.2,
                            right=0.99,
                            top=0.97)
        redraw_labels()
        plt.show()
        self.stage_times = np.array(self.stage_times)
        self.stage_times = np.concatenate(
            (self.stage_times, [self.sleep_length]))
        self.stage_labels = np.concatenate((self.stage_labels, [6]))
Esempio n. 4
0
    def plot(self, experiment, plot_name = None, **kwargs):
        """Plot a table"""
        
        if experiment is None:
            raise util.CytoflowViewError('experiment', "No experiment specified")   
        
        if self.statistic not in experiment.statistics:
            raise util.CytoflowViewError('statistic', 
                                         "Can't find the statistic {} in the experiment"
                                         .format(self.statistic))
        else:
            stat = experiment.statistics[self.statistic]    
            
        data = pd.DataFrame(index = stat.index)
        data[stat.name] = stat   
        
        if self.subset:
            try:
                data = data.query(self.subset)
            except Exception as e:
                raise util.CytoflowViewError('subset',
                                             "Subset string '{0}' isn't valid"
                                             .format(self.subset)) from e
                
            if len(data) == 0:
                raise util.CytoflowViewError('subset',
                                             "Subset string '{0}' returned no values"
                                             .format(self.subset))
            
        names = list(data.index.names)
        for name in names:
            unique_values = data.index.get_level_values(name).unique()
            if len(unique_values) == 1:
                warn("Only one value for level {}; dropping it.".format(name),
                     util.CytoflowViewWarning)
                try:
                    data.index = data.index.droplevel(name)
                except AttributeError as e:
                    raise util.CytoflowViewError(None,
                                                 "Must have more than one "
                                                 "value to plot.") from e
        
        if not (self.row_facet or self.column_facet):
            raise util.CytoflowViewError('row_facet',
                                         "Must set at least one of row_facet "
                                         "or column_facet")
            
        if self.subrow_facet and not self.row_facet:
            raise util.CytoflowViewError('subrow_facet',
                                         "Must set row_facet before using "
                                         "subrow_facet")
            
        if self.subcolumn_facet and not self.column_facet:
            raise util.CytoflowViewError('subcolumn_facet',
                                         "Must set column_facet before using "
                                         "subcolumn_facet")
            
        if self.row_facet and self.row_facet not in experiment.conditions:
            raise util.CytoflowViewError('row_facet',
                                         "Row facet {} not in the experiment, "
                                         "must be one of {}"
                                         .format(self.row_facet, experiment.conditions))        

        if self.row_facet and self.row_facet not in data.index.names:
            raise util.CytoflowViewError('row_facet',
                                         "Row facet {} not a statistic index; "
                                         "must be one of {}"
                                         .format(self.row_facet, data.index.names))  
            
        if self.subrow_facet and self.subrow_facet not in experiment.conditions:
            raise util.CytoflowViewError('subrow_facet',
                                         "Subrow facet {} not in the experiment, "
                                         "must be one of {}"
                                         .format(self.subrow_facet, experiment.conditions))  
            
        if self.subrow_facet and self.subrow_facet not in data.index.names:
            raise util.CytoflowViewError('subrow_facet',
                                         "Subrow facet {} not a statistic index; "
                                         "must be one of {}"
                                         .format(self.subrow_facet, data.index.names))  
            
        if self.column_facet and self.column_facet not in experiment.conditions:
            raise util.CytoflowViewError('column_facet',
                                         "Column facet {} not in the experiment, "
                                         "must be one of {}"
                                         .format(self.column_facet, experiment.conditions))  
            
        if self.column_facet and self.column_facet not in data.index.names:
            raise util.CytoflowViewError('column_facet',
                                         "Column facet {} not a statistic index; "
                                         "must be one of {}"
                                         .format(self.column_facet, data.index.names)) 
            
        if self.subcolumn_facet and self.subcolumn_facet not in experiment.conditions:
            raise util.CytoflowViewError('subcolumn_facet',
                                         "Subcolumn facet {} not in the experiment, "
                                         "must be one of {}"
                                         .format(self.subcolumn_facet, experiment.conditions))  
            
        if self.subcolumn_facet and self.subcolumn_facet not in data.index.names:
            raise util.CytoflowViewError('subcolumn_facet',
                                         "Subcolumn facet {} not a statistic index; "
                                         "must be one of {}"
                                         .format(self.subcolumn_facet, data.index.names))  

        facets = [x for x in [self.row_facet, self.subrow_facet, 
                                      self.column_facet, self.subcolumn_facet] if x]
        if len(facets) != len(set(facets)):
            raise util.CytoflowViewError(None, 
                                         "Can't reuse facets")
        
        if set(facets) != set(data.index.names):
            raise util.CytoflowViewError(None,
                                         "Must use all the statistic indices as variables or facets: {}"
                                         .format(data.index.names))
            
        row_groups = data.index.get_level_values(self.row_facet).unique() \
                     if self.row_facet else [None]
                     
        subrow_groups = data.index.get_level_values(self.subrow_facet).unique() \
                        if self.subrow_facet else [None] 
        
        col_groups = data.index.get_level_values(self.column_facet).unique() \
                     if self.column_facet else [None]
                     
        subcol_groups = data.index.get_level_values(self.subcolumn_facet).unique() \
                        if self.subcolumn_facet else [None]

        row_offset = (self.column_facet != "") + (self.subcolumn_facet != "")        
        col_offset = (self.row_facet != "") + (self.subrow_facet != "")
        
        num_cols = len(col_groups) * len(subcol_groups) + col_offset
        
        fig = plt.figure()
        ax = fig.add_subplot(111)
        
        # hide the plot axes that matplotlib tries to make
        ax.xaxis.set_visible(False)
        ax.yaxis.set_visible(False)
        for sp in ax.spines.values():
            sp.set_color('w')
            sp.set_zorder(0)
        
        loc = 'upper left'
        bbox = None
        
        t = Table(ax, loc, bbox, **kwargs)
        t.auto_set_font_size(False)
        for c in range(num_cols):
            t.auto_set_column_width(c)

        width = [0.2] * num_cols

        height = t._approx_text_height() * 1.8
         
        # make the main table       
        for (ri, r) in enumerate(row_groups):
            for (rri, rr) in enumerate(subrow_groups):
                for (ci, c) in enumerate(col_groups):
                    for (cci, cc) in enumerate(subcol_groups):
                        row_idx = ri * len(subrow_groups) + rri + row_offset
                        col_idx = ci * len(subcol_groups) + cci + col_offset
                        
                        # this is not pythonic, but i'm tired
                        agg_idx = []
                        for data_idx in data.index.names:
                            if data_idx == self.row_facet:
                                agg_idx.append(r)
                            elif data_idx == self.subrow_facet:
                                agg_idx.append(rr)
                            elif data_idx == self.column_facet:
                                agg_idx.append(c)
                            elif data_idx == self.subcolumn_facet:
                                agg_idx.append(cc)
                        
                        agg_idx = tuple(agg_idx)
                        if len(agg_idx) == 1:
                            agg_idx = agg_idx[0]
                            
                        try:
                            text = "{:g}".format(data.loc[agg_idx][stat.name])
                        except ValueError:
                            text = data.loc[agg_idx][stat.name]
                        t.add_cell(row_idx, 
                                   col_idx,
                                   width = width[col_idx],
                                   height = height,
                                   text = text)
                        
        # row headers
        if self.row_facet:
            for (ri, r) in enumerate(row_groups):
                row_idx = ri * len(subrow_groups) + row_offset
                try:
                    text = "{0} = {1:g}".format(self.row_facet, r)
                except ValueError:
                    text = "{0} = {1}".format(self.row_facet, r)
                t.add_cell(row_idx,
                           0,
                           width = width[0],
                           height = height,
                           text = text)
                
        # subrow headers
        if self.subrow_facet:
            for (ri, r) in enumerate(row_groups):
                for (rri, rr) in enumerate(subrow_groups):
                    row_idx = ri * len(subrow_groups) + rri + row_offset
                    try:
                        text = "{0} = {1:g}".format(self.subrow_facet, rr)
                    except ValueError:
                        text = "{0} = {1}".format(self.subrow_facet, rr)
                        
                    t.add_cell(row_idx,
                               1,
                               width = width[1],
                               height = height,
                               text = text)
                    
        # column headers
        if self.column_facet:
            for (ci, c) in enumerate(col_groups):
                col_idx = ci * len(subcol_groups) + col_offset
                try:
                    text = "{0} = {1:g}".format(self.column_facet, c)
                except ValueError:
                    text = "{0} = {1}".format(self.column_facet, c)
                t.add_cell(0,
                           col_idx,
                           width = width[col_idx],
                           height = height,
                           text = text)

        # subcolumn headers
        if self.subcolumn_facet:
            for (ci, c) in enumerate(col_groups):
                for (cci, cc) in enumerate(subcol_groups):
                    col_idx = ci * len(subcol_groups) + cci + col_offset
                    try:
                        text = "{0} = {1:g}".format(self.subcolumn_facet, cc)
                    except ValueError:
                        text = "{0} = {1}".format(self.subcolumn_facet, cc)
                    t.add_cell(1,
                               col_idx,
                               width = width[col_idx],
                               height = height,
                               text = text)                
                        
        ax.add_table(t)
Esempio n. 5
0
    def plot(self, experiment, plot_name=None, **kwargs):
        """Plot a table of the conditions, filenames, and number of events"""

        if experiment is None:
            raise util.CytoflowViewError('experiment',
                                         "No experiment specified")

        if self.subset:
            try:
                experiment = experiment.query(self.subset)
            except Exception as e:
                raise util.CytoflowViewError(
                    'subset', "Subset string '{0}' isn't valid".format(
                        self.subset)) from e

            if len(experiment) == 0:
                raise util.CytoflowViewError(
                    'subset', "Subset string '{0}' returned no values".format(
                        self.subset))

        # if path is set, actually do an export. this isn't terribly elegant,
        # but this is the only place we have an experiment! ExportFCS.export()
        # raises CytoflowViewErrors, so it should be compatible with the
        # existing error reporting stuff.
        if self.path:
            self.export(experiment)
            return

        # otherwise, make a table showing what will be exported
        num_cols = len(self.by) + 2

        fig = plt.figure()
        ax = fig.add_subplot(111)

        # hide the plot axes that matplotlib tries to make
        ax.xaxis.set_visible(False)
        ax.yaxis.set_visible(False)
        for sp in ax.spines.values():
            sp.set_color('w')
            sp.set_zorder(0)

        loc = 'upper left'
        bbox = None

        t = Table(ax, loc, bbox)
        t.auto_set_font_size(False)
        for c in range(num_cols):
            t.auto_set_column_width(c)

        width = [0.2] * num_cols

        height = t._approx_text_height() * 1.8

        t.add_cell(0, 0, width=width[0], height=height, text="#")
        for ci, c in enumerate(self.by):
            ci = ci + 1
            t.add_cell(0, ci, width=width[ci], height=height, text=c)

        ci = len(self.by) + 1
        t.add_cell(0, ci, width=width[ci], height=height, text="Filename")

        #         ci = len(self.by) + 2
        #         t.add_cell(0, ci, width = width[ci], height = height, text = "Events")

        for ri, row in enumerate(self.enum_conditions_and_files(experiment)):
            t.add_cell(ri + 1,
                       0,
                       width=width[0],
                       height=height,
                       text="{:g}".format(ri + 1))
            for ci, col in enumerate(row):
                t.add_cell(ri + 1,
                           ci + 1,
                           width=width[ci + 1],
                           height=height,
                           text=col)

        ax.add_table(t)