Exemplo n.º 1
0
def make_ref(df, method='mean'):
    ''' Get a reference spectrum, requried for computing the dynamic spectrum (y-ref).  Usually this is this is set
    to the time-wise mean of the dataset, to 0, or to an external, pre-saved spectrum.  This will generate mean or empy
    reference spectra.  External spectra are easy enough to generate.
    
    Assumes spectral information is along the index of the dataframe!  No attempt to acommadate other styles is made.
    
    df: DataFrame with spectral data along index/row axis (=1) and temporal/physical variable along columns (axis=0)
    
    Method: Style to generate reference spectrum from dataframe.
       "mean" - Columnwise-mean of the dataframe
       "empty" - Fills series with 0.0's to length of spectral index
    
    returns: series of length of df.index'''

    method=method.lower()
    
    if method=='mean':
        refspec=df.mean(axis=1)

    elif method=='empty':
        refspec=Series( [0.0 for i in range(len(df.index))], index=df.index)  #builtin way to do this?
               
        
    else:
        raise badvalue_error(method, 'mean, empty')
        
    refspec.name='refspec' #Not sure if this will be useful
    return refspec
Exemplo n.º 2
0
def cmget(color):
    ''' Takes in a default cm color name and returns the cm.attribute color mapper for convienence.  Some of the
    builtin colors are not lowercase, so can't do color.lower().'''
    try:
        cmap=getattr(cm, color)
    except AttributeError:
        raise badvalue_error(color, 'a custom LInearSegmentedColormap or a string matching \
        the default colormaps available to matplotlib (http://dept.astro.lsa.umich.edu/~msshin/science/code/matplotlib_cm/)') 
    return cmap
Exemplo n.º 3
0
def _df_colormapper(df, cmap, axis=0, style='max', vmin=None, vmax=None):
    ''' Maps matplotlibcolors to a dataframe based on the mean value of each curve along that
    axis.  Useful for df.plot() which doesn't take a normalized colormap natively.  cmap can be
    an instance of an RGB color map, or a string which such that cm.string will produce one.
    Default ones are here:
      http://dept.astro.lsa.umich.edu/~msshin/science/code/matplotlib_cm/   (is this outdated?) 
      
    style: should curve be colored based on its average value or max value.
    
    Note that mapping paints an entire curve, not points on the curve! '''
    
    style=style.lower()
    if isinstance(cmap, basestring): 
        cmap=cmget(cmap)
    
    if axis != 0 and axis != 1:
        raise badvalue_error(axis, 'integers 0 or 1')

    if not vmin:
        vmin=min(df.min(axis=axis))
    if not vmax:
        vmax=max(df.max(axis=axis))
        
    cNorm=Normalize(vmin=vmin, vmax=vmax)
    scalarmap=cm.ScalarMappable(norm=cNorm, cmap=cmap) 
    if style=='mean':
        if axis==0:
            colors=[scalarmap.to_rgba(df[x].mean()) for x in df.columns]
        elif axis==1:
            colors=[scalarmap.to_rgba(df.ix[x].mean()) for x in df.index]    
            
    elif style=='max':
        if axis==0:
            colors=[scalarmap.to_rgba(df[x].max()) for x in df.columns]
        elif axis==1:
            colors=[scalarmap.to_rgba(df.ix[x].max()) for x in df.index]         
    else:    
        raise badvalue_error(style, '"max" or "mean"')         
        
    return colors
