helper function to plot ensemble 1-to-1 plots sbowing the simulated range

    Parameters
    ----------
    ensemble : varies
        the ensemble argument can be a pandas.DataFrame or derived type or a str, which
        is treated as a fileanme.  Optionally, ensemble can be a list of these types or
        a dict, in which case, the keys are treated as facecolor str (e.g., 'b', 'y', etc).
    pst : pyemu.Pst
        pst instance
    facecolor : str
        the histogram facecolor.  Only applies if ensemble is a single thing
    filename : str
        the name of the pdf to create. If None, return figs without saving.  Default is None.
    base_ensemble : varies
        an optional ensemble argument for the observations + noise ensemble.
        This will be plotted as a transparent red bar on the 1to1 plot.

def ensemble_res_1to1(ensemble, pst,facecolor='0.5',logger=None,filename=None,
                      skip_groups=[],base_ensemble=None,**kwargs):
    """helper function to plot ensemble 1-to-1 plots sbowing the simulated range

    Parameters
    ----------
    ensemble : varies
        the ensemble argument can be a pandas.DataFrame or derived type or a str, which
        is treated as a fileanme.  Optionally, ensemble can be a list of these types or
        a dict, in which case, the keys are treated as facecolor str (e.g., 'b', 'y', etc).
    pst : pyemu.Pst
        pst instance
    facecolor : str
        the histogram facecolor.  Only applies if ensemble is a single thing
    filename : str
        the name of the pdf to create. If None, return figs without saving.  Default is None.
    base_ensemble : varies
        an optional ensemble argument for the observations + noise ensemble.
        This will be plotted as a transparent red bar on the 1to1 plot.

    """
    if logger is None:
        logger=Logger('Default_Loggger.log',echo=False)
    logger.log("plot res_1to1")
    obs = pst.observation_data
    ensembles = _process_ensemble_arg(ensemble,facecolor,logger)

    if base_ensemble is not None:
        base_ensemble = _process_ensemble_arg(base_ensemble,"r",logger)

    if "grouper" in kwargs:
        raise NotImplementedError()
    else:
        grouper = obs.groupby(obs.obgnme).groups
        for skip_group in skip_groups:
            grouper.pop(skip_group)

    fig = plt.figure(figsize=figsize)
    if "fig_title" in kwargs:
        plt.figtext(0.5,0.5,kwargs["fig_title"])
    else:
        plt.figtext(0.5, 0.5, "pyemu.Pst.plot(kind='1to1')\nfrom pest control file '{0}'\n at {1}"
                    .format(pst.filename, str(datetime.now())), ha="center")
    #if plot_hexbin:
    #    pdfname = pst.filename.replace(".pst", ".1to1.hexbin.pdf")
    #else:
    #    pdfname = pst.filename.replace(".pst", ".1to1.pdf")
    figs = []
    ax_count = 0
    for g, names in grouper.items():
        logger.log("plotting 1to1 for {0}".format(g))

        obs_g = obs.loc[names, :]
        logger.statement("using control file obsvals to calculate residuals")
        if "include_zero" not in kwargs or kwargs["include_zero"] is False:
            obs_g = obs_g.loc[obs_g.weight > 0, :]
        if obs_g.shape[0] == 0:
            logger.statement("no non-zero obs for group '{0}'".format(g))
            logger.log("plotting 1to1 for {0}".format(g))
            continue

        if ax_count % (nr * nc) == 0:
            if ax_count > 0:
                plt.tight_layout()
            #pdf.savefig()
            #plt.close(fig)
            figs.append(fig)
            fig = plt.figure(figsize=figsize)
            axes = get_page_axes()
            ax_count = 0

        ax = axes[ax_count]
        if base_ensemble is None:
            mx = obs_g.obsval.max()
            mn =  obs_g.obsval.min()
        else:
            mn = base_ensemble["r"].loc[:,names].min().min()
            mx = base_ensemble["r"].loc[:, names].max().max()
        #if obs_g.shape[0] == 1:
        mx *= 1.1
        mn *= 0.9
        #ax.axis('square')
        if base_ensemble is not None:
            obs_gg = obs_g.sort_values(by="obsval")

            for c, en in base_ensemble.items():
                en_g = en.loc[:, obs_gg.obsnme]
                ex = en_g.max()
                en = en_g.min()
                #[ax.plot([ov, ov], [een, eex], color=c,alpha=0.3) for ov, een, eex in zip(obs_g.obsval.values, en.values, ex.values)]
                ax.fill_between(obs_gg.obsval,en,ex,facecolor=c,alpha=0.2)
        #ax.scatter([obs_g.sim], [obs_g.obsval], marker='.', s=10, color='b')
        for c,en in ensembles.items():
            en_g = en.loc[:,obs_g.obsnme]
            ex = en_g.max()
            en = en_g.min()
            [ax.plot([ov,ov],[een,eex],color=c) for ov,een,eex in zip(obs_g.obsval.values,en.values,ex.values)]


        ax.plot([mn,mx],[mn,mx],'k--',lw=1.0)
        xlim = (mn,mx)
        ax.set_xlim(mn,mx)
        ax.set_ylim(mn,mx)
        if mx > 1.0e5:
            ax.xaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%1.0e'))
            ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%1.0e'))
        ax.grid()

        ax.set_xlabel("observed",labelpad=0.1)
        ax.set_ylabel("simulated",labelpad=0.1)
        ax.set_title("{0}) group:{1}, {2} observations".
                                 format(abet[ax_count], g, obs_g.shape[0]), loc="left")

        ax_count += 1
        ax = axes[ax_count]
        #ax.scatter(obs_g.obsval, obs_g.res, marker='.', s=10, color='b')

        if base_ensemble is not None:
            obs_gg = obs_g.sort_values(by="obsval")

            for c, en in base_ensemble.items():
                en_g = en.loc[:, obs_gg.obsnme].subtract(obs_gg.obsval)
                ex = en_g.max()
                en = en_g.min()
                #[ax.plot([ov, ov], [een, eex], color=c,alpha=0.3) for ov, een, eex in zip(obs_g.obsval.values, en.values, ex.values)]
                ax.fill_between(obs_gg.obsval,en,ex,facecolor=c,alpha=0.2)

        for c,en in ensembles.items():
            en_g = en.loc[:,obs_g.obsnme].subtract(obs_g.obsval,axis=1)
            ex = en_g.max()
            en = en_g.min()
            [ax.plot([ov,ov],[een,eex],color=c) for ov,een,eex in zip(obs_g.obsval.values,en.values,ex.values)]
        # if base_ensemble is not None:
        #     if base_ensemble is not None:
        #         for c, en in base_ensemble.items():
        #             en_g = en.loc[:, obs_g.obsnme].subtract(obs_g.obsval,axis=1)
        #             ex = en_g.max()
        #             en = en_g.min()
        #             [ax.plot([ov, ov], [een, eex], color=c, alpha=0.3) for ov, een, eex in
        #              zip(obs_g.obsval.values, en.values, ex.values)]
        ylim = ax.get_ylim()
        mx = max(np.abs(ylim[0]), np.abs(ylim[1]))
        if obs_g.shape[0] == 1:
            mx *= 1.1
        ax.set_ylim(-mx, mx)
        #show a zero residuals line
        ax.plot(xlim, [0,0], 'k--', lw=1.0)

        ax.set_xlim(xlim)
        ax.set_ylabel("residual",labelpad=0.1)
        ax.set_xlabel("observed",labelpad=0.1)
        ax.set_title("{0}) group:{1}, {2} observations".
                     format(abet[ax_count], g, obs_g.shape[0]), loc="left")
        ax.grid()
        if ax.get_xlim()[1] > 1.0e5:
            ax.xaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%1.0e'))

        ax_count += 1

        logger.log("plotting 1to1 for {0}".format(g))

    for a in range(ax_count, nr * nc):
        axes[a].set_axis_off()
        axes[a].set_yticks([])
        axes[a].set_xticks([])

    plt.tight_layout()
    #pdf.savefig()
    #plt.close(fig)
    figs.append(fig)
    if filename is not None:
        plt.tight_layout()
        with PdfPages(filename) as pdf:
            for fig in figs:
                pdf.savefig(fig)
                plt.close(fig)
        logger.log("plot res_1to1")
    else:
        logger.log("plot res_1to1")
        return figs