signature:
def checkplot_png ( lspinfo , times , mags , errs , varepoch = 'min' , magsarefluxes = False , objectinfo = None , findercmap = 'gray_r' , finderconvolve = None , findercachedir = '~/.astrobase/stamp-cache' , normto = 'globalmedian' , normmingap = 4.0 , sigclip = 4.0 , phasewrap = True , phasesort = True , phasebin = 0.002 , minbinelems = 7 , plotxlim = ( - 0.8 , 0.8 ) , xliminsetmode = False , bestperiodhighlight = None , plotdpi = 100 , outfile = None , verbose = True ) :
    '''This makes a checkplot PNG using the output from a period - finder routine .
 A checkplot is a 3 x 3 grid of plots like so : :
 [ periodogram + objectinfo ] [ unphased LC ] [ period 1 phased LC ]
 [ period 1 phased LC / 2 ] [ period 1 phased LC x2 ] [ period 2 phased LC ]
 [ period 3 phased LC ] [ period 4 phased LC ] [ period 5 phased LC ]
 This is used to sanity check the five best periods obtained from a
 period - finder function in ` astrobase . periodbase ` or from your own
 period - finder routines if their results can be turned into a dict with the
 format shown below .
 Parameters
 lspinfo : dict or str
 If this is a dict , it must be a dict produced by an
 ` astrobase . periodbase ` period - finder function or a dict from your own
 period - finder function or routine that is of the form below with at
 least these keys : :
 { ' periods ' : np . array of all periods searched by the period - finder ,
 ' lspvals ' : np . array of periodogram power value for each period ,
 ' bestperiod ' : a float value that is the period with the highest
 peak in the periodogram , i . e . the most - likely actual
 period ,
 ' method ' : a three - letter code naming the period - finder used ; must
 be one of the keys in the
 ` astrobase . periodbase . METHODLABELS ` dict ,
 ' nbestperiods ' : a list of the periods corresponding to periodogram
 peaks ( ` nbestlspvals ` below ) to annotate on the
 periodogram plot so they can be called out
 visually ,
 ' nbestlspvals ' : a list of the power values associated with
 periodogram peaks to annotate on the periodogram
 plot so they can be called out visually ; should be
 the same length as ` nbestperiods ` above }
 ` nbestperiods ` and ` nbestlspvals ` must have at least 5 elements each ,
 e . g . describing the five ' best ' ( highest power ) peaks in the
 periodogram .
 If lspinfo is a str , then it must be a path to a pickle file ( ending
 with the extension ' . pkl ' or ' . pkl . gz ' ) that contains a dict of the form
 described above .
 times , mags , errs : np . array
 The mag / flux time - series arrays to process along with associated errors .
 varepoch : ' min ' or float or None or list of lists
 This sets the time of minimum light finding strategy for the checkplot : :
 the epoch used for all phased
 If ` varepoch ` is None - > light curve plots will be
 ` min ( times ) ` .
 If ` varepoch = ' min ' ` - > automatic epoch finding for all
 periods using light curve fits .
 If varepoch is a single float - > this epoch will be used for all
 phased light curve plots
 If varepoch is a list of floats each epoch will be applied to
 with length = ` len ( nbestperiods ) + 2 ` - > the phased light curve for each
 from period - finder results period specifically
 If you use a list for varepoch , it must be of length
 ` len ( lspinfo [ ' nbestperiods ' ] ) + 2 ` , because we insert half and twice the
 period into the best periods list to make those phased LC plots .
 magsarefluxes : bool
 If True , indicates the input time - series is fluxes and not mags so the
 plot y - axis direction and range can be set appropriately .
 objectinfo : dict or None
 If provided , this is a dict containing information on the object whose
 light curve is being processed . This function will then be able to look
 up and download a finder chart for this object and write that to the
 output checkplot PNG image . The ` objectinfo ` dict must be of the form and
 contain at least the keys described below : :
 { ' objectid ' : the name of the object ,
 ' ra ' : the right ascension of the object in decimal degrees ,
 ' decl ' : the declination of the object in decimal degrees ,
 ' ndet ' : the number of observations of this object }
 You can also provide magnitudes and proper motions of the object using
 the following keys and the appropriate values in the ` objectinfo `
 dict . These will be used to calculate colors , total and reduced proper
 motion , etc . and display these in the output checkplot PNG .
 - SDSS mag keys : ' sdssu ' , ' sdssg ' , ' sdssr ' , ' sdssi ' , ' sdssz '
 - 2MASS mag keys : ' jmag ' , ' hmag ' , ' kmag '
 - Cousins mag keys : ' bmag ' , ' vmag '
 - GAIA specific keys : ' gmag ' , ' teff '
 - proper motion keys : ' pmra ' , ' pmdecl '
 findercmap : str or matplotlib . cm . ColorMap object
 The Colormap object to use for the finder chart image .
 finderconvolve : astropy . convolution . Kernel object or None
 If not None , the Kernel object to use for convolving the finder image .
 findercachedir : str
 The directory where the FITS finder images are downloaded and cached .
 normto : { ' globalmedian ' , ' zero ' } or a float
 This sets the normalization target : :
 ' globalmedian ' - > norms each mag to global median of the LC column
 ' zero ' - > norms each mag to zero
 a float - > norms each mag to this specified float value .
 normmingap : float
 This defines how much the difference between consecutive measurements is
 allowed to be to consider them as parts of different timegroups . By
 default it is set to 4.0 days .
 sigclip : float or int or sequence of two floats / ints or None
 If a single float or int , a symmetric sigma - clip will be performed using
 the number provided as the sigma - multiplier to cut out from the input
 time - series .
 If a list of two ints / floats is provided , the function will perform an
 ' asymmetric ' sigma - clip . The first element in this list is the sigma
 value to use for fainter flux / mag values ; the second element in this
 list is the sigma value to use for brighter flux / mag values . For
 example , ` sigclip = [ 10 . , 3 . ] ` , will sigclip out greater than 10 - sigma
 dimmings and greater than 3 - sigma brightenings . Here the meaning of
 " dimming " and " brightening " is set by * physics * ( not the magnitude
 system ) , which is why the ` magsarefluxes ` kwarg must be correctly set .
 If ` sigclip ` is None , no sigma - clipping will be performed , and the
 time - series ( with non - finite elems removed ) will be passed through to
 the output .
 phasewrap : bool
 If this is True , the phased time - series will be wrapped around phase
 0.0.
 phasesort : bool
 If this is True , the phased time - series will be sorted in phase .
 phasebin : float or None
 If this is provided , indicates the bin size to use to group together
 measurements closer than this amount in phase . This is in units of
 phase . The binned phased light curve will be overplotted on top of the
 phased light curve . Useful for when one has many measurement points and
 needs to pick out a small trend in an otherwise noisy phased light
 curve .
 minbinelems : int
 The minimum number of elements in each phase bin .
 plotxlim : sequence of two floats or None
 The x - axis limits to use when making the phased light curve plot . By
 default , this is ( - 0.8 , 0.8 ) , which places phase 0.0 at the center of
 the plot and covers approximately two cycles in phase to make any trends
 clear .
 xliminsetmode : bool
 If this is True , the generated phased light curve plot will use the
 values of ` plotxlim ` as the main plot x - axis limits ( i . e . zoomed - in if
 ` plotxlim ` is a range smaller than the full phase range ) , and will show
 the full phased light curve plot as an smaller inset . Useful for
 planetary transit light curves .
 bestperiodhighlight : str or None
 If not None , this is a str with a matplotlib color specification to use
 as the background color to highlight the phased light curve plot of the
 ' best ' period and epoch combination . If None , no highlight will be
 applied .
 outfile : str or None
 The file name of the file to save the checkplot to . If this is None ,
 will write to a file called ' checkplot . png ' in the current working
 directory .
 plotdpi : int
 Sets the resolution in DPI for PNG plots ( default = 100 ) .
 verbose : bool
 If False , turns off many of the informational messages . Useful for
 when an external function is driving lots of ` checkplot _ png ` calls .
 Returns
 str
 The file path to the generated checkplot PNG file .'''

implementation:
if not outfile and isinstance ( lspinfo , str ) : # generate the plot filename
    plotfpath = os . path . join ( os . path . dirname ( lspinfo ) , 'checkplot-%s.png' % ( os . path . basename ( lspinfo ) , ) )
elif outfile :
    plotfpath = outfile
else :
    plotfpath = 'checkplot.png'
# get the lspinfo from a pickle file transparently
if isinstance ( lspinfo , str ) and os . path . exists ( lspinfo ) :
    if verbose :
        LOGINFO ( 'loading LSP info from pickle %s' % lspinfo )
    if '.gz' in lspinfo :
        with gzip . open ( lspinfo , 'rb' ) as infd :
            lspinfo = pickle . load ( infd )
    else :
        with open ( lspinfo , 'rb' ) as infd :
            lspinfo = pickle . load ( infd )
# get the things to plot out of the data
if ( 'periods' in lspinfo and 'lspvals' in lspinfo and 'bestperiod' in lspinfo ) :
    bestperiod = lspinfo [ 'bestperiod' ]
    nbestperiods = lspinfo [ 'nbestperiods' ]
    lspmethod = lspinfo [ 'method' ]
else :
    LOGERROR ( 'could not understand lspinfo for this object, skipping...' )
    return None
if not npisfinite ( bestperiod ) :
    LOGWARNING ( 'no best period found for this object, skipping...' )
    return None
# initialize the plot
fig , axes = plt . subplots ( 3 , 3 )
axes = npravel ( axes )
# this is a full page plot
fig . set_size_inches ( 30 , 24 )
# # PLOT 1 is the LSP # #
_make_periodogram ( axes [ 0 ] , lspinfo , objectinfo , findercmap , finderconvolve , verbose = verbose , findercachedir = findercachedir )
# # NOW MAKE THE PHASED LIGHT CURVES # #
stimes , smags , serrs = sigclip_magseries ( times , mags , errs , magsarefluxes = magsarefluxes , sigclip = sigclip )
# take care of the normalization
if normto is not False :
    stimes , smags = normalize_magseries ( stimes , smags , normto = normto , magsarefluxes = magsarefluxes , mingap = normmingap )
# make sure we have some lightcurve points to plot after sigclip
if len ( stimes ) >= 50 : # # PLOT 2 is an unphased LC # #
    _make_magseries_plot ( axes [ 1 ] , stimes , smags , serrs , magsarefluxes = magsarefluxes )
    # # # NOW PLOT PHASED LCS # # #
    # make the plot for each best period
    lspbestperiods = nbestperiods [ : : ]
    lspperiodone = lspbestperiods [ 0 ]
    lspbestperiods . insert ( 1 , lspperiodone * 2.0 )
    lspbestperiods . insert ( 1 , lspperiodone * 0.5 )
    for periodind , varperiod in enumerate ( lspbestperiods ) : # make sure the best period phased LC plot stands out
        if periodind == 0 and bestperiodhighlight :
            if MPLVERSION >= ( 2 , 0 , 0 ) :
                axes [ periodind + 2 ] . set_facecolor ( bestperiodhighlight )
            else :
                axes [ periodind + 2 ] . set_axis_bgcolor ( bestperiodhighlight )
        _make_phased_magseries_plot ( axes [ periodind + 2 ] , periodind , stimes , smags , serrs , varperiod , varepoch , phasewrap , phasesort , phasebin , minbinelems , plotxlim , lspmethod , xliminsetmode = xliminsetmode , magsarefluxes = magsarefluxes , verbose = verbose )
    # end of plotting for each ax
    # save the plot to disk
    fig . set_tight_layout ( True )
    if plotfpath . endswith ( '.png' ) :
        fig . savefig ( plotfpath , dpi = plotdpi )
    else :
        fig . savefig ( plotfpath )
    plt . close ( 'all' )
    if verbose :
        LOGINFO ( 'checkplot done -> %s' % plotfpath )
    return plotfpath
