def make_category_scatter_figure():
    categories = get_data()

    # Build a figure and place a single axes instance in it.
    fig = plt.figure(figsize=(4,3))
    ax1 = fig.add_subplot(1,1,1)

    # Use our helper function to do the category scatter plot.
    cs = CategoryScatter( ax1, categories )

    # Locate the axes spines on the left and bottom.
    spine_placer(ax1, location='left,bottom' )

    # Finalize the category scatter plot (stuff that can only be done
    # after the spines are placed).
    cs.finalize()

    # Add standard y label.
    ax1.set_ylabel('y value (units)')

    # Now, add a final few touchups.
    ax1.spines['bottom'].set_color('none') # don't draw bottom spine
    ax1.yaxis.set_major_locator( mticker.MaxNLocator(nbins=4) )
    auto_reduce_spine_bounds( ax1 )

    fig.tight_layout()
    return fig
def plot_ci_for_ms(path, figname, vals, figsize=(6,4)):

    figname = os.path.splitext(figname)[0]

    NAMES = {'TH>trpA1 head':'TH>trpA1 (head)',
              'TH>trpA1 thorax':'TH>trpA1 (thorax)',
              'TH head':'TH (head)',
              'TH thorax':'TH (thorax)',
              'trpA1 head':'trpA1 (head)',
              'trpA1 thorax':'trpA1 (thorax)',
              'controls head':'controls (head)',
              'controls thorax':'controls (thorax)',
    }
    COLORS = {'TH>trpA1 head':flymad_plot.RED,
              'TH>trpA1 thorax':flymad_plot.ORANGE,
              'TH head':flymad_plot.GREEN,
              'TH thorax':flymad_plot.BLUE,
              'trpA1 head':flymad_plot.GREEN,
              'trpA1 thorax':flymad_plot.BLUE,
              'controls head':flymad_plot.GREEN,
              'controls thorax':flymad_plot.BLUE,
    }

    ORDER = ['TH>trpA1 head',
              'TH>trpA1 thorax',
              'TH head',
              'TH thorax',
              'trpA1 head',
              'trpA1 thorax',
              'controls head',
              'controls thorax',
    ]


    figure_title = "THGAL4 %s cumulative incidence" % figname
    fig = plt.figure(figsize=figsize)
    ax = fig.add_subplot(1,1,1)

    for gt in sorted(vals, cmp=lambda a,b: cmp(ORDER.index(a), ORDER.index(b))):
        ax.plot(vals[gt]['x'],vals[gt]['y'],
                lw=2,clip_on=False,
                color=COLORS[gt],label=NAMES[gt])

    spine_placer(ax, location='left,bottom' )
    ax.legend()

    ax.set_xlabel('Time (s)')
    ax.set_ylabel('Cumulative incidence (%)')
    ax.set_ylim([0,100])
    ax.set_xlim([0,20])

    flymad_plot.retick_relabel_axis(ax, [0,10,20], [0,100])

    fig.savefig(flymad_plot.get_plotpath(path,"thgal4_ci_%s.png" % figname), bbox_inches='tight')
    fig.savefig(flymad_plot.get_plotpath(path,"thgal4_ci_%s.svg" % figname), bbox_inches='tight')
