Numpy基础知识

数据类型

另见
Data type objects todo

数组类型和类型之间的转换

Numpy支持比Python更多种类的数值类型。此部分显示了哪些可用的,以及如何修改数组的数据类型。

数据类型描述
bool_布尔(True或False),存储为一个字节
int_默认整数类型(与C long相同;通常为int64或int32)
intc与C int(通常为int32或int64)相同
intp用于索引的整数(与C ssize_t相同;通常为int32或int64)
int8字节(-128到127)
int16整数(-32768到32767)
int32整数(-2147483648至2147483647)
int64整数(-9223372036854775808至9223372036854775807)
uint8无符号整数(0到255)
uint16无符号整数(0到65535)
uint32无符号整数(0至4294967295)
uint64无符号整数(0至18446744073709551615)
float_float64的简写。
float16半精度浮点:符号位,5位指数,10位尾数
float32单精度浮点:符号位,8位指数,23位尾数
float64双精度浮点:符号位,11位指数,52位尾数
complex_complex128的简写。
complex64复数,由两个32位浮点(实数和虚数分量)
complex128复数,由两个64位浮点(实数和虚数分量)

除了intc,定义了平台相关的C整数类型short,long,longlong

Numpy数值类型是dtype(data-type)对象的实例,每个类型具有唯一的特征。在你使用下面的语句导入NumPy后

>>> import numpy as np

这些类型可以用np.bool_、np.float32等方式访问。

未在上表中列出的高级类型,请参见结构化数组部分。

有5个基本数字类型表示布尔(bool)、整数(int)、无符号整数(uint)、浮点数(float)和复数。那些在其名称中具有数字的类型表示类型的位的大小(即,需要多少位来表示存储器中的单个值)。某些类型,例如int和intp,根据平台(例如32位与64位机器)具有不同的位大小。当与存储器直接寻址的低级代码(例如C或Fortran)接口时,应该考虑这一点。

数据类型可以用作函数将python数字转换为数组标量(有关说明,请参阅数组标量部分)、将python数字序列转换为该类型的数组、或作为许多numpy函数或方法接受的dtype关键字参数。一些例子:

>>> import numpy as np
>>> x = np.float32(1.0)
>>> x
1.0
>>> y = np.int_([1,2,4])
>>> y
array([1, 2, 4])
>>> z = np.arange(3, dtype=np.uint8)
>>> z
array([0, 1, 2], dtype=uint8)

数组类型也可以通过字符代码来引用,主要是为了保持与较旧包(例如Numeric)的向后兼容性。一些文档可能仍然引用这些,例如:

>>> np.array([1, 2, 3], dtype='f')
array([ 1.,  2.,  3.], dtype=float32)

我们建议使用dtype对象。

要转换数组的类型,请使用.astype()方法(首选)或类型本身作为函数。例如:

>>> z.astype(float)
array([  0.,  1.,  2.])
>>> np.int8(z)
array([0, 1, 2], dtype=int8)

注意,上面,我们使用Python float对象作为dtype。NumPy 知道int指代np.int_、bool表示np.bool_、 float为np.float_以及complex为np.complex_。其他数据类型没有Python等效的类型。

要确定数组的类型,请查看dtype属性:

>>> z.dtype
dtype('uint8')

dtype对象还包含有关类型的信息,例如其位宽和字节顺序。数据类型也可以间接用于查询类型的属性,例如是否是整数:

>>> d = np.dtype(int)
>>> d
dtype('int32')

>>> np.issubdtype(d, int)
True

>>> np.issubdtype(d, float)
False

数组标量

Numpy通常将数组的元素返回为数组标量(具有关联dtype的标量)。数组标量与Python标量不同,但在大多数情况下,它们可以互换使用(主要例外是早于v2.x的Python版本,其中整数数组标量不能用作列表和元组的索引)。有一些例外,例如当代码需要标量的非常特定的属性,或者当它特别检查一个值是否是一个Python标量。通常,使用相应的Python类型函数(例如int、float、complex、str,unicode)将数组标量显式转换为Python标量就很容易解决问题。

使用数组标量的主要优点是它们保留数组类型(Python可能没有可用的匹配标量类型,例如int16)。因此,使用数组标量确保数组和标量之间的行为相同,而不管值是否在数组内。NumPy标量也有很多和数组相同的方法。

扩展精度

Python的浮点数通常是64位浮点数,几乎等效于np.float64。在某些不常见的情况下,使用Python的浮点数更精确。这在numpy是否可能取决于硬件和开发环境:具体来说,x86机器提供80位精度的硬件浮点数,大多数C编译器提供它为long double类型,MSVC(Windows版本的标准)让long double和double(64位)完全一样。Numpy使编译器的long double为np.longdouble(复数为np.clongdouble)。你可以用np.finfo(np.longdouble)找出你的numpy提供的是什么。

Numpy 不提供比 C long double 更高精度的数据类型,特别地 128 位的IEEE quad precision 数据类型(FORTRAN的 REAL*16) 不可用。

为了有效地进行内存对齐,np.longdouble通常用零位填充,无论是96位还是128位。哪个更高效取决于硬件和开发环境;通常在32位系统上,它们被填充为96位,而在64位系统上,它们通常被填充为128位。np.longdouble被填充到系统默认值;为需要特定填充的用户提供np.float96和np.float128。尽管有名称,np.float96和np.float128只提供与np.longdouble一样多的精度,即80位大多数x86机器和64位标准Windows构建。

请注意,即使np.longdouble提供比python float更多的精度,也很容易失去额外的精度,因为python通常强制值通过float传递值。例如,%格式化操作符需要将其参数转换为标准python类型,因此即使请求了多个小数位,也不可能保留扩展精度。使用值1 + np.finfo(np.longdouble).eps测试你的代码非常有用。

数组创建

另见 Array creation routines todo

简介

一般有5个机制创建数组:

  • 从其他Python结构(例如,列表,元组)转换
  • numpy原生数组的创建(例如,arange、ones、zeros等)
  • 从磁盘读取数组,无论是标准格式还是自定义格式
  • 通过使用字符串或缓冲区从原始字节创建数组
  • 使用特殊库函数(例如,random)

本节不包括复制、join或以其他方式扩展或改变现有数组的方法。也不会涵盖创建对象数组或结构化数组。这两个都在它们自己的部分讲述。

将Python array_like对象转换为Numpy数组

通常,在Python中排列成array-like结构的数值数据可以通过使用array()函数转换为数组。最明显的例子是列表和元组。有关其使用的详细信息,请参阅array()的文档。一些对象可能支持数组协议,并允许以这种方式转换到数组。找出对象是否可以使用array()转换为一个数组numpy 数组的简单方法很简单,只要交互式试一下,看看它是否工作!(Python方式)。

例子:

>>> x = np.array([2,3,1,0])
>>> x = np.array([2, 3, 1, 0])
>>> x = np.array([[1,2.0],[0,0],(1+1j,3.)]) # note mix of tuple and lists,
    and types
>>> x = np.array([[ 1.+0.j, 2.+0.j], [ 0.+0.j, 0.+0.j], [ 1.+1.j, 3.+0.j]])

Numpy原生数组的创建

Numpy具有从头开始创建数组的内置函数:

zeros(shape)将创建一个用指定形状用0填充的数组。默认dtype为float64。

>>> np.zeros((2, 3)) array([[ 0., 0., 0.], [ 0., 0., 0.]])

ones(shape)将创建一个用1个值填充的数组。它在所有其他方面与zeros相同。

arange()将创建具有定期递增值的数组。检查docstring有关可以使用的各种方法的完整信息。这里将给出几个例子:

>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.arange(2, 10, dtype=np.float)
array([ 2., 3., 4., 5., 6., 7., 8., 9.])
>>> np.arange(2, 3, 0.1)
array([ 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9])