# otherwise , there ' s no valid data for this plot
else :
    LOGWARNING ( 'no good data' )
    for periodind in range ( 5 ) :
        axes [ periodind + 2 ] . text ( 0.5 , 0.5 , ( 'no best aperture light curve available' ) , horizontalalignment = 'center' , verticalalignment = 'center' , transform = axes [ periodind + 2 ] . transAxes )
    fig . set_tight_layout ( True )
    if plotfpath . endswith ( '.png' ) :
        fig . savefig ( plotfpath , dpi = plotdpi )
    else :
        fig . savefig ( plotfpath )
    plt . close ( 'all' )
    if verbose :
        LOGINFO ( 'checkplot done -> %s' % plotfpath )
    return plotfpath


signature:
def twolsp_checkplot_png ( lspinfo1 , lspinfo2 , times , mags , errs , varepoch = 'min' , magsarefluxes = False , objectinfo = None , findercmap = 'gray_r' , finderconvolve = None , findercachedir = '~/.astrobase/stamp-cache' , normto = 'globalmedian' , normmingap = 4.0 , sigclip = 4.0 , phasewrap = True , phasesort = True , phasebin = 0.002 , minbinelems = 7 , plotxlim = ( - 0.8 , 0.8 ) , unphasedms = 2.0 , phasems = 2.0 , phasebinms = 4.0 , xliminsetmode = False , bestperiodhighlight = None , plotdpi = 100 , outfile = None , verbose = True ) :
    '''This makes a checkplot using results from two independent period - finders .
 Adapted from Luke Bouma ' s implementation of a similar function in his
 work . This makes a special checkplot that uses two lspinfo dictionaries ,
 from two independent period - finding methods . For EBs , it ' s probably best to
 use Stellingwerf PDM or Schwarzenberg - Czerny AoV as one of these , and the
 Box Least - squared Search method as the other one .
 The checkplot layout in this case is : :
 [ pgram1 + objectinfo ] [ pgram2 ] [ unphased LC ]
 [ pgram1 P1 phased LC ] [ pgram1 P2 phased LC ] [ pgram1 P3 phased LC ]
 [ pgram2 P1 phased LC ] [ pgram2 P2 phased LC ] [ pgram2 P3 phased LC ]
 where :
 - pgram1 is the plot for the periodogram in the lspinfo1 dict
 - pgram1 P1 , P2 , and P3 are the best three periods from lspinfo1
 - pgram2 is the plot for the periodogram in the lspinfo2 dict
 - pgram2 P1 , P2 , and P3 are the best three periods from lspinfo2
 Note that we take the output file name from lspinfo1 if lspinfo1 is a string
 filename pointing to a ( gzipped ) pickle containing the results dict from a
 period - finding routine similar to those in periodbase .
 Parameters
 lspinfo1 , lspinfo2 : dict or str
 If this is a dict , it must be a dict produced by an
 ` astrobase . periodbase ` period - finder function or a dict from your own
 period - finder function or routine that is of the form below with at
 least these keys : :
 { ' periods ' : np . array of all periods searched by the period - finder ,
 ' lspvals ' : np . array of periodogram power value for each period ,
 ' bestperiod ' : a float value that is the period with the highest
 peak in the periodogram , i . e . the most - likely actual
 period ,
 ' method ' : a three - letter code naming the period - finder used ; must
 be one of the keys in the
 ` astrobase . periodbase . METHODLABELS ` dict ,
 ' nbestperiods ' : a list of the periods corresponding to periodogram
 peaks ( ` nbestlspvals ` below ) to annotate on the
 periodogram plot so they can be called out
 visually ,
 ' nbestlspvals ' : a list of the power values associated with
 periodogram peaks to annotate on the periodogram
 plot so they can be called out visually ; should be
 the same length as ` nbestperiods ` above }
 ` nbestperiods ` and ` nbestlspvals ` must have at least 3 elements each ,
 e . g . describing the three ' best ' ( highest power ) peaks in the
 periodogram .
 If lspinfo is a str , then it must be a path to a pickle file ( ending
 with the extension ' . pkl ' or ' . pkl . gz ' ) that contains a dict of the form
 described above .
 times , mags , errs : np . array
 The mag / flux time - series arrays to process along with associated errors .
 varepoch : ' min ' or float or None or list of lists
 This sets the time of minimum light finding strategy for the checkplot : :
 the epoch used for all phased
 If ` varepoch ` is None - > light curve plots will be
 ` min ( times ) ` .
 If ` varepoch = ' min ' ` - > automatic epoch finding for all
 periods using light curve fits .
 If varepoch is a single float - > this epoch will be used for all
 phased light curve plots
 If varepoch is a list of floats each epoch will be applied to
 with length = ` len ( nbestperiods ) ` - > the phased light curve for each
 from period - finder results period specifically
 If you use a list for varepoch , it must be of length
 ` len ( lspinfo [ ' nbestperiods ' ] ) ` .
 magsarefluxes : bool
 If True , indicates the input time - series is fluxes and not mags so the
 plot y - axis direction and range can be set appropriately /
 objectinfo : dict or None
 If provided , this is a dict containing information on the object whose
 light curve is being processed . This function will then be able to look
 up and download a finder chart for this object and write that to the
 output checkplot PNG image . The ` objectinfo ` dict must be of the form and
 contain at least the keys described below : :
 { ' objectid ' : the name of the object ,
 ' ra ' : the right ascension of the object in decimal degrees ,
 ' decl ' : the declination of the object in decimal degrees ,
 ' ndet ' : the number of observations of this object }
 You can also provide magnitudes and proper motions of the object using
 the following keys and the appropriate values in the ` objectinfo `
 dict . These will be used to calculate colors , total and reduced proper
 motion , etc . and display these in the output checkplot PNG .
 - SDSS mag keys : ' sdssu ' , ' sdssg ' , ' sdssr ' , ' sdssi ' , ' sdssz '
 - 2MASS mag keys : ' jmag ' , ' hmag ' , ' kmag '
 - Cousins mag keys : ' bmag ' , ' vmag '
 - GAIA specific keys : ' gmag ' , ' teff '
 - proper motion keys : ' pmra ' , ' pmdecl '
 findercmap : str or matplotlib . cm . ColorMap object
 The Colormap object to use for the finder chart image .
 finderconvolve : astropy . convolution . Kernel object or None
 If not None , the Kernel object to use for convolving the finder image .
 findercachedir : str
 The directory where the FITS finder images are downloaded and cached .
 normto : { ' globalmedian ' , ' zero ' } or a float
 This sets the LC normalization target : :
 ' globalmedian ' - > norms each mag to global median of the LC column
 ' zero ' - > norms each mag to zero
 a float - > norms each mag to this specified float value .
 normmingap : float
 This defines how much the difference between consecutive measurements is
 allowed to be to consider them as parts of different timegroups . By
 default it is set to 4.0 days .
 sigclip : float or int or sequence of two floats / ints or None
 If a single float or int , a symmetric sigma - clip will be performed using
 the number provided as the sigma - multiplier to cut out from the input
 time - series .
 If a list of two ints / floats is provided , the function will perform an
 ' asymmetric ' sigma - clip . The first element in this list is the sigma
 value to use for fainter flux / mag values ; the second element in this
 list is the sigma value to use for brighter flux / mag values . For
 example , ` sigclip = [ 10 . , 3 . ] ` , will sigclip out greater than 10 - sigma
 dimmings and greater than 3 - sigma brightenings . Here the meaning of
 " dimming " and " brightening " is set by * physics * ( not the magnitude
 system ) , which is why the ` magsarefluxes ` kwarg must be correctly set .
 If ` sigclip ` is None , no sigma - clipping will be performed , and the
 time - series ( with non - finite elems removed ) will be passed through to
 the output .
 phasewrap : bool
 If this is True , the phased time - series will be wrapped around phase
 0.0.
 phasesort : bool
 If this is True , the phased time - series will be sorted in phase .
 phasebin : float or None
 If this is provided , indicates the bin size to use to group together
 measurements closer than this amount in phase . This is in units of
 phase . The binned phased light curve will be overplotted on top of the
 phased light curve . Useful for when one has many measurement points and
 needs to pick out a small trend in an otherwise noisy phased light
 curve .
 minbinelems : int
 The minimum number of elements in each phase bin .
 plotxlim : sequence of two floats or None
 The x - axis limits to use when making the phased light curve plot . By
 default , this is ( - 0.8 , 0.8 ) , which places phase 0.0 at the center of
 the plot and covers approximately two cycles in phase to make any trends
 clear .
 unphasedms : float
 The marker size to use for the main unphased light curve plot symbols .
 phasems : float
 The marker size to use for the main phased light curve plot symbols .
 phasebinms : float
 The marker size to use for the binned phased light curve plot symbols .
 xliminsetmode : bool
 If this is True , the generated phased light curve plot will use the
 values of ` plotxlim ` as the main plot x - axis limits ( i . e . zoomed - in if
 ` plotxlim ` is a range smaller than the full phase range ) , and will show
 the full phased light curve plot as an smaller inset . Useful for
 planetary transit light curves .
 bestperiodhighlight : str or None
 If not None , this is a str with a matplotlib color specification to use
 as the background color to highlight the phased light curve plot of the
 ' best ' period and epoch combination . If None , no highlight will be
 applied .
 outfile : str or None
 The file name of the file to save the checkplot to . If this is None ,
 will write to a file called ' checkplot . png ' in the current working
 directory .
 plotdpi : int
 Sets the resolution in DPI for PNG plots ( default = 100 ) .
 verbose : bool
 If False , turns off many of the informational messages . Useful for
 when an external function is driving lots of ` checkplot _ png ` calls .
 Returns
 str
 The file path to the generated checkplot PNG file .'''

implementation:
# generate the plot filename
if not outfile and isinstance ( lspinfo1 , str ) :
    plotfpath = os . path . join ( os . path . dirname ( lspinfo1 ) , 'twolsp-checkplot-%s.png' % ( os . path . basename ( lspinfo1 ) , ) )
elif outfile :
    plotfpath = outfile
else :
    plotfpath = 'twolsp-checkplot.png'
# get the first LSP from a pickle file transparently
if isinstance ( lspinfo1 , str ) and os . path . exists ( lspinfo1 ) :
    if verbose :
        LOGINFO ( 'loading LSP info from pickle %s' % lspinfo1 )
    if '.gz' in lspinfo1 :
        with gzip . open ( lspinfo1 , 'rb' ) as infd :
            lspinfo1 = pickle . load ( infd )
    else :
        with open ( lspinfo1 , 'rb' ) as infd :
            lspinfo1 = pickle . load ( infd )