def make_many_timeseries_figure():
    # Generate some fake data in 4 timeseries
    times = np.arange( 0, 200.0, 0.01 )
    nt = len(times)
    ys = []
    for i in range(4):
        ys.append( np.sin( times*2*np.pi*0.02 + i*20 ) + 0.13*np.random.randn(nt) )

    # Build a figure and place a two axes instances in it.
    fig = plt.figure(figsize=(8,6))
    ax1 = fig.add_subplot(2,1,1)
    ax2 = fig.add_subplot(2,1,2)

    # Use our helper function to plot the multiple timeseries.
    mts1 = ManyTimeseries( ax1, times, ys, show=['all','mean'] )

    # Use our helper function to plot the multiple timeseries.
    mts2 = ManyTimeseries( ax2, times, ys, show=['mean','std'] )

    # Locate the axes spines.
    spine_placer(ax1, location='left' )
    spine_placer(ax2, location='left,bottom' )

    # Finalize the plots (stuff that can only be done
    # after the spines are placed).
    mts1.finalize()
    mts2.finalize()

    # Add standard axis labels.
    ax1.set_ylabel('y1 value (units)')
    ax2.set_ylabel('y2 value (units)')
    ax2.set_xlabel('time (seconds)')

    # Now, add a final few touchups.
    ax1.yaxis.set_major_locator( mticker.MaxNLocator(nbins=4) )
    ax1.set_xticks([])
    ax2.yaxis.set_major_locator( mticker.MaxNLocator(nbins=4) )
    auto_reduce_spine_bounds( ax1 )
    auto_reduce_spine_bounds( ax2 )

    fig.tight_layout()
    return fig
Beispiel #4
0
def plot_data(path, data):
    ci_html_buf = ''

    for exp_name in data:
        gts = data[exp_name].keys()

        laser = '130ht'
        gts_string = 'vs'.join(gts)
        figname = laser + '_' + gts_string

        fig = plt.figure("Song (%s)" % figname, figsize=(10, 6))
        ax = fig.add_subplot(1, 1, 1)

        datasets = {}
        for gt in gts:

            if flymad_analysis.genotype_is_exp(gt):
                color = flymad_plot.RED
                order = 1
            elif flymad_analysis.genotype_is_ctrl(gt):
                color = flymad_plot.BLACK
                order = 2
            elif flymad_analysis.genotype_is_trp_ctrl(gt):
                order = 3
                color = flymad_plot.BLUE
            else:
                color = 'cyan'
                order = 0

            gtdf = data[exp_name][gt]
            datasets[gt] = dict(xaxis=gtdf['mean']['t'].values,
                                value=gtdf['mean']['zx'].values,
                                std=gtdf['std']['zx'].values,
                                n=gtdf['n']['zx'].values,
                                order=order,
                                df=gtdf,
                                label=flymad_analysis.human_label(gt),
                                color=color,
                                N=len(gtdf['df']['obj_id'].unique()))

        pvalue_buf = ''

        for gt in datasets:
            label = flymad_analysis.human_label(gt)
            if '>' not in label:
                continue

            # OK, this is a Gal4 + UAS - do head vs thorax stats
            gtdf = data[exp_name][gt]['df']
            ci_data = do_cum_incidence(gtdf, label)
            ci_html_buf += ci_data['buf']

            ax_cum = ci_data['ax']
            spine_placer(ax_cum, location='left,bottom')

            ax_cum.set_ylabel('Fraction extending wing (%)')
            ax_cum.set_xlabel('Time (s)')

            note = '%s\n%s\np-value: %.3g\n%d flies\nn=%d, %d' % (
                label, madplot.p2stars(ci_data['p_value']), ci_data['p_value'],
                len(gtdf['obj_id'].unique()), ci_data['n_head'],
                ci_data['n_thorax'])
            ax_cum.text(
                0,
                1,  #top left
                note,
                fontsize=10,
                horizontalalignment='left',
                verticalalignment='top',
                transform=ax_cum.transAxes,
                zorder=0)

            ci_data['fig'].savefig(flymad_plot.get_plotpath(
                path, "song_cum_inc_%s.png" % figname),
                                   bbox_inches='tight')
            ci_data['fig'].savefig(flymad_plot.get_plotpath(
                path, "song_cum_inc_%s.svg" % figname),
                                   bbox_inches='tight')


#            for i in range(len(head_times)):
#                head_values = gtdf[gtdf['t']==head_times[i]]
#                thorax_values = gtdf[gtdf['t']==thorax_times[i]]
#                test1 = head_values['zx'].values
#                test2 = thorax_values['zx'].values
#                hval, pval = kruskal(test1, test2)
#                pvalue_buf += 'Pulse %d: Head vs thorax WEI p-value: %.3g (n=%d, %d)\n'%(
#                    i+1, pval, len(test1), len(test2) )