注意,关于用户应该知道的最后使用的一些细微之处,在arange docstring中描述。

linspace()将创建具有指定数量的元素的数组,并在指定的开始值和结束值之间等间隔。例如:

>>> np.linspace(1., 4., 6)
array([ 1. ,  1.6,  2.2,  2.8,  3.4,  4. ])

这个创建函数的优点是,可以保证元素的数量和起始点和结束点,arange()通常不会对任意的开始,停止和步长值做任何操作。

indices()将创建一组数组(堆叠为一个更高维度的数组),每个维度一个,每个维表示该维度的变化。一个例子说明比口头描述好得多:

>>> np.indices((3,3))
array([[[0, 0, 0], [1, 1, 1], [2, 2, 2]], [[0, 1, 2], [0, 1, 2], [0, 1, 2]]])

这对于在规则网格上评估多个维度的函数特别有用。

从磁盘读取数组

这可能是大数字组创建的最常见的情况。细节,当然,很大程度上取决于磁盘上的数据格式,因此本节只能给出如何处理各种格式的一般指针。

  • 标准二进制格式

各种字段具有用于数组数据的标准格式。下面列出了已知的python库读取它们和返回numpy数组(可能有其他可以读取和转换为numpy数组,所以检查最后一节)

HDF5: PyTables
FITS: PyFITS
不能直接读取但不难转换的格式的示例是像PIL(能够读取和写入许多图像格式,例如jpg,png等)的库支持的那些格式。

  • 常见ASCII格式

逗号分隔值文件(CSV)被广泛使用(以及Excel等程序的导出和导入选项)。在Python中有许多方法可以读取这些文件。Python中有CSV函数,pylab中有函数(matplotlib的一部分)。

更多的通用ascii文件可以使用io包在scipy中读取。

  • 自定义二进制格式

有多种方法可以使用。如果文件具有相对简单的格式,那么可以编写一个简单的I / O库,并使用numpy fromfile()函数和.tofile()方法直接读取和写入numpy数组(注意你的字节序!如果存在读取数据的良好C或C ++库,那么可以使用各种技术打包该库,尽管这当然是更多的工作,需要显着更高级的知识来与C或C ++交互。

  • 使用特殊库

有一些库可以用于为特殊目的生成数组,并且不可能枚举它们。最常见的用途是随机使用许多数组生成函数,其可以生成随机值的阵列,以及一些用于生成特殊矩阵(例如对角线)的效用函数。

NumPy I/O操作

NumPy提供了几个函数来从表格数据创建数组。我们在这里集中于genfromtxt函数。

简而言之,genfromtxt运行两个主循环。第一个循环以字符串序列转换文件的每一行。第二个循环将每个字符串转换为适当的数据类型。这种机制比单个循环慢,但给出更多的灵活性。特别的, genfromtxt考虑到缺失值的情况, 其他更简单的方法如loadtxt 无法做到这点.

注意
在给出示例时,我们将使用以下约定:

>>> import numpy as np
>>> from io import BytesIO

定义输入

genfromtxt的唯一强制参数是数据的源。它可以是字符串,字符串列表或生成器。如果提供了单个字符串,则假定它是本地或远程文件或具有read方法的打开的类文件对象的名称,例如文件或StringIO.StringIO对象。如果提供了字符串列表或返回字符串的生成器,则每个字符串在文件中被视为一行。当传递远程文件的URL时,文件将自动下载到当前目录并打开。

识别的文件类型是文本文件和归档。目前,该函数识别gzip和bz2(bzip2)归档。归档的类型从文件的扩展名确定:如果文件名以’.gz’结尾,则需要一个gzip归档;如果以’bz2’结尾,则假设存在一个bzip2档案。

将行拆分为列

  • The delimiter argument
    一旦文件被定义并打开阅读,genfromtxt将每个非空行拆分为一个字符串序列。刚刚跳过空行或注释行。delimiter关键字用于定义拆分应如何进行。

通常,单个字符标记列之间的间隔。例如,逗号分隔文件(CSV)使用逗号(,)或分号(;)作为分隔符:

>>> data = "1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(BytesIO(data), delimiter=",")
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

另一个常见的分隔符是”\t”,表格字符。但是,我们不限于单个字符,任何字符串都会做。默认情况下,genfromtxt假定delimiter=None,表示该行沿白色空格(包括制表符)分割,并且连续的空格被视为单个白色空格。

或者,我们可能处理固定宽度的文件,其中列被定义为给定数量的字符。在这种情况下,我们需要将delimiter设置为单个整数(如果所有列具有相同的大小)或整数序列(如果列可以具有不同的大小):

>>> data = "  1  2  3\n  4  5 67\n890123  4"
>>> np.genfromtxt(BytesIO(data), delimiter=3)
array([[   1.,    2.,    3.],
       [   4.,    5.,   67.],
       [ 890.,  123.,    4.]])
>>> data = "123456789\n   4  7 9\n   4567 9"
>>> np.genfromtxt(BytesIO(data), delimiter=(4, 3, 2))
array([[ 1234.,   567.,    89.],
       [    4.,     7.,     9.],
       [    4.,   567.,     9.]])
  • autostrip参数

默认情况下,当一行被分解为一系列字符串时,各个条目不会被删除前导或尾随的空格。通过将可选参数autostrip设置为True的值,可以覆盖此行为:

>>> data = "1, abc , 2\n 3, xxx, 4"
>>> # Without autostrip
>>> np.genfromtxt(BytesIO(data), delimiter=",", dtype="|S5")
array([['1', ' abc ', ' 2'],
       ['3', ' xxx', ' 4']],
      dtype='|S5')
>>> # With autostrip
>>> np.genfromtxt(BytesIO(data), delimiter=",", dtype="|S5", autostrip=True)
array([['1', 'abc', '2'],
       ['3', 'xxx', '4']],
      dtype='|S5')
  • comments参数

可选参数comments用于定义标记注释开始的字符串。默认情况下,genfromtxt假设为comments=’#’。注释标记可以出现在该行的任何地方。忽略注释标记后的任何字符:

>>> data = """#
... # Skip me !
... # Skip me too !
... 1, 2
... 3, 4
... 5, 6 #This is the third line of the data
... 7, 8
... # And here comes the last line
... 9, 0
... """
>>> np.genfromtxt(BytesIO(data), comments="#", delimiter=",")
[[ 1.  2.]
 [ 3.  4.]
 [ 5.  6.]
 [ 7.  8.]
 [ 9.  0.]]

注意:

这种行为有一个显着的例外:如果可选参数names=True,则将首先检查第一条注释的行的名称。

Skipping lines and choosing columns

  • The skip_header and skip_footer arguments

文件中头的存在可能阻碍数据处理。在这种情况下,我们需要使用skip_header可选参数。此参数的值必须是对应于在执行任何其他操作之前在文件开头处跳过的行数的整数。类似地,我们可以使用skip_footer属性并赋予n的值来跳过文件的最后n行:

>>> data = "\n".join(str(i) for i in range(10))
>>> np.genfromtxt(BytesIO(data),)
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
>>> np.genfromtxt(BytesIO(data),
...               skip_header=3, skip_footer=5)
array([ 3.,  4.])

默认情况下,skip_header=0和skip_footer=0,表示不跳过任何行。

  • The usecols argument

在某些情况下,我们对数据的所有列不感兴趣,但只对其中的几个列感兴趣。我们可以使用usecols参数选择要导入哪些列。此参数接受单个整数或对应于要导入的列的索引的整数序列。记住,按照惯例,第一列的索引为0。负整数的行为与常规Python负指数相同。

例如,如果我们只想导入第一列和最后一列,可以使用usecols =(0, -1):

>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(BytesIO(data), usecols=(0, -1))
array([[ 1.,  3.],
       [ 4.,  6.]])

如果列具有名称,我们还可以通过将其名称作为字符串序列或逗号分隔字符串的形式,将其名称指定给usecols参数来选择要导入的列:

>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(BytesIO(data),
...               names="a, b, c", usecols=("a", "c"))
array([(1.0, 3.0), (4.0, 6.0)],
      dtype=[('a', '<f8'), ('c', '<f8')])
>>> np.genfromtxt(BytesIO(data),
...               names="a, b, c", usecols=("a, c"))
    array([(1.0, 3.0), (4.0, 6.0)],
          dtype=[('a', '<f8'), ('c', '<f8')])

Choosing the data type

控制如何将从文件中读取的字符串序列转换为其他类型的主要方法是设置dtype参数。此参数的可接受值为:

  • 单个类型,例如dtype=float。除非已使用names参数将名称与每个列相关联(参见下文),否则输出将为具有给定dtype的2D。请注意,dtype=float是genfromtxt的默认值。
  • 类型序列,例如dtype =(int, float, float)。
    逗号分隔的字符串,例如dtype=”i4,f8,|S3”。
  • 具有两个键’names’和’formats’的字典。
  • 元组的序列(名称, 类型),例如dtype = [(’A’, t4 > int), (’B’, float)]。
  • 现有的numpy.dtype对象。
  • 特殊值None。在这种情况下,列的类型将从数据本身确定(见下文)。

在所有情况下,但第一个,输出将是具有结构化dtype的1D数组。此dtype具有与序列中的项目一样多的字段。字段名称使用names关键字定义。

当dtype=None时,每个列的类型从其数据中迭代确定。我们首先检查字符串是否可以转换为布尔值(即,如果字符串在小写字符串中匹配true或false);那么它是否可以转换为整数,然后到一个float,然后到一个复杂,最终到一个字符串。可以通过修改StringConverter类的默认映射器来更改此行为。

为方便起见,提供了选项dtype=None。但是,它明显慢于明确设置dtype。

Setting the names

  • The names argument

处理表格数据时的一种自然方法是为每个列分配一个名称。第一种可能性是使用显式结构化dtype,如前所述:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=[(_, int) for _ in "abc"])
array([(1, 2, 3), (4, 5, 6)],
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])