Exemplo n.º 4
0
def easy_legend(ax, fancy=True, position='top', **legendkwds):
    ''' Wrapper around ax.legend to make it easy to move a legend around the edges of the plot.  Made for sensible
    numbers of lines (1-15) and tries to choose smart placement to avoid conflict with lines, labels etc...
    
    BROKEN!!!
    
    This is a bust, since plotting legend is easy enough.  See guide, especially with the 'loc'      
    http://matplotlib.org/users/legend_guide.html
    
    If coming back to this, here are the issues to resolve:
       legend['loc'] must be enabled for the bounding box to work correctly/consistently.
       bbox coordinates are (left edge, bottom edge) of legend.  
         -controlling bottom edge is really dumb because want the top edge to clear the xlabel for example, and bottom
           edge depends on thickness of plot.  Putting the legend on top actually works nicely for this.
    
       If plotting outside left/right of plot, need to squeeze the plot in afterall, it will not accommadate my legend.
       Had this code, but deleted it.  Basically, need to squeeze left/right width of the axes.bounding width by 20%
       per column of legend.  
       (see second reply here http://stackoverflow.com/questions/4700614/how-to-put-the-legend-out-of-the-plot)
    
    
    '''
    
    ir=lambda x: int(round(x))
    position=position.lower() 
    legendkwds['loc']=3 #MUST BE SET TO SOMETHING, 2 or 3 doesn't matter, or lower left is no longer start point    
    
    if position not in ['top', 'bottom', 'left', 'right']:
        raise badvalue_error(position, 'top, bottom, left, or right')
    
    ################################################################
    ### Choose skinny legend for top bottom, long for left/right ###
    ################################################################
    if 'ncol' not in legendkwds:
        if position=='top' or position=='bottom':
            
            if len(ax.lines) < 4:
                ncol=len(ax.lines)
            else:
                ncol=4           
        else:
            ncol=ir( len(ax.lines)/20.0) #For left right, nice if columns have large vertical extent (10 lines is good)
            if ncol==0:
                ncol=1
        legendkwds['ncol']=ncol

    ###################################################
    ### Choose legend position around plot elements ###
    ###################################################

    box=ax.get_position()   #Can do box.x0 and stuff when resetting ax.set_position()

    if 'bbox_to_anchor' not in legendkwds:  #Don't use has_key(), gone in 3.x

        if position=='top':     
            ''' Legend slightly inside the upper bound of plot'''
            if ax.get_title() == '':
                bbox_to_anchor=(0.2, 1.025)  #0.25 will only center plot if plot is 0.5 units long, then
            else:                             #other end at 0.75.  0.2 looks nice for 8 column uv-vis plot. 
                bbox_to_anchor=(0.2, 1.05)   #Also depends on size of monitor!! so either way, screwed
                

        elif position=='bottom':
            ''' Centered legend under the label'''
            
            if ax.get_xlabel()=='':
                bbox_to_anchor=(0.25, -0.025)
            else:
                bbox_to_anchor=(0.25, -0.05)


        elif position=='right':
            ''' Squeeze 20% width inward per column in legend to make room'''
            bbox_to_anchor=(1.05, 0.0) #WHY DOESNT 1,1 work

        elif position=='left':
            ''' Squeeze 20% width inward per column in legend to make room'''
            if ax.get_ylabel()=='':
                bbox_to_anchor=(-0.07, 1.0)   
            else:
                bbox_to_anchor=(-0.12, 1.0)

            
        legendkwds['bbox_to_anchor']=bbox_to_anchor

    if fancy and 'fancybox' not in legendkwds and 'shadow' not in legendkwds:
        legendkwds['fancybox']=True
        legendkwds['shadow']=True

    if 'borderaxespad' not in legendkwds:
        legendkwds['borderaxespad']=0.0  #Havne't played with this
                                        
      ### WHY IS MODE BROKEN (comes up garbled on plot)  
#    if 'mode' not in legendkwds:
 #       legendkwds['mode']='expand'
                                        

    ax.legend(**legendkwds)
    return ax
Exemplo n.º 5
0
def plot2d(df, contours=6, label=None, colorbar=None, background=None, **pltkwds):
    ''' Wrapper for plt.contour that uses np.meshgrid to generate curves.  For convienence, a few special labels, colorbar and background
    keywords have been implemented.  If these are not adequate, it one can add custom colorbars, linelabels background images etc... easily
    just by using respecitve calls to plot (plt.colorbar(), plt.imshow(), plt.clabel() ); my implementations are only for convienences and
    some predefined, cool styles.
    
    df: Dataframe.
    
    countours: Number of desired contours from output.
    
    label: Predefined label types.  For now, only integer values 1,2.  Use plt.clabel to add a custom label.
    
    background: Integers 1,2 will add gray or autumn colormap under contour plot.  Use plt.imgshow() to generate
                custom background, or pass a PIL-opened image (note, proper image scaling not yet implemented).
                
    **pltkwds: Will be passed directly to plt.contour().'''
             
    xlabel, ylabel, title, pltkwds=smart_label(df, pltkwds)

    ### BUG, PLT.CONTOUR() doesn't take 'color'; rather, takes 'colors' for now
    ### This is only for solid colors.  contour takes cmap argument by default.  
    if 'color' in pltkwds:        
        pltkwds['colors']=pltkwds.pop('color')
        
    ### Convienence method to pass in string colors
    if 'cmap' in pltkwds and isinstance(pltkwds['cmap'], basestring):
        pltkwds['cmap']=cmget(pltkwds['cmap'])
    
    ### I CAN MAKE THIS GENERAL TO ANY PLOT, ADDITION OF IMAGES TO A BACKGROUND MAKE IT A GENERAL ROUTINE TO ALL MY PLOTS ###
    ### More here http://matplotlib.org/examples/pylab_examples/image_demo3.html ###
    if background:
        xmin, xmax, ymin, ymax=min(df.columns), max(df.columns), min(df.index), max(df.index)
        if background==1:
            im = plt.imshow(df, interpolation='bilinear', origin='lower',
                        cmap=cm.gray, extent=(xmin, xmax, ymin, ymax))     
            
        elif background==2:
            im = plt.imshow(df, interpolation='bilinear', origin='lower',
                       cmap=cm.autumn, extent=(xmin, xmax, ymin, ymax))            

    ### This will take a custom image opened in PIL or it will take plt.imshow() returned from somewhere else
        else:
            try:
                im = plt.imshow(background) 
            ### Perhaps image was not correctly opened    
            except Exception:
                raise badvalue_error(background, 'integer 1,2 or a PIL-opened image')
    else:
        im=None
        
    xx,yy=np.meshgrid(df.columns, df.index)
    ax=plt.contour(xx, yy, df, contours, **pltkwds)    #linewidths is a pltkwds arg
 
    ### Pick a few label styles to choose from.
    if label:
        if label==1:
            ax.clabel(inline=1, fontsize=10)
        elif label==2:
            ax.clabel(levels[1::2], inline=1, fontsize=10)   #label every second line      
        else:
            raise badvalue_error(label, 'integer of value 1 or 2')
        
            
    ### Add colorbar, for some reason need to do plt.colorbar() even though can do ax.clabel...
    if colorbar:
        if colorbar==1:  ### WHAT OTHER COLORBAR OPTIONS ARETHERE
            CB=plt.colorbar(ax, shrink=0.8) #shringk is heigh of colorbar relative ot height of plot
    
            if im:
                IMCB=plt.colorbar(im, orientation='horizontal', shrink=0.8)

                ### Move original colorbar to more natural to make room for image colorbar
                l,b,w,h = plt.gca().get_position().bounds
                ll,bb,ww,hh = CB.ax.get_position().bounds
                CB.ax.set_position([ll, b+0.1*h, ww, h*0.8])
                
        else:
            raise badvalue_error(colorbar, 'integer of value 1 is only supported for now')
                
         

    plt.xlabel(xlabel)      
    plt.ylabel(ylabel)                  
    plt.title(title)
    return ax    
