def GetData(fitsfile, EPIC, campaign, clobber=False,
            saturation_tolerance=-0.1,
            bad_bits=[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17],
            get_hires=False, get_nearby=False,
            aperture=None, **kwargs):
    '''
    Returns a :py:obj:`DataContainer` instance with the
    raw data for the target.

    :param str fitsfile: The full raw target pixel file path
    :param bool clobber: Overwrite existing files? Default :py:obj:`False`
    :param float saturation_tolerance: Target is considered saturated \
           if flux is within this fraction of the pixel well depth. \
           Default -0.1
    :param array_like bad_bits: Flagged :py:obj`QUALITY` bits to consider \
           outliers when computing the model. \
           Default `[1,2,3,4,5,6,7,8,9,11,12,13,14,16,17]`
    :param bool get_hires: Download a high resolution image of the target? \
           Default :py:obj:`True`
    :param bool get_nearby: Retrieve location of nearby sources? \
           Default :py:obj:`True`

    '''

    # Get the npz file name
    filename = os.path.join(EVEREST_DAT, 'k2', 'c%02d' % campaign,
                            ('%09d' % EPIC)[:4] +
                            '00000', ('%09d' % EPIC)[4:],
                            'data.npz')

    # Create the dir
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))

    # Check for saved data
    if not os.path.exists(filename) or clobber:

        log.info("Fetching data for target...")

        # Load the tpf
        with pyfits.open(fitsfile) as f:
            qdata = f[1].data

        # Get the header info
        fitsheader = [pyfits.getheader(fitsfile, 0).cards,
                      pyfits.getheader(fitsfile, 1).cards,
                      pyfits.getheader(fitsfile, 2).cards]

        # Get a hi res image of the target
        if get_hires:
            try:
                hires = GetHiResImage(EPIC)
            except ValueError:
                hires = None
        else:
            hires = None

        # Get nearby sources
        if get_nearby:
            try:
                nearby = GetSources(EPIC)
            except ValueError:
                nearby = []
        else:
            nearby = []

        # Get the arrays
        cadn = np.array(qdata.field('CADENCENO'), dtype='int32')
        time = np.array(qdata.field('TIME'), dtype='float64')
        fpix = np.array(qdata.field('FLUX'), dtype='float64')
        fpix_err = np.array(qdata.field('FLUX_ERR'), dtype='float64')
        qual = np.array(qdata.field('QUALITY'), dtype=int)

        # Get rid of NaNs in the time array by interpolating
        naninds = np.where(np.isnan(time))
        time = Interpolate(np.arange(0, len(time)), naninds, time)

        # Get the motion vectors (if available!)
        pc1 = np.array(qdata.field('POS_CORR1'), dtype='float64')
        pc2 = np.array(qdata.field('POS_CORR2'), dtype='float64')
        if not np.all(np.isnan(pc1)) and not np.all(np.isnan(pc2)):
            pc1 = Interpolate(time, np.where(np.isnan(pc1)), pc1)
            pc2 = Interpolate(time, np.where(np.isnan(pc2)), pc2)
        else:
            pc1 = None
            pc2 = None

        # Get the static pixel images for plotting
        pixel_images = [fpix[0], fpix[len(fpix) // 2], fpix[len(fpix) - 1]]

        # Get the aperture interactively
        if aperture is None:
            aperture = ApertureSelector(time[::10], fpix[::10],
                                        title='EPIC %d' % EPIC).aperture
        if np.sum(aperture) == 0:
            raise ValueError("Empty aperture!")

        # Atomically write to disk.
        # http://stackoverflow.com/questions/2333872/
        # atomic-writing-to-file-with-python
        if not os.path.exists(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        f = NamedTemporaryFile("wb", delete=False)
        np.savez_compressed(f, cadn=cadn, time=time, fpix=fpix,
                            fpix_err=fpix_err,
                            qual=qual, aperture=aperture,
                            pc1=pc1, pc2=pc2, fitsheader=fitsheader,
                            pixel_images=pixel_images, nearby=nearby,
                            hires=hires)
        f.flush()
        os.fsync(f.fileno())
        f.close()
        shutil.move(f.name, filename)

    # Load
    data = np.load(filename)
    aperture = data['aperture'][()]
    pixel_images = data['pixel_images']
    nearby = data['nearby'][()]
    hires = data['hires'][()]
    fitsheader = data['fitsheader']
    cadn = data['cadn']
    time = data['time']
    fpix = data['fpix']
    fpix_err = data['fpix_err']
    qual = data['qual']
    pc1 = data['pc1']
    pc2 = data['pc2']

    # Compute the saturation flux and the 97.5th percentile
    # flux in each pixel of the aperture. We're going
    # to compare these to decide if the star is saturated.
    satflx = SaturationFlux(EPIC, campaign=campaign) * \
        (1. + saturation_tolerance)
    f97 = np.zeros((fpix.shape[1], fpix.shape[2]))
    for i in range(fpix.shape[1]):
        for j in range(fpix.shape[2]):
            if aperture[i, j]:
                # Let's remove NaNs...
                tmp = np.delete(fpix[:, i, j], np.where(
                    np.isnan(fpix[:, i, j])))
                # ... and really bad outliers...
                if len(tmp):
                    f = SavGol(tmp)
                    med = np.nanmedian(f)
                    MAD = 1.4826 * np.nanmedian(np.abs(f - med))
                    bad = np.where((f > med + 10. * MAD) |
                                   (f < med - 10. * MAD))[0]
                    np.delete(tmp, bad)
                    # ... so we can compute the 97.5th percentile flux
                    i97 = int(0.975 * len(tmp))
                    tmp = tmp[np.argsort(tmp)[i97]]
                    f97[i, j] = tmp

    # Check if any of the pixels are actually saturated
    if np.nanmax(f97) <= satflx:
        log.info("No saturated columns detected.")
        saturated = False
        aperture[np.isnan(fpix[0])] = 0
        ap = np.where(aperture & 1)
        fpix2D = np.array([f[ap] for f in fpix], dtype='float64')
        fpix_err2D = np.array([p[ap] for p in fpix_err], dtype='float64')
    else:
        # We need to collapse the saturated columns
        saturated = True
        ncol = 0
        fpixnew = []
        ferrnew = []
        for j in range(aperture.shape[1]):
            if np.any(f97[:, j] > satflx):
                marked = False
                collapsed = np.zeros(len(fpix[:, 0, 0]))
                collapsed_err2 = np.zeros(len(fpix[:, 0, 0]))
                for i in range(aperture.shape[0]):
                    if aperture[i, j]:
                        if not marked:
                            aperture[i, j] = AP_COLLAPSED_PIXEL
                            marked = True
                        else:
                            aperture[i, j] = AP_SATURATED_PIXEL
                        collapsed += fpix[:, i, j]
                        collapsed_err2 += fpix_err[:, i, j] ** 2
                if np.any(collapsed):
                    fpixnew.append(collapsed)
                    ferrnew.append(np.sqrt(collapsed_err2))
                    ncol += 1
            else:
                for i in range(aperture.shape[0]):
                    if aperture[i, j]:
                        fpixnew.append(fpix[:, i, j])
                        ferrnew.append(fpix_err[:, i, j])
        fpix2D = np.array(fpixnew).T
        fpix_err2D = np.array(ferrnew).T
        log.info("Collapsed %d saturated column(s)." % ncol)

    # Compute the background
    binds = np.where(aperture ^ 1)
    if RemoveBackground(EPIC, campaign=campaign) and (len(binds[0]) > 0):
        bkg = np.nanmedian(np.array([f[binds]
                                     for f in fpix], dtype='float64'), axis=1)
        # Uncertainty of the median:
        # http://davidmlane.com/hyperstat/A106993.html
        bkg_err = 1.253 * np.nanmedian(np.array([e[binds] for e in fpix_err],
                                       dtype='float64'), axis=1) \
            / np.sqrt(len(binds[0]))
        bkg = bkg.reshape(-1, 1)
        bkg_err = bkg_err.reshape(-1, 1)
    else:
        bkg = 0.
        bkg_err = 0.

    # Make everything 2D and remove the background
    fpix = fpix2D - bkg
    fpix_err = np.sqrt(fpix_err2D ** 2 + bkg_err ** 2)
    flux = np.sum(fpix, axis=1)

    # Get NaN data points
    nanmask = np.where(np.isnan(flux) | (flux == 0))[0]

    # Get flagged data points -- we won't train our model on them
    badmask = []
    for b in bad_bits:
        badmask += list(np.where(qual & 2 ** (b - 1))[0])

    # Flag >10 sigma outliers -- same thing.
    tmpmask = np.array(list(set(np.concatenate([badmask, nanmask]))))
    t = np.delete(time, tmpmask)
    f = np.delete(flux, tmpmask)
    f = SavGol(f)
    med = np.nanmedian(f)
    MAD = 1.4826 * np.nanmedian(np.abs(f - med))
    bad = np.where((f > med + 10. * MAD) | (f < med - 10. * MAD))[0]
    badmask.extend([np.argmax(time == t[i]) for i in bad])

    # Campaign 2 hack: the first day or two are screwed up
    if campaign == 2:
        badmask.extend(np.where(time < 2061.5)[0])

    # Finalize the mask
    badmask = np.array(sorted(list(set(badmask))))

    # Interpolate the nans
    fpix = Interpolate(time, nanmask, fpix)
    fpix_err = Interpolate(time, nanmask, fpix_err)

    # Return
    data = DataContainer()
    data.ID = EPIC
    data.campaign = campaign
    data.cadn = cadn
    data.time = time
    data.fpix = fpix
    data.fpix_err = fpix_err
    data.nanmask = nanmask
    data.badmask = badmask
    data.aperture = aperture
    data.aperture_name = 'custom'
    data.apertures = dict(custom=aperture)
    data.quality = qual
    data.Xpos = pc1
    data.Ypos = pc2
    data.meta = fitsheader
    data.mag = fitsheader[0]['KEPMAG'][1]
    if type(data.mag) is pyfits.card.Undefined:
        data.mag = np.nan
    data.pixel_images = pixel_images
    data.nearby = nearby
    data.hires = hires
    data.saturated = saturated
    data.bkg = bkg

    return data
Returns a :py:obj:`DataContainer` instance with the
    raw data for the target.

    :param str fitsfile: The full raw target pixel file path
    :param bool clobber: Overwrite existing files? Default :py:obj:`False`
    :param float saturation_tolerance: Target is considered saturated \
           if flux is within this fraction of the pixel well depth. \
           Default -0.1
    :param array_like bad_bits: Flagged :py:obj`QUALITY` bits to consider \
           outliers when computing the model. \
           Default `[1,2,3,4,5,6,7,8,9,11,12,13,14,16,17]`
    :param bool get_hires: Download a high resolution image of the target? \
           Default :py:obj:`True`
    :param bool get_nearby: Retrieve location of nearby sources? \
           Default :py:obj:`True`