signature:
def retrieve_dataset_files ( searchresult , getpickle = False , outdir = None , apikey = None ) :
    '''This retrieves a search result dataset ' s CSV and any LC zip files .
 Takes the output from the ` submit _ post _ searchquery ` function above or a
 pickle file generated from that function ' s output if the query timed out .
 Parameters
 searchresult : str or tuple
 If provided as a str , points to the pickle file created using the output
 from the ` submit _ post _ searchquery ` function . If provided as a tuple ,
 this is the result tuple from the ` submit _ post _ searchquery ` function .
 getpickle : False
 If this is True , will also download the dataset ' s pickle . Note that
 LCC - Server is a Python 3.6 + package ( while lccs . py still works with
 Python 2.7 ) and it saves its pickles in pickle . HIGHEST _ PROTOCOL for
 efficiency , so these pickles may be unreadable in lower Pythons . As an
 alternative , the dataset CSV contains the full data table and all the
 information about the dataset in its header , which is JSON
 parseable . You can also use the function ` get _ dataset ` below to get the
 dataset pickle information in JSON form .
 outdir : None or str
 If this is a str , points to the output directory where the results will
 be placed . If it ' s None , they will be placed in the current directory .
 apikey : str or None
 If this is a str , uses the given API key to authenticate the download
 request . This is useful when you have a private dataset you want to get
 products for .
 Returns
 ( local _ dataset _ csv , local _ dataset _ lczip , local _ dataset _ pickle ) : tuple
 This returns a tuple containing paths to the dataset CSV , LC zipfile ,
 and the dataset pickle if getpickle was set to True ( None otherwise ) .'''

implementation:
# this handles the direct result case from submit _ * _ query functions
if isinstance ( searchresult , tuple ) :
    info , setid = searchresult [ 1 : ]
# handles the case where we give the function a existing query pickle
elif isinstance ( searchresult , str ) and os . path . exists ( searchresult ) :
    with open ( searchresult , 'rb' ) as infd :
        info = pickle . load ( infd )
    setid = info [ 'result' ] [ 'setid' ]
else :
    LOGERROR ( 'could not understand input, ' 'we need a searchresult from the ' 'lccs.submit_post_searchquery function or ' 'the path to an existing query pickle' )
    return None , None , None
# now that we have everything , let ' s download some files !
dataset_pickle = 'dataset-%s.pkl.gz' % setid
dataset_csv = 'dataset-%s.csv' % setid
dataset_lczip = 'lightcurves-%s.zip' % setid
if outdir is None :
    localdir = os . getcwd ( )
else :
    localdir = outdir
server_scheme , server_netloc = urlparse ( info [ 'result' ] [ 'seturl' ] ) [ : 2 ]
dataset_pickle_link = '%s://%s/d/%s' % ( server_scheme , server_netloc , dataset_pickle )
dataset_csv_link = '%s://%s/d/%s' % ( server_scheme , server_netloc , dataset_csv )
dataset_lczip_link = '%s://%s/p/%s' % ( server_scheme , server_netloc , dataset_lczip )
if getpickle : # get the dataset pickle
    LOGINFO ( 'getting %s...' % dataset_pickle_link )
    try :
        if os . path . exists ( os . path . join ( localdir , dataset_pickle ) ) :
            LOGWARNING ( 'dataset pickle already exists, ' 'not downloading again..' )
            local_dataset_pickle = os . path . join ( localdir , dataset_pickle )
        else : # if apikey is not None , add it in as an Authorization : Bearer
        # [ apikey ] header
            if apikey :
                headers = { 'Authorization' : 'Bearer: %s' % apikey }
            else :
                headers = { }
            req = Request ( dataset_pickle_link , data = None , headers = headers )
            resp = urlopen ( req )
            # save the file
            LOGINFO ( 'saving %s' % dataset_pickle )
            localf = os . path . join ( localdir , dataset_pickle )
            with open ( localf , 'wb' ) as outfd :
                with resp :
                    data = resp . read ( )
                    outfd . write ( data )
            LOGINFO ( 'OK -> %s' % localf )
            local_dataset_pickle = localf
    except HTTPError as e :
        LOGERROR ( 'could not download %s, ' 'HTTP status code was: %s, reason: %s' % ( dataset_pickle_link , e . code , e . reason ) )
        local_dataset_pickle = None
else :
    local_dataset_pickle = None
# get the dataset CSV
LOGINFO ( 'getting %s...' % dataset_csv_link )
try :
    if os . path . exists ( os . path . join ( localdir , dataset_csv ) ) :
        LOGWARNING ( 'dataset CSV already exists, not downloading again...' )
        local_dataset_csv = os . path . join ( localdir , dataset_csv )
    else : # if apikey is not None , add it in as an Authorization : Bearer
    # [ apikey ] header
        if apikey :
            headers = { 'Authorization' : 'Bearer: %s' % apikey }
        else :
            headers = { }
        req = Request ( dataset_csv_link , data = None , headers = headers )
        resp = urlopen ( req )
        # save the file
        LOGINFO ( 'saving %s' % dataset_csv )
        localf = os . path . join ( localdir , dataset_csv )
        with open ( localf , 'wb' ) as outfd :
            with resp :
                data = resp . read ( )
                outfd . write ( data )
        LOGINFO ( 'OK -> %s' % localf )
        local_dataset_csv = localf
except HTTPError as e :
    LOGERROR ( 'could not download %s, HTTP status code was: %s, reason: %s' % ( dataset_csv_link , e . code , e . reason ) )
    local_dataset_csv = None
# get the dataset LC zip
LOGINFO ( 'getting %s...' % dataset_lczip_link )
try :
    if os . path . exists ( os . path . join ( localdir , dataset_lczip ) ) :
        LOGWARNING ( 'dataset LC ZIP already exists, ' 'not downloading again...' )
        local_dataset_lczip = os . path . join ( localdir , dataset_lczip )
    else : # if apikey is not None , add it in as an Authorization : Bearer
    # [ apikey ] header
        if apikey :
            headers = { 'Authorization' : 'Bearer: %s' % apikey }
        else :
            headers = { }
        req = Request ( dataset_lczip_link , data = None , headers = headers )
        resp = urlopen ( req )
        # save the file
        LOGINFO ( 'saving %s' % dataset_lczip )
        localf = os . path . join ( localdir , dataset_lczip )
        with open ( localf , 'wb' ) as outfd :
            with resp :
                data = resp . read ( )
                outfd . write ( data )
        LOGINFO ( 'OK -> %s' % localf )
        local_dataset_lczip = localf
except HTTPError as e :
    LOGERROR ( 'could not download %s, HTTP status code was: %s, reason: %s' % ( dataset_lczip_link , e . code , e . reason ) )
    local_dataset_lczip = None
return local_dataset_csv , local_dataset_lczip , local_dataset_pickle


signature:
def cone_search ( lcc_server , center_ra , center_decl , radiusarcmin = 5.0 , result_visibility = 'unlisted' , email_when_done = False , collections = None , columns = None , filters = None , sortspec = None , samplespec = None , limitspec = None , download_data = True , outdir = None , maxtimeout = 300.0 , refresh = 15.0 ) :
    '''This runs a cone - search query .
 Parameters
 lcc _ server : str
 This is the base URL of the LCC - Server to talk to . ( e . g . for HAT , use :
 https : / / data . hatsurveys . org )
 center _ ra , center _ decl : float
 These are the central coordinates of the search to conduct . These can be
 either decimal degrees of type float , or sexagesimal coordinates of type
 str :
 - OK : 290.0 , 45.0
 - OK : 15:00:00 + 45:00:00
 - OK : 15 00 00.0 - 45 00 00.0
 - NOT OK : 290.0 + 45:00:00
 - NOT OK : 15:00:00 45.0
 radiusarcmin : float
 This is the search radius to use for the cone - search . This is in
 arcminutes . The maximum radius you can use is 60 arcminutes = 1 degree .
 result _ visibility : { ' private ' , ' unlisted ' , ' public ' }
 This sets the visibility of the dataset produced from the search
 result : :
 ' private ' - > the dataset and its products are not visible or
 accessible by any user other than the one that
 created the dataset .
 ' unlisted ' - > the dataset and its products are not visible in the
 list of public datasets , but can be accessed if the
 dataset URL is known
 ' public ' - > the dataset and its products are visible in the list
 of public datasets and can be accessed by anyone .
 email _ when _ done : bool
 If True , the LCC - Server will email you when the search is complete . This
 will also set ` download _ data ` to False . Using this requires an
 LCC - Server account and an API key tied to that account .
 collections : list of str or None
 This is a list of LC collections to search in . If this is None , all
 collections will be searched .
 columns : list of str or None
 This is a list of columns to return in the results . Matching objects '
 object IDs , RAs , DECs , and links to light curve files will always be
 returned so there is no need to specify these columns . If None , only
 these columns will be returned : ' objectid ' , ' ra ' , ' decl ' , ' lcfname '
 filters : str or None
 This is an SQL - like string to use to filter on database columns in the
 LCC - Server ' s collections . To see the columns available for a search ,
 visit the Collections tab in the LCC - Server ' s browser UI . The filter
 operators allowed are : :
 lt - > less than
 gt - > greater than
 ge - > greater than or equal to
 le - > less than or equal to
 eq - > equal to
 ne - > not equal to
 ct - > contains text
 isnull - > column value is null
 notnull - > column value is not null
 You may use the ` and ` and ` or ` operators between filter specifications
 to chain them together logically .
 Example filter strings : :
 " ( propermotion gt 200.0 ) and ( sdssr lt 11.0 ) "
 " ( dered _ jmag _ kmag gt 2.0 ) and ( aep _ 000 _ stetsonj gt 10.0 ) "
 " ( gaia _ status ct ' ok ' ) and ( propermotion gt 300.0 ) "
 " ( simbad _ best _ objtype ct ' RR ' ) and ( dered _ sdssu _ sdssg lt 0.5 ) "
 sortspec : tuple of two strs or None
 If not None , this should be a tuple of two items : :
 ( ' column to sort by ' , ' asc | desc ' )
 This sets the column to sort the results by . For cone _ search , the
 default column and sort order are ' dist _ arcsec ' and ' asc ' , meaning the
 distance from the search center in ascending order .
 samplespec : int or None
 If this is an int , will indicate how many rows from the initial search
 result will be uniformly random sampled and returned .
 limitspec : int or None
 If this is an int , will indicate how many rows from the initial search
 result to return in total .
 ` sortspec ` , ` samplespec ` , and ` limitspec ` are applied in this order :
 sample - > sort - > limit
 download _ data : bool
 This sets if the accompanying data from the search results will be
 downloaded automatically . This includes the data table CSV , the dataset
 pickle file , and a light curve ZIP file . Note that if the search service
 indicates that your query is still in progress , this function will block
 until the light curve ZIP file becomes available . The maximum wait time
 in seconds is set by maxtimeout and the refresh interval is set by
 refresh .
 To avoid the wait block , set download _ data to False and the function
 will write a pickle file to ` ~ / . astrobase / lccs / query - [ setid ] . pkl `
 containing all the information necessary to retrieve these data files
 later when the query is done . To do so , call the
 ` retrieve _ dataset _ files ` with the path to this pickle file ( it will be
 returned ) .
 outdir : str or None
 If this is provided , sets the output directory of the downloaded dataset
 files . If None , they will be downloaded to the current directory .
 maxtimeout : float
 The maximum time in seconds to wait for the LCC - Server to respond with a
 result before timing out . You can use the ` retrieve _ dataset _ files `
 function to get results later as needed .
 refresh : float
 The time to wait in seconds before pinging the LCC - Server to see if a
 search query has completed and dataset result files can be downloaded .
 Returns
 tuple
 Returns a tuple with the following elements : :
 ( search result status dict ,
 search result CSV file path ,
 search result LC ZIP path )'''

implementation:
# turn the input into a param dict
coords = '%.5f %.5f %.1f' % ( center_ra , center_decl , radiusarcmin )
params = { 'coords' : coords }
if collections :
    params [ 'collections' ] = collections
if columns :
    params [ 'columns' ] = columns
if filters :
    params [ 'filters' ] = filters
if sortspec :
    params [ 'sortspec' ] = json . dumps ( [ sortspec ] )
if samplespec :
    params [ 'samplespec' ] = int ( samplespec )
if limitspec :
    params [ 'limitspec' ] = int ( limitspec )
params [ 'visibility' ] = result_visibility
params [ 'emailwhendone' ] = email_when_done
# we won ' t wait for the LC ZIP to complete if email _ when _ done = True
if email_when_done :
    download_data = False
# check if we have an API key already
have_apikey , apikey , expires = check_existing_apikey ( lcc_server )
# if not , get a new one
if not have_apikey :
    apikey , expires = get_new_apikey ( lcc_server )
# hit the server
api_url = '%s/api/conesearch' % lcc_server
searchresult = submit_post_searchquery ( api_url , params , apikey )
# check the status of the search
status = searchresult [ 0 ]
# now we ' ll check if we want to download the data
if download_data :
    if status == 'ok' :
        LOGINFO ( 'query complete, downloading associated data...' )
        csv , lczip , pkl = retrieve_dataset_files ( searchresult , outdir = outdir , apikey = apikey )
        if pkl :
            return searchresult [ 1 ] , csv , lczip , pkl
        else :
            return searchresult [ 1 ] , csv , lczip
    elif status == 'background' :
        LOGINFO ( 'query is not yet complete, ' 'waiting up to %.1f minutes, ' 'updates every %s seconds (hit Ctrl+C to cancel)...' % ( maxtimeout / 60.0 , refresh ) )
        timewaited = 0.0
        while timewaited < maxtimeout :
            try :
                time . sleep ( refresh )
                csv , lczip , pkl = retrieve_dataset_files ( searchresult , outdir = outdir , apikey = apikey )
                if ( csv and os . path . exists ( csv ) and lczip and os . path . exists ( lczip ) ) :
                    LOGINFO ( 'all dataset products collected' )
                    return searchresult [ 1 ] , csv , lczip
                timewaited = timewaited + refresh
            except KeyboardInterrupt :
                LOGWARNING ( 'abandoned wait for downloading data' )
                return searchresult [ 1 ] , None , None
        LOGERROR ( 'wait timed out.' )
        return searchresult [ 1 ] , None , None
    else :
        LOGERROR ( 'could not download the data for this query result' )
        return searchresult [ 1 ] , None , None
else :
    return searchresult [ 1 ] , None , None


signature:
def xmatch_search ( lcc_server , file_to_upload , xmatch_dist_arcsec = 3.0 , result_visibility = 'unlisted' , email_when_done = False , collections = None , columns = None , filters = None , sortspec = None , limitspec = None , samplespec = None , download_data = True , outdir = None , maxtimeout = 300.0 , refresh = 15.0 ) :
    '''This runs a cross - match search query .
 Parameters
 lcc _ server : str
 This is the base URL of the LCC - Server to talk to . ( e . g . for HAT , use :
 https : / / data . hatsurveys . org )
 file _ to _ upload : str
 This is the path to a text file containing objectid , RA , declination
 rows for the objects to cross - match against the LCC - Server
 collections . This should follow the format of the following example : :
 # example object and coordinate list
 # objectid ra dec
 aaa 289.99698 44.99839
 bbb 293.358 - 23.206
 ccc 294.197 + 23.181
 ddd 19 25 27.9129 + 42 47 03.693
 eee 19:25:27 - 42:47:03.21
 # etc . lines starting with ' # ' will be ignored
 # ( max 5000 objects )
 xmatch _ dist _ arcsec : float
 This is the maximum distance in arcseconds to consider when
 cross - matching objects in the uploaded file to the LCC - Server ' s
 collections . The maximum allowed distance is 30 arcseconds . Multiple
 matches to an uploaded object are possible and will be returned in order
 of increasing distance grouped by input ` objectid ` .
 result _ visibility : { ' private ' , ' unlisted ' , ' public ' }
 This sets the visibility of the dataset produced from the search
 result : :
 ' private ' - > the dataset and its products are not visible or
 accessible by any user other than the one that
 created the dataset .
 ' unlisted ' - > the dataset and its products are not visible in the
 list of public datasets , but can be accessed if the
 dataset URL is known
 ' public ' - > the dataset and its products are visible in the list
 of public datasets and can be accessed by anyone .
 email _ when _ done : bool
 If True , the LCC - Server will email you when the search is complete . This
 will also set ` download _ data ` to False . Using this requires an
 LCC - Server account and an API key tied to that account .
 collections : list of str or None
 This is a list of LC collections to search in . If this is None , all
 collections will be searched .
 columns : list of str or None
 This is a list of columns to return in the results . Matching objects '
 object IDs , RAs , DECs , and links to light curve files will always be
 returned so there is no need to specify these columns . If None , only
 these columns will be returned : ' objectid ' , ' ra ' , ' decl ' , ' lcfname '
 filters : str or None
 This is an SQL - like string to use to filter on database columns in the
 LCC - Server ' s collections . To see the columns available for a search ,
 visit the Collections tab in the LCC - Server ' s browser UI . The filter
 operators allowed are : :
 lt - > less than
 gt - > greater than
 ge - > greater than or equal to
 le - > less than or equal to
 eq - > equal to
 ne - > not equal to
 ct - > contains text
 isnull - > column value is null
 notnull - > column value is not null
 You may use the ` and ` and ` or ` operators between filter specifications
 to chain them together logically .
 Example filter strings : :
 " ( propermotion gt 200.0 ) and ( sdssr lt 11.0 ) "
 " ( dered _ jmag _ kmag gt 2.0 ) and ( aep _ 000 _ stetsonj gt 10.0 ) "
 " ( gaia _ status ct ' ok ' ) and ( propermotion gt 300.0 ) "
 " ( simbad _ best _ objtype ct ' RR ' ) and ( dered _ sdssu _ sdssg lt 0.5 ) "
 sortspec : tuple of two strs or None
 If not None , this should be a tuple of two items : :
 ( ' column to sort by ' , ' asc | desc ' )
 This sets the column to sort the results by . For cone _ search , the
 default column and sort order are ' dist _ arcsec ' and ' asc ' , meaning the
 distance from the search center in ascending order .
 samplespec : int or None
 If this is an int , will indicate how many rows from the initial search
 result will be uniformly random sampled and returned .
 limitspec : int or None
 If this is an int , will indicate how many rows from the initial search
 result to return in total .
 ` sortspec ` , ` samplespec ` , and ` limitspec ` are applied in this order :
 sample - > sort - > limit
 download _ data : bool
 This sets if the accompanying data from the search results will be
 downloaded automatically . This includes the data table CSV , the dataset
 pickle file , and a light curve ZIP file . Note that if the search service
 indicates that your query is still in progress , this function will block
 until the light curve ZIP file becomes available . The maximum wait time
 in seconds is set by maxtimeout and the refresh interval is set by
 refresh .
 To avoid the wait block , set download _ data to False and the function
 will write a pickle file to ` ~ / . astrobase / lccs / query - [ setid ] . pkl `
 containing all the information necessary to retrieve these data files
 later when the query is done . To do so , call the
 ` retrieve _ dataset _ files ` with the path to this pickle file ( it will be
 returned ) .
 outdir : str or None
 If this is provided , sets the output directory of the downloaded dataset
 files . If None , they will be downloaded to the current directory .
 maxtimeout : float
 The maximum time in seconds to wait for the LCC - Server to respond with a
 result before timing out . You can use the ` retrieve _ dataset _ files `
 function to get results later as needed .
 refresh : float
 The time to wait in seconds before pinging the LCC - Server to see if a
 search query has completed and dataset result files can be downloaded .
 Returns
 tuple
 Returns a tuple with the following elements : :
 ( search result status dict ,
 search result CSV file path ,
 search result LC ZIP path )'''

implementation:
with open ( file_to_upload ) as infd :
    xmq = infd . read ( )
# check the number of lines in the input
xmqlines = len ( xmq . split ( '\n' ) [ : - 1 ] )
if xmqlines > 5000 :
    LOGERROR ( 'you have more than 5000 lines in the file to upload: %s' % file_to_upload )
    return None , None , None
# turn the input into a param dict
params = { 'xmq' : xmq , 'xmd' : xmatch_dist_arcsec }
if collections :
    params [ 'collections' ] = collections
if columns :
    params [ 'columns' ] = columns
if filters :
    params [ 'filters' ] = filters
if sortspec :
    params [ 'sortspec' ] = json . dumps ( [ sortspec ] )
if samplespec :
    params [ 'samplespec' ] = int ( samplespec )
if limitspec :
    params [ 'limitspec' ] = int ( limitspec )
params [ 'visibility' ] = result_visibility
params [ 'emailwhendone' ] = email_when_done
# we won ' t wait for the LC ZIP to complete if email _ when _ done = True
if email_when_done :
    download_data = False
# check if we have an API key already
have_apikey , apikey , expires = check_existing_apikey ( lcc_server )
# if not , get a new one
if not have_apikey :
    apikey , expires = get_new_apikey ( lcc_server )
# hit the server
api_url = '%s/api/xmatch' % lcc_server
searchresult = submit_post_searchquery ( api_url , params , apikey )
# check the status of the search
status = searchresult [ 0 ]
# now we ' ll check if we want to download the data
if download_data :
    if status == 'ok' :
        LOGINFO ( 'query complete, downloading associated data...' )
        csv , lczip , pkl = retrieve_dataset_files ( searchresult , outdir = outdir , apikey = apikey )
        if pkl :
            return searchresult [ 1 ] , csv , lczip , pkl
        else :
            return searchresult [ 1 ] , csv , lczip
    elif status == 'background' :
        LOGINFO ( 'query is not yet complete, ' 'waiting up to %.1f minutes, ' 'updates every %s seconds (hit Ctrl+C to cancel)...' % ( maxtimeout / 60.0 , refresh ) )
        timewaited = 0.0
        while timewaited < maxtimeout :
            try :
                time . sleep ( refresh )
                csv , lczip , pkl = retrieve_dataset_files ( searchresult , outdir = outdir , apikey = apikey )
                if ( csv and os . path . exists ( csv ) and lczip and os . path . exists ( lczip ) ) :
                    LOGINFO ( 'all dataset products collected' )
                    return searchresult [ 1 ] , csv , lczip
                timewaited = timewaited + refresh
            except KeyboardInterrupt :
                LOGWARNING ( 'abandoned wait for downloading data' )
                return searchresult [ 1 ] , None , None
        LOGERROR ( 'wait timed out.' )
        return searchresult [ 1 ] , None , None
    else :
        LOGERROR ( 'could not download the data for this query result' )
        return searchresult [ 1 ] , None , None
else :
    return searchresult [ 1 ] , None , None


signature:
def get_dataset ( lcc_server , dataset_id , strformat = False , page = 1 ) :
    '''This downloads a JSON form of a dataset from the specified lcc _ server .
 If the dataset contains more than 1000 rows , it will be paginated , so you
 must use the ` page ` kwarg to get the page you want . The dataset JSON will
 contain the keys ' npages ' , ' currpage ' , and ' rows _ per _ page ' to help with
 this . The ' rows ' key contains the actual data rows as a list of tuples .
 The JSON contains metadata about the query that produced the dataset ,
 information about the data table ' s columns , and links to download the
 dataset ' s products including the light curve ZIP and the dataset CSV .
 Parameters
 lcc _ server : str
 This is the base URL of the LCC - Server to talk to .
 dataset _ id : str
 This is the unique setid of the dataset you want to get . In the results
 from the ` * _ search ` functions above , this is the value of the
 ` infodict [ ' result ' ] [ ' setid ' ] ` key in the first item ( the infodict ) in
 the returned tuple .
 strformat : bool
 This sets if you want the returned data rows to be formatted in their
 string representations already . This can be useful if you ' re piping the
 returned JSON straight into some sort of UI and you don ' t want to deal
 with formatting floats , etc . To do this manually when strformat is set
 to False , look at the ` coldesc ` item in the returned dict , which gives
 the Python and Numpy string format specifiers for each column in the
 data table .
 page : int
 This sets which page of the dataset should be retrieved .
 Returns
 dict
 This returns the dataset JSON loaded into a dict .'''