Exemplo n.º 6
0
def plot3d(df, kind='contour', elev=0, azim=0, proj_xy=True, proj_zy=True, proj_xz=True,
               contour_color=None, contour_cmap=None, c_iso=10, r_iso=10,*args, **pltkwds):
    ''' Matplotlib Axes3d wrapper for dataframe. Made to handle surface plots, so pure contour plots,
    polygon plots etc... these need their own classes.
    parameters:
     kind
      contour: Based on Axes3d.contour(x,y,z,*args,**kwargs), returns a plot_surface() with
               relevant spectral projections.
      contourf: Filled contour plot based on Axes3D.conourf(x,y,z,*args,**kwargs).  Same a contour
                except contours are filled.
                
    pltwargs can be any Axes3d keywords, with the following extra keywords being germane to this class.
      xlabel, ylabel, zlabel: Axis labels.  Defaults are provided here.  If none are provided, the 
                              attributes df.index.name and df.columns.name will be examined, aftwards, 
                              defaults are used (see below).
                              
      xlim, ylim, zlim:  These can be passed, but if not, the are inferred from the data.  It's smarter
                         to manipulate the df input than to mess with these.  For now, I'm keeping them
                         because they are relevant to proj_padding, but otherwise, probably not too essential.
                              
      proj_padding: Padding percentage if one does not want contour projections right on the wall of the grid.
                    This actually needs fixed.  It is intended to work as in the user says... 1.1 and this means 
                    that the projections should be 10% outside of the axes grid.  0.9 would be 10% in.  
                    The operation to computes the new limit is actually not correct, and if a minimum axis 
                    value is 0, it won't move it beyond.  
                    If this ever becomes useful again, I think it would be wise to first set the x,y,z lims
                    on the plot, then after that, compute the paddings and set the offset to these values.
                    Something like 
                      ax.set(xlim)
                      newxlim=xlim*padding correction
                      contour(offset=newxlim)
                      
      A note on strides: Strides are step sizes.  Say you have 100 curves, then a stride of 5 would give you 20 
                         isolines over your surface.  As such, a smart keyword (c_iso and r_iso) will make sure
                         that a fixed number of contours are drawn when the user passes in a dataframe.  
                        
                        
      c_iso, r_iso:  See above.  These control how man column and row iso lines will be projected onto the 3d plot.
                    For example, if c_iso=10, then 10 isolines will be plotted as columns, despite the actual length of the
                    columns.  Alternatively, one can pass in c_stride directly, which is a column step size rather than
                    an absolute number, and c_iso will be disregarded.
                    
     contour_color/contour_cmap:  
           Either a solid color, or colormap, passed to the contour projections (proj_xy etc...).
           For now, will be passed to all contour projections (xy, zy, xz).

      '''
    
    ### Error raised by cfunc() is not very clear so here's an easier one
    if contour_cmap and contour_color:
        raise AttributeError('plot3d can have non-null values for attributes contour_cmap and contour_color, but not both.')

    zlabel_def=''     
    zlabel=pltkwds.pop('zlabel', zlabel_def)                    
    
    xlabel, ylabel, title, pltkwds=smart_label(df, pltkwds)    
        
    ### If plane (xy) is input backwards (yx), still works    
    proj_xy=pltkwds.pop('proj_yx', proj_xy)
    proj_zy=pltkwds.pop('proj_yz', proj_zy)
    proj_xz=pltkwds.pop('proj_zx', proj_xz)
    
                    
    ### Make mesh grid based on dataframe indicies ###
    fig = plt.figure()
    ax = fig.gca(projection='3d')    
    xx,yy=np.meshgrid(df.columns, df.index)

    ### Set axis limits explicitly from padding and data.
    ### PLOT PADDING NEEDS FIXED AS OF NOW IT IS NOT CORRECT.  FOR EXAMPLE, IF MIN IS 0, NEED AN OPERATION
    ### THAT WILL STILL REDUCE IT TO LESS THAN ZERO.  ONLY WORTH FIGURING OUT IF NEED PROJECTIONS OUTSIDE OF THE GRID.
    ### ALSO BUGS IF THE AXIS VALUES AREN'T NUMBERS.  FOR EXAMPLE, IF THEY ARE NAMED COLUMNS.  FIX WITH TRY EXCEPT
    proj_padding=pltkwds.pop('projpad', 0.0)
    
    xlim=pltkwds.pop('xlim', (min(df.columns)*(1.0-proj_padding), max(df.columns)*(1.0+proj_padding)) )
    ylim=pltkwds.pop('ylim', (min(df.index)*(1.0-proj_padding), max(df.index)*(1.0+proj_padding)) )
    
    ### Min/max of total data (only works if the data is 2d)
    zlim=pltkwds.pop('zlim',  ((1.0-proj_padding)*(df.min()).min(), (1.0+proj_padding)*(df.max().max())) )  
    
    cstride= _ir( len(df.columns)/float(c_iso) ) 
    rstride= _ir( len(df.index)/float(r_iso) )   
    
    ### This occurs if the 
    if cstride==0:
        print "Warning, dataset is too small to accomodate c_iso of %i, setting to 1."%c_iso
        cstride=1
    if rstride==0:
        print "Warning, dataset is too small to accomodate r_iso of %i, setting to 1."%r_iso        
        rstride=1 #to prevent errors
    
    ### If these values are already passed in, they won't be overwritten.  Cmap here is for surface, not countour
    _surface_defaults={'alpha':0.2, 'rstride':rstride, 'cstride':cstride, 'cmap':cmget('autumn')} #Don't have any special
    _surface_defaults.update(pltkwds)
    pltkwds=_surface_defaults #Need to do this in two steps or it errors

    ax.plot_surface(xx,yy,df, *args, **pltkwds)   

    if kind=='contourf' or kind=='contour':           
        if kind=='contourf':
            cfunc=ax.contourf
        else:
            cfunc=ax.contour
            
        if proj_xy:
            cset = cfunc(xx, yy, df, zdir='z', colors=contour_color, cmap=contour_cmap, offset=zlim[0])   #project z onto xy (zmid to bottom)
    
        if proj_zy:
            cset = cfunc(xx, yy, df, zdir='x',colors=contour_color, cmap=contour_cmap, offset=xlim[0]) #project x onto zy (timestart)  (negative half time interval)

        if proj_xz:
            cset = cfunc(xx, yy, df, zdir='y',colors=contour_color, cmap=contour_cmap, offset=ylim[1]) #project y onto xz (ymid to 0)  (negative mid wavelength)

    else:    
        raise badvalue_error(kind, 'contour, contourf')
   
    ax.set_xlabel(xlabel)        #x 
    ax.set_ylabel(ylabel)  #Y
    ax.set_zlabel(zlabel)   #data     
    ax.set_title(title)       
    
    ax.view_init(elev, azim) 
    
    return ax