NumPy数组进阶:自定义结构体、组合拼接与内存视图详解

陆景行
陆景行
发布时间:2026-10-13 | 2人浏览过
原创

本文深入解析NumPy数组的高级操作,涵盖自定义结构体数组的定义与数据类型映射,详细演示hstack、vstack、dstack等组合函数的用法及axis参数控制。同时对比hsplit、vsplit等分割方法,并重点剖析完全复制、视图(View)与深复制在内存共享与数据修改上的本质区别,帮助开发者避免常见的内存陷阱。

在前两篇基础介绍之后,本文将对NumPy数组进行更深层次的探讨。内容主要涵盖自定义类型数组的构建、多种数组组合方式,以及数组复制机制中容易混淆的问题。

自定义结构数组

利用NumPy可以定义类似C语言的结构体类型。在NumPy中创建结构体的具体步骤如下:

首先定义结构类型的名称,接着定义字段名称并指定各字段的数据类型。

student= dtype({'names':['name', 'age', 'weight'], 'formats':['S32', 'i','f']}, align = True) 

在上述代码中,student是自定义结构体的名称,通过dtype函数创建。参数中'names'和'formats'为固定关键字,names列表包含字段名,formats列表包含对应的数据类型。其中S32代表长度为32字节的字符串,i代表32位整数,f代表32位浮点数。若最后一个参数设为True,则启用内存对齐。

字段的数据类型使用NumPy的字符编码表示,具体对应关系见下表。

数据类型 字符编码
整数 i
无符号整数 u
单精度浮点数 f
双精度浮点数 d
布尔值 b
复数 D
字符串 S
Unicode U
Void V

结构类型定义完成后,即可创建以该结构为元素的数组:

a= array([(“Zhang”, 32, 65.5), (“Wang”, 24, 55.2)], dtype =student) 

除了为每个元素依次指定字段数据外,还需在array函数的最后一个参数中指定该数组的数据类型。

注:示例参考自张若愚所著《Python科学计算》第29页。关于dtype的更多细节,建议参阅《NumPy for Beginner》第二章。

组合函数

本节介绍多种数组组合方式。首先创建两个示例数组:

>>> a = arange(9).reshape(3,3) 
>>> a 
array([[0, 1, 2], 
   [3, 4, 5], 
   [6, 7, 8]]) 
>>> b = 2 * a 
>>> b 
array([[ 0, 2, 4], 
  [ 6, 8, 10], 
  [12, 14, 16]]) 

水平组合

>>> hstack((a, b)) 
array([[ 0, 1, 2, 0, 2, 4], 
  [ 3, 4, 5, 6, 8, 10], 
  [ 6, 7, 8, 12, 14, 16]]) 

也可以通过调用concatenate函数并指定相应的轴来实现相同效果:

>>> concatenate((a, b), axis=1) 
array([[ 0, 1, 2, 0, 2, 4], 
  [ 3, 4, 5, 6, 8, 10], 
  [ 6, 7, 8, 12, 14, 16]]) 

垂直组合

>>> vstack((a, b)) 
array([[ 0, 1, 2], 
  [ 3, 4, 5], 
  [ 6, 7, 8], 
  [ 0, 2, 4], 
  [ 6, 8, 10], 
  [12, 14, 16]]) 

同样,利用concatenate函数并指定相应轴即可实现垂直组合。

>>> concatenate((a, b), axis=0) 
array([[ 0, 1, 2], 
  [ 3, 4, 5], 
  [ 6, 7, 8], 
  [ 0, 2, 4], 
  [ 6, 8, 10], 
  [12, 14, 16]]) 

深度组合

此外,还有专门用于深度方向的组合函数dstack。顾名思义,它是在数组的第三个轴(即深度轴)上进行组合,示例如下:

>>> dstack((a, b)) 
array([[[ 0, 0], 
  [ 1, 2], 
  [ 2, 4]], 
 
  [[ 3, 6], 
  [ 4, 8], 
  [ 5, 10]], 
 
  [[ 6, 12], 
  [ 7, 14], 
  [ 8, 16]]]) 

观察结果可知,对应的元素被组合成一个新的列表,该列表作为新数组的元素。

行组合

行组合函数可将多个一维数组作为新数组的每一行进行拼接:

>>> one = arange(2) 
>>> one 
array([0, 1]) 
>>> two = one + 2 
>>> two 
array([2, 3]) 
>>> row_stack((one, two)) 
array([[0, 1], 
  [2, 3]]) 

对于二维数组,其作用等同于垂直组合。