implementation:
urlparams = { 'strformat' : 1 if strformat else 0 , 'page' : page , 'json' : 1 }
urlqs = urlencode ( urlparams )
dataset_url = '%s/set/%s?%s' % ( lcc_server , dataset_id , urlqs )
LOGINFO ( 'retrieving dataset %s from %s, using URL: %s ...' % ( lcc_server , dataset_id , dataset_url ) )
try : # check if we have an API key already
    have_apikey , apikey , expires = check_existing_apikey ( lcc_server )
    # if not , get a new one
    if not have_apikey :
        apikey , expires = get_new_apikey ( lcc_server )
    # if apikey is not None , add it in as an Authorization : Bearer [ apikey ]
    # header
    if apikey :
        headers = { 'Authorization' : 'Bearer: %s' % apikey }
    else :
        headers = { }
    # hit the server
    req = Request ( dataset_url , data = None , headers = headers )
    resp = urlopen ( req )
    dataset = json . loads ( resp . read ( ) )
    return dataset
except Exception as e :
    LOGEXCEPTION ( 'could not retrieve the dataset JSON!' )
    return None


signature:
def object_info ( lcc_server , objectid , db_collection_id ) :
    '''This gets information on a single object from the LCC - Server .
 Returns a dict with all of the available information on an object , including
 finding charts , comments , object type and variability tags , and
 period - search results ( if available ) .
 If you have an LCC - Server API key present in ` ~ / . astrobase / lccs / ` that is
 associated with an LCC - Server user account , objects that are visible to this
 user will be returned , even if they are not visible to the public . Use this
 to look up objects that have been marked as ' private ' or ' shared ' .
 NOTE : you can pass the result dict returned by this function directly into
 the ` astrobase . checkplot . checkplot _ pickle _ to _ png ` function , e . g . : :
 astrobase . checkplot . checkplot _ pickle _ to _ png ( result _ dict ,
 ' object - % s - info . png ' %
 result _ dict [ ' objectid ' ] )
 to generate a quick PNG overview of the object information .
 Parameters
 lcc _ server : str
 This is the base URL of the LCC - Server to talk to .
 objectid : str
 This is the unique database ID of the object to retrieve info for . This
 is always returned as the ` db _ oid ` column in LCC - Server search results .
 db _ collection _ id : str
 This is the collection ID which will be searched for the object . This is
 always returned as the ` collection ` column in LCC - Server search results .
 Returns
 dict
 A dict containing the object info is returned . Some important items in
 the result dict :
 - ` objectinfo ` : all object magnitude , color , GAIA cross - match , and
 object type information available for this object
 - ` objectcomments ` : comments on the object ' s variability if available
 - ` varinfo ` : variability comments , variability features , type tags ,
 period and epoch information if available
 - ` neighbors ` : information on the neighboring objects of this object in
 its parent light curve collection
 - ` xmatch ` : information on any cross - matches to external catalogs
 ( e . g . KIC , EPIC , TIC , APOGEE , etc . )
 - ` finderchart ` : a base - 64 encoded PNG image of the object ' s DSS2 RED
 finder chart . To convert this to an actual PNG , try the function :
 ` astrobase . checkplot . pkl _ io . _ b64 _ to _ file ` .
 - ` magseries ` : a base - 64 encoded PNG image of the object ' s light
 curve . To convert this to an actual PNG , try the function :
 ` astrobase . checkplot . pkl _ io . _ b64 _ to _ file ` .
 - ` pfmethods ` : a list of period - finding methods applied to the object if
 any . If this list is present , use the keys in it to get to the actual
 period - finding results for each method . These will contain base - 64
 encoded PNGs of the periodogram and phased light curves using the best
 three peaks in the periodogram , as well as period and epoch
 information .'''

implementation:
urlparams = { 'objectid' : objectid , 'collection' : db_collection_id }
urlqs = urlencode ( urlparams )
url = '%s/api/object?%s' % ( lcc_server , urlqs )
try :
    LOGINFO ( 'getting info for %s in collection %s from %s' % ( objectid , db_collection_id , lcc_server ) )
    # check if we have an API key already
    have_apikey , apikey , expires = check_existing_apikey ( lcc_server )
    # if not , get a new one
    if not have_apikey :
        apikey , expires = get_new_apikey ( lcc_server )
    # if apikey is not None , add it in as an Authorization : Bearer [ apikey ]
    # header
    if apikey :
        headers = { 'Authorization' : 'Bearer: %s' % apikey }
    else :
        headers = { }
    # hit the server
    req = Request ( url , data = None , headers = headers )
    resp = urlopen ( req )
    objectinfo = json . loads ( resp . read ( ) ) [ 'result' ]
    return objectinfo
except HTTPError as e :
    if e . code == 404 :
        LOGERROR ( 'additional info for object %s not ' 'found in collection: %s' % ( objectid , db_collection_id ) )
    else :
        LOGERROR ( 'could not retrieve object info, ' 'URL used: %s, error code: %s, reason: %s' % ( url , e . code , e . reason ) )
    return None


signature:
def list_recent_datasets ( lcc_server , nrecent = 25 ) :
    '''This lists recent publicly visible datasets available on the LCC - Server .
 If you have an LCC - Server API key present in ` ~ / . astrobase / lccs / ` that is
 associated with an LCC - Server user account , datasets that belong to this
 user will be returned as well , even if they are not visible to the public .
 Parameters
 lcc _ server : str
 This is the base URL of the LCC - Server to talk to .
 nrecent : int
 This indicates how many recent public datasets you want to list . This is
 always capped at 1000.
 Returns
 list of dicts
 Returns a list of dicts , with each dict containing info on each dataset .'''

implementation:
urlparams = { 'nsets' : nrecent }
urlqs = urlencode ( urlparams )
url = '%s/api/datasets?%s' % ( lcc_server , urlqs )
try :
    LOGINFO ( 'getting list of recent publicly ' 'visible and owned datasets from %s' % ( lcc_server , ) )
    # check if we have an API key already
    have_apikey , apikey , expires = check_existing_apikey ( lcc_server )
    # if not , get a new one
    if not have_apikey :
        apikey , expires = get_new_apikey ( lcc_server )
    # if apikey is not None , add it in as an Authorization : Bearer [ apikey ]
    # header
    if apikey :
        headers = { 'Authorization' : 'Bearer: %s' % apikey }
    else :
        headers = { }
    # hit the server
    req = Request ( url , data = None , headers = headers )
    resp = urlopen ( req )
    recent_datasets = json . loads ( resp . read ( ) ) [ 'result' ]
    return recent_datasets
except HTTPError as e :
    LOGERROR ( 'could not retrieve recent datasets list, ' 'URL used: %s, error code: %s, reason: %s' % ( url , e . code , e . reason ) )
    return None


signature:
def list_lc_collections ( lcc_server ) :
    '''This lists all light curve collections made available on the LCC - Server .
 If you have an LCC - Server API key present in ` ~ / . astrobase / lccs / ` that is
 associated with an LCC - Server user account , light curve collections visible
 to this user will be returned as well , even if they are not visible to the
 public .
 Parameters
 lcc _ server : str
 The base URL of the LCC - Server to talk to .
 Returns
 dict
 Returns a dict containing lists of info items per collection . This
 includes collection _ ids , lists of columns , lists of indexed columns ,
 lists of full - text indexed columns , detailed column descriptions , number
 of objects in each collection , collection sky coverage , etc .'''

implementation:
url = '%s/api/collections' % lcc_server
try :
    LOGINFO ( 'getting list of recent publicly visible ' 'and owned LC collections from %s' % ( lcc_server , ) )
    # check if we have an API key already
    have_apikey , apikey , expires = check_existing_apikey ( lcc_server )
    # if not , get a new one
    if not have_apikey :
        apikey , expires = get_new_apikey ( lcc_server )
    # if apikey is not None , add it in as an Authorization : Bearer [ apikey ]
    # header
    if apikey :
        headers = { 'Authorization' : 'Bearer: %s' % apikey }
    else :
        headers = { }
    # hit the server
    req = Request ( url , data = None , headers = headers )
    resp = urlopen ( req )
    lcc_list = json . loads ( resp . read ( ) ) [ 'result' ] [ 'collections' ]
    return lcc_list
except HTTPError as e :
    LOGERROR ( 'could not retrieve list of collections, ' 'URL used: %s, error code: %s, reason: %s' % ( url , e . code , e . reason ) )
    return None


signature:
def stetson_jindex ( ftimes , fmags , ferrs , weightbytimediff = False ) :
    '''This calculates the Stetson index for the magseries , based on consecutive
 pairs of observations .
 Based on Nicole Loncke ' s work for her Planets and Life certificate at
 Princeton in 2014.
 Parameters
 ftimes , fmags , ferrs : np . array
 The input mag / flux time - series with all non - finite elements removed .
 weightbytimediff : bool
 If this is True , the Stetson index for any pair of mags will be
 reweighted by the difference in times between them using the scheme in
 Fruth + 2012 and Zhange + 2003 ( as seen in Sokolovsky + 2017 ) : :
 w _ i = exp ( - ( t _ i + 1 - t _ i ) / delta _ t )
 Returns
 float
 The calculated Stetson J variability index .'''

implementation:
ndet = len ( fmags )
if ndet > 9 : # get the median and ndet
    medmag = npmedian ( fmags )
    # get the stetson index elements
    delta_prefactor = ( ndet / ( ndet - 1 ) )
    sigma_i = delta_prefactor * ( fmags - medmag ) / ferrs
    # Nicole ' s clever trick to advance indices by 1 and do x _ i * x _ ( i + 1)
    sigma_j = nproll ( sigma_i , 1 )
    if weightbytimediff :
        difft = npdiff ( ftimes )
        deltat = npmedian ( difft )
        weights_i = npexp ( - difft / deltat )
        products = ( weights_i * sigma_i [ 1 : ] * sigma_j [ 1 : ] )
    else : # ignore first elem since it ' s actually x _ 0 * x _ n
        products = ( sigma_i * sigma_j ) [ 1 : ]
    stetsonj = ( npsum ( npsign ( products ) * npsqrt ( npabs ( products ) ) ) ) / ndet
    return stetsonj
else :
    LOGERROR ( 'not enough detections in this magseries ' 'to calculate stetson J index' )
    return npnan


signature:
def stetson_kindex ( fmags , ferrs ) :
    '''This calculates the Stetson K index ( a robust measure of the kurtosis ) .
 Parameters
 fmags , ferrs : np . array
 The input mag / flux time - series to process . Must have no non - finite
 elems .
 Returns
 float
 The Stetson K variability index .'''

implementation:
# use a fill in value for the errors if they ' re none
if ferrs is None :
    ferrs = npfull_like ( fmags , 0.005 )
ndet = len ( fmags )
if ndet > 9 : # get the median and ndet
    medmag = npmedian ( fmags )
    # get the stetson index elements
    delta_prefactor = ( ndet / ( ndet - 1 ) )
    sigma_i = delta_prefactor * ( fmags - medmag ) / ferrs
    stetsonk = ( npsum ( npabs ( sigma_i ) ) / ( npsqrt ( npsum ( sigma_i * sigma_i ) ) ) * ( ndet ** ( - 0.5 ) ) )
    return stetsonk
else :
    LOGERROR ( 'not enough detections in this magseries ' 'to calculate stetson K index' )
    return npnan


