numpy库学习笔记一——ndarray

Numpy库学习

NumPy,是Numerical Python的简称,它是目前Python数值计算中最为重要的基础包。大多数计算包都提供了基于NumPy的科学函数功能,将NumPy的数组对象作为数据交换的通用语。
以下内容将会出现在NumPy中:

  • ndarray——一种高效多维数组,提供了基于数组的便捷算术操作以及灵活的广播功能。
  • 对所有数据进行快速的矩阵计算,而无须编写循环程序。
  • 对硬盘中数组数据进行读写的工具,并对内存映射文件进行操作。
  • 线性代数、随机数生成以及傅里叶变换功能。
  • 用于连接NumPy到C、C++和FORTRAN语言类库的C语言API。

NumPy本身并不提供建模和科学函数

学习目标

  • 在数据处理、清洗、构造子集、过滤、变换以及其他计算中进行快速的向量化计算。
  • 常见的数组算法,比sort、unique以及set操作等。
  • 高效的描述性统计和聚合/概述数据。
  • 数据排列和相关数据操作,例如对异构数据进行merge和join。
  • 使用数组表达式来表明条件逻辑,代替if-elif-else条件分支的循环。- 分组数据的操作(聚合、变换以及函数式操作)

ndarray:多维数组对象

NumPy的核心特征之一就是N-维数组对象——ndarray。ndarray是Python中一个快速、灵活的大型数据集容器。数组允许你使用类似于标量的操作语法在整块数据上进行数学计算。

  • numpy的批量计算
    下面是一个展现NumPy如何使用类似于Python内建对象的标量计算语法进行批量计算的小例子
In [1]: import numpy as np

In [2]: data = np.random.randn(2,3)
#生成一个小的随机数组
In [3]: data
Out[3]:
array([[-0.0421879 , -0.21588562, -0.52903881],
       [-1.59346557, -1.19710799, -1.352975  ]])

In [4]: data*100
Out[4]:
array([[  -4.21878966,  -21.58856219,  -52.90388053],
       [-159.34655725, -119.71079936, -135.29750021]])
#data数组内所有的元素都同时乘以了10
In [5]: data + data
Out[5]:
array([[-0.08437579, -0.43177124, -1.05807761],
       [-3.18693115, -2.39421599, -2.70595   ]])
#数组中的对应元素进行了相加。

使用标准的NumPy导入方式import numpy as np。你当然也可以在代码中写from numpy import*来省略多写的一个np.然而建议你保持写标准导入的方式。numpy这个命名空间包含了大量与Python内建函数重名的函数(比如min和max)

  • 数组的shape属性与数据类型
    一个ndarray是一个通用的多维同类数据容器,也就是说,它包含的每一个元素均为相同类型。每一个数组都有一个shape属性,用来表征数组每一维度的数量;每一个数组都有一个dtype属性,用来描述数组的数据类型.
In [6]: data.shape
Out[6]: (2, 3)

In [7]: data.dtype
Out[7]: dtype('float64')
  • 生成adarray
    array函数接收任意的序列型对象(当然也包括其他的数组),生成一个新的包含传递数据的NumPy数组。例如:

    • 列表的转换
    In [7]: data.dtype
    Out[7]: dtype('float64')
    
    In [8]: data1 = [5,7,4,8,7,0]
    In [9]: arr1 = np.array(data1)
    In [10]: arr1
    Out[10]: array([5, 7, 4, 8, 7, 0])
    
    • 嵌套序列,例如同等长度的列表,将会自动转换成多维数组:
    In [11]: data2 = [[65,6,75,8],[76,8,3,1]]
    
    In [12]: arr2 = np.array(data2)
    In [13]: arr2
    Out[13]:
    array([[65,  6, 75,  8],
           [76,  8,  3,  1]])
    

    因为data2是一个包含列表的列表,所以Numpy数组arr2形成了二维数组。我们可以通过检查ndim和shape属性来确认这一点:

    In [14]: arr2.ndim
    Out[14]: 2
    
    In [15]: arr2.shape
    Out[15]: (2, 4)
    

    除了np.array,还有很多其他函数可以创建新数组。例如,给定长度及形状后,zeros可以一次性创造全0数组,ones可以一次性创造全1数组。empty则可以创建一个没有初始化数值的数组。想要创建高维数组,则需要为shape传递一个元组

    • 创建特殊数组
    In [16]: np.zeros(10)
    Out[16]: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
    
    In [17]: np.ones(5)
    Out[17]: array([1., 1., 1., 1., 1.])
    
    In [18]: np.empty((2,3))
    Out[18]:
    array([[0.08437579, 0.43177124, 1.05807761],
           [3.18693115, 2.39421599, 2.70595   ]])
    

    使用np.empty来生成一个全0数组,并不安全,有些时候它可能会返回未初始化的垃圾数值。

    • arange是Python内建函数range的数组版
    In [19]: np.arange(10)
    Out[19]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    

    由于NumPy专注于数值计算,如果没有特别指明的话,默认的数据类型是float64(浮点型).

    • 数据生成函数
    函数名 描述
    array 将输入数据转换为ndarray,如不显示表示数据类型.则自动推断;默认复制所有的数据类型
    asarray 将输入转换为ndarray,如果输入已经是ndarray类型,则不复制
    arange Python内建函数range的数组版,返回一个数组
    • 数据类型
      数据类型,即dytpe,是一个特殊的对象,它包含了ndarray需要为某一种类型数据所申明的内存块信息(也称为元数据,即表示数据的数据).
