get_lightcurve(data, copy=False, name=None, predictor=None, periodogram=Lomb_Scargle, sigma_clipping=mad, scoring='r2', scoring_cv=3, scoring_processes=1, period=None, min_period=0.2, max_period=32, coarse_precision=1e-5, fine_precision=1e-9, period_processes=1, sigma=20, shift=None, min_phase_cover=0.0, n_phases=100, verbosity=None, **kwargs)

    Fits a light curve to the given `data` using the specified methods,
    with default behavior defined for all methods.

    **Parameters**

    data : array-like, shape = [n_samples, 2] or [n_samples, 3]
        Photometry array with columns *time*, *magnitude*, and (optional)
        *error*. *time* should be unphased.
    name : string or None, optional
        Name of star being processed.
    predictor : object that has "fit" and "predict" methods, optional
        Object which fits the light curve obtained from *data* after rephasing
        (default ``make_predictor(scoring=scoring, scoring_cv=scoring_cv)``).
    periodogram : function, optional
        Function which finds one or more *period*\s. If *period* is already
        provided, the function is not used. Defaults to
        :func:`plotypus.periodogram.Lomb_Scargle`
    sigma_clipping : function, optional
        Function which takes an array and assigns sigma scores to each element.
        Defaults to :func:`plotypus.utils.mad`.
    scoring : str, optional
        Scoring method used by *predictor*. This parameter can be
            * "r2", in which case use :func:`R^2` (the default)
            * "mse", in which case use mean square error
    scoring_cv : positive integer, optional
        Number of cross validation folds used in scoring (default 3).
    scoring_processes : positive integer, optional
        Number of processes to use for scoring cross validation (default 1).
    period : number or None, optional
        Period of oscillation used in the fit. This parameter can be:
            * None, in which case the period is obtained with the given
              *periodogram* function (the default).
            * A single positive number, giving the period to phase *data*.
    min_period : non-negative number, optional
        Lower bound on period obtained by *periodogram* (default 0.2).
    max_period : non-negative number, optional
        Upper bound on period obtained by *periodogram* (default 32.0).
    course_precision : positive number, optional
        Precision used in first period search sweep (default 1e-5).
    fine_precision : positive number, optional
        Precision used in second period search sweep (default 1e-9).
    period_processes : positive integer, optional
        Number of processes to use for period finding (default 1).
    sigma : number, optional
        Upper bound on score obtained by *sigma_clipping* for a point to be
        considered an inlier.
    shift : number or None, optional
        Phase shift to apply to light curve if provided. Light curve is shifted
        such that max light occurs at ``phase[0]`` if None given (default None).
    min_phase_cover : number on interval [0, 1], optional
        Fraction of binned light curve that must contain points in order to
        proceed. If light curve has insufficient coverage, a warning is
        printed if "outlier" *verbosity* is on, and None is returned
        (default 0.0).
    n_phases : positive integer
        Number of equally spaced phases to predict magnitudes at (default 100)
    verbosity : list or None, optional
        Verbosity level. See :func:`plotypus.utils.verbose_print`.

    **Returns**

    out : dict
        Results of the fit in a dictionary. The keys are:
            * name : str or None
                The name of the star.
            * period : number
                The star's period.
            * lightcurve : array-like, shape = [n_phases]
                Magnitudes of fitted light curve sampled at sample phases.
            * coefficients : array-like, shape = [n_coeffs]
                Fitted light curve coefficients.
            * dA_0 : non-negative number
                Error on mean magnitude.
            * phased_data : array-like, shape = [n_samples]
                *data* transformed from temporal to phase space.
            * model : predictor object
                The predictor used to fit the light curve.
            * R2 : number
                The :math:`R^2` score of the fit.
            * MSE : number
                The mean square error of the fit.
            * degree : positive integer
                The degree of the Fourier fit.
            * shift : number
                The phase shift applied.
            * coverage : number on interval [0, 1]
                The light curve coverage.

    **See also**

    :func:`get_lightcurve_from_file`