# get the second LSP from a pickle file transparently
if isinstance ( lspinfo2 , str ) and os . path . exists ( lspinfo2 ) :
    if verbose :
        LOGINFO ( 'loading LSP info from pickle %s' % lspinfo2 )
    if '.gz' in lspinfo2 :
        with gzip . open ( lspinfo2 , 'rb' ) as infd :
            lspinfo2 = pickle . load ( infd )
    else :
        with open ( lspinfo2 , 'rb' ) as infd :
            lspinfo2 = pickle . load ( infd )
# get the things to plot out of the data
if ( 'periods' in lspinfo1 and 'periods' in lspinfo2 and 'lspvals' in lspinfo1 and 'lspvals' in lspinfo2 and 'bestperiod' in lspinfo1 and 'bestperiod' in lspinfo2 ) :
    bestperiod1 = lspinfo1 [ 'bestperiod' ]
    nbestperiods1 = lspinfo1 [ 'nbestperiods' ]
    lspmethod1 = lspinfo1 [ 'method' ]
    bestperiod2 = lspinfo2 [ 'bestperiod' ]
    nbestperiods2 = lspinfo2 [ 'nbestperiods' ]
    lspmethod2 = lspinfo2 [ 'method' ]
else :
    LOGERROR ( 'could not understand lspinfo1 or lspinfo2 ' 'for this object, skipping...' )
    return None
if ( not npisfinite ( bestperiod1 ) ) or ( not npisfinite ( bestperiod2 ) ) :
    LOGWARNING ( 'no best period found for this object, skipping...' )
    return None
# initialize the plot
fig , axes = plt . subplots ( 3 , 3 )
axes = npravel ( axes )
# this is a full page plot
fig . set_size_inches ( 30 , 24 )
# # PLOT 1 is the LSP from lspinfo1 , including objectinfo and finder # #
_make_periodogram ( axes [ 0 ] , lspinfo1 , objectinfo , findercmap , finderconvolve , verbose = verbose , findercachedir = findercachedir )
# # PLOT 2 is the LSP from lspinfo2 # #
_make_periodogram ( axes [ 1 ] , lspinfo2 , None , findercmap , finderconvolve )
# # FIX UP THE MAGS AND REMOVE BAD STUFF # #
# sigclip first
stimes , smags , serrs = sigclip_magseries ( times , mags , errs , magsarefluxes = magsarefluxes , sigclip = sigclip )
# take care of the normalization
if normto is not False :
    stimes , smags = normalize_magseries ( stimes , smags , normto = normto , magsarefluxes = magsarefluxes , mingap = normmingap )
# make sure we have some lightcurve points to plot after sigclip
if len ( stimes ) >= 50 : # # PLOT 3 is an unphased LC # #
    _make_magseries_plot ( axes [ 2 ] , stimes , smags , serrs , magsarefluxes = magsarefluxes , ms = unphasedms )
    # make the plot for each best period
    lspbestperiods1 = nbestperiods1 [ : : ]
    lspbestperiods2 = nbestperiods2 [ : : ]
    # # # NOW PLOT PHASED LCS FOR 3 BEST PERIODS IN LSPINFO1 # # #
    for periodind , varperiod , plotaxes in zip ( [ 0 , 1 , 2 ] , lspbestperiods1 [ : 3 ] , [ axes [ 3 ] , axes [ 4 ] , axes [ 5 ] ] ) : # make sure the best period phased LC plot stands out
        if periodind == 0 and bestperiodhighlight :
            if MPLVERSION >= ( 2 , 0 , 0 ) :
                plotaxes . set_facecolor ( bestperiodhighlight )
            else :
                plotaxes . set_axis_bgcolor ( bestperiodhighlight )
        _make_phased_magseries_plot ( plotaxes , periodind , stimes , smags , serrs , varperiod , varepoch , phasewrap , phasesort , phasebin , minbinelems , plotxlim , lspmethod1 , lspmethodind = 0 , twolspmode = True , magsarefluxes = magsarefluxes , xliminsetmode = xliminsetmode , verbose = verbose , phasems = phasems , phasebinms = phasebinms )
    # # # NOW PLOT PHASED LCS FOR 3 BEST PERIODS IN LSPINFO2 # # #
    for periodind , varperiod , plotaxes in zip ( [ 0 , 1 , 2 ] , lspbestperiods2 [ : 3 ] , [ axes [ 6 ] , axes [ 7 ] , axes [ 8 ] ] ) : # make sure the best period phased LC plot stands out
        if periodind == 0 and bestperiodhighlight :
            if MPLVERSION >= ( 2 , 0 , 0 ) :
                plotaxes . set_facecolor ( bestperiodhighlight )
            else :
                plotaxes . set_axis_bgcolor ( bestperiodhighlight )
        _make_phased_magseries_plot ( plotaxes , periodind , stimes , smags , serrs , varperiod , varepoch , phasewrap , phasesort , phasebin , minbinelems , plotxlim , lspmethod2 , lspmethodind = 1 , twolspmode = True , magsarefluxes = magsarefluxes , xliminsetmode = xliminsetmode , verbose = verbose , phasems = phasems , phasebinms = phasebinms )
    # end of plotting for each ax
    # save the plot to disk
    fig . set_tight_layout ( True )
    if plotfpath . endswith ( '.png' ) :
        fig . savefig ( plotfpath , dpi = plotdpi )
    else :
        fig . savefig ( plotfpath )
    plt . close ( )
    if verbose :
        LOGINFO ( 'checkplot done -> %s' % plotfpath )
    return plotfpath
# otherwise , there ' s no valid data for this plot
else :
    LOGWARNING ( 'no good data' )
    for periodind in range ( 5 ) :
        axes [ periodind + 2 ] . text ( 0.5 , 0.5 , ( 'no best aperture light curve available' ) , horizontalalignment = 'center' , verticalalignment = 'center' , transform = axes [ periodind + 2 ] . transAxes )
    fig . set_tight_layout ( True )
    if plotfpath . endswith ( '.png' ) :
        fig . savefig ( plotfpath , dpi = plotdpi )
    else :
        fig . savefig ( plotfpath )
    plt . close ( )
    if verbose :
        LOGINFO ( 'checkplot done -> %s' % plotfpath )
    return plotfpath


signature:
def precess_coordinates ( ra , dec , epoch_one , epoch_two , jd = None , mu_ra = 0.0 , mu_dec = 0.0 , outscalar = False ) :
    '''Precesses target coordinates ` ra ` , ` dec ` from ` epoch _ one ` to ` epoch _ two ` .
 This takes into account the jd of the observations , as well as the proper
 motion of the target mu _ ra , mu _ dec . Adapted from J . D . Hartman ' s
 VARTOOLS / converttime . c [ coordprecess ] .
 Parameters
 ra , dec : float
 The equatorial coordinates of the object at ` epoch _ one ` to precess in
 decimal degrees .
 epoch _ one : float
 Origin epoch to precess from to target epoch . This is a float , like :
 1985.0 , 2000.0 , etc .
 epoch _ two : float
 Target epoch to precess from origin epoch . This is a float , like :
 2000.0 , 2018.0 , etc .
 jd : float
 The full Julian date to use along with the propermotions in ` mu _ ra ` , and
 ` mu _ dec ` to handle proper motion along with the coordinate frame
 precession . If one of ` jd ` , ` mu _ ra ` , or ` mu _ dec ` is missing , the proper
 motion will not be used to calculate the final precessed coordinates .
 mu _ ra , mu _ dec : float
 The proper motion in mas / yr in right ascension and declination . If these
 are provided along with ` jd ` , the total proper motion of the object will
 be taken into account to calculate the final precessed coordinates .
 outscalar : bool
 If True , converts the output coordinates from one - element np . arrays to
 scalars .
 Returns
 precessed _ ra , precessed _ dec : float
 A tuple of precessed equatorial coordinates in decimal degrees at
 ` epoch _ two ` taking into account proper motion if ` jd ` , ` mu _ ra ` , and
 ` mu _ dec ` are provided .'''

implementation:
raproc , decproc = np . radians ( ra ) , np . radians ( dec )
if ( ( mu_ra != 0.0 ) and ( mu_dec != 0.0 ) and jd ) :
    jd_epoch_one = JD2000 + ( epoch_one - epoch_two ) * 365.25
    raproc = ( raproc + ( jd - jd_epoch_one ) * mu_ra * MAS_P_YR_TO_RAD_P_DAY / np . cos ( decproc ) )
    decproc = decproc + ( jd - jd_epoch_one ) * mu_dec * MAS_P_YR_TO_RAD_P_DAY
ca = np . cos ( raproc )
cd = np . cos ( decproc )
sa = np . sin ( raproc )
sd = np . sin ( decproc )
if epoch_one != epoch_two :
    t1 = 1.0e-3 * ( epoch_two - epoch_one )
    t2 = 1.0e-3 * ( epoch_one - 2000.0 )
    a = ( t1 * ARCSEC_TO_RADIANS * ( 23062.181 + t2 * ( 139.656 + 0.0139 * t2 ) + t1 * ( 30.188 - 0.344 * t2 + 17.998 * t1 ) ) )
    b = t1 * t1 * ARCSEC_TO_RADIANS * ( 79.280 + 0.410 * t2 + 0.205 * t1 ) + a
    c = ( ARCSEC_TO_RADIANS * t1 * ( 20043.109 - t2 * ( 85.33 + 0.217 * t2 ) + t1 * ( - 42.665 - 0.217 * t2 - 41.833 * t2 ) ) )
    sina , sinb , sinc = np . sin ( a ) , np . sin ( b ) , np . sin ( c )
    cosa , cosb , cosc = np . cos ( a ) , np . cos ( b ) , np . cos ( c )
    precmatrix = np . matrix ( [ [ cosa * cosb * cosc - sina * sinb , sina * cosb + cosa * sinb * cosc , cosa * sinc ] , [ - cosa * sinb - sina * cosb * cosc , cosa * cosb - sina * sinb * cosc , - sina * sinc ] , [ - cosb * sinc , - sinb * sinc , cosc ] ] )
    precmatrix = precmatrix . transpose ( )
    x = ( np . matrix ( [ cd * ca , cd * sa , sd ] ) ) . transpose ( )
    x2 = precmatrix * x
    outra = np . arctan2 ( x2 [ 1 ] , x2 [ 0 ] )
    outdec = np . arcsin ( x2 [ 2 ] )
    outradeg = np . rad2deg ( outra )
    outdecdeg = np . rad2deg ( outdec )
    if outradeg < 0.0 :
        outradeg = outradeg + 360.0
    if outscalar :
        return float ( outradeg ) , float ( outdecdeg )
    else :
        return outradeg , outdecdeg
else : # if the epochs are the same and no proper motion , this will be the same
# as the input values . if the epochs are the same , but there IS proper
# motion ( and a given JD ) , then these will be perturbed from the input
# values of ra , dec by the appropriate amount of motion
    return np . degrees ( raproc ) , np . degrees ( decproc )


