Skip to content

shape-1 fields in dtypes are collapsed to scalars #13112

Closed

Description

@anntzer

Consider the following example

import numpy as np
t = np.empty(1, [
    ("a", int),
    ("b", int, ()),
    ("c", int, 1),
    ("d", int, (1,)),
    ("e", int, 2),
    ("f", int, (2,)),
])
for field in "abcdef":
    print(t[field].shape)

This prints

(1,)
(1,)
(1,)
(1, 1)
(1, 2)
(1, 2)

i.e. the ("c", int, 1) field is treated as a scalar, but not the ("d", int, (1,)) field.

While a careful reading of https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html does show that this behavior is documented ("Note that a 3-tuple with a third argument equal to 1 is equivalent to a 2-tuple."), it looks a bit contrary to normal expectations (for most numpy functions, when a single integer is allowed where a shape is expected, that single integer is usually treated as a single-element tuple containing that value). Note that there are already two other ways to specify a scalar field (either don't pass the shape at all, or pass an empty tuple), so it would be nice if the behavior of ("c", int, 1) could be made more consistent.

Reproducing code example:

(see above)

Numpy/Python version information:

1.16.2 3.7.2 (default, Dec 29 2018, 06:19:36) 
[GCC 7.3.0]

Activity

  1. eric-wieser commented on Mar 13, 2019

    @eric-wieser
    Member

    I'd be in favor of deprecating this - I introduced a bug internally thanks to this at some point.

  2. anntzer commented on Mar 13, 2019

    @anntzer
    ContributorAuthor

    (The nice thing is that a deprecation path is available: if one passes 1 as shape, emit a warning asking to either use () [or no shape at all] or (1,) depending on what they want).

  3. eric-wieser commented on Apr 14, 2019

    @eric-wieser
    Member

    @anntzer: Want to make a PR to deprecate this?

  4. anntzer commented on Apr 14, 2019

    @anntzer
    ContributorAuthor

    I have no idea where the logic for dtype construction is :/

  5. eric-wieser commented on Apr 14, 2019

    @eric-wieser
    Member

    /*
    * If (type, 1) was given, it is equivalent to type...
    * or (type, ()) was given it is equivalent to type...
    */
    if ((shape.len == 1
    && shape.ptr[0] == 1
    && PyNumber_Check(val))
    || (shape.len == 0
    && PyTuple_Check(val))) {
    npy_free_cache_dim_obj(shape);
    return type;
    }

    Note that the (type, 1) → type alias was added deliberately in 071f111, while (type, ()) → type was added 9 months later in 87e3010. Had () come first, I doubt we would have ever added 1.

  6. seberg commented on Jul 12, 2019

    @seberg
    Member

    The deprecation is ongoing and in 1.17 now, so I think I will close tihs 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