Generate a plotly figure of the heatmap.
        
        Parameters
        ----------
        emin : int, float, or None, optional
            The expression value corresponding to the lower end of the
            colorscale. If None, determine, automatically. [None]
        emax : int, float, or None, optional
            The expression value corresponding to the upper end of the
            colorscale. If None, determine automatically. [None]
        margin_left : int, optional
            The size of the left margin (in px). [100]
        margin_right : int, optional
            The size of the right margin (in px). [0]
        margin_top : int, optional
            The size of the top margin (in px). [30]
        margin_bottom : int, optional
            The size of the bottom margin (in px). [60]
        colorbar_size : int or float, optional
            The sze of the colorbar, relative to the figure size. [0.4]
        xaxis_label : str or None, optional
            X-axis label. If None, use `ExpMatrix` default. [None]
        yaxis_label : str or None, optional
            y-axis label. If None, use `ExpMatrix` default. [None]
        xtick_angle : int or float, optional
            X-axis tick angle (in degrees). [30]
        font : str, optional
            Name of font to use. Can be multiple, separated by comma, to
            specify a prioritized list.
            [' "Droid Serif", "Open Serif", "serif"']
        font_size : int or float, optional
            Font size to use throughout the figure, in points. [12]
        title_font_size : int or float or None, optional
            Font size to use for labels on axes and the colorbar. If None,
            use `font_size` value. [None]
        show_sample_labels : bool, optional
            Whether to show the sample labels. [True]

        Returns
        -------
        `plotly.graph_objs.Figure`
            The plotly figure.