signature:
def _single_true ( iterable ) :
    '''This returns True if only one True - ish element exists in ` iterable ` .
 Parameters
 iterable : iterable
 Returns
 bool
 True if only one True - ish element exists in ` iterable ` . False otherwise .'''

implementation:
# return True if exactly one true found
iterator = iter ( iterable )
# consume from " i " until first true or it ' s exhausted
has_true = any ( iterator )
# carry on consuming until another true value / exhausted
has_another_true = any ( iterator )
return has_true and not has_another_true


signature:
def get_epochs_given_midtimes_and_period ( t_mid , period , err_t_mid = None , t0_fixed = None , t0_percentile = None , verbose = False ) :
    '''This calculates the future epochs for a transit , given a period and a
 starting epoch
 The equation used is : :
 t _ mid = period * epoch + t0
 Default behavior if no kwargs are used is to define ` t0 ` as the median
 finite time of the passed ` t _ mid ` array .
 Only one of ` err _ t _ mid ` or ` t0 _ fixed ` should be passed .
 Parameters
 t _ mid : np . array
 A np . array of transit mid - time measurements
 period : float
 The period used to calculate epochs , per the equation above . For typical
 use cases , a period precise to ~ 1e - 5 days is sufficient to get correct
 epochs .
 err _ t _ mid : None or np . array
 If provided , contains the errors of the transit mid - time
 measurements . The zero - point epoch is then set equal to the average of
 the transit times , weighted as ` 1 / err _ t _ mid ^ 2 ` . This minimizes the
 covariance between the transit epoch and the period ( e . g . , Gibson et
 al . 2013 ) . For standard O - C analysis this is the best method .
 t0 _ fixed : None or float :
 If provided , use this t0 as the starting epoch . ( Overrides all others ) .
 t0 _ percentile : None or float
 If provided , use this percentile of ` t _ mid ` to define ` t0 ` .
 Returns
 tuple
 This is the of the form ` ( integer _ epoch _ array , t0 ) ` .
 ` integer _ epoch _ array ` is an array of integer epochs ( float - type ) ,
 of length equal to the number of * finite * mid - times passed .'''

implementation:
kwargarr = np . array ( [ isinstance ( err_t_mid , np . ndarray ) , t0_fixed , t0_percentile ] )
if not _single_true ( kwargarr ) and not np . all ( ~ kwargarr . astype ( bool ) ) :
    raise AssertionError ( 'can have at most one of err_t_mid, t0_fixed, t0_percentile' )
t_mid = t_mid [ np . isfinite ( t_mid ) ]
N_midtimes = len ( t_mid )
if t0_fixed :
    t0 = t0_fixed
elif isinstance ( err_t_mid , np . ndarray ) : # get the weighted average . then round it to the nearest transit epoch .
    t0_avg = np . average ( t_mid , weights = 1 / err_t_mid ** 2 )
    t0_options = np . arange ( min ( t_mid ) , max ( t_mid ) + period , period )
    t0 = t0_options [ np . argmin ( np . abs ( t0_options - t0_avg ) ) ]
else :
    if not t0_percentile : # if there are an odd number of times , take the median time as
    # epoch = 0 . elif there are an even number of times , take the lower
    # of the two middle times as epoch = 0.
        if N_midtimes % 2 == 1 :
            t0 = np . median ( t_mid )
        else :
            t0 = t_mid [ int ( N_midtimes / 2 ) ]
    else :
        t0 = np . sort ( t_mid ) [ int ( N_midtimes * t0_percentile / 100 ) ]
epoch = ( t_mid - t0 ) / period
# do not convert numpy entries to actual ints , because np . nan is float type
int_epoch = np . round ( epoch , 0 )
if verbose :
    LOGINFO ( 'epochs before rounding' )
    LOGINFO ( '\n{:s}' . format ( repr ( epoch ) ) )
    LOGINFO ( 'epochs after rounding' )
    LOGINFO ( '\n{:s}' . format ( repr ( int_epoch ) ) )
return int_epoch , t0


signature:
def unixtime_to_jd ( unix_time ) :
    '''This converts UNIX time in seconds to a Julian date in UTC ( JD _ UTC ) .
 Parameters
 unix _ time : float
 A UNIX time in decimal seconds since the 1970 UNIX epoch .
 Returns
 jd : float
 The Julian date corresponding to the provided UNIX time .'''

implementation:
# use astropy ' s time module
jdutc = astime . Time ( unix_time , format = 'unix' , scale = 'utc' )
return jdutc . jd


signature:
def datetime_to_jd ( dt ) :
    '''This converts a Python datetime object ( naive , time in UT ) to JD _ UTC .
 Parameters
 dt : datetime
 A naive Python ` datetime ` object ( e . g . with no tz attribute ) measured at
 UTC .
 Returns
 jd : float
 The Julian date corresponding to the ` datetime ` object .'''

implementation:
jdutc = astime . Time ( dt , format = 'datetime' , scale = 'utc' )
return jdutc . jd


signature:
def jd_to_datetime ( jd , returniso = False ) :
    '''This converts a UTC JD to a Python ` datetime ` object or ISO date string .
 Parameters
 jd : float
 The Julian date measured at UTC .
 returniso : bool
 If False , returns a naive Python ` datetime ` object corresponding to
 ` jd ` . If True , returns the ISO format string corresponding to the date
 and time at UTC from ` jd ` .
 Returns
 datetime or str
 Depending on the value of ` returniso ` .'''

implementation:
tt = astime . Time ( jd , format = 'jd' , scale = 'utc' )
if returniso :
    return tt . iso
else :
    return tt . datetime


signature:
def jd_corr ( jd , ra , dec , obslon = None , obslat = None , obsalt = None , jd_type = 'bjd' ) :
    '''Returns BJD _ TDB or HJD _ TDB for input JD _ UTC .
 The equation used is : :
 BJD _ TDB = JD _ UTC + JD _ to _ TDB _ corr + romer _ delay
 where :
 - JD _ to _ TDB _ corr is the difference between UTC and TDB JDs
 - romer _ delay is the delay caused by finite speed of light from Earth - Sun
 This is based on the code at :
 https : / / mail . scipy . org / pipermail / astropy / 2014 - April / 003148 . html
 Note that this does not correct for :
 1 . precession of coordinates if the epoch is not 2000.0
 2 . precession of coordinates if the target has a proper motion
 3 . Shapiro delay
 4 . Einstein delay
 Parameters
 jd : float or array - like
 The Julian date ( s ) measured at UTC .
 ra , dec : float
 The equatorial coordinates of the object in decimal degrees .
 obslon , obslat , obsalt : float or None
 The longitude , latitude of the observatory in decimal degrees and
 altitude of the observatory in meters . If these are not provided , the
 corrected JD will be calculated with respect to the center of the Earth .
 jd _ type : { ' bjd ' , ' hjd ' }
 Conversion type to perform , either to Baryocentric Julian Date ( ' bjd ' )
 or to Heliocenter Julian Date ( ' hjd ' ) .
 Returns
 float or np . array
 The converted BJD or HJD .'''

implementation:
if not HAVEKERNEL :
    LOGERROR ( 'no JPL kernel available, can\'t continue!' )
    return
# Source unit - vector
# # Assume coordinates in ICRS
# # Set distance to unit ( kilometers )
# convert the angles to degrees
rarad = np . radians ( ra )
decrad = np . radians ( dec )
cosra = np . cos ( rarad )
sinra = np . sin ( rarad )
cosdec = np . cos ( decrad )
sindec = np . sin ( decrad )
# this assumes that the target is very far away
src_unitvector = np . array ( [ cosdec * cosra , cosdec * sinra , sindec ] )
# Convert epochs to astropy . time . Time
# # Assume JD ( UTC )
if ( obslon is None ) or ( obslat is None ) or ( obsalt is None ) :
    t = astime . Time ( jd , scale = 'utc' , format = 'jd' )
else :
    t = astime . Time ( jd , scale = 'utc' , format = 'jd' , location = ( '%.5fd' % obslon , '%.5fd' % obslat , obsalt ) )
# Get Earth - Moon barycenter position
# # NB : jplephem uses Barycentric Dynamical Time , e . g . JD ( TDB )
# # and gives positions relative to solar system barycenter
barycenter_earthmoon = jplkernel [ 0 , 3 ] . compute ( t . tdb . jd )
# Get Moon position vectors from the center of Earth to the Moon
# this means we get the following vectors from the ephemerides
# Earth Barycenter ( 3 ) - > Moon ( 301)
# Earth Barycenter ( 3 ) - > Earth ( 399)
# so the final vector is [ 3,301 ] - [ 3,399]
# units are in km
moonvector = ( jplkernel [ 3 , 301 ] . compute ( t . tdb . jd ) - jplkernel [ 3 , 399 ] . compute ( t . tdb . jd ) )
# Compute Earth position vectors ( this is for the center of the earth with
# respect to the solar system barycenter )
# all these units are in km
pos_earth = ( barycenter_earthmoon - moonvector * 1.0 / ( 1.0 + EMRAT ) )
if jd_type == 'bjd' : # Compute BJD correction
# # Assume source vectors parallel at Earth and Solar System
# # Barycenter
# # i . e . source is at infinity
# the romer _ delay correction is ( r . dot . n ) / c where :
# r is the vector from SSB to earth center
# n is the unit vector from
    correction_seconds = np . dot ( pos_earth . T , src_unitvector ) / CLIGHT_KPS
    correction_days = correction_seconds / SEC_P_DAY
elif jd_type == 'hjd' : # Compute HJD correction via Sun ephemeris
# this is the position vector of the center of the sun in km
# Solar System Barycenter ( 0 ) - > Sun ( 10)
    pos_sun = jplkernel [ 0 , 10 ] . compute ( t . tdb . jd )
    # this is the vector from the center of the sun to the center of the
    # earth
    sun_earth_vec = pos_earth - pos_sun
    # calculate the heliocentric correction
    correction_seconds = np . dot ( sun_earth_vec . T , src_unitvector ) / CLIGHT_KPS
    correction_days = correction_seconds / SEC_P_DAY
# TDB is the appropriate time scale for these ephemerides
new_jd = t . tdb . jd + correction_days
return new_jd


signature:
def _lclist_parallel_worker ( task ) :
    '''This is a parallel worker for makelclist .
 Parameters
 task : tuple
 This is a tuple containing the following items :
 task [ 0 ] = lcf
 task [ 1 ] = columns
 task [ 2 ] = lcformat
 task [ 3 ] = lcformatdir
 task [ 4 ] = lcndetkey
 Returns
 dict or None
 This contains all of the info for the object processed in this LC read
 operation . If this fails , returns None'''

implementation:
lcf , columns , lcformat , lcformatdir , lcndetkey = task
# get the bits needed for lcformat handling
# NOTE : we re - import things in this worker function because sometimes
# functions can ' t be pickled correctly for passing them to worker functions
# in a processing pool
try :
    formatinfo = get_lcformat ( lcformat , use_lcformat_dir = lcformatdir )
    if formatinfo :
        ( dfileglob , readerfunc , dtimecols , dmagcols , derrcols , magsarefluxes , normfunc ) = formatinfo
    else :
        LOGERROR ( "can't figure out the light curve format" )
        return None
except Exception as e :
    LOGEXCEPTION ( "can't figure out the light curve format" )
    return None
