Skip to content

Slicing FITS_rec objects produces unexpected dtypes. #5490

Closed

Description

@weaverba137

Consider the following snippet:

import numpy as np
from astropy.io import fits

def create_data():
    tbhdu = fits.BinTableHDU.from_columns([
        fits.Column(name='a', format='I', array=np.arange(2**16, dtype=np.int16)),
        fits.Column(name='b', format='I', bscale=1, bzero=2**15, array=np.arange(2**16, dtype=np.uint16))])
    tbhdu.writeto('test_scaled_slicing.fits')

def load_data(nonnegative=False):
    with fits.open('test_scaled_slicing.fits') as hdulist:
        data = hdulist[1].data
    if nonnegative:
        return data[data['a'] >= 0]
    else:
        return data

create_data()
t1 = load_data()
t2 = load_data(True)
assert t1['b'].dtype is np.dtype(np.uint16)
# assert t2['b'].dtype is np.dtype(np.float64)
assert t2['b'].dtype is np.dtype(np.uint16)

The second assert fails because the slicing the FITS_rec object does not trigger the mechanism that handles the scaling of the column 'b'. In other words, some data access methods on FITS_rec trigger the scaling but some don't.

If however, I use a method that forces the scaling, then this problem goes away. Here's an example:

def load_data(nonnegative=False):
    with fits.open('test_scaled_slicing.fits') as hdulist:
        data = hdulist[1].data
    #
    # WORKAROUND
    #
    if any([c.bzero is not None for c in data.columns]):
        foo = [data[k].dtype for k in data.dtype.names]
    if nonnegative:
        return data[data['a'] >= 0]
    else:
        return data

That is, merely accessing the dtype of each column triggers the scaling. Looking into the code itself, it looks like the ._converted attribute needs to be filled in for slicing to work properly.