In [24]: arr = np.array([654,56,67,56,65,65])

In [25]: arr.dtype
Out[25]: dtype('int32')
#使用astype方法显式地转换数组的数据类型:
In [26]: float_arr = arr.astype(np.float64)

In [27]: float_arr.dtype
Out[27]: dtype('float64')

如果有一个数组,里面的元素都是表达数字含义的字符串,也可以通过astype将字符串转换为数字

In [29]: arr = np.array([-3.5,65.4,65,6],dtype=np.string_)

In [31]: arr.astype(float)
Out[31]: array([-3.5, 65.4, 65. ,  6. ])

NumPy可以使用相同别名来表征与Python精度相同的Python数据类型。也可以使用另一个数组的dtype属性:

In [39]: int_arr.dtype
Out[39]: dtype('int32')

In [40]: float_arr = np.array([5.5,6.4,7.8,2.5,6.8],dtype=float)
In [41]: int_arr.astype(float_arr.dtype)
Out[41]: array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])

也可以使用类型代码来传入数据类型:

In [39]: int_arr.dtype
Out[39]: dtype('int32')

In [40]: float_arr = np.array([5.5,6.4,7.8,2.5,6.8],dtype=float)
In [41]: int_arr.astype(float_arr.dtype)
Out[41]: array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])

In [42]: int_arr.astype('u4')
Out[42]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint32)
  • NumPy数组算术

数组之所以重要是因为它允许你进行批量操作而无须任何for循环。NumPy用户称这种特性为向量化.

注意:任何在两个等尺寸数组之间的算术操作都应用了逐元素操作的方式.

  • 带有标量计算的算术操作,会把计算参数传递给数组的每一个元素
In [48]: arr = np.array([[5,6,5,8],[6,7,6,3]])

In [49]: arr
Out[49]:
array([[5, 6, 5, 8],
       [6, 7, 6, 3]])

In [50]: arr*10
Out[50]:
array([[50, 60, 50, 80],
       [60, 70, 60, 30]])

In [51]: arr*arr
Out[51]:
array([[25, 36, 25, 64],
       [36, 49, 36,  9]])

In [52]: arr-arr
Out[52]:
array([[0, 0, 0, 0],
       [0, 0, 0, 0]])

In [53]: arr+arr
Out[53]:
array([[10, 12, 10, 16],
       [12, 14, 12,  6]])
  • 同尺寸数组之间的比较,会产生一个布尔值数组
In [2]: arr1 = np.random.randn(3,4)

In [3]: arr2 = np.random.randn(3,4)

In [4]: arr1
Out[4]:
array([[ 0.03381384,  1.87791581,  1.39403355, -0.58645012],
       [ 1.83638141,  0.91968343,  0.44893528, -0.03805066],
       [-1.71230329, -0.6489988 ,  0.63053987,  0.09063347]])

In [5]: arr2
Out[5]:
array([[ 1.4200954 ,  0.1432846 ,  1.00890235,  0.81021525],
       [ 0.42412295,  0.33147589,  1.05764754,  0.08025581],
       [-0.35912561,  0.51876235,  1.43744617,  0.99076419]])

In [6]: arr1>arr2
Out[6]:
array([[False,  True,  True, False],
       [ True,  True, False, False],
       [False, False, False, False]])
  • 基础索引与切片

    • 一维数组切片比较简单,看起来和Python的列表很类似:
In [7]: arr = np.arange(10)

