Writing FITS files¶
Four builder functions convert numpy / Python data into HDU specs:
fitsy.image()– image HDUs from a numpy arrayfitsy.compressed_image()– tile-compressed image HDUsfitsy.bintable()– BINTABLE from a column dictfitsy.ascii_table()– ASCII TABLE from a column dict
Hand the resulting list to fitsy.write().
Anywhere pixel data or a numeric column is expected, any array-like
works – a numpy array, a nested list, a tuple, or an object
implementing __array__. Non-arrays are converted once via
numpy.asarray(), which is also what decides the resulting
BITPIX / TFORMn; pass a numpy array with an explicit dtype
when you care about the on-disk type. A numpy array already in the
platform’s byte order is used as-is, with no copy and no conversion.
"""Write a new FITS file holding an image and a binary table.
Run from the repository root:
python examples/python/writing_files.py
"""
import os
import tempfile
import fitsy
import numpy as np
img = np.random.default_rng(0).normal(size=(64, 64)).astype("f4")
tbl = {
# Numeric columns take any array-like: an array pins the dtype
# (and so TFORMn), a plain list lets numpy infer it.
"RA": np.array([10.0, 11.0, 12.0]),
"DEC": [-5.0, -5.5, -6.0],
"NAME": ["a", "bb", "ccc"],
}
with tempfile.TemporaryDirectory() as td:
path = os.path.join(td, "out.fits")
fitsy.write(
path,
[
fitsy.image(img, header={"OBJECT": "noise"}),
fitsy.bintable(tbl, extname="CATALOG"),
],
)
# Round-trip: read it back and check.
with fitsy.open(path) as f:
print("HDU count:", len(f))
print("primary axes:", f[0].axes)
print("table columns:", f[1].column_names)
By default fitsy.write() refuses to clobber an existing file.
Pass overwrite=True to replace it.
Headers¶
The header argument to fitsy.image(),
fitsy.compressed_image(), and fitsy.append() accepts
either a fitsy.Header or a plain dict. Dict values may be
scalars or (value, comment) tuples.
Pixel scaling on write (BSCALE / BZERO)¶
fitsy.image() writes the supplied numpy array verbatim: the
buffer’s dtype determines BITPIX and the pixel bytes are emitted
without further transformation. fitsy does not invert
BSCALE or BZERO from physical units back to a raw integer
representation.
Two consequences:
If the input header carries
BSCALEandBZERO, the values in the array are interpreted on read asphysical = BZERO + BSCALE * raw. Writing them back without changing the keywords means the new file’s “raw” pixels are your current array, not the original raw integers.To round-trip a scaled integer image (e.g. one that was opened with
hdu.datareturning floats), drop theBSCALE/BZEROcards from the new header and write the data as the intended dtype, or apply the inverse transform yourself before building the HDU.
Unsigned integer images (uint16, uint32, uint64) are
the one exception: both fitsy.image() and the Rust
ImageBuilder::from_u16 / from_u32 / from_u64
constructors offset-encode pixels into the matching signed
BITPIX and emit BSCALE = 1 with the standard BZERO
offset automatically (FITS Standard Sec.4.4.2.5). Round-tripping
uint* arrays through fitsy is lossless.
ASCII tables¶
For text-formatted TABLE extensions, use
fitsy.ascii_table(). formats overrides the per-column
TFORM; tnulls supplies a string sentinel for None cells
in integer columns.