Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

IRSA Tutorials

Access HATS Collections Using LSDB: Euclid Q1 and ZTF DR24

Learning Goals

By the end of this tutorial, you will learn how to:

Introduction

IRSA provides several astronomical catalogs in spatially partitioned Parquet format, enabling efficient access and scalable analysis of large datasets. Among these are the HATS (Hierarchical Adaptive Tiling Scheme) collections, which organize catalog data using adaptive HEALPix-based partitioning for parallelizable queries and crossmatching.

For more details on HATS collections, partitioning, and schema organization, see the IRSA documentation on Parquet catalogs.

This notebook demonstrates how to access and analyze HATS collections using the lsdb Python library, which makes it convenient to work with these large datasets. We will explore the following IRSA HATS collections in this tutorial:

We will use lsdb to leverage HATS partitioning for performing fast spatial queries on a relatively large area of sky and for crossmatching sources between Euclid and ZTF to identify variable galaxy candidates and visualize their light curves.

Imports

/home/runner/work/irsa-tutorials/irsa-tutorials/.tox/py312-buildhtml/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

1. Locate HATS collections in the cloud

From IRSA’s cloud data access page, let’s identify the bucket and path prefix for the HATS Collections for Euclid Q1 and ZTF DR24:

s3fs provides a filesystem-like Python interface for AWS S3 buckets. First, we create an S3 client:

Using the S3 client, we can now list the contents of the Euclid Q1 HATS collection:

['nasa-irsa-euclid-q1/contributed/q1/merged_objects/hats/collection.properties', 'nasa-irsa-euclid-q1/contributed/q1/merged_objects/hats/euclid_q1_merged_objects-hats', 'nasa-irsa-euclid-q1/contributed/q1/merged_objects/hats/euclid_q1_merged_objects-hats_index_object_id', 'nasa-irsa-euclid-q1/contributed/q1/merged_objects/hats/euclid_q1_merged_objects-hats_margin_10arcsec']

In this collection, you can see collection properties, catalog, index table, and margin cache in order. You can explore more directories to see how this HATS collection follows the directory structure described in IRSA’s documentation on HATS partitioning and HATS Collections.

As per the documentation, the Parquet file containing the schema for this euclid_q1_merged_objects-hats catalog is stored in dataset/_common_metadata directory. You can find the catalog schema through other sources as well but we use the _common_metadata file here because it contains column metadata (units and descriptions) which the other schemas don’t have. Let’s save its path for later use:

Similarly, we can list the contents of the ZTF DR24 Objects HATS collection and save the path to its schema file:

['ipac-irsa-ztf/ztf/enhanced/dr24/objects/hats/README.txt', 'ipac-irsa-ztf/ztf/enhanced/dr24/objects/hats/collection.properties', 'ipac-irsa-ztf/ztf/enhanced/dr24/objects/hats/download-aws-cli.sh', 'ipac-irsa-ztf/ztf/enhanced/dr24/objects/hats/download-irsa-wget.sh', 'ipac-irsa-ztf/ztf/enhanced/dr24/objects/hats/ztf_dr24_objects-hats', 'ipac-irsa-ztf/ztf/enhanced/dr24/objects/hats/ztf_dr24_objects-hats_index_oid', 'ipac-irsa-ztf/ztf/enhanced/dr24/objects/hats/ztf_dr24_objects-hats_margin_10arcsec']

2. Open the HATS catalogs

Now we can use lsdb to open the HATS catalogs we identified above, starting with the Euclid Q1 catalog. Note that we can simply pass the HATS collection path and it will automatically identify the catalog inside it and make use of the ancillary data (index table and margin cache) when needed. Also, we prefix the path with s3:// to indicate that it is an S3 path.

Loading...

Few things to note here:

Let’s plot the sky coverage of this catalog:

(<Figure size 1000x500 with 2 Axes>, <WCSAxes: title={'center': 'Catalog pixel map - Euclid_Q1_Merged_Objects'}>)
<Figure size 1000x500 with 2 Axes>

Since HATS uses adaptive tile-based partitioning, we see higher order HEALPix tiles in regions with higher source density and lower order tiles in regions with lower source density.

Now let’s open the ZTF DR24 Objects catalog in a similar manner and view its sky coverage:

Loading...
(<Figure size 1000x500 with 2 Axes>, <WCSAxes: title={'center': 'Catalog pixel map - ZTF_DR24_Objects'}>)
<Figure size 1000x500 with 2 Axes>

3. Define filters for querying Euclid Q1 catalog

3.1 Define a spatial filter

From the above two plots, it’s clear that the ZTF DR24 Objects catalog covers a much larger area of the sky compared to the Euclid Q1 catalog. So for cross-matching, we can safely pick the Euclid Deep Field North (EDF-N) region that is covered by both catalogs. From the Euclid user guide at IRSA Table 1, we identify the following for the EDF-N region:

<SkyCoord (ICRS): (ra, dec) in deg (269.73291667, 66.01769444)>
Loading...