#all experiments used identical activation times
        headtargetbetween = dict(
            xaxis=data['pIP10']['wtrpmyc']['first']['t'].values,
            where=data['pIP10']['wtrpmyc']['first']['ttm'].values > 0)
        thoraxtargetbetween = dict(
            xaxis=data['pIP10']['wtrpmyc']['first']['t'].values,
            where=data['pIP10']['wtrpmyc']['first']['ttm'].values < 0)

        flymad_plot.plot_timeseries_with_activation(
            ax,
            targetbetween=[headtargetbetween, thoraxtargetbetween],
            sem=True,
            note=pvalue_buf,
            **datasets)

        ax.set_xlabel('Time (s)')
        ax.set_ylabel('Wing extension index')
        ax.set_ylim([-0.05, 0.4] if gts_string ==
                    "40347vswtrpmycvs40347trpmyc" else [-0.05, 1.0])
        ax.set_xlim([-10, 180])

        flymad_plot.retick_relabel_axis(
            ax, [0, 60, 120, 180], [0, 0.2, 0.4]
            if gts_string == "40347vswtrpmycvs40347trpmyc" else [0, 0.5, 1])

        fig.savefig(flymad_plot.get_plotpath(path, "song_%s.png" % figname),
                    bbox_inches='tight')
        fig.savefig(flymad_plot.get_plotpath(path, "song_%s.svg" % figname),
                    bbox_inches='tight')

    with open(flymad_plot.get_plotpath(path, "song_cum_in.html"),
              mode='w') as fd:
        fd.write(ci_html_buf)
Beispiel #5
0
def plot_ci_for_ms(path, figname, vals, figsize=(6, 4)):

    figname = os.path.splitext(figname)[0]

    NAMES = {
        'TH>trpA1 head': 'TH>trpA1 (head)',
        'TH>trpA1 thorax': 'TH>trpA1 (thorax)',
        'TH head': 'TH (head)',
        'TH thorax': 'TH (thorax)',
        'trpA1 head': 'trpA1 (head)',
        'trpA1 thorax': 'trpA1 (thorax)',
        'controls head': 'controls (head)',
        'controls thorax': 'controls (thorax)',
    }
    COLORS = {
        'TH>trpA1 head': flymad_plot.RED,
        'TH>trpA1 thorax': flymad_plot.ORANGE,
        'TH head': flymad_plot.GREEN,
        'TH thorax': flymad_plot.BLUE,
        'trpA1 head': flymad_plot.GREEN,
        'trpA1 thorax': flymad_plot.BLUE,
        'controls head': flymad_plot.GREEN,
        'controls thorax': flymad_plot.BLUE,
    }

    ORDER = [
        'TH>trpA1 head',
        'TH>trpA1 thorax',
        'TH head',
        'TH thorax',
        'trpA1 head',
        'trpA1 thorax',
        'controls head',
        'controls thorax',
    ]

    figure_title = "THGAL4 %s cumulative incidence" % figname
    fig = plt.figure(figsize=figsize)
    ax = fig.add_subplot(1, 1, 1)

    for gt in sorted(vals,
                     cmp=lambda a, b: cmp(ORDER.index(a), ORDER.index(b))):
        ax.plot(vals[gt]['x'],
                vals[gt]['y'],
                lw=2,
                clip_on=False,
                color=COLORS[gt],
                label=NAMES[gt])

    spine_placer(ax, location='left,bottom')
    ax.legend()

    ax.set_xlabel('Time (s)')
    ax.set_ylabel('Cumulative incidence (%)')
    ax.set_ylim([0, 100])
    ax.set_xlim([0, 20])

    flymad_plot.retick_relabel_axis(ax, [0, 10, 20], [0, 100])

    fig.savefig(flymad_plot.get_plotpath(path, "thgal4_ci_%s.png" % figname),
                bbox_inches='tight')
    fig.savefig(flymad_plot.get_plotpath(path, "thgal4_ci_%s.svg" % figname),
                bbox_inches='tight')