# we store the full path of the light curve
lcobjdict = { 'lcfname' : os . path . abspath ( lcf ) }
try : # read the light curve in
    lcdict = readerfunc ( lcf )
    # this should handle lists / tuples being returned by readerfunc
    # we assume that the first element is the actual lcdict
    # FIXME : figure out how to not need this assumption
    if ( ( isinstance ( lcdict , ( list , tuple ) ) ) and ( isinstance ( lcdict [ 0 ] , dict ) ) ) :
        lcdict = lcdict [ 0 ]
    # insert all of the columns
    for colkey in columns :
        if '.' in colkey :
            getkey = colkey . split ( '.' )
        else :
            getkey = [ colkey ]
        try :
            thiscolval = _dict_get ( lcdict , getkey )
        except Exception as e :
            LOGWARNING ( 'column %s does not exist for %s' % ( colkey , lcf ) )
            thiscolval = np . nan
        # update the lcobjdict with this value
        lcobjdict [ getkey [ - 1 ] ] = thiscolval
except Exception as e :
    LOGEXCEPTION ( 'could not figure out columns for %s' % lcf )
    # insert all of the columns as nans
    for colkey in columns :
        if '.' in colkey :
            getkey = colkey . split ( '.' )
        else :
            getkey = [ colkey ]
        thiscolval = np . nan
        # update the lclistdict with this value
        lcobjdict [ getkey [ - 1 ] ] = thiscolval
# now get the actual ndets ; this excludes nans and infs
for dk in lcndetkey :
    try :
        if '.' in dk :
            getdk = dk . split ( '.' )
        else :
            getdk = [ dk ]
        ndetcol = _dict_get ( lcdict , getdk )
        actualndets = ndetcol [ np . isfinite ( ndetcol ) ] . size
        lcobjdict [ '%s.ndet' % getdk [ - 1 ] ] = actualndets
    except Exception as e :
        lcobjdict [ '%s.ndet' % getdk [ - 1 ] ] = np . nan
return lcobjdict


signature:
def make_lclist ( basedir , outfile , use_list_of_filenames = None , lcformat = 'hat-sql' , lcformatdir = None , fileglob = None , recursive = True , columns = [ 'objectid' , 'objectinfo.ra' , 'objectinfo.decl' , 'objectinfo.ndet' ] , makecoordindex = ( 'objectinfo.ra' , 'objectinfo.decl' ) , field_fitsfile = None , field_wcsfrom = None , field_scale = ZScaleInterval ( ) , field_stretch = LinearStretch ( ) , field_colormap = plt . cm . gray_r , field_findersize = None , field_pltopts = { 'marker' : 'o' , 'markersize' : 10.0 , 'markerfacecolor' : 'none' , 'markeredgewidth' : 2.0 , 'markeredgecolor' : 'red' } , field_grid = False , field_gridcolor = 'k' , field_zoomcontain = True , maxlcs = None , nworkers = NCPUS ) :
    '''This generates a light curve catalog for all light curves in a directory .
 Given a base directory where all the files are , and a light curve format ,
 this will find all light curves , pull out the keys in each lcdict requested
 in the ` columns ` kwarg for each object , and write them to the requested
 output pickle file . These keys should be pointers to scalar values
 ( i . e . something like ` objectinfo . ra ` is OK , but something like ' times ' won ' t
 work because it ' s a vector ) .
 Generally , this works with light curve reading functions that produce
 lcdicts as detailed in the docstring for ` lcproc . register _ lcformat ` . Once
 you ' ve registered your light curve reader functions using the
 ` lcproc . register _ lcformat ` function , pass in the ` formatkey ` associated with
 your light curve format , and this function will be able to read all light
 curves in that format as well as the object information stored in their
 ` objectinfo ` dict .
 Parameters
 basedir : str or list of str
 If this is a str , points to a single directory to search for light
 curves . If this is a list of str , it must be a list of directories to
 search for light curves . All of these will be searched to find light
 curve files matching either your light curve format ' s default fileglob
 ( when you registered your LC format ) , or a specific fileglob that you
 can pass in using the ` fileglob ` kwargh here . If the ` recursive ` kwarg
 is set , the provided directories will be searched recursively .
 If ` use _ list _ of _ filenames ` is not None , it will override this argument
 and the function will take those light curves as the list of files it
 must process instead of whatever is specified in ` basedir ` .
 outfile : str
 This is the name of the output file to write . This will be a pickle
 file , so a good convention to use for this name is something like
 ' my - lightcurve - catalog . pkl ' .
 use _ list _ of _ filenames : list of str or None
 Use this kwarg to override whatever is provided in ` basedir ` and
 directly pass in a list of light curve files to process . This can speed
 up this function by a lot because no searches on disk will be performed
 to find light curve files matching ` basedir ` and ` fileglob ` .
 lcformat : str
 This is the ` formatkey ` associated with your light curve format , which
 you previously passed in to the ` lcproc . register _ lcformat `
 function . This will be used to look up how to find and read the light
 curves specified in ` basedir ` or ` use _ list _ of _ filenames ` .
 lcformatdir : str or None
 If this is provided , gives the path to a directory when you ' ve stored
 your lcformat description JSONs , other than the usual directories lcproc
 knows to search for them in . Use this along with ` lcformat ` to specify
 an LC format JSON file that ' s not currently registered with lcproc .
 fileglob : str or None
 If provided , is a string that is a valid UNIX filename glob . Used to
 override the default fileglob for this LC format when searching for
 light curve files in ` basedir ` .
 recursive : bool
 If True , the directories specified in ` basedir ` will be searched
 recursively for all light curve files that match the default fileglob
 for this LC format or a specific one provided in ` fileglob ` .
 columns : list of str
 This is a list of keys in the lcdict produced by your light curve reader
 function that contain object information , which will be extracted and
 put into the output light curve catalog . It ' s highly recommended that
 your LC reader function produce a lcdict that contains at least the
 default keys shown here .
 The lcdict keys to extract are specified by using an address scheme :
 - First level dict keys can be specified directly :
 e . g . , ' objectid ' will extract lcdict [ ' objectid ' ]
 - Keys at other levels can be specified by using a period to indicate
 the level :
 - e . g . , ' objectinfo . ra ' will extract lcdict [ ' objectinfo ' ] [ ' ra ' ]
 - e . g . , ' objectinfo . varinfo . features . stetsonj ' will extract
 lcdict [ ' objectinfo ' ] [ ' varinfo ' ] [ ' features ' ] [ ' stetsonj ' ]
 makecoordindex : list of two str or None
 This is used to specify which lcdict keys contain the right ascension
 and declination coordinates for this object . If these are provided , the
 output light curve catalog will have a kdtree built on all object
 coordinates , which enables fast spatial searches and cross - matching to
 external catalogs by ` checkplot ` and ` lcproc ` functions .
 field _ fitsfile : str or None
 If this is not None , it should be the path to a FITS image containing
 the objects these light curves are for . If this is provided ,
 ` make _ lclist ` will use the WCS information in the FITS itself if
 ` field _ wcsfrom ` is None ( or from a WCS header file pointed to by
 ` field _ wcsfrom ` ) to obtain x and y pixel coordinates for all of the
 objects in the field . A finder chart will also be made using
 ` astrobase . plotbase . fits _ finder _ chart ` using the corresponding
 ` field _ scale ` , ` _ stretch ` , ` _ colormap ` , ` _ findersize ` , ` _ pltopts ` ,
 ` _ grid ` , and ` _ gridcolors ` kwargs for that function , reproduced here to
 enable customization of the finder chart plot .
 field _ wcsfrom : str or None
 If ` wcsfrom ` is None , the WCS to transform the RA / Dec to pixel x / y will
 be taken from the FITS header of ` fitsfile ` . If this is not None , it
 must be a FITS or similar file that contains a WCS header in its first
 extension .
 field _ scale : astropy . visualization . Interval object
 ` scale ` sets the normalization for the FITS pixel values . This is an
 astropy . visualization Interval object .
 See http : / / docs . astropy . org / en / stable / visualization / normalization . html
 for details on ` scale ` and ` stretch ` objects .
 field _ stretch : astropy . visualization . Stretch object
 ` stretch ` sets the stretch function for mapping FITS pixel values to
 output pixel values . This is an astropy . visualization Stretch object .
 See http : / / docs . astropy . org / en / stable / visualization / normalization . html
 for details on ` scale ` and ` stretch ` objects .
 field _ colormap : matplotlib Colormap object
 ` colormap ` is a matplotlib color map object to use for the output image .
 field _ findersize : None or tuple of two ints
 If ` findersize ` is None , the output image size will be set by the NAXIS1
 and NAXIS2 keywords in the input ` fitsfile ` FITS header . Otherwise ,
 ` findersize ` must be a tuple with the intended x and y size of the image
 in inches ( all output images will use a DPI = 100 ) .
 field _ pltopts : dict
 ` field _ pltopts ` controls how the overlay points will be plotted . This
 a dict with standard matplotlib marker , etc . kwargs as key - val pairs ,
 e . g . ' markersize ' , ' markerfacecolor ' , etc . The default options make red
 outline circles at the location of each object in the overlay .
 field _ grid : bool
 ` grid ` sets if a grid will be made on the output image .
 field _ gridcolor : str
 ` gridcolor ` sets the color of the grid lines . This is a usual matplotib
 color spec string .
 field _ zoomcontain : bool
 ` field _ zoomcontain ` controls if the finder chart will be zoomed to
 just contain the overlayed points . Everything outside the footprint of
 these points will be discarded .
 maxlcs : int or None
 This sets how many light curves to process in the input LC list
 generated by searching for LCs in ` basedir ` or in the list provided as
 ` use _ list _ of _ filenames ` .
 nworkers : int
 This sets the number of parallel workers to launch to collect
 information from the light curves .
 Returns
 str
 Returns the path to the generated light curve catalog pickle file .'''

implementation:
try :
    formatinfo = get_lcformat ( lcformat , use_lcformat_dir = lcformatdir )
    if formatinfo :
        ( dfileglob , readerfunc , dtimecols , dmagcols , derrcols , magsarefluxes , normfunc ) = formatinfo
    else :
        LOGERROR ( "can't figure out the light curve format" )
        return None
except Exception as e :
    LOGEXCEPTION ( "can't figure out the light curve format" )
    return None
if not fileglob :
    fileglob = dfileglob
# this is to get the actual ndet
# set to the magnitudes column
lcndetkey = dmagcols
if isinstance ( use_list_of_filenames , list ) :
    matching = use_list_of_filenames