另一个更简单的可能性是使用names关键字与一系列字符串或逗号分隔的字符串:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, names="A, B, C")
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
      dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

在上面的示例中,我们使用了默认情况下,dtype=float的事实。通过给出一系列名称,我们将输出强制为结构化的dtype。

我们有时可能需要从数据本身定义列名称。在这种情况下,我们必须使用值True的names关键字。然后将从第一行(在skip_header之后)读取名称,即使行被注释掉:

>>> data = BytesIO("So it goes\n#a b c\n1 2 3\n 4 5 6")
>>> np.genfromtxt(data, skip_header=1, names=True)
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
      dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])

names的默认值为None。如果我们为关键字赋予任何其他值,新名称将覆盖我们可能已使用dtype定义的字段名称:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> ndtype=[('a',int), ('b', float), ('c', int)]
>>> names = ["A", "B", "C"]
>>> np.genfromtxt(data, names=names, dtype=ndtype)
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('A', '<i8'), ('B', '<f8'), ('C', '<i8')])
  • The defaultfmt argument

If names=None but a structured dtype is expected, names are defined with the standard NumPy default of “f%i”, yielding names like f0, f1 and so forth:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int))
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('f0', '<i8'), ('f1', '<f8'), ('f2', '<i8')])

同样,如果我们没有给出足够的名称来匹配dtype的长度,那么将使用此默认模板定义缺少的名称:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), names="a")
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('a', '<i8'), ('f0', '<f8'), ('f1', '<i8')])

我们可以使用defaultfmt参数覆盖此默认值,它采用任何格式字符串:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), defaultfmt="var_%02i")
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('var_00', '<i8'), ('var_01', '<f8'), ('var_02', '<i8')])

注意

我们需要记住,defaultfmt仅在预期某些名称但未定义时使用。

Validating names

具有结构化dtype的NumPy数组也可以视为recarray,其中可以像访问属性一样访问字段。因此,我们可能需要确保字段名称不包含任何空格或无效字符,或者不符合标准属性的名称(例如size或shape),这将会混淆解释器。genfromtxt接受三个可选参数,对名称提供更精细的控制:

  • deletechars
    提供一个字符串,组合必须从名称中删除的所有字符。默认情况下,无效字符为〜!@#$%^&amp; *() - = +〜\ |]} [{‘;: /?.& &lt;。
  • excludelist
    提供要排除的名称列表,例如return,file,print …如果输入名称之一是此列表的一部分,将在其后面添加下划线字符(’_’)。
  • case_sensitive
    是否名称应区分大小写(case_sensitive=True),转换为大写(case_sensitive=False或case_sensitive=’upper’)或小写(case_sensitive=’lower’)。

    Tweaking the conversion

  • The converters argument

通常,定义一个dtype足以定义如何转换字符串序列。然而,有时可能需要一些额外的控制。例如,我们可能要确保格式YYYY/MM/DD的日期被转换为datetime对象,或者像xx%已正确转换为0到1之间的浮点值。在这种情况下,我们应该使用converters参数定义转换函数。

此参数的值通常是具有列索引或列名作为键和转换函数作为值的字典。这些转换函数可以是实际函数或lambda函数。在任何情况下,他们应该只接受一个字符串作为输入,只输出所需类型的一个元素。

在以下示例中,第二列从表示百分比的字符串转换为0到1之间的浮点数:

>>> convertfunc = lambda x: float(x.strip("%"))/100.
>>> data = "1, 2.3%, 45.\n6, 78.9%, 0"
>>> names = ("i", "p", "n")
>>> # General case .....
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names)
array([(1.0, nan, 45.0), (6.0, nan, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

我们需要记住,默认情况下,dtype=float。因此,对于第二列期望浮点数。但是,字符串’ 2.3%’和’ 78.9% >无法转换为浮点数,我们最终改为使用np.nan。让我们现在使用转换器:

>>> # Converted case ...
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names,
...               converters={1: convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

使用第二列的名称(”p”)作为键而不是索引(1)可以获得相同的结果:

>>> # Using a name for the converter ...
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names,
...               converters={"p": convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

转换器还可用于为缺少的条目提供默认值。在以下示例中,转换器convert将剥离的字符串转换为相应的浮点型或如果字符串为空,转换为-999。我们需要从空格中显式删除字符串,因为它不是默认做的:

>>> data = "1, , 3\n 4, 5, 6"
>>> convert = lambda x: float(x.strip() or -999)
>>> np.genfromtxt(BytesIO(data), delimiter=",",
...               converters={1: convert})
array([[   1., -999.,    3.],
       [   4.,    5.,    6.]])
  • Using missing and filling values

在我们尝试导入的数据集中可能会丢失某些条目。在前面的示例中,我们使用转换器将空字符串转换为浮点数。然而,用户定义的转换器可能迅速地变得难以管理。

genfromtxt函数提供了另外两个补充机制:missing_values参数用于识别丢失的数据,第二个参数filling_values这些丢失的数据。

    • missing_values
      默认情况下,任何空字符串都标记为缺少。我们还可以考虑更复杂的字符串,例如”N/A”或”???”以表示丢失或无效的数据。missing_values参数接受三种类型的值:
    • 一个字符串或逗号分隔的字符串
      此字符串将用作所有列的缺少数据的标记
    • 字符串序列
      在这种情况下,每个项目按顺序与列相关联。
    • 一本字典
      字典的值是字符串或字符串序列。相应的键可以是列索引(整数)或列名(字符串)。此外,特殊键None可用于定义适用于所有列的默认值。
    • filling_values
      我们知道如何识别丢失的数据,但我们仍然需要为这些丢失的条目提供一个值。默认情况下,此值根据此表从预期的dtype确定:
预期类型默认
boolFalse
int-1
floatnp.nan
complexnp.nan+0j
string‘???’

我们可以使用filling_values可选参数对缺失值的转换进行更精细的控制。像missing_values一样,此参数接受不同类型的值:

单个值:这将是所有列的默认值
一个值序列:每个条目将是相应列的默认值
一本字典:每个键可以是列索引或列名,并且相应的值应该是单个对象。我们可以使用特殊键None为所有列定义默认值。

在下面的例子中,我们假设缺少的值在第一列中用”N/A”标记,”???”在第三列。我们希望将这些缺失值转换为0,如果它们出现在第一列和第二列中,则转换为-999,如果它们出现在最后一列中:

>>> data = "N/A, 2, 3\n4, ,???"
>>> kwargs = dict(delimiter=",",
...               dtype=int,
...               names="a,b,c",
...               missing_values={0:"N/A", 'b':" ", 2:"???"},
...               filling_values={0:0, 'b':0, 2:-999})
>>> np.genfromtxt(BytesIO(data), **kwargs)
array([(0, 2, 3), (4, 0, -999)],
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])
    • usemask
      我们还可能希望通过构造布尔掩码来跟踪丢失数据的出现,其中缺少数据的True条目,否则False。为此,我们只需要将可选参数usemask设置为True(默认值为False)。输出数组将是MaskedArray。

Shortcut functions

除了genfromtxt,numpy.lib.io模块提供了从genfromtxt派生的几个方便函数。这些函数的工作方式与原始函数相同,但它们具有不同的默认值。

  • ndfromtxt
    始终设置usemask=False。输出始终为标准numpy.ndarray。
  • mafromtxt
    始终设置usemask=True。输出始终为MaskedArray
  • recfromtxt
    返回标准numpy.recarray(if usemask=False)或MaskedRecords数组(如果usemaske=True 。默认dtype为dtype=None,表示每个列的类型将自动确定。
  • recfromcsv
    类似于recfromtxt,但使用默认的delimiter=”,”。

索引

另见
Indexing routinestodo

数组索引是指使用方括号([])对数组值进行索引。有很多选项来索引,这使numpy索引很强大,但功能上的强大也带来一些复杂性和潜在的混乱。本节仅仅是与索引相关的各种选项和问题的概述。除了单元素索引,大多数选项的详细信息可以在相关部分找到。

Assignment vs referencing

大多数以下示例显示在引用数组中的数据时使用索引。这些示例在给数组赋值时同样适用。有关如何赋值的具体示例和说明,请参阅末尾的部分。

单个元素索引

1-D数组的单元素索引是人们期望的。它的工作原理与其他标准Python序列一样。它是从0开始的,并且接受负索引来从数组的结尾进行索引。

>>> x = np.arange(10)
>>> x[2]
2
>>> x[-2]
8

与列表和元组不同,numpy数组支持多维数组的多维索引。这意味着没有必要将每个维度的索引分成它自己的一组方括号。

>>> x.shape = (2,5) # now x is 2-dimensional
>>> x[1,3]
8
>>> x[1,-1]
9

注意,如果索引索引具有比维度更少的索引的多维数组,则获得子维数组。例如:

>>> x[0]
array([0, 1, 2, 3, 4])

也就是说,指定的每个索引选择与所选择的其余维相对应的数组。在上面的例子中,选择0表示长度为5的剩余维度未指定,返回的是该维度和大小的数组。必须注意,返回的数组不是原始数据的副本,而是指向内存中与原始数组相同的值。在这种情况下,返回第一位置(0)处的1-D数组。因此,对返回的数组使用单个索引,导致返回单个元素。那是:

>>> x[0][2]
2

因此,请注意,x [0,2] = x [0] [2], 但是第二种情况效率更低,因为一个新的临时数组在第一个索引后创建了,这个临时数组随后才被2这个数字索引。

注意那些用于IDL或Fortran内存顺序的索引。Numpy使用C阶索引。这意味着,最后的索引通常表示最快速变化的存储器位置,不同于Fortran或IDL,其中第一索引表示存储器中最快速变化的位置。这种差异代表着很大的混乱的可能性。

其他索引选项

可以切片和跨步数组来提取相同数量维度的数组,但大小不同于原始数组。切片和跨步的工作方式与对列表和元组完全相同,除此之外它们还可以应用于多个维度。几个例子说明最好:

>>> x = np.arange(10)
>>> x[2:5]
array([2, 3, 4])
>>> x[:-7]
array([0, 1, 2])
>>> x[1:7:2]
array([1, 3, 5])
>>> y = np.arange(35).reshape(5,7)
>>> y[1:5:2,::3]
array([[ 7, 10, 13],
       [21, 24, 27]])

请注意,数组片段不会复制内部数组数据,但也会生成原始数据的新视图。

可以用其他数组来索引数组,以便从数组中选择相应列表的值来产生新的数组。有两种不同的方式来实现这一点。一个使用一个或多个索引值数组。另一个涉及给出适当形状的布尔数组以指示要选择的值。索引数组是一个非常强大的工具,允许避免循环数组中的单个元素,从而大大提高性能。

可以使用特殊功能通过索引有效地增加数组中的维数,以便生成的数组获得在表达式中使用或使用特定函数所需的形状。

索引数组

Numpy数组可以被其他数组(或任何其他可转换为数组的类似序列的对象,例如除了元组之外的列表)索引;有关为什么会出现这种情况,请参阅本文档的末尾。索引数组的使用范围从简单,直接的情况到复杂,难以理解的情况。对于索引数组的所有情况,返回的是原始数据的副本,而不是一个获取切片的视图。

索引数组必须是整数类型。数组中的每个值都指示要使用数组中的哪个值来代替索引。为了显示:

>>> x = np.arange(10,1,-1)
>>> x
array([10,  9,  8,  7,  6,  5,  4,  3,  2])
>>> x[np.array([3, 3, 1, 8])]
array([7, 7, 9, 2])

由值3,3,1和8组成的索引数组相应地创建长度为4(与索引数组相同)的数组,其中每个索引由索引数组在索引中的值替换。

允许使用负值,它们与单个索引或切片一样工作:

>>> x[np.array([3,3,-3,8])]
array([7, 7, 4, 2])

索引值超出范围是一个错误:

>>> x[np.array([3, 3, 20, 8])]
<type 'exceptions.IndexError'>: index 20 out of bounds 0<=index<9

一般来说,当使用索引数组时返回的是一个与索引数组具有相同形状的数组,但是数组的类型和值被索引。例如,我们可以使用多维索引数组:

>>> x[np.array([[1,1],[2,3]])]
array([[9, 9],
       [8, 7]])

索引多维数组

当对多维数组进行索引时,特别是对于多维索引数组,事情变得更加复杂。这些往往是更多的非理性用途,但它们是允许的,它们对一些问题有用。我们将从最简单的多维情况开始(使用前面例子中的数组y):

>>> y[np.array([0,2,4]), np.array([0,1,2])]
array([ 0, 15, 30])

在这种情况下,如果索引数组具有匹配的形状,并且正在索引的数组的每个维度有一个索引数组,则结果数组具有与索引数组相同的形状,并且这些值对应于每个在索引数组中的位置。在本示例中,第一个索引值对于两个索引数组为0,因此结果数组的第一个值为y [0,0]。下一个值是y [2,1],最后一个是y [4,2]。

如果索引数组不具有相同的形状,则尝试将它们广播到相同的形状。如果它们不能广播到相同的形状,则引发异常:

>>> y[np.array([0,2,4]), np.array([0,1])]
<type 'exceptions.ValueError'>: shape mismatch: objects cannot be
broadcast to a single shape

广播机制允许索引数组与其他索引的标量组合。效果是标量值用于索引数组的所有对应值:

>>> y[np.array([0,2,4]), 1]
array([ 1, 15, 29])

跳到下一级的复杂性,可能只有部分索引具有索引数组的数组。需要一点思考来理解在这种情况下会发生什么。例如,如果我们只使用一个索引数组与y:

>>> y[np.array([0,2,4])]
array([[ 0,  1,  2,  3,  4,  5,  6],
       [14, 15, 16, 17, 18, 19, 20],
       [28, 29, 30, 31, 32, 33, 34]])

什么结果是构造新的数组,其中索引数组的每个值从正被索引的数组中选择一行,并且结果数组具有结果形状(行的大小,数目索引元素)。

这可能有用的示例是对于颜色查找表,其中我们想将图像的值映射到RGB三元组以用于显示。查找表可以具有形状(nlookup,3)。使用具有dtype = np.uint8的形状(ny,nx)的图像索引这样的数组(或者任何整数类型,只要值具有查找表的边界)将导致形状的数组(ny,nx, 3),其中三个RGB值与每个像素位置相关联。

一般来说,resulant数组的形状将是索引数组的形状(或所有索引数组被广播到的形状)与被索引的数组中的任何未使用维度(未索引的那些)的形状的连接。

布尔值或掩码索引数组

用作索引的布尔数组以不同于索引数组的方式进行处理。布尔数组必须与要编制索引的数组的初始维度具有相同的形状。在最直接的情况下,布尔数组具有相同的形状:

>>> b = y>20
>>> y[b]
array([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34])

与整数索引数组的情况不同,在布尔数组中,结果是1-D数组,其包含索引数组中的所有元素,对应于布尔数组中的所有真实元素。索引数组中的元素始终以行优先(C样式)顺序进行迭代和返回。结果也与y[np.nonzero(b)]相同。与索引数组一样,返回的是数据的副本,而不是一个获取切片的视图。

如果y比b的维数更高,则结果将是多维的。例如:

>>> b[:,5] # use a 1-D boolean whose first dim agrees with the first dim of y
array([False, False, False,  True,  True], dtype=bool)
>>> y[b[:,5]]
array([[21, 22, 23, 24, 25, 26, 27],
       [28, 29, 30, 31, 32, 33, 34]])

这里,从索引数组中选择第4和第5行,并组合以形成2-D数字组。

通常,当布尔数组具有比被索引的数组更少的维度时,这等同于y [b,…],这意味着y被索引为b,然后是多个:如同填充y。因此,结果的形状是包含布尔数组的True元素的数目的一个维度,后面是被索引的数组的剩余维度。

例如,使用具有四个真实元素的形状(2,3)的2-D布尔数组来从形状(2,3,5)的3-D数字组中选择行导致形状的2-D结果(4 ,5):

>>> x = np.arange(30).reshape(2,3,5)
>>> x
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, 27, 28, 29]]])
>>> b = np.array([[True, True, False], [False, True, True]])
>>> x[b]
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29]])

有关更多详细信息,请参阅数组索引上的numpy参考文档。

组合索引和切片

索引数组可以与切片组合。例如:

>>> y[np.array([0,2,4]),1:3]
array([[ 1,  2],
       [15, 16],
       [29, 30]])

实际上,将切片转换为用索引数组广播的索引数组np.array([[1,2]])(shape(1,2)),以产生形状(3,2)的结果数组。

同样,切片可以与广播的布尔指数组合:

>>> y[b[:,5],1:3]
array([[22, 23],
       [29, 30]])

结构化索引工具

为了便于数组形状与表达式和赋值关系的匹配,可以在数组索引中使用np.newaxis对象来添加大小为1的新维。例如:

>>> y.shape
(5, 7)
>>> y[:,np.newaxis,:].shape
(5, 1, 7)

注意,在数组中没有新的元素,只是维度增加。这可以方便地以一种方式组合两个数组,否则将需要明确重塑操作。例如:

>>> x = np.arange(5)
>>> x[:,np.newaxis] + x[np.newaxis,:]
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6],
       [3, 4, 5, 6, 7],
       [4, 5, 6, 7, 8]])

省略语法可以用于指示完全选择任何剩余的未指定维度。例如:

>>> z = np.arange(81).reshape(3,3,3,3)
>>> z[1,...,2]
array([[29, 32, 35],
       [38, 41, 44],
       [47, 50, 53]])

这相当于:

>>> z[1,:,:,2]
array([[29, 32, 35],
       [38, 41, 44],
       [47, 50, 53]])

给被索引的数组赋值

如上所述,可以使用单个索引,切片,索引和掩码数组来选择数组的子集来分配。分配给索引数组的值必须是形状一致的(相同的形状或可广播到索引产生的形状)。例如,允许为切片分配常量:

>>> x = np.arange(10)
>>> x[2:7] = 1

或正确大小的数组:

>>> x[2:7] = np.arange(5)

请注意,如果将较高类型分配给较低类型(在int类型中添加浮点数(floats))或甚至导致异常(将复数分配给int/float类型),分配可能会导致更改:

>>> x[1] = 1.2
>>> x[1]
1
>>> x[1] = 1.2j
<type 'exceptions.TypeError'>: can't convert complex to long; use
long(abs(z))

与某些引用(例如数组和掩码索引)不同,它们总是对数组中的原始数据进行赋值(事实上,没有其他什么是有意义的!)。请注意,一些操作可能无法正常工作。这个特殊的例子往往是令人惊讶的人:

>>> x = np.arange(0, 50, 10)
>>> x
array([ 0, 10, 20, 30, 40])
>>> x[np.array([1, 1, 3, 1])] += 1
>>> x
array([ 0, 11, 20, 31, 40])

人们期望第一个位置将增加3。实际上,它只会递增1。原因是因为从包含值为1,1,3,1的原始数据(作为临时数据)中提取了一个新的数据组,然后将值1添加到临时数据库,然后将临时数据分配回原始数据组。因此,x [1] +1处的数组的值被分配给x [1]三次,而不是被增加3次。

处理程序中可变数量的索引

索引语法非常强大,但在处理可变数量的索引时限制。例如,如果你想编写一个可以处理具有不同维数的参数的函数,而不必为每个可能的维度编写特殊的情况代码,那么如何做呢?如果向索引提供一个元组,则该元组将被解释为索引列表。例如(使用数组z的先前定义):

>>> indices = (1,1,1,1)
>>> z[indices]
40

因此,可以使用代码来构造任意数量的索引的元组,然后在索引中使用它们。

可以使用Python中的slice()函数在程序中指定切片。例如:

>>> indices = (1,1,1,slice(0,2)) # same as [1,1,1,0:2]
>>> z[indices]
array([39, 40])

同样,省略号可以通过使用省略号对象的代码来指定:

>>> indices = (1, Ellipsis, 1) # same as [1,...,1]
>>> z[indices]
array([[28, 31, 34],
       [37, 40, 43],
       [46, 49, 52]])

因为这个原因,可以直接使用np.where()函数的输出作为索引,因为它总是返回一个索引数组的元组。

因为元组的特殊处理,它们不会像列表那样自动转换为数组。举个例子:

>>> z[[1,1,1,1]] # produces a large array
array([[[[27, 28, 29],
         [30, 31, 32], ...
>>> z[(1,1,1,1)] # returns a single value
40

Broadcasting

另见
numpy.broadcast todo

术语broadcasting描述numpy在算术运算期间如何处理具有不同形状的数组。受限于某些约束,较小的数组依据较大数组“broadcasting”,使得它们具有兼容的形状。Broadcasting提供了一种矢量化数组操作的方法,使得循环发生在C而不是Python。它做到这一点且不用不必要的数据拷贝,通常导致高效的算法实现。然而,有些情况下,broadcasting是一个坏主意,因为它导致低效的内存使用并减慢计算。

NumPy操作通常在逐个元素的基础上对数组对进行。在最简单的情况下,两个数组必须具有完全相同的形状,如下例所示:

>>> a = np.array([1.0, 2.0, 3.0])
>>> b = np.array([2.0, 2.0, 2.0])
>>> a * b
array([ 2.,  4.,  6.])

当数组的形状满足一定的条件时,NumPy的broadcasting规则可以放宽这个限制。最简单的broadcasting示例发生在一个操作包含数组和标量值的时候:

>>> a = np.array([1.0, 2.0, 3.0])
>>> b = 2.0
>>> a * b
array([ 2.,  4.,  6.])

结果等同于之前的示例,其中b是数组。在算术运算期间,我们可以认为标量b被拉伸了,形成与a相同形状的数组。b中的新元素是原始标量简单的拷贝。拉伸这个比喻只是概念性的。NumPy足够聪明,它使用原始的标量值而不会真正拷贝,使broadcasting操作尽可能的内存和计算高效。

第二个例子中的代码比第一个例子中的代码更有效,因为broadcasting在乘法期间移动较少的内存(b是标量而不是数组)。

Broadcasting的一般规则

当在两个数组上操作时,NumPy在元素级别比较它们的形状。它从尾部维度开始,并向前发展。两个维度兼容,当
- 它们是相等的,或
- 其中一个是1

如果不满足这些条件,则抛出ValueError: frames are not aligned异常,指示数组具有不兼容的形状。结果数组的大小是输入数组的每个维度的最大大小。

数组不需要具有相同维度的数目。例如,如果你有一个256x256x3数值的RGB值,并且你想要通过一个不同的值缩放图像中的每个颜色,你可以将图像乘以一个具有3个值的一维数组。根据broadcast规则排列这些数组的最后一个轴的大小,表明它们是兼容的:

Image  (3d array): 256 x 256 x 3
Scale  (1d array):             3
Result (3d array): 256 x 256 x 3

当比较的任何一个维度为1时,则使用另一个。换句话说,大小为1的维被拉伸或“复制”以匹配另一维。

在以下示例中,A和B数组都具有长度为1的轴,在broadcast操作期间将其扩展为更大的大小:

A      (4d array):  8 x 1 x 6 x 1
B      (3d array):      7 x 1 x 5
Result (4d array):  8 x 7 x 6 x 5

这里有一些例子:

A      (2d array):  5 x 4
B      (1d array):      1
Result (2d array):  5 x 4

A      (2d array):  5 x 4
B      (1d array):      4
Result (2d array):  5 x 4

A      (3d array):  15 x 3 x 5
B      (3d array):  15 x 1 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 5
Result (3d array):  15 x 3 x 5

A      (3d array):  15 x 3 x 5
B      (2d array):       3 x 1
Result (3d array):  15 x 3 x 5

以下是不broadcast的形状示例:

A      (1d array):  3
B      (1d array):  4 # trailing dimensions do not match

A      (2d array):      2 x 1
B      (3d array):  8 x 4 x 3 # second from last dimensions mismatched

broadcasting在实践中的一个例子:

>>> x = np.arange(4)
>>> xx = x.reshape(4,1)
>>> y = np.ones(5)
>>> z = np.ones((3,4))

>>> x.shape
(4,)

>>> y.shape
(5,)

>>> x + y
<type 'exceptions.ValueError'>: shape mismatch: objects cannot be broadcast to a single shape

>>> xx.shape
(4, 1)

>>> y.shape
(5,)

>>> (xx + y).shape
(4, 5)

>>> xx + y
array([[ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.],
       [ 4.,  4.,  4.,  4.,  4.]])

>>> x.shape
(4,)

>>> z.shape
(3, 4)

>>> (x + z).shape
(3, 4)

>>> x + z
array([[ 1.,  2.,  3.,  4.],
       [ 1.,  2.,  3.,  4.],
       [ 1.,  2.,  3.,  4.]])

Broadcasting提供了获取两个数组的外积(或任何其他outer操作)的方便方式。以下示例显示了两个1-d数组的外积操作:

>>> a = np.array([0.0, 10.0, 20.0, 30.0])
>>> b = np.array([1.0, 2.0, 3.0])
>>> a[:, np.newaxis] + b
array([[  1.,   2.,   3.],
       [ 11.,  12.,  13.],
       [ 21.,  22.,  23.],
       [ 31.,  32.,  33.]])

这里,newaxis索引运算符将新轴插入a,使其成为二维4x1数组。将4x1数组与形状为(3,)的b组合,产生一个4x3数组。

有关broadcasting概念的图解,请参阅本文。 todo

字节交换

字节排序和ndarrays简介

ndarray是一个对象,它为内存中的数据提供了一个python数组接口。

它经常发生,你想用数组查看的内存不是与运行Python的计算机相同的字节顺序。

例如,我可能在一个带有小端CPU的计算机上工作 - 例如Intel Pentium,但是我已经从大计算机写入的文件加载了一些数据。假设我从一个Sun(big-endian)计算机写入的文件中加载了4个字节。我知道这4个字节表示两个16位整数。在大端机器上,以最高有效字节(MSB),然后是最低有效字节(LSB)存储两字节整数。因此,字节按存储顺序:

  • MSB整数1
  • LSB整数1
  • MSB整数2
  • LSB整数2

假设两个整数实际上是1和770。因为770 = 256 * 3 + 2,存储器中的4个字节将分别包含:0,1,3,2。我从文件加载的字节将有这些内容:

>>> big_end_str = chr(0) + chr(1) + chr(3) + chr(2)
>>> big_end_str
'\x00\x01\x03\x02'

我们可能想使用ndarray来访问这些整数。在这种情况下,我们可以围绕这个内存创建一个数组,并告诉numpy有两个整数,它们是16位和big-endian:

>>> import numpy as np
>>> big_end_arr = np.ndarray(shape=(2,),dtype='>i2', buffer=big_end_str)
>>> big_end_arr[0]
1
>>> big_end_arr[1]
770

请注意dtype上的数据类型>i2,>意味着“big-endian”(<是小端字节序),而i2意味着“有符号2字节整数”。例如,如果我们的数据表示单个无符号的4字节小端整数,则dtype字符串将是

>>> little_end_u4 = np.ndarray(shape=(1,),dtype='<u4', buffer=big_end_str)
>>> little_end_u4[0] == 1 * 256**1 + 3 * 256**2 + 2 * 256**3
True

回到我们的big_end_arr - 在这种情况下,我们的底层数据是big-endian(数据字节序),我们设置dtype匹配(dtype也是big-endian)。然而,有时你需要翻转这些。

警告:
标量当前不包括字节顺序信息,因此从数组提取标量将以本地字节顺序返回一个整数。因此:

>>> big_end_arr[0].dtype.byteorder == little_end_u4[0].dtype.byteorder
True

更改字节顺序

正如你从介绍可以想象的,有两种方式可以影响数组的字节顺序和它正在查看的底层内存之间的关系:

更改数组dtype中的字节排序信息,以便将不合法的数据解释为不同的字节顺序。这是arr.newbyteorder()的作用
更改基础数据的字节顺序,保留dtype解释。这是arr.byteswap()的作用。
你需要改变字节顺序的常见情况是:

  1. 您的数据和dtype字节序不匹配,并且您要更改dtype以使其与数据匹配。
  2. 您的数据和dtype字节序不匹配,并且您要交换数据,以便它们匹配dtype
  3. 你的数据和dtype endianess匹配,但你想要的数据交换和dtype反映这一点

    • 数据和dtype字节顺序不匹配,将dtype更改为匹配数据

我们做一些他们不匹配的东西:

>>> wrong_end_dtype_arr = np.ndarray(shape=(2,),dtype='<i2', buffer=big_end_str)
>>> wrong_end_dtype_arr[0]
256

这种情况的明显修复是更改dtype,以便它给出正确的字节序:

>>> fixed_end_dtype_arr = wrong_end_dtype_arr.newbyteorder()
>>> fixed_end_dtype_arr[0]
1

注意数组在内存中没有更改:

>>> fixed_end_dtype_arr.tobytes() == big_end_str
True
  • 数据和类型字节顺序不匹配,更改数据以匹配dtype

你可能想这样做,如果你需要内存中的数据是一定的顺序。例如,你可能正在将内存写入需要某个字节顺序的文件。

>>> fixed_end_mem_arr = wrong_end_dtype_arr.byteswap()
>>> fixed_end_mem_arr[0]
1

现在数组在内存中已更改:

>>> fixed_end_mem_arr.tobytes() == big_end_str
False
  • 数据和dtype字节顺序匹配,交换数据和dtype

你可能为一个数组指定了正确的dtype,但你需要数组在内存中有相反的字节顺序,你想让dtype匹配,所以数组值是有意义的。在这种情况下,你只需要做前面的两个操作:

>>> swapped_end_arr = big_end_arr.byteswap().newbyteorder()
>>> swapped_end_arr[0]
1
>>> swapped_end_arr.tobytes() == big_end_str
False

使用ndarray astype方法可以实现将数据转换为特定类型和字节顺序的更简单方法:

>>> swapped_end_arr = big_end_arr.astype('<i2')
>>> swapped_end_arr[0]
1
>>> swapped_end_arr.tobytes() == big_end_str
False

结构化数组

简介

Numpy提供强大的功能来创建结构化数据类型的数组。这些数组允许通过命名字段来操纵数据。一个简单的例子将演示它是什么意思:

>>> x = np.array([(1,2.,'Hello'), (2,3.,"World")],
...              dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])
>>> x
array([(1, 2.0, 'Hello'), (2, 3.0, 'World')],
     dtype=[('foo', '>i4'), ('bar', '>f4'), ('baz', '|S10')])

这里我们创建了一个长度为2的一维数组。此数组的每个元素都是一个包含三个项目的结构,一个32位整数,一个32位浮点数以及长度为10或更小的字符串。如果我们在第二个位置索引这个数组,我们得到第二个结构:

>>> x[1]
(2,3.,"World")

方便地,可以通过使用命名该字段的字符串进行索引来访问数组的任何字段。

>>> y = x['bar']
>>> y
array([ 2.,  3.], dtype=float32)
>>> y[:] = 2*y
>>> y
array([ 4.,  6.], dtype=float32)
>>> x
array([(1, 4.0, 'Hello'), (2, 6.0, 'World')],
      dtype=[('foo', '>i4'), ('bar', '>f4'), ('baz', '|S10')])

在这些示例中,y是由结构化类型中的第二个字段组成的简单浮点数组。但是,它不是结构化数据组中的数据的副本,而是视图,即,它共享完全相同的存储器位置。因此,当我们通过将其数值加倍来更新该数组时,结构化数组也将对应的值显示为加倍。同样,如果更改结构化数据组,字段视图也会更改:

>>> x[1] = (-1,-1.,"Master")
>>> x
array([(1, 4.0, 'Hello'), (-1, -1.0, 'Master')],
      dtype=[('foo', '>i4'), ('bar', '>f4'), ('baz', '|S10')])
>>> y
array([ 4., -1.], dtype=float32)

定义结构化数组

通过dtype对象定义一个结构化数组。有几种替代方法来定义记录的字段。这些变体中的一些提供与Numeric,numarray或另一个模块的向后兼容性,并且除了这些目的之外不应该使用。这些将被如此注意。使用参数(如提供给dtype函数关键字或dtype对象构造函数本身)通过四种可选方法之一指定记录结构。此参数必须是以下之一:1)string,2)tuple,3)list,或4)dictionary。以下简要描述这些。

1)字符串参数。在这种情况下,构造函数需要一个逗号分隔的类型说明符列表,可选地包含额外的形状信息。字段被赋予默认名称’f0’,’f1’,’f2’等。类型说明符可以采用4种不同的形式:

a) b1, i1, i2, i4, i8, u1, u2, u4, u8, f2, f4, f8, c8, c16, a
(representing bytes, ints, unsigned ints, floats, complex and
fixed length strings of specified byte lengths)
b) int8,…,uint8,…,float16, float32, float64, complex64, complex128
(this time with bit sizes)
c) older Numeric/numarray type specifications (e.g. Float32).
Don’t use these in new code!
d) Single character type specifiers (e.g H for unsigned short ints).
Avoid using these unless you must. Details can be found in the
Numpy book
这些不同的样式可以混合在同一个字符串(但为什么你会想这样做?)。此外,每个类型说明符可以以重复数或形状为前缀。在这些情况下,将创建一个数组元素,即记录中的数组。该数组仍称为单个字段。一个例子:

>>> x = np.zeros(3, dtype='3int8, float32, (2,3)float64')
>>> x
array([([0, 0, 0], 0.0, [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]),
       ([0, 0, 0], 0.0, [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]),
       ([0, 0, 0], 0.0, [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]])],
      dtype=[('f0', '|i1', 3), ('f1', '>f4'), ('f2', '>f8', (2, 3))])

通过使用字符串来定义记录结构,它排除了能够在原始定义中命名字段。但是,名称可以更改,如后所示。

2)元组参数:适用于记录结构的唯一相关元组是当结构映射到现有数据类型时。这是通过在元组中配对现有数据类型与匹配的dtype定义(使用此处描述的任何变体)来完成的。作为示例(使用列表的定义,参见3)以获得更多细节:

>>> x = np.zeros(3, dtype=('i4',[('r','u1'), ('g','u1'), ('b','u1'), ('a','u1')]))
>>> x
array([0, 0, 0])
>>> x['r']
array([0, 0, 0], dtype=uint8)

在这种情况下,产生一个数组,其外观和行为就像一个简单的int32数组,但也有只使用int32的一个字节(有点像Fortran等效)的字段的定义。

3)列表参数:在这种情况下,记录结构用元组列表定义。每个元组具有2或3个元素,指定:1)字段的名称(允许使用”),2)字段的类型,以及3)形状(可选)。例如:

>>> x = np.zeros(3, dtype=[('x','f4'),('y',np.float32),('value','f4',(2,2))])
>>> x
array([(0.0, 0.0, [[0.0, 0.0], [0.0, 0.0]]),
       (0.0, 0.0, [[0.0, 0.0], [0.0, 0.0]]),
       (0.0, 0.0, [[0.0, 0.0], [0.0, 0.0]])],
      dtype=[('x', '>f4'), ('y', '>f4'), ('value', '>f4', (2, 2))])

4)字典参数:允许两种不同的形式。第一个包含一个具有两个必需键(’names’和’formats’)的字典,每个键都有一个相等大小的值列表。格式列表包含在其他上下文中允许的任何类型/形状说明符。名称必须是字符串。有两个可选键:“offsets”和“titles”。每个都必须是相应匹配的列表,其中偏移量包含每个字段的整数偏移量,标题是包含每个字段的元数据的对象(这些对象不必是字符串),其中允许值为None。举个例子:

>>> x = np.zeros(3, dtype={'names':['col1', 'col2'], 'formats':['i4','f4']})
>>> x
array([(0, 0.0), (0, 0.0), (0, 0.0)],
      dtype=[('col1', '>i4'), ('col2', '>f4')])

允许的其他字典形式是具有指定类型,偏移和可选标题的元组值的名称键的字典。

>>> x = np.zeros(3, dtype={'col1':('i1',0,'title 1'), 'col2':('f4',1,'title 2')})
>>> x
array([(0, 0.0), (0, 0.0), (0, 0.0)],
      dtype=[(('title 1', 'col1'), '|i1'), (('title 2', 'col2'), '>f4')])

访问和修改字段名称

字段名称是定义结构的dtype对象的属性。对于最后一个例子:

>>> x.dtype.names
('col1', 'col2')
>>> x.dtype.names = ('x', 'y')
>>> x
array([(0, 0.0), (0, 0.0), (0, 0.0)],
     dtype=[(('title 1', 'x'), '|i1'), (('title 2', 'y'), '>f4')])
>>> x.dtype.names = ('x', 'y', 'z') # wrong number of names
<type 'exceptions.ValueError'>: must replace all names at once with a sequence of length 2

访问字段标题

字段标题提供了一个标准位置来放置字段的关联信息。他们不必是字符串。

>>> x.dtype.fields['x'][2]
'title 1'

一次访问多个字段

您可以使用字段名称列表一次访问多个字段:

>>> x = np.array([(1.5,2.5,(1.0,2.0)),(3.,4.,(4.,5.)),(1.,3.,(2.,6.))],
        dtype=[('x','f4'),('y',np.float32),('value','f4',(2,2))])

请注意,x是使用元组列表创建的。

>>> x[['x','y']]
array([(1.5, 2.5), (3.0, 4.0), (1.0, 3.0)],
     dtype=[('x', '<f4'), ('y', '<f4')])
>>> x[['x','value']]
array([(1.5, [[1.0, 2.0], [1.0, 2.0]]), (3.0, [[4.0, 5.0], [4.0, 5.0]]),
      (1.0, [[2.0, 6.0], [2.0, 6.0]])],
     dtype=[('x', '<f4'), ('value', '<f4', (2, 2))])

字段按请求的顺序返回。:

>>> x[['y','x']]
array([(2.5, 1.5), (4.0, 3.0), (3.0, 1.0)],
     dtype=[('y', '<f4'), ('x', '<f4')])

填充结构化数组

结构化数组可以按字段或逐行填充。

>>> arr = np.zeros((5,), dtype=[('var1','f8'),('var2','f8')])
>>> arr['var1'] = np.arange(5)

如果你逐行填充它,它需要一个元组(但不是一个列表或数组!):

>>> arr[0] = (10,20)
>>> arr
array([(10.0, 20.0), (1.0, 0.0), (2.0, 0.0), (3.0, 0.0), (4.0, 0.0)],
     dtype=[('var1', '<f8'), ('var2', '<f8')])

记录数组

为了方便起见,numpy提供了“记录数组”,允许通过属性而不是索引来访问结构化数组的字段。记录数组是使用ndarray,numpy.recarray的子类包装的结构化数组,它允许通过数组对象上的属性访问字段,记录数组也使用特殊的数据类型numpy.record

创建记录数组的最简单的方法是使用numpy.rec.array:

>>> recordarr = np.rec.array([(1,2.,'Hello'),(2,3.,"World")],
...                    dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])
>>> recordarr.bar
array([ 2.,  3.], dtype=float32)
>>> recordarr[1:2]
rec.array([(2, 3.0, 'World')],
      dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])
>>> recordarr[1:2].foo
array([2], dtype=int32)
>>> recordarr.foo[1:2]
array([2], dtype=int32)
>>> recordarr[1].baz
'World'

numpy.rec.array可以将各种参数转换为记录数组,包括正常的结构化数组:

>>> arr = array([(1,2.,'Hello'),(2,3.,"World")],
...             dtype=[('foo', 'i4'), ('bar', 'f4'), ('baz', 'S10')])
>>> recordarr = np.rec.array(arr)

numpy.rec模块提供了一些创建记录数组的其他便利函数,请参见record array creation routines。

可以使用适当的视图获得结构化数组的记录数组表示:

>>> arr = np.array([(1,2.,'Hello'),(2,3.,"World")],
...                dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'a10')])
>>> recordarr = arr.view(dtype=dtype((np.record, arr.dtype)),
...                      type=np.recarray)

为方便起见,以np.recarray类型查看ndarray将自动转换为np.record数据类型,因此dtype可以不在视图中:

>>> recordarr = arr.view(np.recarray)
>>> recordarr.dtype
dtype((numpy.record, [('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')]))

要返回到一个普通的ndarray,dtype和type必须重置。以下视图是这样做的,考虑到recordarr不是结构化类型的不寻常情况:

>>> arr2 = recordarr.view(recordarr.dtype.fields or recordarr.dtype, np.ndarray)

通过索引或属性访问的记录数组字段将作为记录数组返回,如果字段具有结构化类型,则返回为纯数组。

>>> recordarr = np.rec.array([('Hello', (1,2)),("World", (3,4))],
...                 dtype=[('foo', 'S6'),('bar', [('A', int), ('B', int)])])
>>> type(recordarr.foo)
<type 'numpy.ndarray'>
>>> type(recordarr.bar)
<class 'numpy.core.records.recarray'>

请注意,如果字段与ndarray属性具有相同的名称,则ndarray属性优先。这些字段将无法通过属性访问,但仍可通过索引访问。

子类化ndarray

Introduction

子类化ndarray相对简单,但与其他Python对象相比,它有一些复杂性。在这一页上,我们解释了允许你对ndarray进行子类化的机制,以及实现子类的含义。

ndarrays and object creation
子类化ndarray很复杂,因为ndarray类的新实例可以以三种不同的方式来实现。这些是:

  • 显式构造函数调用 - 如MySubClass(params)。这是创建Python实例的通常路由。
  • 查看转换 - 将现有的ndarray转换为给定的子类
  • 从模板新建 - 从模板实例创建新实例。示例包括返回来自子类数组的切片,从ufuncs创建返回类型,以及复制数组。有关详细信息,请参阅下边的Creating new from template

最后两个是ndarrays的特性 - 以支持数组切片。子类化ndarray的并发症是由于机制numpy必须支持后两种路由的实例创建。

View casting

视图转换是标准的ndarray机制,通过它,您可以获取任何子类的ndarray,并返回数组的视图作为另一个(指定的)子类:

>>> import numpy as np
>>> # create a completely useless ndarray subclass
>>> class C(np.ndarray): pass
>>> # create a standard ndarray
>>> arr = np.zeros((3,))
>>> # take a view of it, as our useless subclass
>>> c_arr = arr.view(C)
>>> type(c_arr)
<class 'C'>

Creating new from template
当numpy发现它需要从模板实例创建一个新实例时,ndarray子类的新实例也可以通过非常类似于View casting的机制来实现。最明显的地方是,当你正在采取切片的子类数组。例如:

>>> v = c_arr[1:]
>>> type(v) # the view is of type 'C'
<class 'C'>
>>> v is c_arr # but it's a new instance
False

该切片是到原始c_arr数据的视图。因此,当我们从ndarray获取视图时,我们返回一个新的ndarray,同一个类,指向原始数据。

There are other points in the use of ndarrays where we need such views, such as copying arrays (c_arr.copy()), creating ufunc output arrays (see also array_wrap for ufuncs), and reducing methods (like c_arr.mean().

Relationship of view casting and new-from-template

这些路径都使用相同的机械。我们在这里区分,因为他们导致不同的输入到你的方法。具体来说,View casting意味着您已从ndarray的任何潜在子类中创建了数组类型的新实例。Creating new from template意味着您已从先前存在的实例创建了一个新类的实例,允许您例如跨特定于您的子类的属性进行复制。

Implications for subclassing

如果我们子类化ndarray,我们不仅需要处理我们的数组类型的显式构造,还要处理View casting或Creating new from template。Numpy有机械做到这一点,和这种机械,使子类略微不标准。

ndarray用来支持子类中的视图和new-from-template的机制有两个方面。

第一种是使用ndarray.new方法进行对象初始化的主要工作,而不是使用更常用的init方法。第二种是使用array_finalize方法允许子类在从模板创建视图和新实例后清理。

  • A brief Python primer on __new__ and __init__
    todo
  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值