Activity

  1. added a commit that references this issue on Jul 11, 2019
    173599f
  2. added a commit that references this issue on Jul 15, 2019
    718c019
  3. peppedilillo commented on Sep 8, 2024

    @peppedilillo

    This issue was closed but I can still get the same bug with astropy 6.1.3.

  4. neutrinoceros commented on Sep 9, 2024

    @neutrinoceros
    Contributor

    @peppedilillo This is surprising given that the resolving PR contained a regression test.
    I also couldn't reproduce the problem with either astropy 6.1.3 or 7.0.0dev.
    Can you please open a new issue ?

  5. peppedilillo commented on Sep 9, 2024

    @peppedilillo

    Yes sure i will. Are you sure you removed the comment to the second assert before trying it? I get:

        assert t2['b'].dtype is np.dtype(np.float64)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    AssertionError
    
  6. neutrinoceros commented on Sep 9, 2024

    @neutrinoceros
    Contributor

    Oh, I didn't get that this was the problematic assert. Indeed I can reproduce it then, and I would recommend re-opening this issue instead of creating a new one.

  7. peppedilillo commented on Sep 9, 2024

    @peppedilillo

    @neutrinoceros I cannot re-open the issue. Likely no permissions.

  8. neutrinoceros commented on Sep 9, 2024

    @neutrinoceros
    Contributor

    I know, this was meant for @weaverba137 or @saimn :)

  9. weaverba137 commented on Sep 9, 2024

    @weaverba137
    MemberAuthor

    I also do not see an obvious way to reopen the issue. I also note that #6837 is still open. Is that still genuinely open?

  10. reopened this on Sep 9, 2024
  11. pllim commented on Sep 9, 2024

    @pllim
    Member

    I re-opened this issue as requested but I will defer to @astrofrog and @saimn for #6837

  12. pllim commented on Sep 9, 2024

    @pllim
    Member

    Though I wonder if #5490 (comment) is from numpy 2.0 🤔 and also why didn't our regression test suite catch it?

  13. pllim commented on Sep 9, 2024

    @pllim
    Member

    @peppedilillo , can you please give us a minimally reproducible code snippet that you encountered? What numpy version? Python version, OS, distro, etc?

  14. neutrinoceros commented on Sep 10, 2024

    @neutrinoceros
    Contributor

    Though I wonder if #5490 (comment) is from numpy 2.0 🤔

    It's not, the same exception is raised with numpy 1.26

    and also why didn't our regression test suite catch it?

    This is indeed confusing. Simon's test from #8982 makes sense to me. I keep wondering if Benjamin meant to refer to the commented assert statement in

    The second assert fails

    Because it doesn't make much sense to me to expect float64 where only 16 bits data types were used originally.
    @weaverba137 Can you please clarify ?

  15. peppedilillo commented on Sep 10, 2024

    @peppedilillo

    @peppedilillo , can you please give us a minimally reproducible code snippet that you encountered? What numpy version? Python version, OS, distro, etc?

    from astropy.io import fits
    
    
    if __name__ == "__main__":
        hdul = fits.open("mre.fits")
        fields = ["CHANNEL", "PHA", "CID", "SDDID"]
        for field in fields:
            print(f"slice, then access {field} field: ", hdul[2].data[:1][field])
        for field in fields:
            print(f"access field, then access {field} slice: ", hdul[2].data[field][:1])
        hdul.close()

    This outputs:

    slice, then access CHANNEL field:  [array([  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,
              0,   0,   3, 228,   0,   0,   0,   0,   0,  76, 106, 162,  65,
            140,  52,  98,  12,   1, 165,  55,   0,  64, 215,  77,  89, 166,
            110,   0,   0,   0,   2,   0,   0,   0,   0,   0,   0,   0,   2,
              0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
              0,   0,   2,   0,   0,   0,   0,   0,  12,  61, 246,   0,   0,
              0,   0,   0,   0,   0,   2,   0,   0,   0,   0,   0,  36, 185,
            226,   0,   0,   0,   0,   0,   0,   0,   2,   0,   0,   0,   0,
              0,  48, 247, 216,   1,   1,  68,  37, 228, 119,   0,   0,   0,
            203], dtype=uint8)                                              ]
    slice, then access PHA field:  [array([], dtype=int16)]
    slice, then access CID field:  [array([], dtype=uint8)]
    slice, then access SDDID field:  [array([], dtype=uint8)]
    
    access CHANNEL field, then slice:  [array([13, 19], dtype=uint8)]
    access PHA field, then slice:  [array([21786, 22649], dtype=int16)]
    access CID field, then slice:  [array([7, 7], dtype=uint8)]
    access SDDID field, then slice:  [array([1, 0], dtype=uint8)]

    The first four prints are garbage. The last four are correct, and take a long time in each statement. You can download the input file and the script here. Note that the problem occurs also if fields is set to [PHA], which is signed. Some of these columns are variable length.

    This occurred to me because I am working on a FITS viewer application for the terminal. The application show FITS tables in short "pages". Each page consists of a slice of FITS_rec. I could not get the right output for certain input files because of this problem. I'm trying to work on slices because, if some scaling or other slow astropy operation are carried out over the whole table, that would result in annoying delays on the readout.

    Just tested on Debian 12 bookworm, but observed the same behaviour also on Windows 11 and MacOS Ventura.
    Yes i'm on numpy 2 and Python 3.12.5. The whole environment is the following, I'm on anaconda presently:

    # packages in environment at /home/deppep/miniforge3/envs/misfits:
    #
    # Name                    Version                   Build  Channel
    _libgcc_mutex             0.1                 conda_forge    conda-forge
    _openmp_mutex             4.5                       2_gnu    conda-forge
    astropy                   6.1.3                    pypi_0    pypi
    astropy-iers-data         0.2024.9.2.0.33.23          pypi_0    pypi
    bzip2                     1.0.8                h4bc722e_7    conda-forge
    ca-certificates           2024.8.30            hbcca054_0    conda-forge
    click                     8.1.7                    pypi_0    pypi
    ld_impl_linux-64          2.40                 hf3520f5_7    conda-forge
    libexpat                  2.6.2                h59595ed_0    conda-forge
    libffi                    3.4.2                h7f98852_5    conda-forge
    libgcc                    14.1.0               h77fa898_1    conda-forge
    libgcc-ng                 14.1.0               h69a702a_1    conda-forge
    libgomp                   14.1.0               h77fa898_1    conda-forge
    libnsl                    2.0.1                hd590300_0    conda-forge
    libsqlite                 3.46.0               hde9e2c9_0    conda-forge
    libuuid                   2.38.1               h0b41bf4_0    conda-forge
    libxcrypt                 4.4.36               hd590300_1    conda-forge
    libzlib                   1.3.1                h4ab18f5_1    conda-forge
    linkify-it-py             2.0.3                    pypi_0    pypi
    markdown-it-py            3.0.0                    pypi_0    pypi
    mdit-py-plugins           0.4.1                    pypi_0    pypi
    mdurl                     0.1.2                    pypi_0    pypi
    misfits                   0.0.1                    pypi_0    pypi
    ncurses                   6.5                  he02047a_1    conda-forge
    numpy                     2.1.0                    pypi_0    pypi
    openssl                   3.3.1                hb9d3cd8_3    conda-forge
    packaging                 24.1                     pypi_0    pypi
    pandas                    2.2.2                    pypi_0    pypi
    pip                       24.2               pyh8b19718_1    conda-forge
    platformdirs              4.2.2                    pypi_0    pypi
    pyerfa                    2.0.1.4                  pypi_0    pypi
    pygments                  2.18.0                   pypi_0    pypi
    python                    3.12.5          h2ad013b_0_cpython    conda-forge
    python-dateutil           2.9.0.post0              pypi_0    pypi
    pytz                      2024.1                   pypi_0    pypi
    pyyaml                    6.0.2                    pypi_0    pypi
    readline                  8.2                  h8228510_1    conda-forge
    rich                      13.8.0                   pypi_0    pypi
    setuptools                72.2.0             pyhd8ed1ab_0    conda-forge
    six                       1.16.0                   pypi_0    pypi
    textual                   0.79.1                   pypi_0    pypi
    tk                        8.6.13          noxft_h4845f30_101    conda-forge
    typing-extensions         4.12.2                   pypi_0    pypi
    
    
  16. weaverba137 commented on Sep 10, 2024

    @weaverba137
    MemberAuthor

    This issue was created in 2016(!) and I do not recall why this line was there:

    # assert t2['b'].dtype is np.dtype(np.float64)

    I would not expect this to work, even after the bug was fixed, and I think that is reasonable.

    When I used the term "The second assert fails...", I currently believe that refers to this line:

    assert t2['b'].dtype is np.dtype(np.uint16)
  17. saimn commented on Sep 10, 2024

    @saimn
    Contributor

    I don't think the data type from the original issue should be float, this line is commented so maybe it was just some trial?

    Ah thanks @weaverba137 for commenting :), I was just looking at the issue and writing a comment.

    @peppedilillo - your issue is a different one, it has to do with VLA columns. Interestingly if you first access the full columns and then slice it, it works. Please open a new issue.

  18. pllim commented on Sep 10, 2024

    @pllim
    Member

    Thanks, all! I opened #16990 for you. Please continue there.

  19. peppedilillo commented on Sep 10, 2024

    @peppedilillo

    I realize that the commented assert should not work indeed. Thank you for having opened the new issue.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions