Skip to content

Commit 5aa268d

Browse files
authored
Merge pull request #699 from light-curve/embed-ztf-example
Add ZTF DR23 nearest-neighbour example to embed docs
2 parents 4d7d489 + 740e251 commit 5aa268d

2 files changed

Lines changed: 66 additions & 7 deletions

File tree

light-curve/README.md

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -828,13 +828,16 @@ See the benchmarks described in more detail in
828828
The `light_curve.embed` submodule provides pretrained neural network models that map raw photometric
829829
time series to dense vector embeddings suitable for downstream machine learning tasks such as
830830
classification, anomaly detection, and similarity search.
831-
Models are loaded directly from HuggingFace (weights are cached locally after the first download)
832-
and require `onnxruntime` and `huggingface-hub`:
831+
The examples below use `from_hf()` to download model weights from HuggingFace Hub
832+
(cached locally after the first download), which requires `onnxruntime` and `huggingface-hub`:
833833

834834
```
835-
pip install onnxruntime huggingface_hub
835+
pip install light-curve onnxruntime huggingface_hub
836836
```
837837

838+
Models can also be loaded directly from a local ONNX file via `from_onnx_file()`,
839+
without `huggingface-hub`.
840+
838841
See the [onnxruntime install guide](https://onnxruntime.ai/docs/install/) for GPU and platform-specific
839842
packages (`onnxruntime-gpu`, `onnxruntime-directml`, etc.).
840843
Hardware-specific options such as execution providers can be passed via `ort_session_kwargs`
@@ -916,6 +919,62 @@ print(embedding.shape) # (1, 1, 1, 384)
916919
Input fluxes should be in AB units. The default zero-point is 31.4 (LSST nJy); set `mag_zp=27.5`
917920
for ELAsTiCC / SNANA FITS data or `mag_zp=8.9` for Jy.
918921

922+
### Example: nearest-neighbour search on ZTF DR23
923+
924+
The following example reads a ZTF DR23 [HATS](https://hats.readthedocs.io) pixel directly from the public S3
925+
bucket using
926+
[`nested-pandas`](https://nested-pandas.readthedocs.io) and `s3fs`
927+
(install all dependencies for this example with
928+
`pip install huggingface_hub light-curve nested-pandas onnxruntime s3fs scipy`), embeds all well-observed
929+
light curves with Astromer2, and finds the closest neighbour to a given object by cosine distance.
930+
This particular pixel contains ~50k objects passing the quality cuts; embedding takes roughly
931+
12 minutes on M2 Pro (~15 ms per object).
932+
933+
<!-- name: test_ztf_dr23_nn_example; mark: skip(reason="requires S3 access and ~12 minutes to run") -->
934+
935+
```python
936+
import numpy as np
937+
import nested_pandas as npd
938+
from scipy.spatial.distance import cdist
939+
from upath import UPath
940+
from light_curve.embed import Astromer2
941+
942+
TARGET_OID = 680213300009232 # ZTF r-band light curve
943+
MIN_OBS = 1000
944+
945+
nf = npd.read_parquet(UPath(
946+
"s3://ipac-irsa-ztf/contributed/dr23/lc/hats/ztf_dr23_lc-hats/dataset/Norder=5/Dir=0/Npix=2378/",
947+
anon=True,
948+
))
949+
nf = nf.query("lightcurve.catflags == 0").query(f"lightcurve.list_lengths >= {MIN_OBS}")
950+
951+
model = Astromer2.from_hf(reduction="beginning")
952+
953+
954+
def embed_row(row):
955+
return {"embedding.values": model(row["lightcurve.hmjd"], row["lightcurve.mag"]).squeeze()}
956+
957+
958+
nf = nf.map_rows(embed_row, columns=["lightcurve.hmjd", "lightcurve.mag"], append_columns=True)
959+
960+
oids = nf["objectid"].to_numpy()
961+
matrix = nf["embedding.values"].to_numpy().reshape(len(nf), -1)
962+
963+
query_idx = np.where(oids == TARGET_OID)[0][0]
964+
distances = cdist(matrix[query_idx: query_idx + 1], matrix, metric="cosine")[0]
965+
distances[query_idx] = np.inf
966+
967+
best_idx = np.argmin(distances)
968+
print(f"Nearest neighbour: OID {oids[best_idx]}, cosine distance {distances[best_idx]:.6f}")
969+
# Nearest neighbour: OID 680113300005170, cosine distance 0.000046
970+
```
971+
972+
The nearest neighbour is OID `680113300005170` — the same physical
973+
object ([HZ Her / Her X-1](https://en.wikipedia.org/wiki/Hercules_X-1), an X-ray binary) observed in the *g*
974+
-band,
975+
recovered automatically from an *r*-band query through embedding similarity.
976+
See the light curve on [SNAD Viewer](https://ztf.snad.space/dr23/view/680113300005170).
977+
919978
## dm-dt map
920979

921980
In addition to the feature extractors above, the package provides a separate dm–dt mapping tool.

light-curve/pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ full = [
4646
# no linting tools, compatible with free-threading
4747
test-base = [
4848
"pytest",
49-
"markdown-pytest",
49+
"markdown-pytest>=0.6",
5050
"pytest-benchmark",
5151
"pytest-subtests>=0.10",
5252
"arro3-core",
@@ -60,7 +60,7 @@ test-base = [
6060
# Full testing environment (adds packages that don't support free-threading yet:
6161
# cesium, iminuit, nanoarrow, polars)
6262
test = [
63-
{include-group = "test-base"},
63+
{ include-group = "test-base" },
6464
"numpy",
6565
"pyarrow",
6666
"pandas",
@@ -72,7 +72,7 @@ test = [
7272
]
7373
# Development environment for free-threaded Python builds
7474
dev-free-threading = [
75-
{include-group = "test-base"},
75+
{ include-group = "test-base" },
7676
"numpy",
7777
"pyarrow",
7878
"pandas",
@@ -81,7 +81,7 @@ dev-free-threading = [
8181
]
8282
# Full development environment
8383
dev = [
84-
{include-group = "dev-free-threading"},
84+
{ include-group = "dev-free-threading" },
8585
"iminuit>=2.21,<3",
8686
"nanoarrow",
8787
"polars",

0 commit comments

Comments
 (0)