Plotting Target Pixel Files with Lightkurve#
Learning Goals#
By the end of this tutorial, you will:
Learn how to download and plot target pixel files from the data archive using Lightkurve.
Be able to plot the target pixel file background.
Be able to extract and plot flux from a target pixel file.
Introduction#
The Kepler, K2, and TESS telescopes observe stars for long periods of time, from just under a month to four years. By doing so they observe how the brightnesses of stars change over time.
Pixels around targeted stars are cut out and stored as target pixel files at each observing cadence. In this tutorial, we will learn how to use Lightkurve to download and understand the different photometric data stored in a target pixel file, and how to extract flux using basic aperture photometry.
It is useful to read the accompanying tutorial discussing how to use target pixel file products with Lightkurve before starting this tutorial. It is recommended that you also read the tutorial on using Kepler light curve products with Lightkurve, which will introduce you to some specifics on how Kepler, K2, and TESS make observations, and how these are displayed as light curves. It also introduces some important terms and concepts that are referred to in this tutorial.
Kepler observed a single field in the sky, although not all stars in this field were recorded. Instead, pixels were selected around certain targeted stars. These cutout images are called target pixel files, or TPFs. By combining the amount of flux in the pixels where the star appears, you can make a measurement of the amount of light from a star in that observation. The pixels chosen to include in this measurement are referred to as an aperture.
TPFs are typically the first port of call when studying a star with Kepler, K2, or TESS. They allow us to see where our data is coming from, and identify potential sources of noise or systematic trends. In this tutorial, we will use the Kepler mission as the main example, but these tools equally apply to TESS and K2 as well.
Imports#
This tutorial requires: - Lightkurve to work with TPF files. - Matplotlib for plotting.
[1]:
from astropy import units as u
import lightkurve as lk
import matplotlib.pyplot as plt
%matplotlib inline
1. Downloading a TPF#
A TPF contains the original imaging data from which a light curve is derived. Besides the brightness data measured by the charge-coupled device (CCD) camera, a TPF also includes post-processing information such as an estimate of the astronomical background, and a recommended pixel aperture for extracting a light curve.
First, we download a target pixel file. We will use one quarter’s worth of Kepler data for the star named Kepler-8, a star somewhat larger than the Sun, and the host of a hot Jupiter planet. To search for TPF’s that contain our object of interest we will use the search_targetpixelfile function as follows,
[2]:
search_result = lk.search_targetpixelfile("Kepler-8", author="Kepler", quarter=4, cadence="long")
search_result
[2]:
# | mission | year | author | exptime | target_name | distance |
---|---|---|---|---|---|---|
s | arcsec | |||||
0 | Kepler Quarter 04 | 2010 | Kepler | 1800 | kplr006922244 | 0.0 |
[3]:
tpf = search_result.download()
This TPF contains data for every cadence in the quarter we downloaded. Let’s focus on the first cadence for now, which we can select using zero-based indexing as follows:
[4]:
first_cadence = tpf[0]
first_cadence
[4]:
KeplerTargetPixelFile Object (ID: 6922244)
2. Flux and Background#
At each cadence the TPF has a number of photometry data properties. These are:
flux_bkg
: the astronomical background of the image.flux_bkg_err
: the statistical uncertainty on the background flux.flux
: the stellar flux after the background is removed.flux_err
: the statistical uncertainty on the stellar flux after background removal.
These properties can be accessed via a TPF object as follows:
[5]:
first_cadence.flux.value
[5]:
array([[[ nan, 5.6079335e+00, 5.1491142e+01, 8.4241745e+01,
3.0221334e+01],
[4.4045620e+01, 7.6861229e+01, 1.1227759e+03, 3.2262029e+03,
4.5486777e+02],
[2.5911165e+01, 2.2907593e+02, 9.3626543e+03, 2.3606273e+04,
1.2087750e+03],
[4.0100830e+01, 8.8543927e+02, 1.7102118e+03, 2.6254871e+03,
7.0796606e+02],
[1.5719417e+02, 8.3713440e+02, 5.1021539e+02, 1.1501041e+03,
1.8313370e+02]]], dtype=float32)
And you can plot the data as follows:
[6]:
first_cadence.plot(column='flux');
Alternatively, if you are working directly with a FITS file, you can access the data in extension 1 (for example, first_cadence.hdu[1].data['FLUX']
). Note that you can find all of the details on the structure and contents of TPF files in Section 2.3.2 of the Kepler Archive Manual.
When plotting data using the plot()
function, what you are seeing in the TPF is the flux after the background has been removed. This background flux typically consists of zodiacal light or earthshine (especially in TESS observations). The background is typically smooth and changes on scales much larger than a single TPF. In Kepler, the background is estimated for the CCD as a whole, before being extracted from each TPF in that CCD. You
can learn more about background removal in Section 4.2 of the Kepler Data Processing Handbook.
Now, let’s compare the background to the background-subtracted flux to get a sense of scale. We can do this using the plot()
function’s column
keyword. By default the function plots the flux, but we can change this to plot the background, as well as other data such as the error on each pixel’s flux.
[7]:
fig, axes = plt.subplots(2,2, figsize=(16,16))
first_cadence.plot(ax=axes[0,0], column='FLUX')
first_cadence.plot(ax=axes[0,1], column='FLUX_BKG')
first_cadence.plot(ax=axes[1,0], column='FLUX_ERR')
first_cadence.plot(ax=axes[1,1], column='FLUX_BKG_ERR');
From looking at the color scale on both plots, you may see that the background flux is very low compared to the total flux emitted by a star. This is expected — stars are bright! But these small background corrections become important when looking at the very small scale changes caused by planets or stellar oscillations. Understanding the background is an important part of astronomy with Kepler, K2, and TESS.
If the background is particularly bright and you want to see what the TPF looks like with it included, passing the bkg=True
argument to the plot()
method will show the TPF with the flux added on top of the background, representing the total flux recorded by the spacecraft.
[8]:
first_cadence.plot(bkg=True);
In this case, the background is low and the star is bright, so it doesn’t appear to make much of a difference.
3. Apertures#
As part of the data processing done by the Kepler pipeline, each TPF includes a recommended optimal aperture mask. This aperture mask is optimized to ensure that the stellar signal has a high signal-to-noise ratio, with minimal contamination from the background.
The optimal aperture is stored in the TPF as the pipeline_mask property. We can have a look at it by calling it here:
[9]:
first_cadence.pipeline_mask
[9]:
array([[False, False, False, False, False],
[False, False, True, True, False],
[False, False, True, True, False],
[False, True, True, True, False],
[False, False, False, True, False]])
As you can see, it is a Boolean array detailing which pixels are included. We can plot this aperture over the top of our TPF using the plot()
function, and passing in the mask to the aperture_mask
keyword. This will highlight the pixels included in the aperture mask using red hatched lines.
[10]:
first_cadence.plot(aperture_mask=first_cadence.pipeline_mask);
You don’t necessarily have to pass in the pipeline_mask
to the plot()
function; it can be any mask you create yourself, provided it is the right shape. An accompanying tutorial explains how to create such custom apertures, and goes into aperture photometry in more detail. For specifics on the selection of Kepler’s optimal apertures, read the Kepler Data Processing
Handbook, Section 7, Finding Optimal Apertures in Kepler Data.
4. Simple Aperture Photometry#
Finally, let’s learn how to perform simple aperture photometry (SAP) using the provided optimal aperture in pipeline_mask
and the TPF.
Using the full TPF for all cadences in the quarter, we can perform aperture photometry using the to_lightcurve() method as follows:
[11]:
lc = tpf.to_lightcurve()
This method returns a LightCurve
object which details the flux and flux centroid position at each cadence:
[12]:
lc
[12]:
time | flux | flux_err | centroid_col | centroid_row | cadenceno | quality |
---|---|---|---|---|---|---|
electron / s | electron / s | pix | pix | |||
Time | float32 | float32 | float64 | float64 | int32 | int32 |
352.37632485035283 | 43689.1484375 | 6.631562232971191 | 682.6803253766153 | 190.0726135828141 | 11914 | 0 |
352.3967580484896 | 43698.078125 | 6.631830215454102 | 682.679939392134 | 190.0724388237138 | 11915 | 8192 |
352.4376244455707 | 43694.10546875 | 6.6317877769470215 | 682.6796255144184 | 190.07267575992847 | 11917 | 16 |
352.45805764463876 | 43698.31640625 | 6.631948947906494 | 682.6797879974883 | 190.07249571597706 | 11918 | 0 |
352.4784908439324 | 43687.6484375 | 6.631504535675049 | 682.6792868410989 | 190.07246464783114 | 11919 | 0 |
352.4989240434661 | 43686.4765625 | 6.6314263343811035 | 682.6797248240034 | 190.07284002730125 | 11920 | 0 |
352.5193572433491 | 43692.59375 | 6.631662845611572 | 682.6797061866289 | 190.07275265635383 | 11921 | 0 |
352.53979034345684 | 43712.01953125 | 6.6356940269470215 | 682.6787299772047 | 190.07316832254241 | 11922 | 128 |
352.56022364380624 | 43683.98046875 | 6.631390571594238 | 682.679365411703 | 190.0730725082332 | 11923 | 0 |
352.5806568445041 | 43696.76171875 | 6.632009029388428 | 682.6796517987715 | 190.07296984009605 | 11924 | 0 |
352.6010899455432 | 43693.1484375 | 6.631709098815918 | 682.6794946321877 | 190.073062208751 | 11925 | 0 |
352.6215232468239 | 43691.34375 | 6.631857395172119 | 682.6793119546483 | 190.07329265625233 | 11926 | 8192 |
352.64195644845313 | 43703.13671875 | 6.632218837738037 | 682.6794600165127 | 190.07310394362827 | 11927 | 0 |
352.6623896503079 | 43692.90625 | 6.631864547729492 | 682.6791962953619 | 190.07331932569323 | 11928 | 0 |
352.68282285240275 | 43696.2734375 | 6.632040977478027 | 682.6792754510204 | 190.07326117409048 | 11929 | 0 |
352.70325605484686 | 43666.19921875 | 6.630620956420898 | 682.6794975721688 | 190.07290799818392 | 11930 | 0 |
352.7236892575165 | 43683.390625 | 6.631417751312256 | 682.679147977405 | 190.0734239489556 | 11931 | 0 |
352.7441224605427 | 43691.4140625 | 6.631997585296631 | 682.679107402596 | 190.07312093294664 | 11932 | 0 |
352.76455566380173 | 43701.4765625 | 6.632231712341309 | 682.6788244683077 | 190.07310037302085 | 11933 | 0 |
352.7849888674027 | 43693.109375 | 6.632004737854004 | 682.6792450021618 | 190.07342472501742 | 11934 | 0 |
352.80542197124305 | 43694.50390625 | 6.6319146156311035 | 682.678968872893 | 190.07344222963226 | 11935 | 0 |
... | ... | ... | ... | ... | ... | ... |
441.77438253051514 | 43155.48828125 | 6.599724292755127 | 682.5028500825504 | 190.28122668371682 | 16289 | 8192 |
441.7948168942894 | 43164.19921875 | 6.599976062774658 | 682.5028704829895 | 190.28084985845067 | 16290 | 0 |
441.8152511581502 | 43154.4140625 | 6.599671363830566 | 682.503034387268 | 190.28080193887232 | 16291 | 0 |
441.8356856221799 | 43153.21875 | 6.599508762359619 | 682.5029903397564 | 190.2813147419433 | 16292 | 0 |
441.8561199863325 | 43161.00390625 | 6.5999755859375 | 682.5031149987257 | 190.2808494851943 | 16293 | 0 |
441.87655425060075 | 43149.25390625 | 6.599336624145508 | 682.5033363751197 | 190.28077231207783 | 16294 | 0 |
441.89698871497967 | 43157.8515625 | 6.600053787231445 | 682.503043437076 | 190.28114132203186 | 16295 | 128 |
441.91742307953973 | 43154.7109375 | 6.599435329437256 | 682.5029196936422 | 190.28074883797223 | 16296 | 0 |
441.93785734418634 | 43154.68359375 | 6.599567890167236 | 682.5031285134896 | 190.28106458358056 | 16297 | 0 |
441.95829170897196 | 43155.34765625 | 6.599438190460205 | 682.502819166476 | 190.28129772456774 | 16298 | 0 |
441.9991604389361 | 43155.49609375 | 6.599555492401123 | 682.5027229343457 | 190.28110987603694 | 16300 | 0 |
442.01959480413643 | 43153.08203125 | 6.599308490753174 | 682.5027122524675 | 190.28150665607106 | 16301 | 0 |
442.0400292694467 | 43162.8515625 | 6.5997772216796875 | 682.5029172042948 | 190.28117470942624 | 16302 | 0 |
442.0604635348791 | 43167.78125 | 6.600048065185547 | 682.5028434809847 | 190.28150709323504 | 16303 | 0 |
442.0808979004214 | 43156.33203125 | 6.599843978881836 | 682.5027064349352 | 190.2813695406399 | 16304 | 128 |
442.1013323661464 | 43164.0703125 | 6.599811553955078 | 682.5025883479478 | 190.28110702349872 | 16305 | 0 |
442.1217667319579 | 43160.6640625 | 6.599737167358398 | 682.5028468248179 | 190.28114515294416 | 16306 | 8192 |
442.1422009979142 | 43157.625 | 6.5994954109191895 | 682.5026386061921 | 190.28167292648035 | 16307 | 0 |
442.16263546398113 | 43155.80078125 | 6.599367618560791 | 682.5025609327599 | 190.2812354101034 | 16308 | 0 |
442.1830698302001 | 43148.46484375 | 6.599063873291016 | 682.502362185607 | 190.2815430315113 | 16309 | 0 |
442.20350409652747 | 43151.5625 | 6.599262714385986 | 682.5024686560814 | 190.28141944952384 | 16310 | 0 |
Note that this KeplerLightCurve
object has fewer data columns than in light curves downloaded directly from MAST. This is because we are extracting our light curve directly from the TPF using minimal processing, whereas light curves created using the official pipeline include more processing and more columns.
We can visualize the light curve as follows:
[13]:
lc.plot();
This light curve is similar to the SAP light curve we previously encountered in the light curve tutorial.
Note#
The background flux can be plotted in a similar way, using the get_bkg_lightcurve()
method. This does not require an aperture, but instead sums the flux in the TPF’s FLUX_BKG
column at each timestamp.
[14]:
bkg = tpf.get_bkg_lightcurve()
bkg.plot();
Inspecting the background in this way is useful to identify signals which appear to be present in the background rather than in the astronomical object under study.
Exercises#
Some stars, such as the planet-hosting star Kepler-10, have been observed both with Kepler and TESS. In this exercise, download and plot both the TESS and Kepler TPFs, along with the optimal apertures. You can do this by either selecting the TPFs from the list returned by search_targetpixelfile(), or by using the mission
keyword argument when searching.
Both Kepler and TESS produce target pixel file data products, but these can look different across the two missions. TESS is focused on brighter stars and has larger pixels, so a star that might occupy many pixels in Kepler may only occupy a few in TESS.
How do light curves extracted from both of them compare?
[15]:
#datalist = lk.search_targetpixelfile(...)
[16]:
#soln:
datalist = lk.search_targetpixelfile("Kepler-10")
datalist
[16]:
# | mission | year | author | exptime | target_name | distance |
---|---|---|---|---|---|---|
s | arcsec | |||||
0 | Kepler Quarter 03 | 2009 | Kepler | 60 | kplr011904151 | 0.0 |
1 | Kepler Quarter 03 | 2009 | Kepler | 60 | kplr011904151 | 0.0 |
2 | Kepler Quarter 03 | 2009 | Kepler | 60 | kplr011904151 | 0.0 |
3 | Kepler Quarter 02 | 2009 | Kepler | 60 | kplr011904151 | 0.0 |
4 | Kepler Quarter 03 | 2009 | Kepler | 1800 | kplr011904151 | 0.0 |
5 | Kepler Quarter 02 | 2009 | Kepler | 1800 | kplr011904151 | 0.0 |
6 | Kepler Quarter 00 | 2009 | Kepler | 1800 | kplr011904151 | 0.0 |
7 | Kepler Quarter 01 | 2009 | Kepler | 1800 | kplr011904151 | 0.0 |
8 | Kepler Quarter 05 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
9 | Kepler Quarter 05 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
10 | Kepler Quarter 06 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
11 | Kepler Quarter 06 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
12 | Kepler Quarter 06 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
13 | Kepler Quarter 07 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
14 | Kepler Quarter 07 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
15 | Kepler Quarter 04 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
16 | Kepler Quarter 07 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
17 | Kepler Quarter 05 | 2010 | Kepler | 60 | kplr011904151 | 0.0 |
18 | Kepler Quarter 06 | 2010 | Kepler | 1800 | kplr011904151 | 0.0 |
19 | Kepler Quarter 05 | 2010 | Kepler | 1800 | kplr011904151 | 0.0 |
20 | Kepler Quarter 07 | 2010 | Kepler | 1800 | kplr011904151 | 0.0 |
... | ... | ... | ... | ... | ... | ... |
50 | TESS Sector 40 | 2021 | SPOC | 20 | 377780790 | 0.0 |
51 | TESS Sector 41 | 2021 | SPOC | 20 | 377780790 | 0.0 |
52 | TESS Sector 40 | 2021 | SPOC | 120 | 377780790 | 0.0 |
53 | TESS Sector 41 | 2021 | SPOC | 120 | 377780790 | 0.0 |
54 | TESS Sector 53 | 2022 | SPOC | 20 | 377780790 | 0.0 |
55 | TESS Sector 55 | 2022 | SPOC | 20 | 377780790 | 0.0 |
56 | TESS Sector 54 | 2022 | SPOC | 20 | 377780790 | 0.0 |
57 | TESS Sector 55 | 2022 | SPOC | 120 | 377780790 | 0.0 |
58 | TESS Sector 54 | 2022 | SPOC | 120 | 377780790 | 0.0 |
59 | TESS Sector 53 | 2022 | SPOC | 120 | 377780790 | 0.0 |
60 | TESS Sector 75 | 2024 | SPOC | 120 | 377780790 | 0.0 |
61 | TESS Sector 74 | 2024 | SPOC | 120 | 377780790 | 0.0 |
62 | TESS Sector 81 | 2024 | SPOC | 120 | 377780790 | 0.0 |
63 | TESS Sector 80 | 2024 | SPOC | 120 | 377780790 | 0.0 |
64 | TESS Sector 15 | 2019 | TESS-SPOC | 1800 | 377780790 | 0.0 |
65 | TESS Sector 14 | 2019 | TESS-SPOC | 1800 | 377780790 | 0.0 |
66 | TESS Sector 26 | 2020 | TESS-SPOC | 1800 | 377780790 | 0.0 |
67 | TESS Sector 41 | 2021 | TESS-SPOC | 600 | 377780790 | 0.0 |
68 | TESS Sector 40 | 2021 | TESS-SPOC | 600 | 377780790 | 0.0 |
69 | TESS Sector 55 | 2022 | TESS-SPOC | 600 | 377780790 | 0.0 |
70 | TESS Sector 54 | 2022 | TESS-SPOC | 600 | 377780790 | 0.0 |
71 | TESS Sector 53 | 2022 | TESS-SPOC | 600 | 377780790 | 0.0 |
[17]:
kep = datalist[(datalist.author == "Kepler") & (datalist.exptime == 60*u.second)][0].download()
tes = datalist[(datalist.author == "SPOC") & (datalist.exptime == 120*u.second)][0].download()
[18]:
fig, axes = plt.subplots(1, 2, figsize=(14,6))
kep.plot(ax=axes[0], aperture_mask=kep.pipeline_mask, scale='log')
tes.plot(ax=axes[1], aperture_mask=tes.pipeline_mask)
fig.tight_layout();
[19]:
lc_kep = kep.to_lightcurve()
lc_tes = tes.to_lightcurve()
[20]:
fig, axes = plt.subplots(1, 2, figsize=(14,6), sharey=True)
lc_kep.flatten().plot(ax=axes[0], c='k', alpha=.8)
lc_tes.flatten().plot(ax=axes[1], c='k', alpha=.8);
If you plot the light curves for both missions side by side, you will see a stark difference. The Kepler data has a much smaller scatter, and repeating transits are visible. This is because Kepler’s pixels were smaller, and so could achieve a higher precision on fainter stars. TESS has larger pixels and therefore focuses on brighter stars. For stars like Kepler-10, it would be hard to detect a planet using TESS data alone.
About this Notebook#
Authors: Oliver Hall (oliver.hall@esa.int), Geert Barentsen
Updated On: 2020-09-15
Citing Lightkurve and Astropy#
If you use lightkurve
or astropy
for published research, please cite the authors. Click the buttons below to copy BibTeX entries to your clipboard.
lk.show_citation_instructions()