else : # handle the case where basedir is a list of directories
    if isinstance ( basedir , list ) :
        matching = [ ]
        for bdir in basedir : # now find the files
            LOGINFO ( 'searching for %s light curves in %s ...' % ( lcformat , bdir ) )
            if recursive is False :
                matching . extend ( glob . glob ( os . path . join ( bdir , fileglob ) ) )
            else : # use recursive glob for Python 3.5 +
                if sys . version_info [ : 2 ] > ( 3 , 4 ) :
                    matching . extend ( glob . glob ( os . path . join ( bdir , '**' , fileglob ) , recursive = True ) )
                # otherwise , use os . walk and glob
                else : # use os . walk to go through the directories
                    walker = os . walk ( bdir )
                    for root , dirs , _files in walker :
                        for sdir in dirs :
                            searchpath = os . path . join ( root , sdir , fileglob )
                            foundfiles = glob . glob ( searchpath )
                            if foundfiles :
                                matching . extend ( foundfiles )
    # otherwise , handle the usual case of one basedir to search in
    else : # now find the files
        LOGINFO ( 'searching for %s light curves in %s ...' % ( lcformat , basedir ) )
        if recursive is False :
            matching = glob . glob ( os . path . join ( basedir , fileglob ) )
        else : # use recursive glob for Python 3.5 +
            if sys . version_info [ : 2 ] > ( 3 , 4 ) :
                matching = glob . glob ( os . path . join ( basedir , '**' , fileglob ) , recursive = True )
            # otherwise , use os . walk and glob
            else : # use os . walk to go through the directories
                walker = os . walk ( basedir )
                matching = [ ]
                for root , dirs , _files in walker :
                    for sdir in dirs :
                        searchpath = os . path . join ( root , sdir , fileglob )
                        foundfiles = glob . glob ( searchpath )
                        if foundfiles :
                            matching . extend ( foundfiles )
# now that we have all the files , process them
if matching and len ( matching ) > 0 :
    LOGINFO ( 'found %s light curves' % len ( matching ) )
    # cut down matching to maxlcs
    if maxlcs :
        matching = matching [ : maxlcs ]
    # prepare the output dict
    lclistdict = { 'basedir' : basedir , 'lcformat' : lcformat , 'fileglob' : fileglob , 'recursive' : recursive , 'columns' : columns , 'makecoordindex' : makecoordindex , 'nfiles' : len ( matching ) , 'objects' : { } }
    # columns that will always be present in the output lclistdict
    derefcols = [ 'lcfname' ]
    derefcols . extend ( [ '%s.ndet' % x . split ( '.' ) [ - 1 ] for x in lcndetkey ] )
    for dc in derefcols :
        lclistdict [ 'objects' ] [ dc ] = [ ]
    # fill in the rest of the lclist columns from the columns kwarg
    for col in columns : # dereference the column
        thiscol = col . split ( '.' )
        thiscol = thiscol [ - 1 ]
        lclistdict [ 'objects' ] [ thiscol ] = [ ]
        derefcols . append ( thiscol )
    # start collecting info
    LOGINFO ( 'collecting light curve info...' )
    tasks = [ ( x , columns , lcformat , lcformatdir , lcndetkey ) for x in matching ]
    with ProcessPoolExecutor ( max_workers = nworkers ) as executor :
        results = executor . map ( _lclist_parallel_worker , tasks )
    results = [ x for x in results ]
    # update the columns in the overall dict from the results of the
    # parallel map
    for result in results :
        for xcol in derefcols :
            lclistdict [ 'objects' ] [ xcol ] . append ( result [ xcol ] )
    executor . shutdown ( )
    # done with collecting info
    # turn all of the lists in the lclistdict into arrays
    for col in lclistdict [ 'objects' ] :
        lclistdict [ 'objects' ] [ col ] = np . array ( lclistdict [ 'objects' ] [ col ] )
    # handle duplicate objectids with different light curves
    uniques , counts = np . unique ( lclistdict [ 'objects' ] [ 'objectid' ] , return_counts = True )
    duplicated_objectids = uniques [ counts > 1 ]
    if duplicated_objectids . size > 0 : # redo the objectid array so it has a bit larger dtype so the extra
    # tag can fit into the field
        dt = lclistdict [ 'objects' ] [ 'objectid' ] . dtype . str
        dt = '<U%s' % ( int ( dt . replace ( '<' , '' ) . replace ( 'U' , '' ) . replace ( 'S' , '' ) ) + 3 )
        lclistdict [ 'objects' ] [ 'objectid' ] = np . array ( lclistdict [ 'objects' ] [ 'objectid' ] , dtype = dt )
        for objid in duplicated_objectids :
            objid_inds = np . where ( lclistdict [ 'objects' ] [ 'objectid' ] == objid )
            # mark the duplicates , assume the first instance is the actual
            # one
            for ncounter , nind in enumerate ( objid_inds [ 0 ] [ 1 : ] ) :
                lclistdict [ 'objects' ] [ 'objectid' ] [ nind ] = '%s-%s' % ( lclistdict [ 'objects' ] [ 'objectid' ] [ nind ] , ncounter + 2 )
                LOGWARNING ( 'tagging duplicated instance %s of objectid: ' '%s as %s-%s, lightcurve: %s' % ( ncounter + 2 , objid , objid , ncounter + 2 , lclistdict [ 'objects' ] [ 'lcfname' ] [ nind ] ) )
    # if we ' re supposed to make a spatial index , do so
    if ( makecoordindex and isinstance ( makecoordindex , ( list , tuple ) ) and len ( makecoordindex ) == 2 ) :
        try : # deref the column names
            racol , declcol = makecoordindex
            racol = racol . split ( '.' ) [ - 1 ]
            declcol = declcol . split ( '.' ) [ - 1 ]
            # get the ras and decls
            objra , objdecl = ( lclistdict [ 'objects' ] [ racol ] , lclistdict [ 'objects' ] [ declcol ] )
            # get the xyz unit vectors from ra , decl
            # since i had to remind myself :
            # https : / / en . wikipedia . org / wiki / Equatorial _ coordinate _ system
            cosdecl = np . cos ( np . radians ( objdecl ) )
            sindecl = np . sin ( np . radians ( objdecl ) )
            cosra = np . cos ( np . radians ( objra ) )
            sinra = np . sin ( np . radians ( objra ) )
            xyz = np . column_stack ( ( cosra * cosdecl , sinra * cosdecl , sindecl ) )
            # generate the kdtree
            kdt = sps . cKDTree ( xyz , copy_data = True )
            # put the tree into the dict
            lclistdict [ 'kdtree' ] = kdt
            LOGINFO ( 'kdtree generated for (ra, decl): (%s, %s)' % ( makecoordindex [ 0 ] , makecoordindex [ 1 ] ) )
        except Exception as e :
            LOGEXCEPTION ( 'could not make kdtree for (ra, decl): (%s, %s)' % ( makecoordindex [ 0 ] , makecoordindex [ 1 ] ) )
            raise
    # generate the xy pairs if fieldfits is not None
    if field_fitsfile and os . path . exists ( field_fitsfile ) : # read in the FITS file
        if field_wcsfrom is None :
            hdulist = pyfits . open ( field_fitsfile )
            hdr = hdulist [ 0 ] . header
            hdulist . close ( )
            w = WCS ( hdr )
            wcsok = True
        elif os . path . exists ( field_wcsfrom ) :
            w = WCS ( field_wcsfrom )
            wcsok = True
        else :
            LOGERROR ( 'could not determine WCS info for input FITS: %s' % field_fitsfile )
            wcsok = False
        if wcsok : # first , transform the ra / decl to x / y and put these in the
        # lclist output dict
            radecl = np . column_stack ( ( objra , objdecl ) )
            lclistdict [ 'objects' ] [ 'framexy' ] = w . all_world2pix ( radecl , 1 )
            # next , we ' ll make a PNG plot for the finder
            finder_outfile = os . path . join ( os . path . dirname ( outfile ) , os . path . splitext ( os . path . basename ( outfile ) ) [ 0 ] + '.png' )
            finder_png = fits_finder_chart ( field_fitsfile , finder_outfile , wcsfrom = field_wcsfrom , scale = field_scale , stretch = field_stretch , colormap = field_colormap , findersize = field_findersize , overlay_ra = objra , overlay_decl = objdecl , overlay_pltopts = field_pltopts , overlay_zoomcontain = field_zoomcontain , grid = field_grid , gridcolor = field_gridcolor )
            if finder_png is not None :
                LOGINFO ( 'generated a finder PNG ' 'with an object position overlay ' 'for this LC list: %s' % finder_png )
    # write the pickle
    with open ( outfile , 'wb' ) as outfd :
        pickle . dump ( lclistdict , outfd , protocol = pickle . HIGHEST_PROTOCOL )
    LOGINFO ( 'done. LC info -> %s' % outfile )
    return outfile
else :
    LOGERROR ( 'no files found in %s matching %s' % ( basedir , fileglob ) )
    return None