signature:
def lightcurve_moments ( ftimes , fmags , ferrs ) :
    '''This calculates the weighted mean , stdev , median , MAD , percentiles , skew ,
 kurtosis , fraction of LC beyond 1 - stdev , and IQR .
 Parameters
 ftimes , fmags , ferrs : np . array
 The input mag / flux time - series with all non - finite elements removed .
 Returns
 dict
 A dict with all of the light curve moments calculated .'''

implementation:
ndet = len ( fmags )
if ndet > 9 : # now calculate the various things we need
    series_median = npmedian ( fmags )
    series_wmean = ( npsum ( fmags * ( 1.0 / ( ferrs * ferrs ) ) ) / npsum ( 1.0 / ( ferrs * ferrs ) ) )
    series_mad = npmedian ( npabs ( fmags - series_median ) )
    series_stdev = 1.483 * series_mad
    series_skew = spskew ( fmags )
    series_kurtosis = spkurtosis ( fmags )
    # get the beyond1std fraction
    series_above1std = len ( fmags [ fmags > ( series_median + series_stdev ) ] )
    series_below1std = len ( fmags [ fmags < ( series_median - series_stdev ) ] )
    # this is the fraction beyond 1 stdev
    series_beyond1std = ( series_above1std + series_below1std ) / float ( ndet )
    # get the magnitude percentiles
    series_mag_percentiles = nppercentile ( fmags , [ 5.0 , 10 , 17.5 , 25 , 32.5 , 40 , 60 , 67.5 , 75 , 82.5 , 90 , 95 ] )
    return { 'median' : series_median , 'wmean' : series_wmean , 'mad' : series_mad , 'stdev' : series_stdev , 'skew' : series_skew , 'kurtosis' : series_kurtosis , 'beyond1std' : series_beyond1std , 'mag_percentiles' : series_mag_percentiles , 'mag_iqr' : series_mag_percentiles [ 8 ] - series_mag_percentiles [ 3 ] , }
else :
    LOGERROR ( 'not enough detections in this magseries ' 'to calculate light curve moments' )
    return None


signature:
def lightcurve_flux_measures ( ftimes , fmags , ferrs , magsarefluxes = False ) :
    '''This calculates percentiles and percentile ratios of the flux .
 Parameters
 ftimes , fmags , ferrs : np . array
 The input mag / flux time - series with all non - finite elements removed .
 magsarefluxes : bool
 If the ` fmags ` array actually contains fluxes , will not convert ` mags `
 to fluxes before calculating the percentiles .
 Returns
 dict
 A dict with all of the light curve flux percentiles and percentile
 ratios calculated .'''

implementation:
ndet = len ( fmags )
if ndet > 9 : # get the fluxes
    if magsarefluxes :
        series_fluxes = fmags
    else :
        series_fluxes = 10.0 ** ( - 0.4 * fmags )
    series_flux_median = npmedian ( series_fluxes )
    # get the percent _ amplitude for the fluxes
    series_flux_percent_amplitude = ( npmax ( npabs ( series_fluxes ) ) / series_flux_median )
    # get the flux percentiles
    series_flux_percentiles = nppercentile ( series_fluxes , [ 5.0 , 10 , 17.5 , 25 , 32.5 , 40 , 60 , 67.5 , 75 , 82.5 , 90 , 95 ] )
    series_frat_595 = ( series_flux_percentiles [ - 1 ] - series_flux_percentiles [ 0 ] )
    series_frat_1090 = ( series_flux_percentiles [ - 2 ] - series_flux_percentiles [ 1 ] )
    series_frat_175825 = ( series_flux_percentiles [ - 3 ] - series_flux_percentiles [ 2 ] )
    series_frat_2575 = ( series_flux_percentiles [ - 4 ] - series_flux_percentiles [ 3 ] )
    series_frat_325675 = ( series_flux_percentiles [ - 5 ] - series_flux_percentiles [ 4 ] )
    series_frat_4060 = ( series_flux_percentiles [ - 6 ] - series_flux_percentiles [ 5 ] )
    # calculate the flux percentile ratios
    series_flux_percentile_ratio_mid20 = series_frat_4060 / series_frat_595
    series_flux_percentile_ratio_mid35 = series_frat_325675 / series_frat_595
    series_flux_percentile_ratio_mid50 = series_frat_2575 / series_frat_595
    series_flux_percentile_ratio_mid65 = series_frat_175825 / series_frat_595
    series_flux_percentile_ratio_mid80 = series_frat_1090 / series_frat_595
    # calculate the ratio of F595 / median flux
    series_percent_difference_flux_percentile = ( series_frat_595 / series_flux_median )
    series_percentile_magdiff = - 2.5 * nplog10 ( series_percent_difference_flux_percentile )
    return { 'flux_median' : series_flux_median , 'flux_percent_amplitude' : series_flux_percent_amplitude , 'flux_percentiles' : series_flux_percentiles , 'flux_percentile_ratio_mid20' : series_flux_percentile_ratio_mid20 , 'flux_percentile_ratio_mid35' : series_flux_percentile_ratio_mid35 , 'flux_percentile_ratio_mid50' : series_flux_percentile_ratio_mid50 , 'flux_percentile_ratio_mid65' : series_flux_percentile_ratio_mid65 , 'flux_percentile_ratio_mid80' : series_flux_percentile_ratio_mid80 , 'percent_difference_flux_percentile' : series_percentile_magdiff , }
else :
    LOGERROR ( 'not enough detections in this magseries ' 'to calculate flux measures' )
    return None


signature:
def lightcurve_ptp_measures ( ftimes , fmags , ferrs ) :
    '''This calculates various point - to - point measures ( ` eta ` in Kim + 2014 ) .
 Parameters
 ftimes , fmags , ferrs : np . array
 The input mag / flux time - series with all non - finite elements removed .
 Returns
 dict
 A dict with values of the point - to - point measures , including the ` eta `
 variability index ( often used as its inverse ` inveta ` to have the same
 sense as increasing variability index - > more likely a variable star ) .'''

implementation:
ndet = len ( fmags )
if ndet > 9 :
    timediffs = npdiff ( ftimes )
    # get rid of stuff with time diff = 0.0
    nzind = npnonzero ( timediffs )
    ftimes , fmags , ferrs = ftimes [ nzind ] , fmags [ nzind ] , ferrs [ nzind ]
    # recalculate ndet and diffs
    ndet = ftimes . size
    timediffs = npdiff ( ftimes )
    # calculate the point to point measures
    p2p_abs_magdiffs = npabs ( npdiff ( fmags ) )
    p2p_squared_magdiffs = npdiff ( fmags ) * npdiff ( fmags )
    robstd = npmedian ( npabs ( fmags - npmedian ( fmags ) ) ) * 1.483
    robvar = robstd * robstd
    # these are eta from the Kim + 2014 paper - ratio of point - to - point
    # difference to the variance of the entire series
    # this is the robust version
    eta_robust = npmedian ( p2p_abs_magdiffs ) / robvar
    eta_robust = eta_robust / ( ndet - 1.0 )
    # this is the usual version
    eta_normal = npsum ( p2p_squared_magdiffs ) / npvar ( fmags )
    eta_normal = eta_normal / ( ndet - 1.0 )
    timeweights = 1.0 / ( timediffs * timediffs )
    # this is eta _ e modified for uneven sampling from the Kim + 2014 paper
    eta_uneven_normal = ( ( npsum ( timeweights * p2p_squared_magdiffs ) / ( npvar ( fmags ) * npsum ( timeweights ) ) ) * npmean ( timeweights ) * ( ftimes . max ( ) - ftimes . min ( ) ) * ( ftimes . max ( ) - ftimes . min ( ) ) )
    # this is robust eta _ e modified for uneven sampling from the Kim + 2014
    # paper
    eta_uneven_robust = ( ( npsum ( timeweights * p2p_abs_magdiffs ) / ( robvar * npsum ( timeweights ) ) ) * npmedian ( timeweights ) * ( ftimes [ - 1 ] - ftimes [ 0 ] ) * ( ftimes [ - 1 ] - ftimes [ 0 ] ) )
    return { 'eta_normal' : eta_normal , 'eta_robust' : eta_robust , 'eta_uneven_normal' : eta_uneven_normal , 'eta_uneven_robust' : eta_uneven_robust }
else :
    return None


signature:
def nonperiodic_lightcurve_features ( times , mags , errs , magsarefluxes = False ) :
    '''This calculates the following nonperiodic features of the light curve ,
 listed in Richards , et al . 2011 ) :
 - amplitude
 - beyond1std
 - flux _ percentile _ ratio _ mid20
 - flux _ percentile _ ratio _ mid35
 - flux _ percentile _ ratio _ mid50
 - flux _ percentile _ ratio _ mid65
 - flux _ percentile _ ratio _ mid80
 - linear _ trend
 - max _ slope
 - median _ absolute _ deviation
 - median _ buffer _ range _ percentage
 - pair _ slope _ trend
 - percent _ amplitude
 - percent _ difference _ flux _ percentile
 - skew
 - stdev
 - timelength
 - mintime
 - maxtime
 Parameters
 times , mags , errs : np . array
 The input mag / flux time - series to process .
 magsarefluxes : bool
 If True , will treat values in ` mags ` as fluxes instead of magnitudes .
 Returns
 dict
 A dict containing all of the features listed above .'''

implementation:
# remove nans first
finiteind = npisfinite ( times ) & npisfinite ( mags ) & npisfinite ( errs )
ftimes , fmags , ferrs = times [ finiteind ] , mags [ finiteind ] , errs [ finiteind ]
# remove zero errors
nzind = npnonzero ( ferrs )
ftimes , fmags , ferrs = ftimes [ nzind ] , fmags [ nzind ] , ferrs [ nzind ]
ndet = len ( fmags )
if ndet > 9 : # calculate the moments
    moments = lightcurve_moments ( ftimes , fmags , ferrs )
    # calculate the flux measures
    fluxmeasures = lightcurve_flux_measures ( ftimes , fmags , ferrs , magsarefluxes = magsarefluxes )
    # calculate the point - to - point measures
    ptpmeasures = lightcurve_ptp_measures ( ftimes , fmags , ferrs )
    # get the length in time
    mintime , maxtime = npmin ( ftimes ) , npmax ( ftimes )
    timelength = maxtime - mintime
    # get the amplitude
    series_amplitude = 0.5 * ( npmax ( fmags ) - npmin ( fmags ) )
    # calculate the linear fit to the entire mag series
    fitcoeffs = nppolyfit ( ftimes , fmags , 1 , w = 1.0 / ( ferrs * ferrs ) )
    series_linear_slope = fitcoeffs [ 1 ]
    # roll fmags by 1
    rolled_fmags = nproll ( fmags , 1 )
    # calculate the magnitude ratio ( from the WISE paper )
    series_magratio = ( ( npmax ( fmags ) - moments [ 'median' ] ) / ( npmax ( fmags ) - npmin ( fmags ) ) )
    # this is the dictionary returned containing all the measures
    measures = { 'ndet' : fmags . size , 'mintime' : mintime , 'maxtime' : maxtime , 'timelength' : timelength , 'amplitude' : series_amplitude , 'ndetobslength_ratio' : ndet / timelength , 'linear_fit_slope' : series_linear_slope , 'magnitude_ratio' : series_magratio , }
    if moments :
        measures . update ( moments )
    if ptpmeasures :
        measures . update ( ptpmeasures )
    if fluxmeasures :
        measures . update ( fluxmeasures )
    return measures
else :
    LOGERROR ( 'not enough detections in this magseries ' 'to calculate non-periodic features' )
    return None


