Construct a figure from a 2D grid of sub-figures

    Parameters
    ----------
    figures_grid: list of list of (dict or None)
        2D list of plotly figure dicts that will be combined in a grid to
        produce the resulting figure.  None values maybe used to leave empty
        grid cells
    row_heights: list of float (default None)
        List of the relative heights of each row in the grid (these values
        will be normalized by the function)
    column_widths: list of float (default None)
        List of the relative widths of each column in the grid (these values
        will be normalized by the function)
    row_spacing: float (default 0.15)
        Vertical spacing between rows in the gird in normalized coordinates
    column_spacing: float (default 0.15)
        Horizontal spacing between columns in the grid in normalized
        coordinates
    share_xaxis: bool (default False)
        Share x-axis between sub-figures in the same column. This will only
        work if each sub-figure has a single x-axis
    share_yaxis: bool (default False)
        Share y-axis between sub-figures in the same row. This will only work
        if each subfigure has a single y-axis

    Returns
    -------
    dict
        A plotly figure dict

def figure_grid(figures_grid,
                row_heights=None,
                column_widths=None,
                row_spacing=0.15,
                column_spacing=0.15,
                share_xaxis=False,
                share_yaxis=False):
    """
    Construct a figure from a 2D grid of sub-figures

    Parameters
    ----------
    figures_grid: list of list of (dict or None)
        2D list of plotly figure dicts that will be combined in a grid to
        produce the resulting figure.  None values maybe used to leave empty
        grid cells
    row_heights: list of float (default None)
        List of the relative heights of each row in the grid (these values
        will be normalized by the function)
    column_widths: list of float (default None)
        List of the relative widths of each column in the grid (these values
        will be normalized by the function)
    row_spacing: float (default 0.15)
        Vertical spacing between rows in the gird in normalized coordinates
    column_spacing: float (default 0.15)
        Horizontal spacing between columns in the grid in normalized
        coordinates
    share_xaxis: bool (default False)
        Share x-axis between sub-figures in the same column. This will only
        work if each sub-figure has a single x-axis
    share_yaxis: bool (default False)
        Share y-axis between sub-figures in the same row. This will only work
        if each subfigure has a single y-axis

    Returns
    -------
    dict
        A plotly figure dict
    """

    # compute number of rows/cols
    rows = len(figures_grid)
    columns = len(figures_grid[0])

    # Initialize row heights / column widths
    if not row_heights:
        row_heights = [1 for _ in range(rows)]

    if not column_widths:
        column_widths = [1 for _ in range(columns)]

    # Compute domain widths/heights for subplots
    column_domains = _compute_subplot_domains(column_widths, column_spacing)
    row_domains = _compute_subplot_domains(row_heights, row_spacing)

    output_figure = {'data': [], 'layout': {}}

    for r, (fig_row, row_domain) in enumerate(zip(figures_grid, row_domains)):
        for c, (fig, column_domain) in enumerate(zip(fig_row, column_domains)):
            if fig:
                fig = copy.deepcopy(fig)

                _normalize_subplot_ids(fig)

                subplot_offsets = _get_max_subplot_ids(output_figure)

                if share_xaxis:
                    subplot_offsets['xaxis'] = c
                    if r != 0:
                        # Only use xaxes from bottom row
                        fig.get('layout', {}).pop('xaxis', None)

                if share_yaxis:
                    subplot_offsets['yaxis'] = r
                    if c != 0:
                        # Only use yaxes from first column
                        fig.get('layout', {}).pop('yaxis', None)

                _offset_subplot_ids(fig, subplot_offsets)

                scale_x = column_domain[1] - column_domain[0]
                scale_y = row_domain[1] - row_domain[0]
                _scale_translate(fig,
                                 scale_x, scale_y,
                                 column_domain[0], row_domain[0])

                merge_figure(output_figure, fig)

    return output_figure