signature:
def filter_lclist ( lc_catalog , objectidcol = 'objectid' , racol = 'ra' , declcol = 'decl' , xmatchexternal = None , xmatchdistarcsec = 3.0 , externalcolnums = ( 0 , 1 , 2 ) , externalcolnames = [ 'objectid' , 'ra' , 'decl' ] , externalcoldtypes = 'U20,f8,f8' , externalcolsep = None , externalcommentchar = '#' , conesearch = None , conesearchworkers = 1 , columnfilters = None , field_fitsfile = None , field_wcsfrom = None , field_scale = ZScaleInterval ( ) , field_stretch = LinearStretch ( ) , field_colormap = plt . cm . gray_r , field_findersize = None , field_pltopts = { 'marker' : 'o' , 'markersize' : 10.0 , 'markerfacecolor' : 'none' , 'markeredgewidth' : 2.0 , 'markeredgecolor' : 'red' } , field_grid = False , field_gridcolor = 'k' , field_zoomcontain = True , copylcsto = None ) :
    '''This is used to perform cone - search , cross - match , and column - filter
 operations on a light curve catalog generated by ` make _ lclist ` .
 Uses the output of ` make _ lclist ` above . This function returns a list of
 light curves matching various criteria specified by the ` xmatchexternal ` ,
 ` conesearch ` , and ` columnfilters kwargs ` . Use this function to generate
 input lists for other lcproc functions ,
 e . g . ` lcproc . lcvfeatures . parallel _ varfeatures ` ,
 ` lcproc . periodfinding . parallel _ pf ` , and ` lcproc . lcbin . parallel _ timebin ` ,
 among others .
 The operations are applied in this order if more than one is specified :
 ` xmatchexternal ` - > ` conesearch ` - > ` columnfilters ` . All results from these
 operations are joined using a logical AND operation .
 Parameters
 objectidcol : str
 This is the name of the object ID column in the light curve catalog .
 racol : str
 This is the name of the RA column in the light curve catalog .
 declcol : str
 This is the name of the Dec column in the light curve catalog .
 xmatchexternal : str or None
 If provided , this is the filename of a text file containing objectids ,
 ras and decs to match the objects in the light curve catalog to by their
 positions .
 xmatchdistarcsec : float
 This is the distance in arcseconds to use when cross - matching to the
 external catalog in ` xmatchexternal ` .
 externalcolnums : sequence of int
 This a list of the zero - indexed column numbers of columns to extract
 from the external catalog file .
 externalcolnames : sequence of str
 This is a list of names of columns that will be extracted from the
 external catalog file . This is the same length as
 ` externalcolnums ` . These must contain the names provided as the
 ` objectid ` , ` ra ` , and ` decl ` column names so this function knows which
 column numbers correspond to those columns and can use them to set up
 the cross - match .
 externalcoldtypes : str
 This is a CSV string containing numpy dtype definitions for all columns
 listed to extract from the external catalog file . The number of dtype
 definitions should be equal to the number of columns to extract .
 externalcolsep : str or None
 The column separator to use when extracting columns from the external
 catalog file . If None , any whitespace between columns is used as the
 separator .
 externalcommentchar : str
 The character indicating that a line in the external catalog file is to
 be ignored .
 conesearch : list of float
 This is used to specify cone - search parameters . It should be a three
 element list :
 [ center _ ra _ deg , center _ decl _ deg , search _ radius _ deg ]
 conesearchworkers : int
 The number of parallel workers to launch for the cone - search operation .
 columnfilters : list of str
 This is a list of strings indicating any filters to apply on each column
 in the light curve catalog . All column filters are applied in the
 specified sequence and are combined with a logical AND operator . The
 format of each filter string should be :
 ' < lc _ catalog column > | < operator > | < operand > '
 where :
 - < lc _ catalog column > is a column in the lc _ catalog pickle file
 - < operator > is one of : ' lt ' , ' gt ' , ' le ' , ' ge ' , ' eq ' , ' ne ' , which
 correspond to the usual operators : < , > , < = , > = , = = , ! = respectively .
 - < operand > is a float , int , or string .
 field _ fitsfile : str or None
 If this is not None , it should be the path to a FITS image containing
 the objects these light curves are for . If this is provided ,
 ` make _ lclist ` will use the WCS information in the FITS itself if
 ` field _ wcsfrom ` is None ( or from a WCS header file pointed to by
 ` field _ wcsfrom ` ) to obtain x and y pixel coordinates for all of the
 objects in the field . A finder chart will also be made using
 ` astrobase . plotbase . fits _ finder _ chart ` using the corresponding
 ` field _ scale ` , ` _ stretch ` , ` _ colormap ` , ` _ findersize ` , ` _ pltopts ` ,
 ` _ grid ` , and ` _ gridcolors ` kwargs for that function , reproduced here to
 enable customization of the finder chart plot .
 field _ wcsfrom : str or None
 If ` wcsfrom ` is None , the WCS to transform the RA / Dec to pixel x / y will
 be taken from the FITS header of ` fitsfile ` . If this is not None , it
 must be a FITS or similar file that contains a WCS header in its first
 extension .
 field _ scale : astropy . visualization . Interval object
 ` scale ` sets the normalization for the FITS pixel values . This is an
 astropy . visualization Interval object .
 See http : / / docs . astropy . org / en / stable / visualization / normalization . html
 for details on ` scale ` and ` stretch ` objects .
 field _ stretch : astropy . visualization . Stretch object
 ` stretch ` sets the stretch function for mapping FITS pixel values to
 output pixel values . This is an astropy . visualization Stretch object .
 See http : / / docs . astropy . org / en / stable / visualization / normalization . html
 for details on ` scale ` and ` stretch ` objects .
 field _ colormap : matplotlib Colormap object
 ` colormap ` is a matplotlib color map object to use for the output image .
 field _ findersize : None or tuple of two ints
 If ` findersize ` is None , the output image size will be set by the NAXIS1
 and NAXIS2 keywords in the input ` fitsfile ` FITS header . Otherwise ,
 ` findersize ` must be a tuple with the intended x and y size of the image
 in inches ( all output images will use a DPI = 100 ) .
 field _ pltopts : dict
 ` field _ pltopts ` controls how the overlay points will be plotted . This
 a dict with standard matplotlib marker , etc . kwargs as key - val pairs ,
 e . g . ' markersize ' , ' markerfacecolor ' , etc . The default options make red
 outline circles at the location of each object in the overlay .
 field _ grid : bool
 ` grid ` sets if a grid will be made on the output image .
 field _ gridcolor : str
 ` gridcolor ` sets the color of the grid lines . This is a usual matplotib
 color spec string .
 field _ zoomcontain : bool
 ` field _ zoomcontain ` controls if the finder chart will be zoomed to
 just contain the overlayed points . Everything outside the footprint of
 these points will be discarded .
 copylcsto : str
 If this is provided , it is interpreted as a directory target to copy
 all the light curves that match the specified conditions .
 Returns
 tuple
 Returns a two elem tuple : ( matching _ object _ lcfiles , matching _ objectids )
 if conesearch and / or column filters are used . If ` xmatchexternal ` is
 also used , a three - elem tuple is returned : ( matching _ object _ lcfiles ,
 matching _ objectids , extcat _ matched _ objectids ) .'''

implementation:
with open ( lc_catalog , 'rb' ) as infd :
    lclist = pickle . load ( infd )
# generate numpy arrays of the matching object indexes . we do it this way so
# we can AND everything at the end , instead of having to look up the objects
# at these indices and running the columnfilter on them
xmatch_matching_index = np . full_like ( lclist [ 'objects' ] [ objectidcol ] , False , dtype = np . bool )
conesearch_matching_index = np . full_like ( lclist [ 'objects' ] [ objectidcol ] , False , dtype = np . bool )
# do the xmatch first
ext_matches = [ ]
ext_matching_objects = [ ]
if ( xmatchexternal and isinstance ( xmatchexternal , str ) and os . path . exists ( xmatchexternal ) ) :
    try : # read in the external file
        extcat = np . genfromtxt ( xmatchexternal , usecols = externalcolnums , delimiter = externalcolsep , names = externalcolnames , dtype = externalcoldtypes , comments = externalcommentchar )
        ext_cosdecl = np . cos ( np . radians ( extcat [ 'decl' ] ) )
        ext_sindecl = np . sin ( np . radians ( extcat [ 'decl' ] ) )
        ext_cosra = np . cos ( np . radians ( extcat [ 'ra' ] ) )
        ext_sinra = np . sin ( np . radians ( extcat [ 'ra' ] ) )
        ext_xyz = np . column_stack ( ( ext_cosra * ext_cosdecl , ext_sinra * ext_cosdecl , ext_sindecl ) )
        ext_xyzdist = 2.0 * np . sin ( np . radians ( xmatchdistarcsec / 3600.0 ) / 2.0 )
        # get our kdtree
        our_kdt = lclist [ 'kdtree' ]
        # get the external kdtree
        ext_kdt = sps . cKDTree ( ext_xyz )
        # do a query _ ball _ tree
        extkd_matchinds = ext_kdt . query_ball_tree ( our_kdt , ext_xyzdist )
        for extind , mind in enumerate ( extkd_matchinds ) :
            if len ( mind ) > 0 :
                ext_matches . append ( mind [ 0 ] )
                # get the whole matching row for the ext objects recarray
                ext_matching_objects . append ( extcat [ extind ] )
        ext_matches = np . array ( ext_matches )
        if ext_matches . size > 0 : # update the xmatch _ matching _ index
            xmatch_matching_index [ ext_matches ] = True
            LOGINFO ( 'xmatch: objects matched to %s within %.1f arcsec: %s' % ( xmatchexternal , xmatchdistarcsec , ext_matches . size ) )
        else :
            LOGERROR ( "xmatch: no objects were cross-matched to external " "catalog spec: %s, can't continue" % xmatchexternal )
            return None , None , None
    except Exception as e :
        LOGEXCEPTION ( 'could not match to external catalog spec: %s' % repr ( xmatchexternal ) )
        raise
# do the cone search next
if ( conesearch and isinstance ( conesearch , ( list , tuple ) ) and len ( conesearch ) == 3 ) :
    try :
        racenter , declcenter , searchradius = conesearch
        cosdecl = np . cos ( np . radians ( declcenter ) )
        sindecl = np . sin ( np . radians ( declcenter ) )
        cosra = np . cos ( np . radians ( racenter ) )
        sinra = np . sin ( np . radians ( racenter ) )
        # this is the search distance in xyz unit vectors
        xyzdist = 2.0 * np . sin ( np . radians ( searchradius ) / 2.0 )
        # get the kdtree
        our_kdt = lclist [ 'kdtree' ]
        # look up the coordinates
        kdtindices = our_kdt . query_ball_point ( [ cosra * cosdecl , sinra * cosdecl , sindecl ] , xyzdist , n_jobs = conesearchworkers )
        if kdtindices and len ( kdtindices ) > 0 :
            LOGINFO ( 'cone search: objects within %.4f deg ' 'of (%.3f, %.3f): %s' % ( searchradius , racenter , declcenter , len ( kdtindices ) ) )
            # update the conesearch _ matching _ index
            matchingind = kdtindices
            conesearch_matching_index [ np . array ( matchingind ) ] = True
        # we fail immediately if we found nothing . this assumes the user
        # cares more about the cone - search than the regular column filters
        else :
            LOGERROR ( "cone-search: no objects were found within " "%.4f deg of (%.3f, %.3f): %s, can't continue" % ( searchradius , racenter , declcenter , len ( kdtindices ) ) )
            return None , None
    except Exception as e :
        LOGEXCEPTION ( 'cone-search: could not run a cone-search, ' 'is there a kdtree present in %s?' % lc_catalog )
        raise
# now that we ' re done with cone - search , do the column filtering
allfilterinds = [ ]
if columnfilters and isinstance ( columnfilters , list ) : # go through each filter
    for cfilt in columnfilters :
        try :
            fcol , foperator , foperand = cfilt . split ( '|' )
            foperator = FILTEROPS [ foperator ]
            # generate the evalstring
            filterstr = ( "np.isfinite(lclist['objects']['%s']) & " "(lclist['objects']['%s'] %s %s)" ) % ( fcol , fcol , foperator , foperand )
            filterind = eval ( filterstr )
            ngood = lclist [ 'objects' ] [ objectidcol ] [ filterind ] . size
            LOGINFO ( 'filter: %s -> objects matching: %s ' % ( cfilt , ngood ) )
            allfilterinds . append ( filterind )
        except Exception as e :
            LOGEXCEPTION ( 'filter: could not understand filter spec: %s' % cfilt )
            LOGWARNING ( 'filter: not applying this broken filter' )
# now that we have all the filter indices good to go
# logical - AND all the things
# make sure we only do filtering if we were told to do so
if ( xmatchexternal or conesearch or columnfilters ) :
    filterstack = [ ]
    if xmatchexternal :
        filterstack . append ( xmatch_matching_index )
    if conesearch :
        filterstack . append ( conesearch_matching_index )
    if columnfilters :
        filterstack . extend ( allfilterinds )
    finalfilterind = np . column_stack ( filterstack )
    finalfilterind = np . all ( finalfilterind , axis = 1 )
    # get the filtered object light curves and object names
    filteredobjectids = lclist [ 'objects' ] [ objectidcol ] [ finalfilterind ]
    filteredlcfnames = lclist [ 'objects' ] [ 'lcfname' ] [ finalfilterind ]
else :
    filteredobjectids = lclist [ 'objects' ] [ objectidcol ]
    filteredlcfnames = lclist [ 'objects' ] [ 'lcfname' ]