signature:
def gilliland_cdpp ( times , mags , errs , windowlength = 97 , polyorder = 2 , binsize = 23400 , # in seconds : 6.5 hours for classic CDPP
sigclip = 5.0 , magsarefluxes = False , ** kwargs ) :
    '''This calculates the CDPP of a timeseries using the method in the paper :
 Gilliland , R . L . , Chaplin , W . J . , Dunham , E . W . , et al . 2011 , ApJS , 197 , 6
 ( http : / / adsabs . harvard . edu / abs / 2011ApJS . . 197 . . . . 6G )
 The steps are :
 - pass the time - series through a Savitsky - Golay filter .
 - we use ` scipy . signal . savgol _ filter ` , ` * * kwargs ` are passed to this .
 - also see : http : / / scipy . github . io / old - wiki / pages / Cookbook / SavitzkyGolay .
 - the ` windowlength ` is the number of LC points to use ( Kepler uses 2 days
 = ( 1440 minutes / day / 30 minutes / LC point ) x 2 days = 96 - > 97 LC
 points ) .
 - the ` polyorder ` is a quadratic by default .
 - subtract the smoothed time - series from the actual light curve .
 - sigma clip the remaining LC .
 - get the binned mag series by averaging over 6.5 hour bins , only retaining
 bins with at least 7 points .
 - the standard deviation of the binned averages is the CDPP .
 - multiply this by 1.168 to correct for over - subtraction of white - noise .
 Parameters
 times , mags , errs : np . array
 The input mag / flux time - series to calculate CDPP for .
 windowlength : int
 The smoothing window size to use .
 polyorder : int
 The polynomial order to use in the Savitsky - Golay smoothing .
 binsize : int
 The bin size to use for binning the light curve .
 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 .
 magsarefluxes : bool
 If True , indicates the input time - series is fluxes and not mags .
 kwargs : additional kwargs
 These are passed directly to ` scipy . signal . savgol _ filter ` .
 Returns
 float
 The calculated CDPP value .'''

implementation:
# if no errs are given , assume 0.1 % errors
if errs is None :
    errs = 0.001 * mags
# get rid of nans first
find = npisfinite ( times ) & npisfinite ( mags ) & npisfinite ( errs )
ftimes = times [ find ]
fmags = mags [ find ]
ferrs = errs [ find ]
if ftimes . size < ( 3 * windowlength ) :
    LOGERROR ( 'not enough LC points to calculate CDPP' )
    return npnan
# now get the smoothed mag series using the filter
# kwargs are provided to the savgol _ filter function
smoothed = savgol_filter ( fmags , windowlength , polyorder , ** kwargs )
subtracted = fmags - smoothed
# sigclip the subtracted light curve
stimes , smags , serrs = sigclip_magseries ( ftimes , subtracted , ferrs , magsarefluxes = magsarefluxes )
# bin over 6.5 hour bins and throw away all bins with less than 7 elements
binned = time_bin_magseries_with_errs ( stimes , smags , serrs , binsize = binsize , minbinelems = 7 )
bmags = binned [ 'binnedmags' ]
# stdev of bin mags x 1.168 - > CDPP
cdpp = npstd ( bmags ) * 1.168
return cdpp


signature:
def all_nonperiodic_features ( times , mags , errs , magsarefluxes = False , stetson_weightbytimediff = True ) :
    '''This rolls up the feature functions above and returns a single dict .
 NOTE : this doesn ' t calculate the CDPP to save time since binning and
 smoothing takes a while for dense light curves .
 Parameters
 times , mags , errs : np . array
 The input mag / flux time - series to calculate CDPP for .
 magsarefluxes : bool
 If True , indicates ` mags ` is actually an array of flux values .
 stetson _ weightbytimediff : bool
 If this is True , the Stetson index for any pair of mags will be
 reweighted by the difference in times between them using the scheme in
 Fruth + 2012 and Zhange + 2003 ( as seen in Sokolovsky + 2017 ) : :
 w _ i = exp ( - ( t _ i + 1 - t _ i ) / delta _ t )
 Returns
 dict
 Returns a dict with all of the variability features .'''

implementation:
# remove nans first
finiteind = npisfinite ( times ) & npisfinite ( mags ) & npisfinite ( errs )
ftimes , fmags , ferrs = times [ finiteind ] , mags [ finiteind ] , errs [ finiteind ]
# remove zero errors
nzind = npnonzero ( ferrs )
ftimes , fmags , ferrs = ftimes [ nzind ] , fmags [ nzind ] , ferrs [ nzind ]
xfeatures = nonperiodic_lightcurve_features ( times , mags , errs , magsarefluxes = magsarefluxes )
stetj = stetson_jindex ( ftimes , fmags , ferrs , weightbytimediff = stetson_weightbytimediff )
stetk = stetson_kindex ( fmags , ferrs )
xfeatures . update ( { 'stetsonj' : stetj , 'stetsonk' : stetk } )
return xfeatures


signature:
def _bls_runner ( times , mags , nfreq , freqmin , stepsize , nbins , minduration , maxduration ) :
    '''This runs the pyeebls . eebls function using the given inputs .
 Parameters
 times , mags : np . array
 The input magnitude time - series to search for transits .
 nfreq : int
 The number of frequencies to use when searching for transits .
 freqmin : float
 The minimum frequency of the period - search - > max period that will be
 used for the search .
 stepsize : float
 The step - size in frequency to use to generate a frequency - grid .
 nbins : int
 The number of phase bins to use .
 minduration : float
 The minimum fractional transit duration that will be considered .
 maxduration : float
 The maximum fractional transit duration that will be considered .
 Returns
 dict
 Returns a dict of the form : :
 ' power ' : the periodogram power array ,
 ' bestperiod ' : the best period found ,
 ' bestpower ' : the highest peak of the periodogram power ,
 ' transdepth ' : transit depth found by eebls . f ,
 ' transduration ' : transit duration found by eebls . f ,
 ' transingressbin ' : transit ingress bin found by eebls . f ,
 ' transegressbin ' : transit egress bin found by eebls . f ,'''

implementation:
workarr_u = npones ( times . size )
workarr_v = npones ( times . size )
blsresult = eebls ( times , mags , workarr_u , workarr_v , nfreq , freqmin , stepsize , nbins , minduration , maxduration )
return { 'power' : blsresult [ 0 ] , 'bestperiod' : blsresult [ 1 ] , 'bestpower' : blsresult [ 2 ] , 'transdepth' : blsresult [ 3 ] , 'transduration' : blsresult [ 4 ] , 'transingressbin' : blsresult [ 5 ] , 'transegressbin' : blsresult [ 6 ] }


signature:
def _parallel_bls_worker ( task ) :
    '''This wraps the BLS function for the parallel driver below .
 Parameters
 tasks : tuple
 This is of the form : :
 task [ 0 ] = times
 task [ 1 ] = mags
 task [ 2 ] = nfreq
 task [ 3 ] = freqmin
 task [ 4 ] = stepsize
 task [ 5 ] = nbins
 task [ 6 ] = minduration
 task [ 7 ] = maxduration
 Returns
 dict
 Returns a dict of the form : :
 ' power ' : the periodogram power array ,
 ' bestperiod ' : the best period found ,
 ' bestpower ' : the highest peak of the periodogram power ,
 ' transdepth ' : transit depth found by eebls . f ,
 ' transduration ' : transit duration found by eebls . f ,
 ' transingressbin ' : transit ingress bin found by eebls . f ,
 ' transegressbin ' : transit egress bin found by eebls . f ,'''

implementation:
try :
    return _bls_runner ( * task )
except Exception as e :
    LOGEXCEPTION ( 'BLS failed for task %s' % repr ( task [ 2 : ] ) )
    return { 'power' : nparray ( [ npnan for x in range ( task [ 2 ] ) ] ) , 'bestperiod' : npnan , 'bestpower' : npnan , 'transdepth' : npnan , 'transduration' : npnan , 'transingressbin' : npnan , 'transegressbin' : npnan }


signature:
def bls_serial_pfind ( times , mags , errs , magsarefluxes = False , startp = 0.1 , # search from 0.1 d to . . .
endp = 100.0 , # . . . 100.0 d - - don ' t search full timebase
stepsize = 5.0e-4 , mintransitduration = 0.01 , # minimum transit length in phase
maxtransitduration = 0.4 , # maximum transit length in phase
nphasebins = 200 , autofreq = True , # figure out f0 , nf , and df automatically
periodepsilon = 0.1 , nbestpeaks = 5 , sigclip = 10.0 , verbose = True , get_stats = True , ) :
    '''Runs the Box Least Squares Fitting Search for transit - shaped signals .
 Based on eebls . f from Kovacs et al . 2002 and python - bls from Foreman - Mackey
 et al . 2015 . This is the serial version ( which is good enough in most cases
 because BLS in Fortran is fairly fast ) . If nfreq > 5e5 , this will take a
 while .
 Parameters
 times , mags , errs : np . array
 The magnitude / flux time - series to search for transits .
 magsarefluxes : bool
 If the input measurement values in ` mags ` and ` errs ` are in fluxes , set
 this to True .
 startp , endp : float
 The minimum and maximum periods to consider for the transit search .
 stepsize : float
 The step - size in frequency to use when constructing a frequency grid for
 the period search .
 mintransitduration , maxtransitduration : float
 The minimum and maximum transitdurations ( in units of phase ) to consider
 for the transit search .
 nphasebins : int
 The number of phase bins to use in the period search .
 autofreq : bool
 If this is True , the values of ` stepsize ` and ` nphasebins ` will be
 ignored , and these , along with a frequency - grid , will be determined
 based on the following relations : :
 nphasebins = int ( ceil ( 2.0 / mintransitduration ) )
 if nphasebins > 3000:
 nphasebins = 3000
 stepsize = 0.25 * mintransitduration / ( times . max ( ) - times . min ( ) )
 minfreq = 1.0 / endp
 maxfreq = 1.0 / startp
 nfreq = int ( ceil ( ( maxfreq - minfreq ) / stepsize ) )
 periodepsilon : float
 The fractional difference between successive values of ' best ' periods
 when sorting by periodogram power to consider them as separate periods
 ( as opposed to part of the same periodogram peak ) . This is used to avoid
 broad peaks in the periodogram and make sure the ' best ' periods returned
 are all actually independent .
 nbestpeaks : int
 The number of ' best ' peaks to return from the periodogram results ,
 starting from the global maximum of the periodogram peak values .
 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 .
 verbose : bool
 If this is True , will indicate progress and details about the frequency
 grid used for the period search .
 get _ stats : bool
 If True , runs : py : func : ` . bls _ stats _ singleperiod ` for each of the best
 periods in the output and injects the output into the output dict so you
 only have to run this function to get the periods and their stats .
 Returns
 dict
 This function returns a dict , referred to as an ` lspinfo ` dict in other
 astrobase functions that operate on periodogram results . This is a
 standardized format across all astrobase period - finders , and is of the
 form below : :
 { ' bestperiod ' : the best period value in the periodogram ,
 ' bestlspval ' : the periodogram peak associated with the best period ,
 ' nbestpeaks ' : the input value of nbestpeaks ,
 ' nbestlspvals ' : nbestpeaks - size list of best period peak values ,
 ' nbestperiods ' : nbestpeaks - size list of best periods ,
 ' stats ' : BLS stats for each best period ,
 ' lspvals ' : the full array of periodogram powers ,
 ' frequencies ' : the full array of frequencies considered ,
 ' periods ' : the full array of periods considered ,
 ' blsresult ' : the result dict from the eebls . f wrapper function ,
 ' stepsize ' : the actual stepsize used ,
 ' nfreq ' : the actual nfreq used ,
 ' nphasebins ' : the actual nphasebins used ,
 ' mintransitduration ' : the input mintransitduration ,
 ' maxtransitduration ' : the input maxtransitdurations ,
 ' method ' : ' bls ' - > the name of the period - finder method ,
 ' kwargs ' : { dict of all of the input kwargs for record - keeping } }'''