Now, using lsdb, we define a cone search object for this region that can be passed as a spatial filter when querying the catalogs:

We can overplot this spatial filter on the Euclid catalog’s sky coverage to verify (note we limited the field of view so that we can see the cone better):

<Figure size 1000x500 with 2 Axes>

3.2 Identify columns of interest from the schema

Before we can define column and row filters for querying any HATS catalog, we need to know what columns are available in that catalog. So let’s inspect the schema of the Euclid Q1 catalog.

Using pyarrow parquet, we can read the schema from the _common_metadata parquet file we identified above (note that we again prefixed the path with s3:// to indicate that it is an S3 path):

pyarrow.lib.Schema

Since the schema of this catalog is a pyarrow object, let’s convert it to a pandas DataFrame for easier inspection:

1594

As we saw above, the Euclid HATS catalog contains 1594 columns in its schema!

The columns at the beginning of this schema (such as “tileid”, “objectid”, “ra”, “dec”) define positions and IDs. These can be useful for our column filters — the columns we want to SELECT when querying the catalog.

We can also filter the schema DataFrame by name, unit, type, etc., to identify columns most relevant for our row filters — WHERE rows satisfy conditions on column values for our query. For example, let’s explore the columns that are part of the PHZ (photometric redshift) catalog to identify photometric redshifts and source types:

Loading...

“phz_phz_median” gives us the median photometric redshift of sources, while “phz_phz_classification” can be used to filter the Euclid catalog to only galaxy-type sources.

You can explore the schema DataFrame further to identify any other columns of interest. It is also useful to go through the description of Euclid Q1 data products and papers that contain different PHZ, MER, etc. catalog tables that are merged in this HATS catalog, which are linked in the Euclid Q1 Merged Objects HATS Catalog: Introduction.

3.3 Define column filters

For this tutorial, the following columns are most relevant to us:

The selected columns with their metadata are:

Loading...

3.4 Define row filters

To identify the galaxies with quality redshifts in this catalog, the following filters can be applied to the rows (from Tucci et al. 2025 section 5.3):

Note that these filters are defined as lists of lists, where each inner list represents a single condition in the format [column_name, operator, value] and the conditions are combined using logical AND. More on filters that lsdb supports can be found in its documentation.

4. Define filters for querying ZTF DR24 Objects catalog

Since we are interested in cross-matching ZTF DR24 Objects with the Euclid Q1 catalog, we will use the same spatial filter defined above.

We can define column and row filters for the ZTF catalog based on its schema, similar to the previous section:

You can filter the schema further by units, type, etc. to identify other columns of interest. It’s also useful to go through the ZTF DR24 release notes and explanatory supplement at IRSA for more details on column selections and caveats.

For this tutorial, the following columns are most relevant to us:

The rationale for selecting these columns is as follows:

Loading...

For a quality cut, we apply the following filter on the number of good epochs (from Coughlin et al. (2021)) section 2):

5. Crossmatch: Euclid Q1 x ZTF DR24 Objects

Now that we have defined the spatial, column, and row filters for both catalogs, we can use lsdb to perform the crossmatch.

5.1 Create crossmatch query using the filters

Following lsdb’s documentation on performing filters, we open the Euclid Q1 catalog with the defined spatial, column, and row filters:

Loading...

Notice how the number of partitions reduced from 114 (in section 2) to only a few partitions that are within the spatial filter. This allows LSDB to avoid loading unused parts of the catalog. Also, we see only the columns we selected in the column filters.

Now, similarly, we open the ZTF DR24 Objects catalog with its defined filters:

Loading...

We see a similar reduction in the number of partitions for this catalog as well, and our column selection is applied.

Now we can use lsdb’s crossmatch functionality to plan a crossmatch between these two filtered catalogs. Note that the order of crossmatch matters: lsdb’s algorithm takes each object in the left catalog and finds the closest spatial match from the right catalog. Since Euclid is denser than ZTF, we put it on the left side of the crossmatch to maximize the number of matches. For more details on the parameters, refer to the documentation linked.

Loading...

Note how the resulting crossmatched catalog contains columns from both catalogs with the suffixes we passed. As we noticed when opening the catalog, this created a crossmatch catalog object lazily but did not execute the crossmatch.

5.2 Execute the query and load data

Now we can run the crossmatch query we have been planning so far by using the compute() method of the crossmatch catalog object, which will perform all the tasks and return the result as a DataFrame. lsdb utilizes Dask to parallelize the tasks on different partitions, so we need to create a Dask client to manage the resources (and track progress) for the scope of this computation:

This may take more than a few minutes to complete. You can monitor progress in Dask dashboard at http://127.0.0.1:8787/status
Computing Catalog:   0%|          | 0/6 [00:00<?, ?it/s]
Computing Catalog:  17%|█▋        | 1/6 [00:12<01:04, 12.92s/it]
Computing Catalog:  33%|███▎      | 2/6 [00:13<00:22,  5.54s/it]
Computing Catalog:  67%|██████▋   | 4/6 [00:16<00:06,  3.03s/it]
Computing Catalog:  83%|████████▎ | 5/6 [00:16<00:02,  2.23s/it]
Computing Catalog: 100%|██████████| 6/6 [00:22<00:00,  3.38s/it]
Computing Catalog: 100%|██████████| 6/6 [00:22<00:00,  3.80s/it]

