ndarray:n-dimensional array object,即多维数组对象,是python自带的array对象的扩展,array对象和list对象的区别是array对象的每一个元素都是数值,而list保存的是每个元素对象的指针,而作为array对象的扩展,ndarray在科学计算中就非常适合并且功能强大。
创建ndarray
1. 使用列表对象创建ndarray
import bumpy as np
a = np.array([1,2,3,4])
以上为一种创建ndarray的方式,即用一个列表对象来创建,注意,ndarray中的所有元素类型都必须一致(atomic vector),上例中,如果其中一个元素为字符类型,则所有的元素都会自动转换为字符类型,通过dtype属性来获取ndarray中每个元素的类型,同时numpy中还提供了numpy.typeDict来存放各种类型的简称-类型字典;
由于ndarray是多维数组对象,可以通过shape属性来获取数组的各维情况,并且可以通过reshape方法改变当前的形状;
2. 使用numpy中特有函数创建ndarray
除了使用python中的列表对象来创建ndarray之外,numpy中还提供了一系列特定的函数用来创建ndarray
numpy.arange(0,1,0.1):arange和range类似,通过指定起始值、终止值、步长来创建数组,是一个前闭后开的区间,如该例创建的数组为:array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]);
numpy.linspace(0,1,10):linspace是通过指定起始值、终止值、元素个数来创建一个等差数组,默认为前闭后闭,通过设置endpoint=False可以对终止值设置是否包含;
numpy.logspace(0,1,12,base=2,endpoint=False):logspace是和linspace的区别是创建的是等比数组,默认以10为底,通过设置base改变底值;
numpy.zeros()、numpy.ones()、numpy.empty()等函数创建特定形状的数组,和matlab中类似;
numpy.fromstring():通过字符串来创建数组,字符串的每一个字符占用一个字节的空间,也就是8个字节,通过指定dtype,创建不同的数组;当dtype=int8时,每个字符对应一个元素,当dtype=16时,两个字符对应一个元素,并且以16进制保存(高位的字符需要乘以16);
numpy.fromfunction(func, (10,)):该函数通过指定一个创建函数和一个shape来创建数组,当shape只有一个元素(一维数组)时,func函数应该也只有一个参数,并且该参数自动从0开始递增带入得到一系列返回值,从而创建ndarray;
存取ndarray
1. 存取一维ndarray
ndarray的元素访问支持python中的列表的访问方式(下标以及切片)访问ndarray元素,通过下标和切片访问得到的新数组是原始数组的一个视图(view),和原始数组共享存储;
除了列表的访问方式,ndarray还支持整数列表、整数数组、布尔数组等访问方式,通过这些访问方式得到的新数组是原始数组的拷贝,并不共享存储;
整数列表的访问会按照给定列表的元素作为下标依次访问原始数组中的每个元素:
a = np.arange(10)
b = a[[3,3,-3,8]]
整数数组的访问在列表的基础上,访问得到的新数组会保留数组的形状;
布尔数组的访问,指使用一个布尔数组,只访问到布尔数组中True对应下标的元素;注意,只对布尔数组有效,如果是列表,则会讲True和False当做1和0作为下标处理;
布尔数组的访问通常由ufunc产生;
2. 存取多维数组
访问多维数组需要指定多维的下标,在python中实现多维下标的方式是通过元组(tuple),由于 1,2 和 (1,2)表示的语义相同,所以用元组表示下标时,也变得非常自然;ndarray的多维数组中,可以通过元组中分别指定切片来访问多维数组(得到的仍然是视图);同时也可以通过数组和切片的组合、数组和数组的组合来访问多维数组,记住将元组作为下标,元组中的每个元素分别对应某一维上的下标,则一切迎刃而解;
需要注意的是,使用切片是指定某一行或者某一列等(保留形状),但是用整数数组访问时,访问的是某一个下标(不会保留形状,只是简单地将值取出);
如果一个多维数组访问时,指定的下标小于维数,则剩余的维数下标默认全部访问(相当于切片 : 代替);
如果用数组和数组的结合来访问多维数组,则只有当两个数组的形状相同时,得到的数组和下标数组的形状相同;
numpy中可以使用一个列数组和一个行数组相加得到一个二维数组;
结构数组
通过使用结构数组,可以实现类似C语言中struct的功能;结构数组使用numpy.dtype()实例,实例化时,使用一个字典指定结构的名称和对应的类型(类型可以使用numpy.typeDict中的简称,字符串通过S32等来表示,32指定保存时的字符串长度,用以内存对齐);
结构数组除了数组用下标的访问方式之外,还可以通过字段访问;
通过numpy定义的结构数组可以通过tofile方法直接保存到文件中,并且该文件可以被c语言获取结构;
a.tofile(’test.bin')
通过c语言读取:
#include <stdio.h>
struct person
{
char name[32];
int age;
float weight;
}
struct person p[3];
void main(){
FILE *fp;
int i;
fp = fopen("test.bin","rb");
fread(p, sizeof(struct person), 2, fp);
fclose(fp);
for(i=0; i<2; i++){
printf("%s %d %f\n",p[i].name, p[i].age, p[i].weight);
}
}
内存结构
adarray的信息使用一个数组来描述,该数组引用两个对象:dtype和data,其中data表示一个连续的数据存储区域,并保存数组的形状以及间隔,从而访问连续存储区域中的指定位置存放的数据,实现对数组的访问;
如上图,dtype指向元素类型的对象;
dim count表示维数,dimensions是一个长度为dim count的数组,指定数组每一维的长度;
strides是一个长度为dim count的数组,每个元素是一个整数值,用来指定各维元素的间隔,如上图中表示第一维元素每增加1,则地址增加12,第二维元素每增加1,则地址增加4;
numpy中默认的数据存储排序方式是c语言中的排序方式,即第一维是最上位的,第一维的值增加1时,地址增加的范围最大,而在fortran语言中,第一维是最下位的,第一维增加1时,只增加一个元素的字节长度,如上例中,strides如果按照fortran语言中的排序方式,则应该为:(4,12),通过在定义数组时,指定order=“F”,来使用fortran语言的排序方式;
了解内存结构可以解释以下问题:为什么切片访问数组时,得到的数组是视图,而通过数组访问数组时,得到的数组是一份拷贝?
由于切片访问数组时,只需要改变strides的值,就可以对数组进行另外一种方式的访问,如如果存在数组a按照上图的方式保存,则b = [::2,::2]得到一个新的数组时,只需要将strides改为(24,8)就可以,而data则可以引用相同的存储区域;而当使用数组进行访问时,由于并不能保证是等间隔分布的,无法通过改变其他属性的方式保持数据不变,所以只能新建一份数据的拷贝来实现;
通过数组的flags属性可以查看数据存储区域的属性;