implementation:
# get rid of nans first and sigclip
stimes , smags , serrs = sigclip_magseries ( times , mags , errs , magsarefluxes = magsarefluxes , sigclip = sigclip )
# make sure there are enough points to calculate a spectrum
if len ( stimes ) > 9 and len ( smags ) > 9 and len ( serrs ) > 9 : # if we ' re setting up everything automatically
    if autofreq : # figure out the best number of phasebins to use
        nphasebins = int ( npceil ( 2.0 / mintransitduration ) )
        if nphasebins > 3000 :
            nphasebins = 3000
        # use heuristic to figure out best timestep
        stepsize = 0.25 * mintransitduration / ( stimes . max ( ) - stimes . min ( ) )
        # now figure out the frequencies to use
        minfreq = 1.0 / endp
        maxfreq = 1.0 / startp
        nfreq = int ( npceil ( ( maxfreq - minfreq ) / stepsize ) )
        # say what we ' re using
        if verbose :
            LOGINFO ( 'min P: %s, max P: %s, nfreq: %s, ' 'minfreq: %s, maxfreq: %s' % ( startp , endp , nfreq , minfreq , maxfreq ) )
            LOGINFO ( 'autofreq = True: using AUTOMATIC values for ' 'freq stepsize: %s, nphasebins: %s, ' 'min transit duration: %s, max transit duration: %s' % ( stepsize , nphasebins , mintransitduration , maxtransitduration ) )
    else :
        minfreq = 1.0 / endp
        maxfreq = 1.0 / startp
        nfreq = int ( npceil ( ( maxfreq - minfreq ) / stepsize ) )
        # say what we ' re using
        if verbose :
            LOGINFO ( 'min P: %s, max P: %s, nfreq: %s, ' 'minfreq: %s, maxfreq: %s' % ( startp , endp , nfreq , minfreq , maxfreq ) )
            LOGINFO ( 'autofreq = False: using PROVIDED values for ' 'freq stepsize: %s, nphasebins: %s, ' 'min transit duration: %s, max transit duration: %s' % ( stepsize , nphasebins , mintransitduration , maxtransitduration ) )
    if nfreq > 5.0e5 :
        if verbose :
            LOGWARNING ( 'more than 5.0e5 frequencies to go through; ' 'this will take a while. ' 'you might want to use the ' 'periodbase.bls_parallel_pfind function instead' )
    if minfreq < ( 1.0 / ( stimes . max ( ) - stimes . min ( ) ) ) :
        if verbose :
            LOGWARNING ( 'the requested max P = %.3f is larger than ' 'the time base of the observations = %.3f, ' ' will make minfreq = 2 x 1/timebase' % ( endp , stimes . max ( ) - stimes . min ( ) ) )
        minfreq = 2.0 / ( stimes . max ( ) - stimes . min ( ) )
        if verbose :
            LOGINFO ( 'new minfreq: %s, maxfreq: %s' % ( minfreq , maxfreq ) )
    # run BLS
    try :
        blsresult = _bls_runner ( stimes , smags , nfreq , minfreq , stepsize , nphasebins , mintransitduration , maxtransitduration )
        # find the peaks in the BLS . this uses wavelet transforms to
        # smooth the spectrum and find peaks . a similar thing would be
        # to do a convolution with a gaussian kernel or a tophat
        # function , calculate d / dx ( result ) , then get indices where this
        # is zero
        # blspeakinds = find _ peaks _ cwt ( blsresults [ ' power ' ] ,
        # nparray ( [ 2.0,3.0,4.0,5.0 ] ) )
        frequencies = minfreq + nparange ( nfreq ) * stepsize
        periods = 1.0 / frequencies
        lsp = blsresult [ 'power' ]
        # find the nbestpeaks for the periodogram : 1 . sort the lsp array
        # by highest value first 2 . go down the values until we find
        # five values that are separated by at least periodepsilon in
        # period
        # make sure to get only the finite peaks in the periodogram
        # this is needed because BLS may produce infs for some peaks
        finitepeakind = npisfinite ( lsp )
        finlsp = lsp [ finitepeakind ]
        finperiods = periods [ finitepeakind ]
        # make sure that finlsp has finite values before we work on it
        try :
            bestperiodind = npargmax ( finlsp )
        except ValueError :
            LOGERROR ( 'no finite periodogram values ' 'for this mag series, skipping...' )
            return { 'bestperiod' : npnan , 'bestlspval' : npnan , 'nbestpeaks' : nbestpeaks , 'nbestlspvals' : None , 'nbestperiods' : None , 'lspvals' : None , 'periods' : None , 'method' : 'bls' , 'kwargs' : { 'startp' : startp , 'endp' : endp , 'stepsize' : stepsize , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'nphasebins' : nphasebins , 'autofreq' : autofreq , 'periodepsilon' : periodepsilon , 'nbestpeaks' : nbestpeaks , 'sigclip' : sigclip , 'magsarefluxes' : magsarefluxes } }
        sortedlspind = npargsort ( finlsp ) [ : : - 1 ]
        sortedlspperiods = finperiods [ sortedlspind ]
        sortedlspvals = finlsp [ sortedlspind ]
        # now get the nbestpeaks
        nbestperiods , nbestlspvals , peakcount = ( [ finperiods [ bestperiodind ] ] , [ finlsp [ bestperiodind ] ] , 1 )
        prevperiod = sortedlspperiods [ 0 ]
        # find the best nbestpeaks in the lsp and their periods
        for period , lspval in zip ( sortedlspperiods , sortedlspvals ) :
            if peakcount == nbestpeaks :
                break
            perioddiff = abs ( period - prevperiod )
            bestperiodsdiff = [ abs ( period - x ) for x in nbestperiods ]
            # print ( ' prevperiod = % s , thisperiod = % s , '
            # ' perioddiff = % s , peakcount = % s ' %
            # ( prevperiod , period , perioddiff , peakcount ) )
            # this ensures that this period is different from the last
            # period and from all the other existing best periods by
            # periodepsilon to make sure we jump to an entire different
            # peak in the periodogram
            if ( perioddiff > ( periodepsilon * prevperiod ) and all ( x > ( periodepsilon * period ) for x in bestperiodsdiff ) ) :
                nbestperiods . append ( period )
                nbestlspvals . append ( lspval )
                peakcount = peakcount + 1
            prevperiod = period
        # generate the return dict
        resultdict = { 'bestperiod' : finperiods [ bestperiodind ] , 'bestlspval' : finlsp [ bestperiodind ] , 'nbestpeaks' : nbestpeaks , 'nbestlspvals' : nbestlspvals , 'nbestperiods' : nbestperiods , 'lspvals' : lsp , 'frequencies' : frequencies , 'periods' : periods , 'blsresult' : blsresult , 'stepsize' : stepsize , 'nfreq' : nfreq , 'nphasebins' : nphasebins , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'method' : 'bls' , 'kwargs' : { 'startp' : startp , 'endp' : endp , 'stepsize' : stepsize , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'nphasebins' : nphasebins , 'autofreq' : autofreq , 'periodepsilon' : periodepsilon , 'nbestpeaks' : nbestpeaks , 'sigclip' : sigclip , 'magsarefluxes' : magsarefluxes } }
        # get stats if requested
        if get_stats :
            resultdict [ 'stats' ] = [ ]
            for bp in nbestperiods :
                if verbose :
                    LOGINFO ( "Getting stats for best period: %.6f" % bp )
                this_pstats = bls_stats_singleperiod ( times , mags , errs , bp , magsarefluxes = resultdict [ 'kwargs' ] [ 'magsarefluxes' ] , sigclip = resultdict [ 'kwargs' ] [ 'sigclip' ] , nphasebins = resultdict [ 'nphasebins' ] , mintransitduration = resultdict [ 'mintransitduration' ] , maxtransitduration = resultdict [ 'maxtransitduration' ] , verbose = verbose , )
                resultdict [ 'stats' ] . append ( this_pstats )
        return resultdict
    except Exception as e :
        LOGEXCEPTION ( 'BLS failed!' )
        return { 'bestperiod' : npnan , 'bestlspval' : npnan , 'nbestpeaks' : nbestpeaks , 'nbestlspvals' : None , 'nbestperiods' : None , 'lspvals' : None , 'periods' : None , 'blsresult' : None , 'stepsize' : stepsize , 'nfreq' : nfreq , 'nphasebins' : nphasebins , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'method' : 'bls' , 'kwargs' : { 'startp' : startp , 'endp' : endp , 'stepsize' : stepsize , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'nphasebins' : nphasebins , 'autofreq' : autofreq , 'periodepsilon' : periodepsilon , 'nbestpeaks' : nbestpeaks , 'sigclip' : sigclip , 'magsarefluxes' : magsarefluxes } }
else :
    LOGERROR ( 'no good detections for these times and mags, skipping...' )
    return { 'bestperiod' : npnan , 'bestlspval' : npnan , 'nbestpeaks' : nbestpeaks , 'nbestlspvals' : None , 'nbestperiods' : None , 'lspvals' : None , 'periods' : None , 'blsresult' : None , 'stepsize' : stepsize , 'nfreq' : None , 'nphasebins' : None , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'method' : 'bls' , 'kwargs' : { 'startp' : startp , 'endp' : endp , 'stepsize' : stepsize , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'nphasebins' : nphasebins , 'autofreq' : autofreq , 'periodepsilon' : periodepsilon , 'nbestpeaks' : nbestpeaks , 'sigclip' : sigclip , 'magsarefluxes' : magsarefluxes } }


signature:
def bls_parallel_pfind ( times , mags , errs , magsarefluxes = False , startp = 0.1 , # by default , search from 0.1 d to . . .
endp = 100.0 , # . . . 100.0 d - - don ' t search full timebase
stepsize = 1.0e-4 , mintransitduration = 0.01 , # minimum transit length in phase
maxtransitduration = 0.4 , # maximum transit length in phase
nphasebins = 200 , autofreq = True , # figure out f0 , nf , and df automatically
nbestpeaks = 5 , periodepsilon = 0.1 , # 0.1
sigclip = 10.0 , verbose = True , nworkers = None , get_stats = True , ) :
    '''Runs the Box Least Squares Fitting Search for transit - shaped signals .
 Based on eebls . f from Kovacs et al . 2002 and python - bls from Foreman - Mackey
 et al . 2015 . Breaks up the full frequency space into chunks and passes them
 to parallel BLS workers .
 NOTE : the combined BLS spectrum produced by this function is not identical
 to that produced by running BLS in one shot for the entire frequency
 space . There are differences on the order of 1.0e - 3 or so in the respective
 peak values , but peaks appear at the same frequencies for both methods . This
 is likely due to different aliasing caused by smaller chunks of the
 frequency space used by the parallel workers in this function . When in
 doubt , confirm results for this parallel implementation by comparing to
 those from the serial implementation above .
 Parameters
 times , mags , errs : np . array
 The magnitude / flux time - series to search for transits .
 magsarefluxes : bool
 If the input measurement values in ` mags ` and ` errs ` are in fluxes , set
 this to True .
 startp , endp : float
 The minimum and maximum periods to consider for the transit search .
 stepsize : float
 The step - size in frequency to use when constructing a frequency grid for
 the period search .
 mintransitduration , maxtransitduration : float
 The minimum and maximum transitdurations ( in units of phase ) to consider
 for the transit search .
 nphasebins : int
 The number of phase bins to use in the period search .
 autofreq : bool
 If this is True , the values of ` stepsize ` and ` nphasebins ` will be
 ignored , and these , along with a frequency - grid , will be determined
 based on the following relations : :
 nphasebins = int ( ceil ( 2.0 / mintransitduration ) )
 if nphasebins > 3000:
 nphasebins = 3000
 stepsize = 0.25 * mintransitduration / ( times . max ( ) - times . min ( ) )
 minfreq = 1.0 / endp
 maxfreq = 1.0 / startp
 nfreq = int ( ceil ( ( maxfreq - minfreq ) / stepsize ) )
 periodepsilon : float
 The fractional difference between successive values of ' best ' periods
 when sorting by periodogram power to consider them as separate periods
 ( as opposed to part of the same periodogram peak ) . This is used to avoid
 broad peaks in the periodogram and make sure the ' best ' periods returned
 are all actually independent .
 nbestpeaks : int
 The number of ' best ' peaks to return from the periodogram results ,
 starting from the global maximum of the periodogram peak values .
 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 .
 verbose : bool
 If this is True , will indicate progress and details about the frequency
 grid used for the period search .
 nworkers : int or None
 The number of parallel workers to launch for period - search . If None ,
 nworkers = NCPUS .
 get _ stats : bool
 If True , runs : py : func : ` . bls _ stats _ singleperiod ` for each of the best
 periods in the output and injects the output into the output dict so you
 only have to run this function to get the periods and their stats .
 Returns
 dict
 This function returns a dict , referred to as an ` lspinfo ` dict in other
 astrobase functions that operate on periodogram results . This is a
 standardized format across all astrobase period - finders , and is of the
 form below : :
 { ' bestperiod ' : the best period value in the periodogram ,
 ' bestlspval ' : the periodogram peak associated with the best period ,
 ' nbestpeaks ' : the input value of nbestpeaks ,
 ' nbestlspvals ' : nbestpeaks - size list of best period peak values ,
 ' nbestperiods ' : nbestpeaks - size list of best periods ,
 ' stats ' : list of stats dicts returned for each best period ,
 ' lspvals ' : the full array of periodogram powers ,
 ' frequencies ' : the full array of frequencies considered ,
 ' periods ' : the full array of periods considered ,
 ' blsresult ' : list of result dicts from eebls . f wrapper functions ,
 ' stepsize ' : the actual stepsize used ,
 ' nfreq ' : the actual nfreq used ,
 ' nphasebins ' : the actual nphasebins used ,
 ' mintransitduration ' : the input mintransitduration ,
 ' maxtransitduration ' : the input maxtransitdurations ,
 ' method ' : ' bls ' - > the name of the period - finder method ,
 ' kwargs ' : { dict of all of the input kwargs for record - keeping } }'''

