Python numpy 结构化数组之索引

463 阅读4分钟

image.png

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战

前言

我们在前面已经对 numpy 结构化数组中,知道 numpy 结构化数组与普通数组最大的区别,可以支持多类型的数据类型字段。究其原因,在于 numpy 结构化数组借助 numpy.dtype 方法来创建多类型的数据字段。

numpy 结构化数据类型是字段的结集合。每一个字段由 name、type 和 offset 组成。

字段的数据类型可以是任何numpy数据类型、其他结构化数据类型和子数组数据类型。

numpy也提供了结构化数组类型创建的四种方法:

  • 元组列表形式,每个字段一个元组
  • 以逗号分隔形式, name 为系统默认名称f0,f1等
  • 以字典形式表示字段参数
  • 以字典形式表示字段名称(不推荐)

image.png

对于 numpy 结构化数组的操作,我们已经在 numpy 结构化数组赋值 学习了结构化数组的三种赋值方法分别为

  • 来自元组赋值
  • 标量赋值
  • 来自其他结构化数组的赋值

在 numpy 中普通数组最常见的操作就是索引,同理对于结构化数组也同样支持索引操作。

本期,我们将学习 numpy 结构化数组的索引操作相关方法,Let's go~

1. 结构化数组索引

在 numpy 索引切片 以及 numpy 高级索引中,可以知道 numpy 索引返回的是副本

同理,对于 numpy 结构化数组索引,返回的值是视图还是副本呢?

我们来看看一个案例,首先创建一个结构化数组

>>> x = np.array([("Thu",3),("Wen",4),("Fri",5)],dtype=[("name","U5"),("num","i8")])
>>> x
array([('Thu', 3), ('Wen', 4), ('Fri', 5)],
      dtype=[('name', '<U5'), ('num', '<i8')])
>>>

我们以python 元组形式如 x[1] 索引,通过x[1].base 查询,可以查询到数据来源于x

>>> print(x[1].base)
[('Thu', 3) ('Wen', 4) ('Fri', 5)]

因此,标量索引时,返回的结果是视图

我们以 x[1]["name"] 索引到具体的数据,同样查询x[1]["name"].base 查询,我们可以看到结果回的是None, 因此返回的结果是副本。

>>> x[1]["name"]
'Wen'
>>> print(x[1]["name"].base)
None

x["name"][1] 索引到的数据与x[1]["name"],但是其返回的结果是副本。

>>> x["name"][1]
'Wen'
>>> print(x["name"][1].base)
None

同样,x["name"][[1,0]] 索引多个数据,查看其base属性,可以看到同样是None,返回的结果是副本。

>>> x["name"][[0,1]]
array(['Thu', 'Wen'], dtype='<U5')
>>> print(x["name"][[0,1]].base)
None

那么,x[[1,0]]["name"] 索引多个数据,查看其base属性,来源于x,返回的却是视图

>>> x[[0,1]]["name"]
array(['Thu', 'Wen'], dtype='<U5')
>>> print(x[[0,1]]["name"].base)
[('Thu', 3) ('Wen', 4)]
>>>

2. 访问单个字段

我们可以使用字段名称来进行访问和修改结构化数组的各个字段的值

我们可以看到,访问单个字段时,返回的结果是原始数组的视图。

>>> x["name"]
array(['Thu', 'Wen', 'Fri'], dtype='<U5')
>>> print(x["name"].base)
[('Thu', 3) ('Wen', 4) ('Fri', 5)]
>>>

我们可以通过索引访问形式,重新修改结构化数组的值

>>> x = np.array([("Thu",3),("Wen",4),("Fri",5)],dtype=[("name","U5"),("num","i8")])
>>> y = x["num"]
>>> y[:] = 1
>>> x
array([('Thu', 1), ('Wen', 1), ('Fri', 1)],
      dtype=[('name', '<U5'), ('num', '<i8')])
>>>

被赋值的y,与x["num"]拥有相同的dtype,shape,strides

>>> print(y.dtype,y.shape,y.strides)
int64 (3,) (28,)
>>> print(x["num"].dtype,x["num"].shape,x["num"].strides)
int64 (3,) (28,)
>>>

3. 访问多个字段

我们可以通过索引以列表形式进行访问结构化数组的相关字段的值。

  • 索引是字段名称列表形式展示

  • 多字段索引的行为从numpy 1.15 更改为 numpy 1.16

>>> a = np.ones(3,dtype=[("one","i4"),("two","i4"),("three","i4")])
>>> a[["one","three"]]
array([(1, 1), (1, 1), (1, 1)],
      dtype={'names':['one','three'], 'formats':['<i4','<i4'], 'offsets':[0,8], 'itemsize':12})

  • 多索引访问时,返回的结果是原始数组的视图

    >>> print(a[["one","three"]].base)
    [(1, 1, 1) (1, 1, 1) (1, 1, 1)]
    >>> print(a[["one","three"]].flags.owndata)
    False
    
    

numpy 结果化数组多字段索引时,返回的是视图。我们都知道视图会修改原始数组,视图字段将按照它们被索引的顺序排序。

>>> a[["one","three"]] = (1,3)
>>> a
array([(1, 1, 3), (1, 1, 3), (1, 1, 3)],
      dtype=[('one', '<i4'), ('two', '<i4'), ('three', '<i4')])
>>>

4. 获取结构化标量

我们可以通过整数索引来访问结构化数组数据

  • 单个整数来索引结构化数组的单个元素,会返回结构化标量

    >>> a = np.ones(3,dtype=[("one","i4"),("two","i4"),("three","i4")])
    >>> a
    array([(1, 1, 1), (1, 1, 1), (1, 1, 1)],
          dtype=[('one', '<i4'), ('two', '<i4'), ('three', '<i4')])
    >>> b = a[0]
    >>> b
    (1, 1, 1)
    >>>
    
    
    

    与普通数组的单元素索引不同,结构化标量是可变,因此返回的是原始数组的视图。

    >>> print(b.base)
    [(1, 1, 1) (1, 1, 1) (1, 1, 1)]
    >>> print(b.flags.owndata)
    False
    >>>
    
    
  • 可以使用整数列表标量,访问多个结构化数组数据

    >>> x = np.array([("Thu",3),("Wen",4),("Fri",5)],dtype=[("name","U5"),("num","i8")])
    >>> x[[0,1]]["num"]
    array([3, 4], dtype=int64)
    >>>
    
    

总结

本期,我们主要对numpy 结构化数组索引单字段索引、多字段索引、整数标量索引三种方法进行学习。

其中,结构化数组的索引与普通索引不一样,返回的结果都是视图,都可以修改原始数组数据。

以上是本期内容,欢迎大佬们点赞评论,下期见~~