def get_lightcurve(data, copy=False, name=None,
                   predictor=None, periodogram=Lomb_Scargle,
                   sigma_clipping=mad,
                   scoring='r2', scoring_cv=3, scoring_processes=1,
                   period=None, min_period=0.2, max_period=32,
                   coarse_precision=1e-5, fine_precision=1e-9,
                   period_processes=1,
                   sigma=20,
                   shift=None,
                   min_phase_cover=0.0, min_observations=1, n_phases=100,
                   verbosity=None, **kwargs):
    """get_lightcurve(data, copy=False, name=None, predictor=None, periodogram=Lomb_Scargle, sigma_clipping=mad, scoring='r2', scoring_cv=3, scoring_processes=1, period=None, min_period=0.2, max_period=32, coarse_precision=1e-5, fine_precision=1e-9, period_processes=1, sigma=20, shift=None, min_phase_cover=0.0, n_phases=100, verbosity=None, **kwargs)

    Fits a light curve to the given `data` using the specified methods,
    with default behavior defined for all methods.

    **Parameters**

    data : array-like, shape = [n_samples, 2] or [n_samples, 3]
        Photometry array with columns *time*, *magnitude*, and (optional)
        *error*. *time* should be unphased.
    name : string or None, optional
        Name of star being processed.
    predictor : object that has "fit" and "predict" methods, optional
        Object which fits the light curve obtained from *data* after rephasing
        (default ``make_predictor(scoring=scoring, scoring_cv=scoring_cv)``).
    periodogram : function, optional
        Function which finds one or more *period*\s. If *period* is already
        provided, the function is not used. Defaults to
        :func:`plotypus.periodogram.Lomb_Scargle`
    sigma_clipping : function, optional
        Function which takes an array and assigns sigma scores to each element.
        Defaults to :func:`plotypus.utils.mad`.
    scoring : str, optional
        Scoring method used by *predictor*. This parameter can be
            * "r2", in which case use :func:`R^2` (the default)
            * "mse", in which case use mean square error
    scoring_cv : positive integer, optional
        Number of cross validation folds used in scoring (default 3).
    scoring_processes : positive integer, optional
        Number of processes to use for scoring cross validation (default 1).
    period : number or None, optional
        Period of oscillation used in the fit. This parameter can be:
            * None, in which case the period is obtained with the given
              *periodogram* function (the default).
            * A single positive number, giving the period to phase *data*.
    min_period : non-negative number, optional
        Lower bound on period obtained by *periodogram* (default 0.2).
    max_period : non-negative number, optional
        Upper bound on period obtained by *periodogram* (default 32.0).
    course_precision : positive number, optional
        Precision used in first period search sweep (default 1e-5).
    fine_precision : positive number, optional
        Precision used in second period search sweep (default 1e-9).
    period_processes : positive integer, optional
        Number of processes to use for period finding (default 1).
    sigma : number, optional
        Upper bound on score obtained by *sigma_clipping* for a point to be
        considered an inlier.
    shift : number or None, optional
        Phase shift to apply to light curve if provided. Light curve is shifted
        such that max light occurs at ``phase[0]`` if None given (default None).
    min_phase_cover : number on interval [0, 1], optional
        Fraction of binned light curve that must contain points in order to
        proceed. If light curve has insufficient coverage, a warning is
        printed if "outlier" *verbosity* is on, and None is returned
        (default 0.0).
    n_phases : positive integer
        Number of equally spaced phases to predict magnitudes at (default 100)
    verbosity : list or None, optional
        Verbosity level. See :func:`plotypus.utils.verbose_print`.

    **Returns**

    out : dict
        Results of the fit in a dictionary. The keys are:
            * name : str or None
                The name of the star.
            * period : number
                The star's period.
            * lightcurve : array-like, shape = [n_phases]
                Magnitudes of fitted light curve sampled at sample phases.
            * coefficients : array-like, shape = [n_coeffs]
                Fitted light curve coefficients.
            * dA_0 : non-negative number
                Error on mean magnitude.
            * phased_data : array-like, shape = [n_samples]
                *data* transformed from temporal to phase space.
            * model : predictor object
                The predictor used to fit the light curve.
            * R2 : number
                The :math:`R^2` score of the fit.
            * MSE : number
                The mean square error of the fit.
            * degree : positive integer
                The degree of the Fourier fit.
            * shift : number
                The phase shift applied.
            * coverage : number on interval [0, 1]
                The light curve coverage.

    **See also**

    :func:`get_lightcurve_from_file`
    """
    data = numpy.ma.array(data, copy=copy)
    phases = numpy.linspace(0, 1, n_phases, endpoint=False)