Beispiel #6
0
                    color='black',
                    linewidth=0.5,
                )
                ax.errorbar(
                    [this_x_value],
                    [np.mean(this_y_values)],
                    [scipy.stats.sem(this_y_values)],
                    clip_on=False,
                    color='black',
                    linewidth=0.5,
                    capsize=2,
                )

                xticks.append(this_x_value)
                tick_labels.append(name_key)
            spine_placer(ax, location='left,bottom')
            ax.spines['bottom'].set_color('none')
            ax.spines['bottom'].set_position(('outward', 5))
            ax.set_yticks([0, 20])
            ax.set_xticks(xticks)
            ax.set_xticklabels(tick_labels, rotation='vertical')
            ax.set_ylabel('latency (sec)')
            fig1.subplots_adjust(left=0.4, bottom=0.65)  # do not clip text
            svg1_fname = 'th_gal4_latency_%s_%s.svg' % (measurement,
                                                        pooled_str)
            fig1.savefig(svg1_fname)

            # --- plots --------------------------------
            fig2 = plt.figure('scatter ' + measurement + pooled_str,
                              figsize=(3, 5))
            ax = fig2.add_subplot(111)
                        clip_on=False,
                        color='black',
                        linewidth=0.5,
                        )
                ax.errorbar( [this_x_value],
                             [ np.mean(this_y_values) ],
                             [ scipy.stats.sem( this_y_values ) ],
                             clip_on=False,
                             color='black',
                             linewidth=0.5,
                             capsize=2,
                             )

                xticks.append( this_x_value )
                tick_labels.append( name_key )
            spine_placer(ax, location='left,bottom' )
            ax.spines['bottom'].set_color('none')
            ax.spines['bottom'].set_position(('outward',5))
            ax.set_yticks([0,20])
            ax.set_xticks(xticks)
            ax.set_xticklabels(tick_labels, rotation='vertical')
            ax.set_ylabel('latency (sec)')
            fig1.subplots_adjust(left=0.4,bottom=0.65) # do not clip text
            svg1_fname = 'th_gal4_latency_%s_%s.svg'%(measurement,pooled_str)
            fig1.savefig(svg1_fname)

            # --- plots --------------------------------
            fig2 = plt.figure('scatter '+measurement+pooled_str,figsize=(3,5))
            ax = fig2.add_subplot(111)
            tick_labels = []
            xticks = []
Beispiel #8
0
def plot_data(path, data):
    ci_html_buf = ''

    for exp_name in data:
        gts = data[exp_name].keys()

        laser = '130ht'
        gts_string = 'vs'.join(gts)
        figname = laser + '_' + gts_string

        fig = plt.figure("Song (%s)" % figname,figsize=(10,6))
        ax = fig.add_subplot(1,1,1)

        datasets = {}
        for gt in gts:

            if flymad_analysis.genotype_is_exp(gt):
                color = flymad_plot.RED
                order = 1
            elif flymad_analysis.genotype_is_ctrl(gt):
                color = flymad_plot.BLACK
                order = 2
            elif flymad_analysis.genotype_is_trp_ctrl(gt):
                order = 3
                color = flymad_plot.BLUE
            else:
                color = 'cyan'
                order = 0

            gtdf = data[exp_name][gt]
            datasets[gt] = dict(xaxis=gtdf['mean']['t'].values,
                                value=gtdf['mean']['zx'].values,
                                std=gtdf['std']['zx'].values,
                                n=gtdf['n']['zx'].values,
                                order=order,
                                df=gtdf,
                                label=flymad_analysis.human_label(gt),
                                color=color,
                                N=len(gtdf['df']['obj_id'].unique()))

        pvalue_buf = ''

        for gt in datasets:
            label=flymad_analysis.human_label(gt)
            if '>' not in label:
                continue

            # OK, this is a Gal4 + UAS - do head vs thorax stats
            gtdf = data[exp_name][gt]['df']
            ci_data = do_cum_incidence(gtdf, label)
            ci_html_buf += ci_data['buf']

            ax_cum = ci_data['ax']
            spine_placer(ax_cum, location='left,bottom' )

            ax_cum.set_ylabel('Fraction extending wing (%)')
            ax_cum.set_xlabel('Time (s)')

            note = '%s\n%s\np-value: %.3g\n%d flies\nn=%d, %d'%(label,
                                                                madplot.p2stars(ci_data['p_value']),
                                                            ci_data['p_value'],
                                                            len(gtdf['obj_id'].unique()),
                                                            ci_data['n_head'],
                                                            ci_data['n_thorax'])
            ax_cum.text(0, 1, #top left
                        note,
                        fontsize=10,
                        horizontalalignment='left',
                        verticalalignment='top',
                        transform=ax_cum.transAxes,
                        zorder=0)

            ci_data['fig'].savefig(flymad_plot.get_plotpath(path,"song_cum_inc_%s.png" % figname),
                                    bbox_inches='tight')
            ci_data['fig'].savefig(flymad_plot.get_plotpath(path,"song_cum_inc_%s.svg" % figname),
                                    bbox_inches='tight')


