Skip to content

fits: Inconsistent type conversion for FitsRecord #4034

Closed

Description

@michaelaye

I have this fitsfile with a FitsRecord that has the following dtype:

dtype([('SC_CLK_COARSE', '>i4'), ('SC_CLK_FINE', '>i2'), ('FUV_CHIP_TEMP_C', '>f8'),
('FUV_P3_3_V_ADC_V', '>f4'),...]

and while dtypes like >i2 and >i4 are being converted to numpy.float64 when I read the arrays with the standard key access of the FitsRecord, dtypes like >f4 or >f8 keep stuck in BigEndian mode:

print(hk.AnalogConv['SC_CLK_COARSE'].dtype)
print(hk.AnalogConv['SC_CLK_FINE'].dtype)
print(hk.AnalogConv['FUV_CHIP_TEMP_C'].dtype)
print(hk.AnalogConv['FUV_P3_3_V_ADC_V'].dtype)

float64
float64
>f8
>f4

This fact throws off working with Pandas arrays, b/c for some reason if some of the columns in the dataframe are float64, i.e. when I include the SC_CLK_xx columns, then the whole dataframe is being converted to float64, but if I exclude the integer columns that are being auto-converted to float64 and only read >f4/8 types, then the resulting pandas array (that uses numpy under the hood) is remaining in BigEndian mode and math operations like df.describe() are failing:

KeyError: ValueError('Big-endian buffer not supported on little-endian compiler',)

My question: Is this behavior of the FitsRecord reading as designed and I have to write my own conversion to make sure things work out, or is this a bug somewhere?

Activity

  1. embray commented on Aug 4, 2015

    @embray
    Member

    I'm not totally sure I understand the issue--I feel like I'm missing some context.

    If you have a look at this comment it might help explain what's going on though: #3073 (comment)

    The lack of big-endian support in Pandas has been a problem before with working with FITS files. I would recommend just looping over each column and ensuring that it's converted to native byte-order before passing it to Pandas.

  2. michaelaye commented on Aug 4, 2015

    @michaelaye
    Author

    Well, forget the Pandas part for a moment that's just an effect on what happens on numpy level. Why are the types converted to little endian for '>ix' and not for '>fx' ? Isn't that inconsistent?

    Edited: correcting DamnYouAutoCorrect.

  3. embray commented on Aug 4, 2015

    @embray
    Member

    Heh.

    My point was that some of those columns might be scaled, and the rescaled array is just in native byte order, since there's no reason to add the overhead of converting it back to big-endian until and unless it's written back out to a a FITS file. The columns that are still in big-endian are just accessing the raw data directly out of the FITS file since they aren't scaled.

    It's true that the dtype is deceptive. I think I have a fix for that in a branch.

  4. michaelaye commented on Aug 4, 2015

    @michaelaye
    Author

    Hm, indeed the integer dtypes are the only ones that have scaling and offset information:

    for i,item in enumerate(hk.AnalogConv.dtype.descr):
        if item[1][1] == 'i':
            print(i+1, item)
    
    4 ('APID', '>i2')
    6 ('SSC', '>i2')
    7 ('DATA_LEN', '>i2')
    8 ('SC_CLK_COARSE', '>i4')
    9 ('SC_CLK_FINE', '>i2')
    10 ('DFB_TERTIARY', '>i2')

    and from the header:

    ('TSCAL4', 1, '')
    ('TSCAL6', 1, '')
    ('TSCAL7', 1, '')
    ('TSCAL8', 1, '')
    ('TSCAL9', 1, '')
    ('TSCAL10', 1, '')
    ('TZERO4', 32768, '')
    ('TZERO6', 32768, '')
    ('TZERO7', 32768, '')
    ('TZERO8', 2147483648, '')
    ('TZERO9', 32768, '')
    ('TZERO10', 32768, '')

    I still think that this is quite a bad interface for the user. I'm speaking as the naive user here (easy for me ;) and if I get a mix of big endian and little endian back into numpy, that's just very confusing. As the scaling and offset operations seem to convert to little-endian without doing any harm, is there a reason not to simply enforce conversion to little-endian whenever accessing it via the FitsRecord[key] interface?

  5. embray commented on Aug 4, 2015

    @embray
    Member

    Is there a reason not to simply enforce conversion to little-endian whenever accessing it via the FitsRecord[key] interface?

    Yes--that requires (at least naively, without using on-disk caching) copying the entire array into memory, rather than allowing it to be read from disk as needed. Make sure also that you've read this if you haven't.

    In most cases the endianness isn't relevant since Numpy handles that pretty transparently. It's only when interfacing with other software that doesn't handle it that this is a problem, such as Pandas and some SciPy routines. This has come up before. But I agree it's a bad interface. There's no silver bullet though when it comes to trying to perfectly hide away FITS-isms that make working with FITS files hazardous, though I've outlined plans elsewhere for improvement (that said, consistent byte orders between FITS columns is not a to-do item since in most cases byte order should be irrelevant to the user and hidden away entirely).

    Oh, also, it looks like the columns you have there are getting rescaled are pseudo-unsigned integers, so really you want those being converted to unsigned ints instead of floats. To get that, use uint=True when opening the file. In Astropy v1.1 this will be the default.

  6. embray commented on Aug 4, 2015

    @embray
    Member

    Though this makes me think, I could add a bullet point to my plans for the FITSData class (this is my planned container for FITS arrays and FITS table columns) to include a convenience method for returning the logical data in native byteorder.

  7. michaelaye commented on Aug 4, 2015

    @michaelaye
    Author

    Thanks a lot! I was not imaging that Numpy would be handling this transparently, I was worried that I end up with wrong numbers if I don't have little-endians upfront.

  8. embray commented on Aug 4, 2015

    @embray
    Member

    No, it's not a problem for Numpy--if a Numpy array is specified to contain big-endian values it will automatically swap them to little-endian before displaying them or doing any other calculations with them.

    It's just a bit of a pain if you want to convert multiple columns to native byte order. Since you can't be sure up front what the array is stored in you have to do something like:

    sys_byteorder = ('>', '<')[sys.byteorder == 'little']
    if col.dtype.byteorder not in ('=', sys_byteorder):
        col = col.byteswap().newbyteorder(sys_byteorder)
        # Do something with col array...
  9. michaelaye commented on Nov 2, 2015

    @michaelaye
    Author

    i don't understand the needs_clarification label? What is missing?

  10. embray commented on Nov 16, 2015

    @embray
    Member

    Well originally it was because I didn't understand the question. I'll have to go back and re-read this now.

  11. saimn commented on Jun 4, 2026

    @saimn
    Contributor

    Not sure if something needs fixing here but given the last comments I will close :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions