多维数组怎么降维_谈一谈多维数组

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif afe40576dbf1af5928d457aa77a1ea92.gif

在各种语言中,都提供了多维数组。而多维数组又是很让人迷惑的一个语法结构。今天给大家聊一下多维数组使用中的一些问题。

这里,以Python和IDL为主讲解。

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

多维数组的概念

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

首先说说多维数组的概念。多维数组在不同语言中有大同小异的语法定义,但是逻辑定义上基本上一致,就是同数据类型、同逻辑含义的数据单元构成的能够用整数为坐标访问的超立方体的数据集合。

这里我用的“超立方体”,指的是数据可以表达为多个维度的下标,例如线段、矩形、立方体……它们的反例就是所谓的“参差数组”,比如下面这样的:

1, 2, 3, 4, 5

6, 7, 8

9, 1, 2, 3

这就是典型的一个参差数组,某一个维度的下标范围受到另外的维度的下标取值影响。而超立方体呢,每个维度的下标取值范围(宽度)是固定的。这个情况在python的列表嵌套、C语言动态分配实现的多维数组中都会遇到。在某些特殊情况下确实有用,但是不在这里规范化的数组讨论范围内。

第二个特点,同数据类型。正常情况下的数组都是由同类数据构成的,但是也有例外,如python的列表(list)、元组(tuple)、字典(dict),javascript中的数组,这些都是允许元素类型随意的。这种情况往往出现在脚本语言中,充分体现了语言的灵活性,但是这个真不是数组,后面会谈到。

第三个特点,同数据含义。同样的数据类型,不代表含义就相同,这个是做科学的人编程经常犯的一个毛病。典型案例就是记录数组。例如表达光谱,做一个3×n的数组,其中第一行代表波长,第二行代表流量,第三行代表误差……从语法上来说,这没错,很多光谱也是这么发布的,但是从逻辑含义上来说,我更愿意把这个东西看做3个一维数组的强行拼凑,只是因为他们的数据类型都是浮点数而已。

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

多维数组 VS 记录表

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

上面提到的最后一个情况,有些叫做二维表,我更愿意叫做记录表,以区别于二维数组。除了上面的光谱,我再举个例子:

RA

Dec

Mag_V

06:54:15.29

+56:52:09.4

16.65

22:09:38.91

-03:51:48.1

13.25

这是天文上很常见的一张记录表。这个表中,列数是固定的,每一列的含义也是独立的、固定的,但是行是可以扩展的。在numpy中,这种数据用记录(record)数组来表达,而IDL则使用结构体(structure)数组。在各种语言中基本上也都是用记录、结构体、类/对象等等来表达的。在关系型数据库中,这就是一个表。

记录表和二维数组的相似性,有这么几点:

① 都可以看做是二维的,有些语言中也确实当做二维数组访问;

② 如果各列的数据类型刚好一致(或者可以升级到相同类型),那么也可以用二维数组来装;

但是它们有本质的区别:

1,二维数组的下标之间是等价的,例如表示一幅图像时是x,y,表达矩阵是行列,可以转置、翻转,不影响其性质;而记录表的行一般表达记录,而列往往为字段,二者不可互换。

2,二维数组的大小形状和数据来源有关,是一起提供的;而记录表的列(字段)来源和行(记录)的来源是不同的。

3,二维数组不同维度的下标都是整数(或者是切片),而记录表的行是整数或切片下标,列是字段名来访问。

4,二维数组在语法上是二维的,而记录表在大部分语言的语法中,是类型为记录的一维数组。

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

多维数组 VS 数组的数组

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

这是一个很奇怪的命题……但是在许多语言的语法中确实是这样的。典型的例子:C语言。

二者从语法上来看,区别就是:前者是a[1,2],后者是a[1][2]。

出现后者的原因,主要是看语法本身的支持程度了。对于后者,有些时候是语法限制下表达多维数组的某种办法而已。例如在python中,用list或者tuple来表达多维数组的时候就是这样的。

在各种语言中,表达多维数组常数的时候,往往使用的也是数组的数组的表达方式。例如:

[ [1,2,3,4], [5,6,7,8], [9,10,11,12] ]

这实际上就是一个由3个长度为4的一维数组组成的一个“数组的数组”,但是实际表达的是3x4的二维数组。

对于数组的数组,大家要注意的就是别变成参差数组。当然参差数组也并不是坏事,它的最大特点是节约内存,例如,字符串数组,实际上就是参差数组……

还是要提一下python,它本身是不提供数组的,list/tuple都是所谓的动态数据结构。而真正的数组是通过numpy.ndarray来提供的,内部本质是面向对象加运算符重载,不过对于大家来说当做多维数组用就是了。

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

多维数组的存储

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

对于多维数组来说,逻辑上的概念中是多维的,而实际上计算机内存大家都知道是线性的,磁盘文件存储抛开柱面、磁道、扇区等等物理概念,逻辑上也是线性的。那么这二者是怎么协调的呢?

这时候,多维数组实际上被“摊平”了,变形成一维数组进行存放。这个情况下有两种方法来摊平,一种是行优先,一种是列优先,或者换句话说就是下标从左到右摊平还是从右到左。

可能听得有点晕,不过不要紧,大家只要记得会摊平就行了。不过不要以为这个操作对咱们处理数据没影响,实际上有的。举两个常见的例子:

IDL的where函数,大家基本上都用过。这个函数返回的是满足条件的元素的下标(实际上就是参数中为1的元素下标)。这时候返回的下标,就是原数组摊平之后的下标,是一维的。相对来说,np.where返回的,则是原始的下标。

在IDL中,除了多维下标的方式访问数据,也可以直接使用摊平后的一维下标来访问数据。这在各种编程语言中都是比较特殊的。

另外一个例子,如果大家在matplotlib中画直方图(hist),画的是一个二维数组的原始的分布图,就会发现很尴尬的一件事,m*n的数组,它给你画n个直方图叠在一起……因为它对每一行都去画了一个直方图。这个时候,我们就需要主动把数组摊平:plt.hist(a.flatten()),这样就把整个数组单独拿来画图了。当然用reshape也可以。

除了这两个地方不同,还有一个地方可能大家编程的时候都会被提醒的:在astropy读入的fits图像中,要选择某个像元,必须是a[y,x]。这其实也是因为数据的内部行、列优先不同造成的。

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

多维数组的统计

2275a1d24ddac2b0f7f815984e460dbd.gif afe40576dbf1af5928d457aa77a1ea92.gif

最后说一下,多维数组的统计的事情。假如有数组a,是m*n*k的,那么我们可以求出以下一系列均值:

python:

m = np.mean(a):求出全部元素的均值,是一个值

m = np.mean(a, axis=0):对第0维求均值,m是n*k的二维数组,m[i, j]是mean(a[:, i, j])

m = np.mean(a, axis=(1,2)),对第1、2维求均值,m是长为m的一维数组,m[i]是mean(a[i, :, :])

上面的例子中,选取的是前面或者后面的维度进行统计,实际上维度是可以任意组合的。

IDL也类似:

m = mean(a):求全部元素平均值。

m = mean(a, dim=1):对第1维求均值,m是n*k的数组。注意:尽管IDL的下标是从0开始的,但是在表达维度的时候,是从1开始的。其实内在原因是,dim=0的话,这个参数就会被默认当做没有赋值……

至于上面的最后那个写法,IDL不支持……

好了,就这么多了……我实在憋不出别的东西来了。有啥问题,下期再见

fabd76b969d54f68641e03e50d7a7556.gif

欢迎关注“小林在线”

一个可能有点看不懂的公号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值