#            for i in range(len(head_times)):
#                head_values = gtdf[gtdf['t']==head_times[i]]
#                thorax_values = gtdf[gtdf['t']==thorax_times[i]]
#                test1 = head_values['zx'].values
#                test2 = thorax_values['zx'].values
#                hval, pval = kruskal(test1, test2)
#                pvalue_buf += 'Pulse %d: Head vs thorax WEI p-value: %.3g (n=%d, %d)\n'%(
#                    i+1, pval, len(test1), len(test2) )

        #all experiments used identical activation times
        headtargetbetween = dict(xaxis=data['pIP10']['wtrpmyc']['first']['t'].values,
                                 where=data['pIP10']['wtrpmyc']['first']['ttm'].values > 0)
        thoraxtargetbetween = dict(xaxis=data['pIP10']['wtrpmyc']['first']['t'].values,
                                   where=data['pIP10']['wtrpmyc']['first']['ttm'].values < 0)

        flymad_plot.plot_timeseries_with_activation(ax,
                    targetbetween=[headtargetbetween,thoraxtargetbetween],
                    sem=True,
                                                    note=pvalue_buf,
                    **datasets
        )

        ax.set_xlabel('Time (s)')
        ax.set_ylabel('Wing extension index')
        ax.set_ylim([-0.05,0.4] if gts_string == "40347vswtrpmycvs40347trpmyc" else [-0.05,1.0])
        ax.set_xlim([-10,180])

        flymad_plot.retick_relabel_axis(ax, [0, 60, 120, 180],
                [0, 0.2, 0.4] if gts_string == "40347vswtrpmycvs40347trpmyc" else [0, 0.5, 1])

        fig.savefig(flymad_plot.get_plotpath(path,"song_%s.png" % figname), bbox_inches='tight')
        fig.savefig(flymad_plot.get_plotpath(path,"song_%s.svg" % figname), bbox_inches='tight')

    with open(flymad_plot.get_plotpath(path,"song_cum_in.html"),mode='w') as fd:
        fd.write(ci_html_buf)