# TODO ###
# Replace dA_0 with error matrix dA
    if predictor is None:
        predictor = make_predictor(scoring=scoring, scoring_cv=scoring_cv)

    while True:
        signal = get_signal(data)
        if len(signal) <= scoring_cv:
            verbose_print(
                "{}: length of signal ({}) less than cv folds ({})".format(
                    name, len(signal), scoring_cv),
                operation="coverage", verbosity=verbosity)
            return
        elif len(signal) < min_observations:
            verbose_print(
                "{}: length of signal ({}) "
                "less than min_observations ({})".format(
                    name, len(signal), min_observations),
                operation="coverage", verbosity=verbosity)
            return
        # Find the period of the inliers
        if period is not None:
            _period = period
        else:
            verbose_print("{}: finding period".format(name),
                          operation="period", verbosity=verbosity)
            _period = find_period(signal,
                                  min_period, max_period,
                                  coarse_precision, fine_precision,
                                  periodogram, period_processes)

        verbose_print("{}: using period {}".format(name, _period),
                      operation="period", verbosity=verbosity)
        phase, mag, *err = rephase(signal, _period).T

# TODO ###
# Generalize number of bins to function parameter ``coverage_bins``, which
# defaults to 100, the current hard-coded behavior
        # Determine whether there is sufficient phase coverage
        coverage = numpy.zeros((100))
        for p in phase:
            coverage[int(floor(p*100))] = 1
        coverage = sum(coverage)/100
        if coverage < min_phase_cover:
            verbose_print("{}: {} {}".format(name, coverage, min_phase_cover),
                          operation="coverage",
                          verbosity=verbosity)
            verbose_print("Insufficient phase coverage",
                          operation="outlier",
                          verbosity=verbosity)
            return

        # Predict light curve
        with warnings.catch_warnings(record=True) as w:
            try:
                predictor = predictor.fit(colvec(phase), mag)
            except Warning:
                # not sure if this should be only in verbose mode
                print(name, w, file=stderr)
                return

        # Reject outliers and repeat the process if there are any
        if sigma:
            outliers = find_outliers(rephase(data.data, _period), predictor,
                                     sigma, sigma_clipping)
            num_outliers = sum(outliers)[0]
            if num_outliers == 0 or \
               set.issubset(set(numpy.nonzero(outliers.T[0])[0]),
                            set(numpy.nonzero(data.mask.T[0])[0])):
                data.mask = outliers
                break
            if num_outliers > 0:
                verbose_print("{}: {} outliers".format(name, sum(outliers)[0]),
                              operation="outlier",
                              verbosity=verbosity)
            data.mask = numpy.ma.mask_or(data.mask, outliers)

    # Build light curve and optionally shift to max light
    lightcurve = predictor.predict([[i] for i in phases])
    if shift is None:
        arg_max_light = lightcurve.argmin()
        lightcurve = numpy.concatenate((lightcurve[arg_max_light:],
                                        lightcurve[:arg_max_light]))
        shift = arg_max_light/len(phases)

    data.T[0] = rephase(data.data, _period, shift).T[0]

    # Grab the coefficients from the model
    coefficients = predictor.named_steps['Regressor'].coef_ \
        if isinstance(predictor, Pipeline) \
        else predictor.best_estimator_.named_steps['Regressor'].coef_,

    # compute R^2 and MSE if they haven't already been
    # (one or zero have been computed, depending on the predictor)
    estimator = predictor.best_estimator_ \
        if hasattr(predictor, 'best_estimator_') \
        else predictor

    get_score = lambda scoring: predictor.best_score_ \
        if hasattr(predictor, 'best_score_') \
        and predictor.scoring == scoring \
        else cross_val_score(estimator, colvec(phase), mag,
                             cv=scoring_cv, scoring=scoring,
                             n_jobs=scoring_processes).mean()

    return {'name':         name,
            'period':       _period,
            'lightcurve':   lightcurve,
            'coefficients': coefficients[0],
            'dA_0':         sem(lightcurve),
            'phased_data':  data,
            'model':        predictor,
            'R2':           get_score('r2'),
            'MSE':          abs(get_score('mean_squared_error')),
            'degree':       estimator.get_params()['Fourier__degree'],
            'shift':        shift,
            'coverage':     coverage}