def get_figure(
            self, emin=None, emax=None,
            width=800, height=400,
            margin_left=100, margin_bottom=60, margin_top=30, margin_right=0,
            colorbar_size=0.4,
            xaxis_label=None, yaxis_label=None,
            xaxis_nticks=None, yaxis_nticks=None,
            xtick_angle=30,
            font='"Droid Serif", "Open Serif", serif',
            font_size=12, title_font_size=None,
            show_sample_labels=True, **kwargs):
        """Generate a plotly figure of the heatmap.
        
        Parameters
        ----------
        emin : int, float, or None, optional
            The expression value corresponding to the lower end of the
            colorscale. If None, determine, automatically. [None]
        emax : int, float, or None, optional
            The expression value corresponding to the upper end of the
            colorscale. If None, determine automatically. [None]
        margin_left : int, optional
            The size of the left margin (in px). [100]
        margin_right : int, optional
            The size of the right margin (in px). [0]
        margin_top : int, optional
            The size of the top margin (in px). [30]
        margin_bottom : int, optional
            The size of the bottom margin (in px). [60]
        colorbar_size : int or float, optional
            The sze of the colorbar, relative to the figure size. [0.4]
        xaxis_label : str or None, optional
            X-axis label. If None, use `ExpMatrix` default. [None]
        yaxis_label : str or None, optional
            y-axis label. If None, use `ExpMatrix` default. [None]
        xtick_angle : int or float, optional
            X-axis tick angle (in degrees). [30]
        font : str, optional
            Name of font to use. Can be multiple, separated by comma, to
            specify a prioritized list.
            [' "Droid Serif", "Open Serif", "serif"']
        font_size : int or float, optional
            Font size to use throughout the figure, in points. [12]
        title_font_size : int or float or None, optional
            Font size to use for labels on axes and the colorbar. If None,
            use `font_size` value. [None]
        show_sample_labels : bool, optional
            Whether to show the sample labels. [True]

        Returns
        -------
        `plotly.graph_objs.Figure`
            The plotly figure.
        """

        # emin and/or emax are unspecified, set to data min/max values
        if emax is None:
            emax = self.matrix.X.max()
        if emin is None:
            emin = self.matrix.X.min()

        title = self.title

        if title_font_size is None:
            title_font_size = font_size

        colorbar_label = self.colorbar_label or 'Expression'

        colorbar = go.ColorBar(
            lenmode='fraction',
            len=colorbar_size,
            title=colorbar_label,
            titlefont=dict(
                size=title_font_size,
            ),
            titleside='right',
            xpad=0,
            ypad=0,
            outlinewidth=0,  # no border
            thickness=20,  # in pixels
            # outlinecolor = '#000000',
        )

        def fix_plotly_label_bug(labels):
            """
            This fixes a bug whereby plotly treats labels that look
            like numbers (integers or floats) as numeric instead of
            categorical, even when they are passed as strings. The fix consists
            of appending an underscore to any label that looks like a number.
            """
            assert isinstance(labels, Iterable)
            fixed_labels = []
            for l in labels:
                try:
                    float(l)
                except (ValueError, TypeError):
                    fixed_labels.append(str(l))
                else:
                    fixed_labels.append(str(l) + '_')
            return fixed_labels

        x = fix_plotly_label_bug(self.matrix.samples)

        gene_labels = self.matrix.genes.tolist()

        if self.gene_aliases:
            for i, gene in enumerate(gene_labels):
                try:
                    alias = self.gene_aliases[gene]
                except KeyError:
                    pass
                else:
                    gene_labels[i] = '%s/%s' % (gene, alias)
            
        gene_labels = fix_plotly_label_bug(gene_labels)


        data = [
            go.Heatmap(
                z=self.matrix.X,
                x=x,
                y=gene_labels,
                zmin=emin,
                zmax=emax,
                colorscale=self.colorscale,
                colorbar=colorbar,
                hoverinfo='x+y+z',
                **kwargs
            ),
        ]

        xticks = 'outside'
        if not show_sample_labels:
            xticks = ''

        if xaxis_label is None:
            if self.matrix.samples.name is not None:
                xaxis_label = self.matrix.samples.name
            else:
                xaxis_label = 'Samples'
            xaxis_label = xaxis_label + ' (n = %d)' % self.matrix.n

        if yaxis_label is None:
            if self.matrix.genes.name is not None:
                yaxis_label = self.matrix.genes.name
            else:
                yaxis_label = 'Genes'
            yaxis_label = yaxis_label + ' (p = %d)' % self.matrix.p

        layout = go.Layout(
            width=width,
            height=height,
            title=title,
            titlefont=go.Font(
                size=title_font_size
            ),
            font=go.Font(
                size=font_size,
                family=font
            ),
            xaxis=go.XAxis(
                title=xaxis_label,
                titlefont=dict(size=title_font_size),
                showticklabels=show_sample_labels,
                ticks=xticks,
                nticks=xaxis_nticks,
                tickangle=xtick_angle,
                showline=True
            ),
            yaxis=go.YAxis(
                title=yaxis_label,
                titlefont=dict(size=title_font_size),
                nticks=yaxis_nticks,
                autorange='reversed',
                showline=True
            ),

            margin=go.Margin(
                l=margin_left,
                t=margin_top,
                b=margin_bottom,
                r=margin_right,
                pad=0
            ),
        )

        # add annotations

        # we need separate, but overlaying, axes to place the annotations
        layout['xaxis2'] = go.XAxis(
            overlaying = 'x',
            showline = False,
            tickfont = dict(size=0),
            autorange=False,
            range=[-0.5, self.matrix.n-0.5],
            ticks='',
            showticklabels=False
        )

        layout['yaxis2'] = go.YAxis(
            overlaying='y',
            showline=False,
            tickfont=dict(size=0),
            autorange=False,
            range=[self.matrix.p-0.5, -0.5],
            ticks='',
            showticklabels=False
        )

        # gene (row) annotations
        for ann in self.gene_annotations:
            i = self.matrix.genes.get_loc(ann.gene)
            xmn = -0.5
            xmx = self.matrix.n-0.5
            ymn = i-0.5
            ymx = i+0.5
            #logger.debug('Transparency is %.1f', ann.transparency)
            data.append(
                go.Scatter(
                    x=[xmn, xmx, xmx, xmn, xmn],
                    y=[ymn, ymn, ymx, ymx, ymn],
                    mode='lines',
                    hoverinfo='none',
                    showlegend=False,
                    line=dict(color=ann.color),
                    xaxis='x2',
                    yaxis='y2',
                    #opacity=0.5,
                    opacity=1-ann.transparency,
                )
            )
            if ann.label is not None:
                layout.annotations.append(
                    go.Annotation(
                        text=ann.label,
                        x=0.01,
                        y=i-0.5,
                        #y=i+0.5,
                        xref='paper',
                        yref='y2',
                        xanchor='left',
                        yanchor='bottom',
                        showarrow=False,
                        bgcolor='white',
                        #opacity=1-ann.transparency,
                        opacity=0.8,
                        borderpad=0,
                        #textangle=30,
                        font=dict(color=ann.color)
                    )
                )

        # sample (column) annotations
        for ann in self.sample_annotations:
            j = self.matrix.samples.get_loc(ann.sample)
            xmn = j-0.5
            xmx = j+0.5
            ymn = -0.5
            ymx = self.matrix.p-0.5
            data.append(
                go.Scatter(
                    x=[xmn, xmx, xmx, xmn, xmn],
                    y=[ymn, ymn, ymx, ymx, ymn],
                    mode='lines',
                    hoverinfo='none',
                    showlegend=False,
                    line=dict(color=ann.color),
                    xaxis='x2',
                    yaxis='y2',
                    opacity=1.0)
            )
            if ann.label is not None:
                layout.annotations.append(
                    go.Annotation(
                        text=ann.label,
                        y=0.99,
                        x=j+0.5,
                        #y=i+0.5,
                        xref='x2',
                        yref='paper',
                        xanchor='left',
                        yanchor='top',
                        showarrow=False,
                        bgcolor='white',
                        opacity=1-ann.transparency,
                        borderpad=0,
                        textangle=90,
                        font=dict(color=ann.color)
                    )
                )

        fig = go.Figure(
            data=data,
            layout=layout
        )

        return fig