Thanks to visit codestin.com
Credit goes to github.com

Skip to content

BUG: numpy.ma.min fails for uint dtypes #27580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jbkoch7 opened this issue Oct 16, 2024 · 4 comments · May be fixed by #27584
Open

BUG: numpy.ma.min fails for uint dtypes #27580

jbkoch7 opened this issue Oct 16, 2024 · 4 comments · May be fixed by #27584

Comments

@jbkoch7
Copy link

jbkoch7 commented Oct 16, 2024

Describe the issue:

The numpy.ma.min operation fails for cases where the input array has a dtype = uint and the resultant array and mask has ndim > 0. There could be other operations where this same case fails, e.g., numpy.ma.max.

Reproduce the code example:

import numpy as np
import numpy.ma as ma

_dtype = np.uint8
fill_value = _dtype( 0 )
a = np.array( [ [ 1, 127, 2 ],
                [ 2, 127, 1 ] ], dtype = _dtype )
a = ma.masked_array( a, a == 127 )
a.fill_value = fill_value
amin = a.min( axis = 0 )

Error message:

╭───────────────────────── Traceback (most recent call last) ──────────────────────────╮
│ test.py:10 in <module>                                                               │
│                                                                                      │
│    7 │   │   │   │   [ 2, 127, 1 ] ], dtype = _dtype )                               │
│    8 a = ma.masked_array( a, a == 127 )                                              │
│    9 a.fill_value = fill_value                                                       │
│ ❱ 10 amin = a.min( axis = 0 )                                                        │
│   11                                                                                 │
│                                                                                      │
│ .venv/lib/python3.12/site-packages/numpy/ma/core.py:5985                             │
│ in min                                                                               │
│                                                                                      │
│   5982 │   │   │   │   result.__setmask__(newmask)                                   │
│   5983 │   │   │   │   # Get rid of Infs                                             │
│   5984 │   │   │   │   if newmask.ndim:                                              │
│ ❱ 5985 │   │   │   │   │   np.copyto(result, result.fill_value, where=newmask)       │
│   5986 │   │   │   elif newmask:                                                     │
│   5987 │   │   │   │   result = masked                                               │
│   5988 │   │   │   return result                                                     │
╰──────────────────────────────────────────────────────────────────────────────────────╯
TypeError: Cannot cast scalar from dtype('int64') to dtype('uint8') according to the
rule 'same_kind'

Python and NumPy Versions:

2.1.2
3.12.5 (main, Oct 14 2024, 12:30:20) [GCC 13.2.0]

Runtime Environment:

No response

Context for the issue:

No response

@eendebakpt
Copy link
Contributor

eendebakpt commented Oct 16, 2024

Here is another example:

import numpy as np
import numpy.ma as ma
print(np.__version__)

a = np.array( [ [ 1, 127, 2 ],
                [ 2, 127, 1 ] ], dtype =  np.uint8 )

mask = (a == 127)
b = ma.masked_array( a, mask )
print(f'{b=}')

m = b.min() # works
print('## {m}')
b.min(axis=0) # fails

The output of print(f'{b=}') is

b=masked_array(data=[1, --, 2],
             mask=[False,  True, False],
       fill_value=np.int64(999999),
            dtype=uint8)

The masked array b as type uint8, but the fill value is an uint64. This creates issues in a.min. It is a bit strange that b.min() works, but b.min(axis=0) fails.

The value np.int64(999999) is retrieved from numpy.ma.core.default_fill_value, which has docstring

def default_fill_value(obj):
    """
    Return the default fill value for the argument object.

    The default filling value depends on the datatype of the input
    array or the type of the input scalar:

       ========  ========
       datatype  default
       ========  ========
       bool      True
       int       999999
       float     1.e20
       complex   1.e20+0j
       object    '?'
       string    'N/A'
       ========  ========

Maybe np.ma.core.default_fill_value(np.dtype(np.uint8)) should return 254, but I am not familiar enough with masked arrays to make that call.

@fengluoqiuwu
Copy link
Contributor

There.

np.copyto(result, result.fill_value, where=newmask)

In document we can find that fill_value has int64 for integer type, but in the default casting mode , int64 could not be cast to uint8, that is why dtype int8 can run but uint8 failed. Maybe we should add uint64(999999) in default fill_value for usigned integer type or add type check and transform before copyto.

@fengluoqiuwu
Copy link
Contributor

It is a bit strange that b.min() works, but b.min(axis=0) fails.

In b.min(), result.ndim equals to 0, and it skip copyto.

result = self.filled(fill_value).min(
                axis=axis, out=out, **kwargs).view(type(self))
            if result.ndim:
                # Set the mask
                result.__setmask__(newmask)
                # Get rid of Infs
                if newmask.ndim:
                    test = result.fill_value
                    np.copyto(result, result.fill_value, where=newmask)
            elif newmask:
                result = masked
            return result

@fengluoqiuwu
Copy link
Contributor

fengluoqiuwu commented Oct 17, 2024

I delete the infinity checking module.

seberg added a commit to seberg/numpy that referenced this issue Nov 20, 2024
This is the most minimal fix I could think off... If we have uints
still use the out-of-bounds value, but make it uint.  This is because
the code uses copyto with the default same-kind casting in places...

This doesn't remove all the other quirks, and surely, it is a behavior
change in some awkward situations (since you got a uint, someone might
mix the newly uint value with an integer and get float64 results, etc.)

But... it is by far the most minimal fix I could think of after a longer
time.

A more thorough fix may be to just always store the exact dtype, but
it would propagate differently e.g. when casting.  A start for this
is currently in numpygh-27596.

Closes numpygh-27269, numpygh-27580
seberg added a commit to seberg/numpy that referenced this issue Nov 20, 2024
This is the most minimal fix I could think off... If we have uints
still use the out-of-bounds value, but make it uint.  This is because
the code uses copyto with the default same-kind casting in places...

This doesn't remove all the other quirks, and surely, it is a behavior
change in some awkward situations (since you got a uint, someone might
mix the newly uint value with an integer and get float64 results, etc.)

But... it is by far the most minimal fix I could think of after a longer
time.

A more thorough fix may be to just always store the exact dtype, but
it would propagate differently e.g. when casting.  A start for this
is currently in numpygh-27596.

Closes numpygh-27269, numpygh-27580
ArvidJB pushed a commit to ArvidJB/numpy that referenced this issue Jan 8, 2025
This is the most minimal fix I could think off... If we have uints
still use the out-of-bounds value, but make it uint.  This is because
the code uses copyto with the default same-kind casting in places...

This doesn't remove all the other quirks, and surely, it is a behavior
change in some awkward situations (since you got a uint, someone might
mix the newly uint value with an integer and get float64 results, etc.)

But... it is by far the most minimal fix I could think of after a longer
time.

A more thorough fix may be to just always store the exact dtype, but
it would propagate differently e.g. when casting.  A start for this
is currently in numpygh-27596.

Closes numpygh-27269, numpygh-27580
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants