def res_1to1(pst,logger=None,filename=None,plot_hexbin=False,histogram=False,**kwargs):
    """ make 1-to-1 plots and also observed vs residual by observation group
    Parameters
    ----------
    pst : pyemu.Pst
    logger : Logger
        if None, a generic one is created.  Default is None
    filename : str
        PDF filename to save figures to.  If None, figures are returned.  Default is None
    kwargs : dict
        optional keyword args to pass to plotting functions

    TODO: color symbols by weight


    """
    if logger is None:
        logger=Logger('Default_Loggger.log',echo=False)
    logger.log("plot res_1to1")

    if "ensemble" in kwargs:
        try:
            res=pst_utils.res_from_en(pst,kwargs['ensemble'])
        except Exception as e:
            logger.lraise("res_1to1: error loading ensemble file: {0}".format( str(e)))
    else:
        try:
            res = pst.res
        except:
            logger.lraise("res_phi_pie: pst.res is None, couldn't find residuals file")

    obs = pst.observation_data

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

    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, :]
        obs_g.loc[:, "sim"] = res.loc[names, "modelled"]
        logger.statement("using control file obsvals to calculate residuals")
        obs_g.loc[:,'res'] = obs_g.sim - obs_g.obsval
        if "include_zero" not in kwargs or kwargs["include_zero"] is True:
            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 obs_g.shape[0] == 1:
        #    ax.scatter(list(obs_g.sim),list(obs_g.obsval),marker='.',s=30,color='b')
        #else:
        mx = max(obs_g.obsval.max(), obs_g.sim.max())
        mn = min(obs_g.obsval.min(), obs_g.sim.min())

        #if obs_g.shape[0] == 1:
        mx *= 1.1
        mn *= 0.9
        ax.axis('square')
        if plot_hexbin:
            ax.hexbin(obs_g.obsval.values, obs_g.sim.values, mincnt=1, gridsize=(75, 75),
                      extent=(mn, mx, mn, mx), bins='log', edgecolors=None)
#               plt.colorbar(ax=ax)
        else:
            ax.scatter([obs_g.obsval], [obs_g.sim], marker='.', s=10, color='b')



        ax.plot([mn,mx],[mn,mx],'k--',lw=1.0)
        xlim = (mn,mx)
        ax.set_xlim(mn,mx)
        ax.set_ylim(mn,mx)
        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

        if histogram==False:
            ax = axes[ax_count]
            ax.scatter(obs_g.obsval, obs_g.res, marker='.', s=10, color='b')
            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)
            meanres= obs_g.res.mean()
            # show mean residuals line
            ax.plot(xlim,[meanres,meanres], 'r-', 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()
            ax_count += 1
        else:
            ax = axes[ax_count]
            ax.hist(obs_g.res, 50, color='b')
            meanres= obs_g.res.mean()
            ax.axvline(meanres, color='r', lw=1)
            b,t = ax.get_ylim()
            ax.text(meanres + meanres/10,
                     t - t/10,
                     'Mean: {:.2f}'.format(meanres))
            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:
        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
make 1-to-1 plots and also observed vs residual by observation group
    Parameters
    ----------
    pst : pyemu.Pst
    logger : Logger
        if None, a generic one is created.  Default is None
    filename : str
        PDF filename to save figures to.  If None, figures are returned.  Default is None
    kwargs : dict
        optional keyword args to pass to plotting functions

    TODO: color symbols by weight