In [8]: arr[5]
Out[8]: 5

In [9]: arr[3:8]
Out[9]: array([3, 4, 5, 6, 7])

In [10]: arr[:5]
Out[10]: array([0, 1, 2, 3, 4])

In [11]: arr[3:8] = 6

In [12]: arr
Out[12]: array([0, 1, 2, 6, 6, 6, 6, 6, 8, 9])

如果你传入了一个数值给数组的切片,例如arr[3:8] = 6,数值被传递给了整个切片。区别于Python的内建列表,数组的切片是原数组的视图。这意味着数据并不是被复制了,任何对于视图的修改都会反映到原数组上.原因是:由于NumPy被设计成适合处理非常大的数组,你可以想象如果NumPy持续复制数据会引起很多内存问题

注意:如果你还是想要一份数组切片的拷贝而不是一份视图的话,你就必须显式地复制这个数组,例如arr[5:8].copy()

  • 在一个二维数组中,每个索引值对应的元素不再是一个值,而是一个一维数组:
In [14]: arr
Out[14]:
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

In [15]: arr[1]
Out[15]: array([3, 4, 5])

因此,单个元素可以通过递归的方式获得。但是要多写点代码,你可以通过传递一个索引的逗号分隔列表去选择单个元素,以下两种方式效果一样:

In [16]: arr[1,2]
Out[16]: 5

In [17]: arr[1][2]
Out[17]: 5

在多维数组中,你可以省略后续索引值,返回的对象将是降低一个维度的数组。

以上的数组子集选择中,返回的数组都是视图.

In [18]: arr[:2]
Out[18]:
array([[0, 1, 2],
       [3, 4, 5]])

In [19]: arr[:2,:2]
Out[19]:
array([[0, 1],
       [3, 4]])

如你所见,第18组IO,数组沿着轴0进行了切片。表达式arrzd[:2]的含义为选择arr2d的前两行;第19就组IO,数组沿着轴0,1进行了切片表达式arr[:2,:2]的含义为选择arr的前两行和前两列的交集.

注意:单独一个冒号表示选择整个轴上的数组

  • 布尔索引

考虑以下例子,假设我们的数据都在数组中,并且数组中的数据是一些存在重复的人名。使用numpy.random中的randn函数来生成一些随机正态分布的数据:

In [20]: names = np.array(['Bob','Will','Lee','Will','Lee','Lee'])
In [21]: data = np.random.randn(6,5)