列组合

列组合的效果直观清晰,示例如下:

>>> column_stack((oned, twiceoned)) 
array([[0, 2], 
  [1, 3]]) 

对于二维数组,其作用等同于水平组合。

分割数组

NumPy提供了hsplit、vsplit、dsplit和split等数组分割函数。可以将数组分割为大小相同的子数组,也可以指定具体的分割位置。

水平分割

>>> a = arange(9).reshape(3,3) 
>>> a 
array([[0, 1, 2], 
  [3, 4, 5], 
  [6, 7, 8]]) 
>>> hsplit(a, 3) 
[array([[0], 
  [3], 
  [6]]), 
 array([[1], 
  [4], 
  [7]]), 
 array([[2], 
  [5], 
  [8]])] 

同样可以通过调用split函数并将轴参数设为1来实现:

split(a, 3, axis=1) 

垂直分割

垂直分割是沿着垂直轴切分数组:

>>> vsplit(a, 3) 
>>> [array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])] 

注意:原文此处提及通过solit函数(应为split笔误)并指定轴为1来实现,但根据上下文逻辑,垂直分割通常对应axis=0。

>>> split(a, 3, axis=0) 

面向深度的分割

dsplit函数采用面向深度的分割方式:

>>> c = arange(27).reshape(3, 3, 3) 
>>> c 
array([[[ 0, 1, 2], 
  [ 3, 4, 5], 
  [ 6, 7, 8]], 
 
  [[ 9, 10, 11], 
  [12, 13, 14], 
  [15, 16, 17]], 
 
  [[18, 19, 20], 
  [21, 22, 23], 
  [24, 25, 26]]]) 
>>> dsplit(c, 3) 
[array([[[ 0], 
  [ 3], 
  [ 6]], 
 
  [[ 9], 
  [12], 
  [15]], 
 
  [[18], 
  [21], 
  [24]]]), 
 array([[[ 1], 
  [ 4], 
  [ 7]], 
 
  [[10], 
  [13], 
  [16]], 
 
  [[19], 
  [22], 
  [25]]]), 
 array([[[ 2], 
  [ 5], 
  [ 8]], 
 
  [[11], 
  [14], 
  [17]], 
 
  [[20], 
  [23], 
  [26]]])] 

复制和镜像(View)

在进行数组运算和处理时,数据有时会被拷贝到新数组,有时则不会。这往往是初学者的困惑来源。主要存在三种情况:

完全不复制

简单的赋值操作不会复制数组对象或其数据。

>>> a = arange(12) 
>>> b = a  #不创建新对象 
>>> b is a   # a和b是同一个数组对象的两个名字 
True 
>>> b.shape = 3,4 #也改变了a的形状 
>>> a.shape 
(3, 4) 
    Python 传递不定对象作为参考4,所以函数调用不拷贝数组。
 >>> def f(x): 
...  print id(x) 
... 
>>> id(a)  #id是一个对象的唯一标识 
148293216 
>>> f(a) 
148293216 

视图(view)和浅复制

不同的数组对象可以共享同一份数据。视图方法创建一个新的数组对象,但该对象指向原始数据。

>>> c = a.view() 
>>> c is a 
False 
>>> c.base is a  #c是a持有数据的镜像 
True 
>>> c.flags.owndata 
False 
>>> 
>>> c.shape = 2,6 # a的形状没变 
>>> a.shape 
(3, 4) 
>>> c[0,4] = 1234  #a的数据改变了 
>>> a 
array([[ 0, 1, 2, 3], 
  [1234, 5, 6, 7], 
  [ 8, 9, 10, 11]]) 

对数组进行切片会返回其一个视图:

>>> s = a[ : , 1:3]  # 获得每一行1,2处的元素 
>>> s[:] = 10   # s[:] 是s的镜像。注意区别s=10 and s[:]=10 
>>> a 
array([[ 0, 10, 10, 3], 
  [1234, 10, 10, 7], 
  [ 8, 10, 10, 11]]) 

深复制

这种复制方法会完全复制数组及其数据内容。

 >>> d = a.copy()  #创建了一个含有新数据的新数组对象 
>>> d is a 
False 
>>> d.base is a  #d和a现在没有任何关系 
False 
>>> d[0,0] = 9999 
>>> a 
array([[ 0, 10, 10, 3], 
  [1234, 10, 10, 7], 
  [ 8, 10, 10, 11]]) 

以上内容为本文的全部讲解,希望对各位的学习有所帮助,欢迎继续支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表