implementation:
# get rid of nans first and sigclip
stimes , smags , serrs = sigclip_magseries ( times , mags , errs , magsarefluxes = magsarefluxes , sigclip = sigclip )
# make sure there are enough points to calculate a spectrum
if len ( stimes ) > 9 and len ( smags ) > 9 and len ( serrs ) > 9 : # if we ' re setting up everything automatically
    if autofreq : # figure out the best number of phasebins to use
        nphasebins = int ( npceil ( 2.0 / mintransitduration ) )
        if nphasebins > 3000 :
            nphasebins = 3000
        # use heuristic to figure out best timestep
        stepsize = 0.25 * mintransitduration / ( stimes . max ( ) - stimes . min ( ) )
        # now figure out the frequencies to use
        minfreq = 1.0 / endp
        maxfreq = 1.0 / startp
        nfreq = int ( npceil ( ( maxfreq - minfreq ) / stepsize ) )
        # say what we ' re using
        if verbose :
            LOGINFO ( 'min P: %s, max P: %s, nfreq: %s, ' 'minfreq: %s, maxfreq: %s' % ( startp , endp , nfreq , minfreq , maxfreq ) )
            LOGINFO ( 'autofreq = True: using AUTOMATIC values for ' 'freq stepsize: %s, nphasebins: %s, ' 'min transit duration: %s, max transit duration: %s' % ( stepsize , nphasebins , mintransitduration , maxtransitduration ) )
    else :
        minfreq = 1.0 / endp
        maxfreq = 1.0 / startp
        nfreq = int ( npceil ( ( maxfreq - minfreq ) / stepsize ) )
        # say what we ' re using
        if verbose :
            LOGINFO ( 'min P: %s, max P: %s, nfreq: %s, ' 'minfreq: %s, maxfreq: %s' % ( startp , endp , nfreq , minfreq , maxfreq ) )
            LOGINFO ( 'autofreq = False: using PROVIDED values for ' 'freq stepsize: %s, nphasebins: %s, ' 'min transit duration: %s, max transit duration: %s' % ( stepsize , nphasebins , mintransitduration , maxtransitduration ) )
    # check the minimum frequency
    if minfreq < ( 1.0 / ( stimes . max ( ) - stimes . min ( ) ) ) :
        minfreq = 2.0 / ( stimes . max ( ) - stimes . min ( ) )
        if verbose :
            LOGWARNING ( 'the requested max P = %.3f is larger than ' 'the time base of the observations = %.3f, ' ' will make minfreq = 2 x 1/timebase' % ( endp , stimes . max ( ) - stimes . min ( ) ) )
            LOGINFO ( 'new minfreq: %s, maxfreq: %s' % ( minfreq , maxfreq ) )
    # # NOW RUN BLS IN PARALLEL # #
    # fix number of CPUs if needed
    if not nworkers or nworkers > NCPUS :
        nworkers = NCPUS
        if verbose :
            LOGINFO ( 'using %s workers...' % nworkers )
    # the frequencies array to be searched
    frequencies = minfreq + nparange ( nfreq ) * stepsize
    # break up the tasks into chunks
    csrem = int ( fmod ( nfreq , nworkers ) )
    csint = int ( float ( nfreq / nworkers ) )
    chunk_minfreqs , chunk_nfreqs = [ ] , [ ]
    for x in range ( nworkers ) :
        this_minfreqs = frequencies [ x * csint ]
        # handle usual nfreqs
        if x < ( nworkers - 1 ) :
            this_nfreqs = frequencies [ x * csint : x * csint + csint ] . size
        else :
            this_nfreqs = frequencies [ x * csint : x * csint + csint + csrem ] . size
        chunk_minfreqs . append ( this_minfreqs )
        chunk_nfreqs . append ( this_nfreqs )
    # populate the tasks list
    tasks = [ ( stimes , smags , chunk_minf , chunk_nf , stepsize , nphasebins , mintransitduration , maxtransitduration ) for ( chunk_nf , chunk_minf ) in zip ( chunk_minfreqs , chunk_nfreqs ) ]
    if verbose :
        for ind , task in enumerate ( tasks ) :
            LOGINFO ( 'worker %s: minfreq = %.6f, nfreqs = %s' % ( ind + 1 , task [ 3 ] , task [ 2 ] ) )
        LOGINFO ( 'running...' )
    # return tasks
    # start the pool
    pool = Pool ( nworkers )
    results = pool . map ( _parallel_bls_worker , tasks )
    pool . close ( )
    pool . join ( )
    del pool
    # now concatenate the output lsp arrays
    lsp = npconcatenate ( [ x [ 'power' ] for x in results ] )
    periods = 1.0 / frequencies
    # find the nbestpeaks for the periodogram : 1 . sort the lsp array
    # by highest value first 2 . go down the values until we find
    # five values that are separated by at least periodepsilon in
    # period
    # make sure to get only the finite peaks in the periodogram
    # this is needed because BLS may produce infs for some peaks
    finitepeakind = npisfinite ( lsp )
    finlsp = lsp [ finitepeakind ]
    finperiods = periods [ finitepeakind ]
    # make sure that finlsp has finite values before we work on it
    try :
        bestperiodind = npargmax ( finlsp )
    except ValueError :
        LOGERROR ( 'no finite periodogram values ' 'for this mag series, skipping...' )
        return { 'bestperiod' : npnan , 'bestlspval' : npnan , 'nbestpeaks' : nbestpeaks , 'nbestlspvals' : None , 'nbestperiods' : None , 'lspvals' : None , 'periods' : None , 'blsresult' : None , 'method' : 'bls' , 'kwargs' : { 'startp' : startp , 'endp' : endp , 'stepsize' : stepsize , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'nphasebins' : nphasebins , 'autofreq' : autofreq , 'periodepsilon' : periodepsilon , 'nbestpeaks' : nbestpeaks , 'sigclip' : sigclip , 'magsarefluxes' : magsarefluxes } }
    sortedlspind = npargsort ( finlsp ) [ : : - 1 ]
    sortedlspperiods = finperiods [ sortedlspind ]
    sortedlspvals = finlsp [ sortedlspind ]
    # now get the nbestpeaks
    nbestperiods , nbestlspvals , peakcount = ( [ finperiods [ bestperiodind ] ] , [ finlsp [ bestperiodind ] ] , 1 )
    prevperiod = sortedlspperiods [ 0 ]
    # find the best nbestpeaks in the lsp and their periods
    for period , lspval in zip ( sortedlspperiods , sortedlspvals ) :
        if peakcount == nbestpeaks :
            break
        perioddiff = abs ( period - prevperiod )
        bestperiodsdiff = [ abs ( period - x ) for x in nbestperiods ]
        # this ensures that this period is different from the last
        # period and from all the other existing best periods by
        # periodepsilon to make sure we jump to an entire different
        # peak in the periodogram
        if ( perioddiff > ( periodepsilon * prevperiod ) and all ( x > ( periodepsilon * period ) for x in bestperiodsdiff ) ) :
            nbestperiods . append ( period )
            nbestlspvals . append ( lspval )
            peakcount = peakcount + 1
        prevperiod = period
    # generate the return dict
    resultdict = { 'bestperiod' : finperiods [ bestperiodind ] , 'bestlspval' : finlsp [ bestperiodind ] , 'nbestpeaks' : nbestpeaks , 'nbestlspvals' : nbestlspvals , 'nbestperiods' : nbestperiods , 'lspvals' : lsp , 'frequencies' : frequencies , 'periods' : periods , 'blsresult' : results , 'stepsize' : stepsize , 'nfreq' : nfreq , 'nphasebins' : nphasebins , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'method' : 'bls' , 'kwargs' : { 'startp' : startp , 'endp' : endp , 'stepsize' : stepsize , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'nphasebins' : nphasebins , 'autofreq' : autofreq , 'periodepsilon' : periodepsilon , 'nbestpeaks' : nbestpeaks , 'sigclip' : sigclip , 'magsarefluxes' : magsarefluxes } }
    # get stats if requested
    if get_stats :
        resultdict [ 'stats' ] = [ ]
        for bp in nbestperiods . copy ( ) :
            if verbose :
                LOGINFO ( "Getting stats for best period: %.6f" % bp )
            this_pstats = bls_stats_singleperiod ( times , mags , errs , bp , magsarefluxes = resultdict [ 'kwargs' ] [ 'magsarefluxes' ] , sigclip = resultdict [ 'kwargs' ] [ 'sigclip' ] , nphasebins = resultdict [ 'nphasebins' ] , mintransitduration = resultdict [ 'mintransitduration' ] , maxtransitduration = resultdict [ 'maxtransitduration' ] , verbose = verbose , )
            resultdict [ 'stats' ] . append ( this_pstats )
    return resultdict
else :
    LOGERROR ( 'no good detections for these times and mags, skipping...' )
    return { 'bestperiod' : npnan , 'bestlspval' : npnan , 'nbestpeaks' : nbestpeaks , 'nbestlspvals' : None , 'nbestperiods' : None , 'lspvals' : None , 'periods' : None , 'blsresult' : None , 'stepsize' : stepsize , 'nfreq' : None , 'nphasebins' : None , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'method' : 'bls' , 'kwargs' : { 'startp' : startp , 'endp' : endp , 'stepsize' : stepsize , 'mintransitduration' : mintransitduration , 'maxtransitduration' : maxtransitduration , 'nphasebins' : nphasebins , 'autofreq' : autofreq , 'periodepsilon' : periodepsilon , 'nbestpeaks' : nbestpeaks , 'sigclip' : sigclip , 'magsarefluxes' : magsarefluxes } }


signature:
def _get_bls_stats ( stimes , smags , serrs , thistransdepth , thistransduration , ingressdurationfraction , nphasebins , thistransingressbin , thistransegressbin , thisbestperiod , thisnphasebins , magsarefluxes = False , verbose = False ) :
    '''Actually calculates the stats .'''

implementation:
try : # try getting the minimum light epoch using the phase bin method
    me_epochbin = int ( ( thistransegressbin + thistransingressbin ) / 2.0 )
    me_phases = ( ( stimes - stimes . min ( ) ) / thisbestperiod - npfloor ( ( stimes - stimes . min ( ) ) / thisbestperiod ) )
    me_phases_sortind = npargsort ( me_phases )
    me_sorted_phases = me_phases [ me_phases_sortind ]
    me_sorted_times = stimes [ me_phases_sortind ]
    me_bins = nplinspace ( 0.0 , 1.0 , thisnphasebins )
    me_bininds = npdigitize ( me_sorted_phases , me_bins )
    me_centertransit_ind = me_bininds == me_epochbin
    me_centertransit_phase = ( npmedian ( me_sorted_phases [ me_centertransit_ind ] ) )
    me_centertransit_timeloc = npwhere ( npabs ( me_sorted_phases - me_centertransit_phase ) == npmin ( npabs ( me_sorted_phases - me_centertransit_phase ) ) )
    me_centertransit_time = me_sorted_times [ me_centertransit_timeloc ]
    if me_centertransit_time . size > 1 :
        LOGWARNING ( 'multiple possible times-of-center transits ' 'found for period %.7f, picking the first ' 'one from: %s' % ( thisbestperiod , repr ( me_centertransit_time ) ) )
    thisminepoch = me_centertransit_time [ 0 ]
except Exception as e :
    LOGEXCEPTION ( 'could not determine the center time of transit for ' 'the phased LC, trying SavGol fit instead...' )
    # fit a Savitsky - Golay instead and get its minimum
    savfit = savgol_fit_magseries ( stimes , smags , serrs , thisbestperiod , magsarefluxes = magsarefluxes , verbose = verbose , sigclip = None )
    thisminepoch = savfit [ 'fitinfo' ] [ 'fitepoch' ]
if isinstance ( thisminepoch , npndarray ) :
    if verbose :
        LOGWARNING ( 'minimum epoch is actually an array:\n' '%s\n' 'instead of a float, ' 'are there duplicate time values ' 'in the original input? ' 'will use the first value in this array.' % repr ( thisminepoch ) )
    thisminepoch = thisminepoch [ 0 ]
# set up trapezoid transit model to fit for this LC
transitparams = [ thisbestperiod , thisminepoch , thistransdepth , thistransduration , ingressdurationfraction * thistransduration ]
modelfit = traptransit_fit_magseries ( stimes , smags , serrs , transitparams , sigclip = None , magsarefluxes = magsarefluxes , verbose = verbose )
# if the model fit succeeds , calculate SNR using the trapezoid model fit
if modelfit and modelfit [ 'fitinfo' ] [ 'finalparams' ] is not None :
    fitparams = modelfit [ 'fitinfo' ] [ 'finalparams' ]
    fiterrs = modelfit [ 'fitinfo' ] [ 'finalparamerrs' ]
    modelmags , actualmags , modelphase = ( modelfit [ 'fitinfo' ] [ 'fitmags' ] , modelfit [ 'magseries' ] [ 'mags' ] , modelfit [ 'magseries' ] [ 'phase' ] )
    subtractedmags = actualmags - modelmags
    subtractedrms = npstd ( subtractedmags )
    fit_period , fit_epoch , fit_depth , fit_duration , fit_ingress_dur = ( fitparams )
    npts_in_transit = modelfit [ 'fitinfo' ] [ 'ntransitpoints' ]
    transit_snr = ( npsqrt ( npts_in_transit ) * npabs ( fit_depth / subtractedrms ) )
    if verbose :
        LOGINFO ( 'refit best period: %.6f, ' 'refit center of transit: %.5f' % ( fit_period , fit_epoch ) )
        LOGINFO ( 'npoints in transit: %s' % npts_in_transit )
        LOGINFO ( 'transit depth (delta): %.5f, ' 'frac transit length (q): %.3f, ' ' SNR: %.3f' % ( fit_depth , fit_duration , transit_snr ) )
    return { 'period' : fit_period , 'epoch' : fit_epoch , 'snr' : transit_snr , 'transitdepth' : fit_depth , 'transitduration' : fit_duration , 'nphasebins' : nphasebins , 'transingressbin' : thistransingressbin , 'transegressbin' : thistransegressbin , 'npoints_in_transit' : npts_in_transit , 'blsmodel' : modelmags , 'subtractedmags' : subtractedmags , 'phasedmags' : actualmags , 'phases' : modelphase , 'fitparams' : fitparams , 'fiterrs' : fiterrs , 'fitinfo' : modelfit }
# if the model fit doesn ' t work , then do the SNR calculation the old way
else : # phase using this epoch
    phased_magseries = phase_magseries_with_errs ( stimes , smags , serrs , thisbestperiod , thisminepoch , wrap = False , sort = True )
    tphase = phased_magseries [ 'phase' ]
    tmags = phased_magseries [ 'mags' ]
    # use the transit depth and duration to subtract the BLS transit
    # model from the phased mag series . we ' re centered about 0.0 as the
    # phase of the transit minimum so we need to look at stuff from
    # [0.0 , transitphase ] and [ 1.0 - transitphase , 1.0]
    transitphase = thistransduration / 2.0
    transitindices = ( ( tphase < transitphase ) | ( tphase > ( 1.0 - transitphase ) ) )
    # this is the BLS model
    # constant = median ( tmags ) outside transit
    # constant = thistransitdepth inside transit
    blsmodel = npfull_like ( tmags , npmedian ( tmags ) )
    if magsarefluxes : # eebls . f returns + ve transit depth for fluxes
    # so we need to subtract here to get fainter fluxes in transit
        blsmodel [ transitindices ] = ( blsmodel [ transitindices ] - thistransdepth )
    else : # eebls . f returns - ve transit depth for magnitudes
    # so we need to subtract here to get fainter mags in transits
        blsmodel [ transitindices ] = ( blsmodel [ transitindices ] - thistransdepth )
    # see _ _ init _ _ / get _ snr _ of _ dip docstring for description of transit
    # SNR equation , which is what we use for ` thissnr ` .
    subtractedmags = tmags - blsmodel
    subtractedrms = npstd ( subtractedmags )
    npts_in_transit = len ( tmags [ transitindices ] )
    thissnr = ( npsqrt ( npts_in_transit ) * npabs ( thistransdepth / subtractedrms ) )
    # tell user about stuff if verbose = True
    if verbose :
        LOGINFO ( 'refit best period: %.6f, ' 'refit center of transit: %.5f' % ( thisbestperiod , thisminepoch ) )
        LOGINFO ( 'transit ingress phase = %.3f to %.3f' % ( 1.0 - transitphase , 1.0 ) )
        LOGINFO ( 'transit egress phase = %.3f to %.3f' % ( 0.0 , transitphase ) )
        LOGINFO ( 'npoints in transit: %s' % tmags [ transitindices ] . size )
        LOGINFO ( 'transit depth (delta): %.5f, ' 'frac transit length (q): %.3f, ' ' SNR: %.3f' % ( thistransdepth , thistransduration , thissnr ) )
    return { 'period' : thisbestperiod , 'epoch' : thisminepoch , 'snr' : thissnr , 'transitdepth' : thistransdepth , 'transitduration' : thistransduration , 'nphasebins' : nphasebins , 'transingressbin' : thistransingressbin , 'transegressbin' : thistransegressbin , 'blsmodel' : blsmodel , 'subtractedmags' : subtractedmags , 'phasedmags' : tmags , 'phases' : tphase }


signature:
def bls_stats_singleperiod ( times , mags , errs , period , magsarefluxes = False , sigclip = 10.0 , perioddeltapercent = 10 , nphasebins = 200 , mintransitduration = 0.01 , maxtransitduration = 0.4 , ingressdurationfraction = 0.1 , verbose = True ) :
    '''This calculates the SNR , depth , duration , a refit period , and time of
 center - transit for a single period .
 The equation used for SNR is : :
 SNR = ( transit model depth / RMS of LC with transit model subtracted )
 * sqrt ( number of points in transit )
 NOTE : you should set the kwargs ` sigclip ` , ` nphasebins ` ,
 ` mintransitduration ` , ` maxtransitduration ` to what you used for an initial
 BLS run to detect transits in the input light curve to match those input
 conditions .
 Parameters
 times , mags , errs : np . array
 These contain the magnitude / flux time - series and any associated errors .
 period : float
 The period to search around and refit the transits . This will be used to
 calculate the start and end periods of a rerun of BLS to calculate the
 stats .
 magsarefluxes : bool
 Set to True if the input measurements in ` mags ` are actually fluxes and
 not magnitudes .
 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 .
 perioddeltapercent : float
 The fraction of the period provided to use to search around this
 value . This is a percentage . The period range searched will then be : :
 [ period - ( perioddeltapercent / 100.0 ) * period ,
 period + ( perioddeltapercent / 100.0 ) * period ]
 nphasebins : int
 The number of phase bins to use in the BLS run .
 mintransitduration : float
 The minimum transit duration in phase to consider .
 maxtransitduration : float
 The maximum transit duration to consider .
 ingressdurationfraction : float
 The fraction of the transit duration to use to generate an initial value
 of the transit ingress duration for the BLS model refit . This will be
 fit by this function .
 verbose : bool
 If True , will indicate progress and any problems encountered .
 Returns
 dict
 A dict of the following form is returned : :
 { ' period ' : the refit best period ,
 ' epoch ' : the refit epoch ( i . e . mid - transit time ) ,
 ' snr ' : the SNR of the transit ,
 ' transitdepth ' : the depth of the transit ,
 ' transitduration ' : the duration of the transit ,
 ' nphasebins ' : the input value of nphasebins ,
 ' transingressbin ' : the phase bin containing transit ingress ,
 ' transegressbin ' : the phase bin containing transit egress ,
 ' blsmodel ' : the full BLS model used along with its parameters ,
 ' subtractedmags ' : BLS model - phased light curve ,
 ' phasedmags ' : the phase light curve ,
 ' phases ' : the phase values }'''

implementation:
# get rid of nans first and sigclip
stimes , smags , serrs = sigclip_magseries ( times , mags , errs , magsarefluxes = magsarefluxes , sigclip = sigclip )
# make sure there are enough points to calculate a spectrum
if len ( stimes ) > 9 and len ( smags ) > 9 and len ( serrs ) > 9 : # get the period interval
    startp = period - perioddeltapercent * period / 100.0
    if startp < 0 :
        startp = period
    endp = period + perioddeltapercent * period / 100.0
    # rerun BLS in serial mode around the specified period to get the
    # transit depth , duration , ingress and egress bins
    blsres = bls_serial_pfind ( stimes , smags , serrs , verbose = verbose , startp = startp , endp = endp , nphasebins = nphasebins , mintransitduration = mintransitduration , maxtransitduration = maxtransitduration , magsarefluxes = magsarefluxes , get_stats = False , sigclip = None )
    if ( not blsres or 'blsresult' not in blsres or blsres [ 'blsresult' ] is None ) :
        LOGERROR ( "BLS failed during a period-search " "performed around the input best period: %.6f. " "Can't continue. " % period )
        return None
    thistransdepth = blsres [ 'blsresult' ] [ 'transdepth' ]
    thistransduration = blsres [ 'blsresult' ] [ 'transduration' ]
    thisbestperiod = blsres [ 'bestperiod' ]
    thistransingressbin = blsres [ 'blsresult' ] [ 'transingressbin' ]
    thistransegressbin = blsres [ 'blsresult' ] [ 'transegressbin' ]
    thisnphasebins = nphasebins
    stats = _get_bls_stats ( stimes , smags , serrs , thistransdepth , thistransduration , ingressdurationfraction , nphasebins , thistransingressbin , thistransegressbin , thisbestperiod , thisnphasebins , magsarefluxes = magsarefluxes , verbose = verbose )
    return stats
# if there aren ' t enough points in the mag series , bail out
else :
    LOGERROR ( 'no good detections for these times and mags, skipping...' )
    return None