In [22]: names
Out[22]: array(['Bob', 'Will', 'Lee', 'Will', 'Lee', 'Lee'], dtype=')

In [23]: data
Out[23]:
array([[-2.09778316,  1.0283271 ,  0.93938557, -0.2247455 , -0.32755643],
       [ 1.32232264,  0.12762422, -0.70816897,  1.48471576,  0.3999583 ],
       [ 0.89986716,  0.08729917,  0.24970591, -1.03332823,  0.34245957],
       [ 0.72876191, -0.87117186, -0.03504858, -0.86879753, -0.42663472],
       [ 0.90960067,  0.01485512, -0.89126664, -0.00999851,  0.82528367],
       [ 0.22279861, -0.11088761, -0.48246116, -1.17185184,  0.33091438]])

In [24]: names == 'Will'
Out[24]: array([False,  True, False,  True, False, False])

可以利用数组的比较操作(比如==)进行向量化,通过比较names数组和字符串’Bob’会产生一个布尔值数组,因此在索引数组时可以传入布尔值数组:

In [27]: data[names == 'Lee']
Out[27]:
array([[ 0.89986716,  0.08729917,  0.24970591, -1.03332823,  0.34245957],
       [ 0.90960067,  0.01485512, -0.89126664, -0.00999851,  0.82528367],
       [ 0.22279861, -0.11088761, -0.48246116, -1.17185184,  0.33091438]])

In [28]: data[names == 'Will']
Out[28]:
array([[ 1.32232264,  0.12762422, -0.70816897,  1.48471576,  0.3999583 ],
       [ 0.72876191, -0.87117186, -0.03504858, -0.86879753, -0.42663472]])

注意:布尔值数组的长度必须和数组轴索引长度一致。当布尔值数组的长度不正确时,布尔值选择数据的方法并不会报错,因此在使用该特性的时候要小心。
为了选择除了’Lee’以外的其他数据,你可以使用!=或在条件表达式前使用~对条件取反

In [29]: data[names != 'Lee']
Out[29]:
array([[-2.09778316,  1.0283271 ,  0.93938557, -0.2247455 , -0.32755643],
       [ 1.32232264,  0.12762422, -0.70816897,  1.48471576,  0.3999583 ],
       [ 0.72876191, -0.87117186, -0.03504858, -0.86879753, -0.42663472]])

In [30]: data[~(names == 'Lee')]
Out[30]:
array([[-2.09778316,  1.0283271 ,  0.93938557, -0.2247455 , -0.32755643],
       [ 1.32232264,  0.12762422, -0.70816897,  1.48471576,  0.3999583 ],
       [ 0.72876191, -0.87117186, -0.03504858, -0.86879753, -0.42663472]])

当要选择三个名字中的两个时,可以对多个布尔值条件进行联合,需要使用数学操作符如&(and)和|(or):

In [33]: data[(names == 'Lee')|(names == 'Will')]
Out[33]:
array([[ 1.32232264,  0.12762422, -0.70816897,  1.48471576,  0.3999583 ],
       [ 0.89986716,  0.08729917,  0.24970591, -1.03332823,  0.34245957],
       [ 0.72876191, -0.87117186, -0.03504858, -0.86879753, -0.42663472],
       [ 0.90960067,  0.01485512, -0.89126664, -0.00999851,  0.82528367],
       [ 0.22279861, -0.11088761, -0.48246116, -1.17185184,  0.33091438]])

注意不能使用and以及or,只能使用&或|,每个布尔条件需用圆括号括起.
布尔值索引选择数据时,总是生成数据的拷贝,即使返回的数组并没有任何变化。
总结:data后的方括号所需要的是布尔表达式.

  • 神奇索引

神奇索引是numpy中的术语,用于描述使用整数数组进行数据索引。

假设我们有一个8×4的数组,为了选出一个符合特定顺序的子集,你可以简单地通过传递一个包含指明所需顺序的列表或数组来完成,如果使用负的索引,将从尾部进行选择.

In [36]: arr = np.arange(32).reshape(8,4)

In [37]: arr[[4,6,0,2]]
Out[37]:
array([[16, 17, 18, 19],
       [24, 25, 26, 27],
       [ 0,  1,  2,  3],
       [ 8,  9, 10, 11]])

In [38]: arr[[-4,5,-1,7]]
Out[38]:
array([[16, 17, 18, 19],
       [20, 21, 22, 23],
       [28, 29, 30, 31],
       [28, 29, 30, 31]])

传递多个索引数组时情况有些许不同,这样会根据每个索引元组对应的元素选出一个一维数组:

In [40]: arr[[5,5,4,2],[1,3,1,0]]
Out[40]: array([21, 23, 17,  8])

在上述例子中,元素(5, 1)、(5, 3)、(4, 1)和(2, 0)被选中。如果不考虑数组的维数(本例中是二维),神奇索引的结果总是一维的。

注意:牢记神奇索引与切片不同,它总是将数据复制到一个新的数组中。

  • 数组转置和换轴

转置是一种特殊的数据重组形式,可以返回底层数据的视图而不需要复制任何内容.数组拥有transpose方法,也有特殊的T属性:

In [41]: arr = np.arange(12).reshape(3,4)

In [42]: arr
Out[42]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [43]: arr.T
Out[43]:
array([[ 0,  4,  8],
       [ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11]])

当进行矩阵计算时,可能会经常进行一些特定操作,比如,当计算矩阵内积会使用np.dot:

In [44]: np.dot(arr,arr.T)
Out[44]:
array([[ 14,  38,  62],
       [ 38, 126, 214],
       [ 62, 214, 366]])

对于更高维度的数组,transpose方法可以接收包含轴编号的元组,用于置换轴:

In [45]: arr.transpose((1,0))
Out[45]:
array([[ 0,  4,  8],
       [ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11]])

In [46]: arr.transpose((0,1))
Out[46]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

正如我们所看到的,第一个代码块,轴已经被重新排序,使得原先的第二个轴变为第一个,原先的第一个轴变成了第二个;第二个代码块,轴没有变.

使用.T进行转置是换轴的一个特殊案例。ndarray有一个swapaxes方法,该方法接收一对轴编号作为参数,并对轴进行调整用于重组数据:

In [47]: arr.swapaxes(0,1)
Out[47]:
array([[ 0,  4,  8],
       [ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11]])

swapaxes返回的是数据的视图,而没有对数据进行复制。

你可能感兴趣的:(#,numpy库学习,numpy,python,数据分析)