# if we ' re told to make a finder chart with the selected objects
if field_fitsfile is not None and os . path . exists ( field_fitsfile ) : # get the RA and DEC of the matching objects
    matching_ra = lclist [ 'objects' ] [ racol ] [ finalfilterind ]
    matching_decl = lclist [ 'objects' ] [ declcol ] [ finalfilterind ]
    matching_postfix = [ ]
    if xmatchexternal is not None :
        matching_postfix . append ( 'xmatch_%s' % os . path . splitext ( os . path . basename ( xmatchexternal ) ) [ 0 ] )
    if conesearch is not None :
        matching_postfix . append ( 'conesearch_RA%.3f_DEC%.3f_RAD%.5f' % tuple ( conesearch ) )
    if columnfilters is not None :
        for cfi , cf in enumerate ( columnfilters ) :
            if cfi == 0 :
                matching_postfix . append ( 'filter_%s_%s_%s' % tuple ( cf . split ( '|' ) ) )
            else :
                matching_postfix . append ( '_and_%s_%s_%s' % tuple ( cf . split ( '|' ) ) )
    if len ( matching_postfix ) > 0 :
        matching_postfix = '-%s' % '_' . join ( matching_postfix )
    else :
        matching_postfix = ''
    # next , we ' ll make a PNG plot for the finder
    finder_outfile = os . path . join ( os . path . dirname ( lc_catalog ) , '%s%s.png' % ( os . path . splitext ( os . path . basename ( lc_catalog ) ) [ 0 ] , matching_postfix ) )
    finder_png = fits_finder_chart ( field_fitsfile , finder_outfile , wcsfrom = field_wcsfrom , scale = field_scale , stretch = field_stretch , colormap = field_colormap , findersize = field_findersize , overlay_ra = matching_ra , overlay_decl = matching_decl , overlay_pltopts = field_pltopts , field_zoomcontain = field_zoomcontain , grid = field_grid , gridcolor = field_gridcolor )
    if finder_png is not None :
        LOGINFO ( 'generated a finder PNG ' 'with an object position overlay ' 'for this filtered LC list: %s' % finder_png )
# if copylcsto is not None , copy LCs over to it
if copylcsto is not None :
    if not os . path . exists ( copylcsto ) :
        os . mkdir ( copylcsto )
    if TQDM :
        lciter = tqdm ( filteredlcfnames )
    else :
        lciter = filteredlcfnames
    LOGINFO ( 'copying matching light curves to %s' % copylcsto )
    for lc in lciter :
        shutil . copy ( lc , copylcsto )
LOGINFO ( 'done. objects matching all filters: %s' % filteredobjectids . size )
if xmatchexternal and len ( ext_matching_objects ) > 0 :
    return filteredlcfnames , filteredobjectids , ext_matching_objects
else :
    return filteredlcfnames , filteredobjectids


signature:
def _cpinfo_key_worker ( task ) :
    '''This wraps ` checkplotlist . checkplot _ infokey _ worker ` .
 This is used to get the correct dtype for each element in retrieved results .
 Parameters
 task : tuple
 task [ 0 ] = cpfile
 task [ 1 ] = keyspeclist ( infokeys kwarg from ` add _ cpinfo _ to _ lclist ` )
 Returns
 dict
 All of the requested keys from the checkplot are returned along with
 their values in a dict .'''

implementation:
cpfile , keyspeclist = task
keystoget = [ x [ 0 ] for x in keyspeclist ]
nonesubs = [ x [ - 2 ] for x in keyspeclist ]
nansubs = [ x [ - 1 ] for x in keyspeclist ]
# reform the keystoget into a list of lists
for i , k in enumerate ( keystoget ) :
    thisk = k . split ( '.' )
    if sys . version_info [ : 2 ] < ( 3 , 4 ) :
        thisk = [ ( int ( x ) if x . isdigit ( ) else x ) for x in thisk ]
    else :
        thisk = [ ( int ( x ) if x . isdecimal ( ) else x ) for x in thisk ]
    keystoget [ i ] = thisk
# add in the objectid as well to match to the object catalog later
keystoget . insert ( 0 , [ 'objectid' ] )
nonesubs . insert ( 0 , '' )
nansubs . insert ( 0 , '' )
# get all the keys we need
vals = checkplot_infokey_worker ( ( cpfile , keystoget ) )
# if they have some Nones , nans , etc . , reform them as expected
for val , nonesub , nansub , valind in zip ( vals , nonesubs , nansubs , range ( len ( vals ) ) ) :
    if val is None :
        outval = nonesub
    elif isinstance ( val , float ) and not np . isfinite ( val ) :
        outval = nansub
    elif isinstance ( val , ( list , tuple ) ) :
        outval = ', ' . join ( val )
    else :
        outval = val
    vals [ valind ] = outval
return vals


signature:
def add_cpinfo_to_lclist ( checkplots , # list or a directory path
initial_lc_catalog , magcol , # to indicate checkplot magcol
outfile , checkplotglob = 'checkplot*.pkl*' , infokeys = CPINFO_DEFAULTKEYS , nworkers = NCPUS ) :
    '''This adds checkplot info to the initial light curve catalogs generated by
 ` make _ lclist ` .
 This is used to incorporate all the extra info checkplots can have for
 objects back into columns in the light curve catalog produced by
 ` make _ lclist ` . Objects are matched between the checkplots and the light
 curve catalog using their ` objectid ` . This then allows one to search this
 ' augmented ' light curve catalog by these extra columns . The ' augmented '
 light curve catalog also forms the basis for search interface provided by
 the LCC - Server .
 The default list of keys that will be extracted from a checkplot and added
 as columns in the initial light curve catalog is listed above in the
 ` CPINFO _ DEFAULTKEYS ` list .
 Parameters
 checkplots : str or list
 If this is a str , is interpreted as a directory which will be searched
 for checkplot pickle files using ` checkplotglob ` . If this is a list , it
 will be interpreted as a list of checkplot pickle files to process .
 initial _ lc _ catalog : str
 This is the path to the light curve catalog pickle made by
 ` make _ lclist ` .
 magcol : str
 This is used to indicate the light curve magnitude column to extract
 magnitude column specific information . For example , Stetson variability
 indices can be generated using magnitude measurements in separate
 photometric apertures , which appear in separate ` magcols ` in the
 checkplot . To associate each such feature of the object with its
 specific ` magcol ` , pass that ` magcol ` in here . This ` magcol ` will then
 be added as a prefix to the resulting column in the ' augmented ' LC
 catalog , e . g . Stetson J will appear as ` magcol1 _ stetsonj ` and
 ` magcol2 _ stetsonj ` for two separate magcols .
 outfile : str
 This is the file name of the output ' augmented ' light curve catalog
 pickle file that will be written .
 infokeys : list of tuples
 This is a list of keys to extract from the checkplot and some info on
 how this extraction is to be done . Each key entry is a six - element
 tuple of the following form :
 - key name in the checkplot
 - numpy dtype of the value of this key
 - False if key is associated with a magcol or True otherwise
 - False if subsequent updates to the same column name will append to
 existing key values in the output augmented light curve catalog or
 True if these will overwrite the existing key value
 - character to use to substitute a None value of the key in the
 checkplot in the output light curve catalog column
 - character to use to substitute a nan value of the key in the
 checkplot in the output light curve catalog column
 See the ` CPFINFO _ DEFAULTKEYS ` list above for examples .
 nworkers : int
 The number of parallel workers to launch to extract checkplot
 information .
 Returns
 str
 Returns the path to the generated ' augmented ' light curve catalog pickle
 file .'''

implementation:
# get the checkplots from the directory if one is provided
if not isinstance ( checkplots , list ) and os . path . exists ( checkplots ) :
    checkplots = sorted ( glob . glob ( os . path . join ( checkplots , checkplotglob ) ) )
tasklist = [ ( cpf , infokeys ) for cpf in checkplots ]
with ProcessPoolExecutor ( max_workers = nworkers ) as executor :
    resultfutures = executor . map ( _cpinfo_key_worker , tasklist )
results = [ x for x in resultfutures ]
executor . shutdown ( )
# now that we have all the checkplot info , we need to match to the
# objectlist in the lclist
# open the lclist
with open ( initial_lc_catalog , 'rb' ) as infd :
    lc_catalog = pickle . load ( infd )
catalog_objectids = np . array ( lc_catalog [ 'objects' ] [ 'objectid' ] )
checkplot_objectids = np . array ( [ x [ 0 ] for x in results ] )
# add the extra key arrays in the lclist dict
extrainfokeys = [ ]
actualkeys = [ ]
# set up the extrainfokeys list
for keyspec in infokeys :
    key , dtype , firstlevel , overwrite_append , nonesub , nansub = keyspec
    if firstlevel :
        eik = key
    else :
        eik = '%s.%s' % ( magcol , key )
    extrainfokeys . append ( eik )
    # now handle the output dicts and column list
    eactual = eik . split ( '.' )
    # this handles dereferenced list indices
    if not eactual [ - 1 ] . isdigit ( ) :
        if not firstlevel :
            eactual = '.' . join ( [ eactual [ 0 ] , eactual [ - 1 ] ] )
        else :
            eactual = eactual [ - 1 ]
    else :
        elastkey = eactual [ - 2 ]
        # for list columns , this converts stuff like errs - > err ,
        # and parallaxes - > parallax
        if elastkey . endswith ( 'es' ) :
            elastkey = elastkey [ : - 2 ]
        elif elastkey . endswith ( 's' ) :
            elastkey = elastkey [ : - 1 ]
        if not firstlevel :
            eactual = '.' . join ( [ eactual [ 0 ] , elastkey ] )
        else :
            eactual = elastkey
    actualkeys . append ( eactual )
    # add a new column only if required
    if eactual not in lc_catalog [ 'columns' ] :
        lc_catalog [ 'columns' ] . append ( eactual )
    # we ' ll overwrite earlier existing columns in any case
    lc_catalog [ 'objects' ] [ eactual ] = [ ]
# now go through each objectid in the catalog and add the extra keys to
# their respective arrays
for catobj in tqdm ( catalog_objectids ) :
    cp_objind = np . where ( checkplot_objectids == catobj )
    if len ( cp_objind [ 0 ] ) > 0 : # get the info line for this checkplot
        thiscpinfo = results [ cp_objind [ 0 ] [ 0 ] ]
        # the first element is the objectid which we remove
        thiscpinfo = thiscpinfo [ 1 : ]
        # update the object catalog entries for this object
        for ekind , ek in enumerate ( actualkeys ) : # add the actual thing to the output list
            lc_catalog [ 'objects' ] [ ek ] . append ( thiscpinfo [ ekind ] )
    else : # update the object catalog entries for this object
        for ekind , ek in enumerate ( actualkeys ) :
            thiskeyspec = infokeys [ ekind ]
            nonesub = thiskeyspec [ - 2 ]
            lc_catalog [ 'objects' ] [ ek ] . append ( nonesub )
# now we should have all the new keys in the object catalog
# turn them into arrays
for ek in actualkeys :
    lc_catalog [ 'objects' ] [ ek ] = np . array ( lc_catalog [ 'objects' ] [ ek ] )
# add the magcol to the lc _ catalog
if 'magcols' in lc_catalog :
    if magcol not in lc_catalog [ 'magcols' ] :
        lc_catalog [ 'magcols' ] . append ( magcol )
else :
    lc_catalog [ 'magcols' ] = [ magcol ]
# write back the new object catalog
with open ( outfile , 'wb' ) as outfd :
    pickle . dump ( lc_catalog , outfd , protocol = pickle . HIGHEST_PROTOCOL )
return outfile