Beispiel #9
0
def plot_timeseries_with_activation(ax, targetbetween=None, downsample=1, sem=False,
                                    legend_location='upper right', note="",
                                    individual=None, individual_title=None,
                                    marker=None,linestyle='-',markersize=1,
									return_dict=False,
                                    **datasets):

    ORDER_LAST = 100
    DEFAULT_COLORS = {"exp":RED,"ctrl":BLACK}

    trans = mtransforms.blended_transform_factory(ax.transData, ax.transAxes)
    final_copy = os.environ.get('FLYMAD_FINAL')

    def _ds(a):
        if downsample == 1:
            return a
        else:
            tmp = []
            for i in range(0,len(a),downsample):
                vals = a[i:i+downsample]
                tmp.append( scipy.stats.nanmean(vals) )
            return np.array(tmp)

    def _dn(a,b):
        nans_a, = np.where(np.isnan(a))
        nans_b, = np.where(np.isnan(b))
        nans_all = np.union1d(nans_a, nans_b)
        n_nans = len(nans_all)
        if n_nans:
            print "\tremoving %d nans from plot" % n_nans
        clean_a = np.delete(a,nans_all)
        clean_b = np.delete(b,nans_all)
        return clean_a, clean_b

    def _sort_by_order(a,b):
        return cmp(datasets[a].get('order', ORDER_LAST), datasets[b].get('order', ORDER_LAST))

    def _fill_between(f_ax, f_xaxis, f_where, f_facecolor, f_zorder):
        f_ax.fill_between(f_xaxis, 0, 1, where=f_where,
                          edgecolor='none', facecolor=f_facecolor,
                          alpha=0.15, transform=trans, zorder=f_zorder)


    if targetbetween is not None:
        if not (isinstance(targetbetween, list) or isinstance(targetbetween, tuple)):
            targetbetween = [targetbetween]
        for tb in targetbetween:
            _fill_between(ax, tb['xaxis'], tb['where'], tb.get('facecolor','yellow'),
                          tb.get('zorder',1))

    if any(['std' in datasets[exp] for exp in datasets]):
        note += "+/- SEM\n" if sem else "+/- STD\n"
    note += "" if downsample == 1 else ("downsample x %d\n" % downsample)

    #zorder = 1 = back
    top_zorder = 60
    bottom_zorder = 30

    plotted = {}

    exps_colors = [plt.cm.gnuplot(i) for i in np.linspace(0, 1.0, len(datasets))]

    cur_zorder = 2
    for data,default_color in zip(sorted(datasets.keys(), cmp=_sort_by_order), exps_colors):
        exp = datasets[data]

        label = exp.get('label',data)

        note += "N(%s)=%s\n" % (label,exp.get('N','??'))

        if exp.get('ontop'):
            this_zorder = top_zorder + cur_zorder
        else:
            this_zorder = bottom_zorder + cur_zorder

        print "plotting %s (%s) zorder %s" % (label,data,this_zorder)

        if 'std' in exp:
            if sem:
                spread = exp['std'] / np.sqrt(exp['n'])
            else:
                spread = exp['std']
        else:
            spread = None

        if exp.get('color') is None:
            color = default_color
        else:
            color = DEFAULT_COLORS.get(data,default_color)

        if spread is not None:
            ax.fill_between(exp['xaxis'][::downsample], _ds(exp['value']+spread), _ds(exp['value']-spread),
                        alpha=0.1, color=color,
                        zorder=this_zorder)

        x,y = _dn(exp['xaxis'][::downsample], _ds(exp['value']))
        zorder = this_zorder + 1

        plotted[data] = dict(x=x,y=y,color=color,label=label,zorder=zorder)
        ax.plot(x,y,
                    color=color,label=label,
                    lw=2,linestyle=linestyle,clip_on=exp.get('clip_on',True),
                    marker=marker,markerfacecolor=color,markersize=markersize,markeredgecolor='none',
                    zorder=zorder)

        cur_zorder -= 2

    spine_placer(ax, location='left,bottom' )

    l = ax.legend(loc=legend_location)
    l.set_zorder(1+top_zorder+cur_zorder)

    ax.text(0, 1, #top left
            note,
            fontsize=10,
            horizontalalignment='left',
            verticalalignment='top',
            transform=ax.transAxes,
            color='white' if final_copy else 'k',
            zorder=-100)

    axs = [ax]
    figs = {}
    if (not final_copy) and (individual is not None) and isinstance(individual, dict):
        for data in individual:
            try:
                #try to avoid creating many duplicate figures
                #based on the title be unique if provided
                if individual_title:
                    fignum = hash(individual_title)
                    if plt.fignum_exists(fignum):
                        continue
                else:
                    fignum = None
                    individual_title = ""

                individual_title += data

                gdf = datasets[data]['df']
                groupcol = individual[data]['groupby']
                xcol = individual[data]['xaxis']
                ycol = individual[data]['yaxis']

                grouper = gdf.groupby(groupcol)
                nts = len(grouper)

                gs,nr_nc = get_gridspec_to_fit_nplots(nts)

                fig2 = plt.figure(fignum, figsize=(2*max(nr_nc),2*max(nr_nc)))
                fig2.suptitle(individual_title)
                print 'plotting', nts, 'individual timeseries for', data

                for gridspec_id,(name,group) in zip(gs,grouper):
                    #we can't assume these are numpy arrays here, but
                    #np.array does the right thing and converts pandas things
                    #while being no-op on np arrays
                    grp_xaxis = np.array(group[xcol])
                    grp_yaxis = np.array(group[ycol])

                    iax = fig2.add_subplot(gridspec_id)

                    print "\tplot",name,xcol,"vs",ycol
                    x,y = _dn(grp_xaxis[::downsample], _ds(grp_yaxis))
                    iax.plot(x, y,
                             label=str(name), color='k')
                    iax.legend(loc=legend_location)