Loading...

5.3 Identify objects of interest from the crossmatch

Check the number of unique Euclid and ZTF sources in the crossmatched catalog:

(4959, 4959, 4933)

This means there is one unique Euclid source for each row in the crossmatched catalog as expected (since we put Euclid on the left side of the crossmatch). But for ZTF, this is not true as some ZTF objects have multiple Euclid matches since ZTF has lower resolution than Euclid.

Now let’s plot some variability metrics from ZTF against Euclid redshift to see if there are any trends. We will use hexbin plots to visualize the density of sources in each panel:

<Figure size 1000x600 with 5 Axes>

There’s no strong variability trend for higher redshift galaxies (z > 1) as the distributions of mag RMS, MAD, and Chi-squared are fairly stable with redshift. This is likely because of the limited sensitivity of ZTF, as there are clearly fewer good observations as redshift increases.

Despite this, we can still select some high-variability galaxy sources from the low redshift (z < 1) range for further analysis. Let’s focus only on Chi-squared (measure of significance) and RMS magnitude (measure of variability amplitude; similar to MAD) metrics:

count 4959.0 mean 1.217374 std 0.716777 min 0.647939 25% 0.999045 50% 1.108369 75% 1.30822 max 31.223995 Name: chisq_ztf, dtype: double[pyarrow]
1.7373995065689085
count 4959.0 mean 0.229707 std 0.100641 min 0.010206 25% 0.177047 50% 0.2325 75% 0.295599 max 1.188689 Name: magrms_ztf, dtype: double[pyarrow]
0.3766872316598891

Let’s inspect the top variable galaxies that we can use for plotting their light curves:

Loading...

6. Load ZTF lightcurves by object IDs

Let’s open the ZTF light curves HATS catalog. Note that we are using the same spatial filter as before. We do not specify any column filters, because the default columns already contain the light curve data we need. We also do not apply any row filters, since we will directly filter rows by object IDs.

Loading...

We can inspect the catalog’s collection properties and find which columns have ancillary index tables (as explained in lsdb’s documentation on using index tables).

'objectid'

Let’s just extract the object IDs of the top variable galaxies we identified above:

[848201300014047, 848102400023608, 848202400038072]

Now we can use these IDs to filter the ZTF light curves catalog by the id_search method:

Searching for objectid=[848201300014047, 848102400023608, 848202400038072]
Loading...

As earlier, this creates a lazy catalog object with the partition(s) that contains our IDs. We can load the light curves data into a DataFrame by using the compute() method. Note: You may see a memory warning from lsdb which is expected due to the large size of Lightcurve data.

WARNING:root:The estimated size of the catalog is 1.6 GB. Computing the catalog will load all data into memory and may cause your system to run out of memory.Consider using `write_catalog` to write the catalog to disk, or selecting a subset of the catalog to compute.
Computing Catalog:   0%|          | 0/6 [00:00<?, ?it/s]
Computing Catalog:  17%|█▋        | 1/6 [00:23<01:57, 23.52s/it]
Computing Catalog:  33%|███▎      | 2/6 [00:23<00:39,  9.80s/it]
Computing Catalog:  67%|██████▋   | 4/6 [00:40<00:17,  8.96s/it]
Computing Catalog:  83%|████████▎ | 5/6 [00:40<00:06,  6.25s/it]
Computing Catalog: 100%|██████████| 6/6 [00:40<00:00,  6.81s/it]

Loading...

Let’s merge this light curves DataFrame with the crossmatched catalog (with variability filters and sorting) so that we can use collapsed light curves metrics for annotating the plots:

Loading...

Now we can finally plot the light curves of these variable galaxy candidates. We need to filter out bad epochs using the catflags column as explained in the ZTF DR24 explanatory supplement section 13.6. Then, we will plot the light curves in separate panels for each object:

<Figure size 1000x1200 with 3 Axes>

About this notebook

Updated: 2025-10-02

Contact: the IRSA Helpdesk with questions or to report problems.

References
  1. Coughlin, M. W., Burdge, K., Duev, D. A., Katz, M. L., van Roestel, J., Drake, A., Graham, M. J., Hillenbrand, L., Mahabal, A. A., Masci, F. J., Mróz, P., Prince, T. A., Yao, Y., Bellm, E. C., Burruss, R., Dekany, R., Jaodand, A., Kaplan, D. L., Kupfer, T., … Zolkower, J. (2021). The ZTF Source Classification Project – II. Periodicity and variability processing metrics. Monthly Notices of the Royal Astronomical Society, 505(2), 2954–2965. 10.1093/mnras/stab1502