You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Round tripping non-float Quantities from QTable to fits converts all Quantities to floats #12494
A simple but not elegant solution is to use the dtype info from the HDUL to cast the Quantity back to the desired dtype
This actually doesn't work for the uint case or cases with mixed quantities an non-quantities dtype([('uint', '<i2'), ('int', '<i2'), ('float32', '<f4')]) should be dtype([('uint', '<u2'), ('int', '<i2'), ('float32', '<f4')])
hdul = fits.open('test.fits')
hdul[1].data.columns
#ColDefs(
# name = 'uint'; format = 'I'; unit = 'm'; bzero = 32768
# name = 'int'; format = 'I'; unit = 'm'
# name = 'float32'; format = 'E'; unit = 'm'
#)
fixed_table = QTable([u.Quantity(table_fromfits[c.name], dtype=c.dtype) for c in hdul[1].data.columns])
fixed_table.dtype
# dtype([('uint', '<i2'), ('int', '<i2'), ('float32', '<f4')])
fixed_table = QTable([u.Quantity(table_fromfits[c.name], dtype=c.dtype) for c in hdul[1].data.columns])
could be made a bit more convenient at least as QTable(table_fromfits, dtype=table_fromfits.dtype)
by having QTable._convert_col_for_table apply the dtype from *kwargs individually to q_cls.
As for the I/O part, I had assumed that it was a design choice to construct all Quantity-type columns as float types, but am wondering now if this has in fact been overlooked when discarding the dtype info in _decode_mixins
# Use the `datatype` attribute info to update column attributes that are
# NOT already handled via standard FITS column keys (name, dtype, unit).
forcolininfo['datatype']:
forattrin ['description', 'meta']:
Edit: whatever the intended default behaviour, setting the attrs above will not make any difference (as the tbl[col['name']].info.dtype in question already/still are of integer type at that point); any datatype information would probably need to be passed on with the tbl.meta['__serialized_columns__'] dict to the _construct_mixin[s] utilities.
@samaloney I have pushed a fix for correctly reading back the mixins in #12505 ; if you have chance to test if this addresses your problems, feedback would be great.
For a temporary workaround, as you noted that your use cases typically involve reading in large volumes of data, I strongly recommend to use getheader and getdata to read the tables in their original dtypes and then extract the units to directly construct u.Quantity(tab[col], dtype=tab[col].dtype, name=col, unit=u.Unit(header.get('TUNITn'), format='fits')).
It's a bit cumbersome, but would avoid conversion losses and in particular the temporary creation of arrays 4x the original size or larger, in particular since the type conversion does not let you take advantage of memmap=True.
Sorry for the delay @dhomeier I've just loaded some of my fits files using your branch seems to work as expected the dtypes are respected.
Thanks for the confirmation! Note that the currently favoured implementation will store the dtype information to its own __serialized_columns__ field and thus not recover that info from files written with the current Astropy version. So for existing files the header would still need to be fixed separately. Also we are still discussing in #12505 (comment) whether to set the original dtype automatically or require a new QTable.read() option.
Poking this one, as we've just discovered this behavior in LSST. At the very least, we should make the green note on this page more prominent.
Any kind of "changes everything to a float" behavior can be dangerous for bit fields, ids or large explicitly countable values. This is related to a problem we've had with pandas, where it changes dtypes to float in order to fill in missing data with NaN.
Activity
dhomeier commentedon Nov 18, 2021
The second step of the workaround
could be made a bit more convenient at least as
QTable(table_fromfits, dtype=table_fromfits.dtype)by having
QTable._convert_col_for_tableapply the dtype from*kwargsindividually toq_cls.dhomeier commentedon Nov 18, 2021
As for the I/O part, I had assumed that it was a design choice to construct all Quantity-type columns as float types, but am wondering now if this has in fact been overlooked when discarding the dtype info in
_decode_mixinsastropy/astropy/io/fits/connect.py
Lines 100 to 103 in de53bdc
Edit: whatever the intended default behaviour, setting the attrs above will not make any difference (as the
tbl[col['name']].info.dtypein question already/still are of integer type at that point); anydatatypeinformation would probably need to be passed on with thetbl.meta['__serialized_columns__']dict to the_construct_mixin[s]utilities.dhomeier commentedon Nov 19, 2021
@samaloney I have pushed a fix for correctly reading back the mixins in #12505 ; if you have chance to test if this addresses your problems, feedback would be great.
For a temporary workaround, as you noted that your use cases typically involve reading in large volumes of data, I strongly recommend to use
getheaderandgetdatato read the tables in their original dtypes and then extract the units to directly constructu.Quantity(tab[col], dtype=tab[col].dtype, name=col, unit=u.Unit(header.get('TUNITn'), format='fits')).It's a bit cumbersome, but would avoid conversion losses and in particular the temporary creation of arrays 4x the original size or larger, in particular since the type conversion does not let you take advantage of
memmap=True.samaloney commentedon Nov 25, 2021
Sorry for the delay @dhomeier I've just loaded some of my fits files using your branch seems to work as expected the dtypes are respected.
dhomeier commentedon Nov 25, 2021
Thanks for the confirmation! Note that the currently favoured implementation will store the
dtypeinformation to its own__serialized_columns__field and thus not recover that info from files written with the current Astropy version. So for existing files the header would still need to be fixed separately. Also we are still discussing in #12505 (comment) whether to set the originaldtypeautomatically or require a newQTable.read()option.parejkoj commentedon Nov 8, 2022
Poking this one, as we've just discovered this behavior in LSST. At the very least, we should make the green note on this page more prominent.
Any kind of "changes everything to a float" behavior can be dangerous for bit fields, ids or large explicitly countable values. This is related to a problem we've had with pandas, where it changes dtypes to float in order to fill in missing data with NaN.