#                    if fb_wherecol:
#                        _fill_between(iax,
#                                      grp_xaxis,
#                                      np.array(group[fb_wherecol]) > 0,
#                                      'yellow')

                    axs.append(iax)

                figs[md5.md5(individual_title).hexdigest()] = fig2

            except KeyError, e:
                print "\terror plotting individual timeseries (%s)" % e
Beispiel #10
0
def plot_timeseries_with_activation(ax,
                                    targetbetween=None,
                                    downsample=1,
                                    sem=False,
                                    legend_location='upper right',
                                    note="",
                                    individual=None,
                                    individual_title=None,
                                    marker=None,
                                    linestyle='-',
                                    markersize=1,
                                    return_dict=False,
                                    **datasets):

    ORDER_LAST = 100
    DEFAULT_COLORS = {"exp": RED, "ctrl": BLACK}

    trans = mtransforms.blended_transform_factory(ax.transData, ax.transAxes)
    final_copy = os.environ.get('FLYMAD_FINAL')

    def _ds(a):
        if downsample == 1:
            return a
        else:
            tmp = []
            for i in range(0, len(a), downsample):
                vals = a[i:i + downsample]
                tmp.append(scipy.stats.nanmean(vals))
            return np.array(tmp)

    def _dn(a, b):
        nans_a, = np.where(np.isnan(a))
        nans_b, = np.where(np.isnan(b))
        nans_all = np.union1d(nans_a, nans_b)
        n_nans = len(nans_all)
        if n_nans:
            print "\tremoving %d nans from plot" % n_nans
        clean_a = np.delete(a, nans_all)
        clean_b = np.delete(b, nans_all)
        return clean_a, clean_b

    def _sort_by_order(a, b):
        return cmp(datasets[a].get('order', ORDER_LAST),
                   datasets[b].get('order', ORDER_LAST))

    def _fill_between(f_ax, f_xaxis, f_where, f_facecolor, f_zorder):
        f_ax.fill_between(f_xaxis,
                          0,
                          1,
                          where=f_where,
                          edgecolor='none',
                          facecolor=f_facecolor,
                          alpha=0.15,
                          transform=trans,
                          zorder=f_zorder)

    if targetbetween is not None:
        if not (isinstance(targetbetween, list)
                or isinstance(targetbetween, tuple)):
            targetbetween = [targetbetween]
        for tb in targetbetween:
            _fill_between(ax, tb['xaxis'], tb['where'],
                          tb.get('facecolor', 'yellow'), tb.get('zorder', 1))

    if any(['std' in datasets[exp] for exp in datasets]):
        note += "+/- SEM\n" if sem else "+/- STD\n"
    note += "" if downsample == 1 else ("downsample x %d\n" % downsample)

    #zorder = 1 = back
    top_zorder = 60
    bottom_zorder = 30

    plotted = {}

    exps_colors = [
        plt.cm.gnuplot(i) for i in np.linspace(0, 1.0, len(datasets))
    ]

    cur_zorder = 2
    for data, default_color in zip(sorted(datasets.keys(), cmp=_sort_by_order),
                                   exps_colors):
        exp = datasets[data]

        label = exp.get('label', data)

        note += "N(%s)=%s\n" % (label, exp.get('N', '??'))

        if exp.get('ontop'):
            this_zorder = top_zorder + cur_zorder
        else:
            this_zorder = bottom_zorder + cur_zorder

        print "plotting %s (%s) zorder %s" % (label, data, this_zorder)

        if 'std' in exp:
            if sem:
                spread = exp['std'] / np.sqrt(exp['n'])
            else:
                spread = exp['std']
        else:
            spread = None

        if exp.get('color') is None:
            color = default_color
        else:
            color = DEFAULT_COLORS.get(data, default_color)

        if spread is not None:
            ax.fill_between(exp['xaxis'][::downsample],
                            _ds(exp['value'] + spread),
                            _ds(exp['value'] - spread),
                            alpha=0.1,
                            color=color,
                            zorder=this_zorder)

        x, y = _dn(exp['xaxis'][::downsample], _ds(exp['value']))
        zorder = this_zorder + 1

        plotted[data] = dict(x=x, y=y, color=color, label=label, zorder=zorder)
        ax.plot(x,
                y,
                color=color,
                label=label,
                lw=2,
                linestyle=linestyle,
                clip_on=exp.get('clip_on', True),
                marker=marker,
                markerfacecolor=color,
                markersize=markersize,
                markeredgecolor='none',
                zorder=zorder)

        cur_zorder -= 2

    spine_placer(ax, location='left,bottom')

    l = ax.legend(loc=legend_location)
    l.set_zorder(1 + top_zorder + cur_zorder)

    ax.text(
        0,
        1,  #top left
        note,
        fontsize=10,
        horizontalalignment='left',
        verticalalignment='top',
        transform=ax.transAxes,
        color='white' if final_copy else 'k',
        zorder=-100)

    axs = [ax]
    figs = {}
    if (not final_copy) and (individual is not None) and isinstance(
            individual, dict):
        for data in individual:
            try:
                #try to avoid creating many duplicate figures
                #based on the title be unique if provided
                if individual_title:
                    fignum = hash(individual_title)
                    if plt.fignum_exists(fignum):
                        continue
                else:
                    fignum = None
                    individual_title = ""

                individual_title += data

                gdf = datasets[data]['df']
                groupcol = individual[data]['groupby']
                xcol = individual[data]['xaxis']
                ycol = individual[data]['yaxis']

                grouper = gdf.groupby(groupcol)
                nts = len(grouper)

                gs, nr_nc = get_gridspec_to_fit_nplots(nts)

                fig2 = plt.figure(fignum,
                                  figsize=(2 * max(nr_nc), 2 * max(nr_nc)))
                fig2.suptitle(individual_title)
                print 'plotting', nts, 'individual timeseries for', data

                for gridspec_id, (name, group) in zip(gs, grouper):
                    #we can't assume these are numpy arrays here, but
                    #np.array does the right thing and converts pandas things
                    #while being no-op on np arrays
                    grp_xaxis = np.array(group[xcol])
                    grp_yaxis = np.array(group[ycol])

                    iax = fig2.add_subplot(gridspec_id)

                    print "\tplot", name, xcol, "vs", ycol
                    x, y = _dn(grp_xaxis[::downsample], _ds(grp_yaxis))
                    iax.plot(x, y, label=str(name), color='k')
                    iax.legend(loc=legend_location)

                    #                    if fb_wherecol:
                    #                        _fill_between(iax,
                    #                                      grp_xaxis,
                    #                                      np.array(group[fb_wherecol]) > 0,
                    #                                      'yellow')

                    axs.append(iax)

                figs[md5.md5(individual_title).hexdigest()] = fig2

            except KeyError, e:
                print "\terror plotting individual timeseries (%s)" % e