python数据分析-NumPy (二)

1. NumPy数组基础

python中的数据操作几乎等同于NumPy数组操作,另外一个重要的工具包pandas也是构建在Numpy数组的基础之上的。

1.1 NumPy数组的属性

首先定义三个随机的数组:一个一维数组、二维数组和三维数组。

In[1]: import numpy as np
	   np.random.seed(0) # 设置随机数种子
	   x1 = np.random.randint(10,size=6) # 一维数组
	   x2 = np.random.randint(10,size=(3,4)) # 二维数组
	   x3 = np.random.randint(10,size=(3,4,5)) # 三维数组

每个数组有nidm(数组的维度)、shape(数组每个维度的大小)和size(数组的总大小)属性:

In[2]: print("x3 ndim:",x3.ndim)
	   print("x3 shape:",x3.shape)
	   print("x3 size:",x3.size)
x3 ndim: 3
x3 shape: (3, 4, 5)
x3 size: 60

另外一个有用的属性是dtype,它是数组的数据类型

In[3]: print("dtype:",x3.dtype)
dtype: int64

其他的属性包括表示每个数组元素字节大小的itemsize,以及表示数组总字节大小的属性nbytes:

In[4]: print("itemsize:",x3.itemsize,"bytes")
	   print("nbytes:",x3.nbytes,"bytes")
itemsize: 8 bytes
nbytes: 480 bytes

一般来说,可以认为itemsize与size的乘积大小和nbytes相等。

1.2 数组索引:获取单个元素

获取单个元素的索引方式与python的标准列表索引没什么区别,在一维数组中:

  • 通过中括号指定索引获取第i个值(从0开始计数)
  • 获取数组的末尾索引,可以用负值索引

在多维数组中,可以使用逗号分隔的索引元祖获取元素:

In [5]: x2
Out[5]: 
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 6]])

In [6]: x2[2,3]
Out[6]: 6

In [7]: x2[2,-1]
Out[7]: 6

也可以用以上索引方式修改元素值:

In [8]: x2[2,3] = 1

In [9]: x2
Out[9]: 
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 1]])

要注意一点:Numpy数组是固定类型的,你将一个浮点值插入一个整型数组时,浮点值会被截短成整型。并且这种截短时自动完成的,不会给你提示或警告。

In [11]: x1[0] = 3.14159 #会被自动截短

In [12]: x1
Out[12]: array([3, 8, 1, 6, 7, 7])

1.3 数组切片:获取子数组

NumPy切片语法和python列表多标准切片语法相同,为了获取数组x的一个切片,可以使用:

x[start:stop:step]

如果以上三个参数都未指定,那么它们会被分别设置默认值start=0,stop=维度的大小和step=1.

1.3.1 一维子数组

In [18]: x = np.arange(10)

In [19]: x
Out[19]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [20]: x[:5] # 前5个元素
Out[20]: array([0, 1, 2, 3, 4])

In [21]: x[5:] #索引5之后的元素
Out[21]: array([5, 6, 7, 8, 9])

In [22]: x[4:7] #中间的子数组
Out[22]: array([4, 5, 6])

In [23]: x[::2] # 每隔一个元素
Out[23]: array([0, 2, 4, 6, 8])

In [24]: x[1::2] #每隔一个元素,从索引1开始
Out[24]: array([1, 3, 5, 7, 9])

步长值step为负,可以非常方便的逆序:

In [25]: x[::-1] # 所有元素,逆序
Out[25]: array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])

In [26]: x[5::-2] # 从索引5开始,每隔一个元素逆序
Out[26]: array([5, 3, 1])

1.3.2 多维子数组

多维切片也采用同样的方式处理,用冒号分隔。

In [27]: x2
Out[27]: 
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 1]])

In [28]: x2[:2,:3]  #两行,三列
Out[28]: 
array([[5, 0, 3],
       [7, 9, 3]])

In [29]: x2[:,::2]  #所有行,每隔一列
Out[29]: 
array([[5, 3],
       [7, 3],
       [2, 7]])

子数组也可以同时被逆序:

In [30]: x2[::-1,::-1]
Out[30]: 
array([[1, 7, 4, 2],
       [5, 3, 9, 7],
       [3, 3, 0, 5]])

1.3.3 获取数组的行和列

一种常见的需求是获取数组的单行和单列,用索引和切片组合起来实现:

In [27]: x2
Out[27]: 
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 1]])

In [31]: print(x2[:,0]) # x2的第一列
[5 7 2]
In [32]: print(x2[0,:]) # x2的第一行
[5 0 3 3]

在获取行时,出于语法的简洁性,可以省略空的切片:

In [33]: print(x2[0])
[5 0 3 3]

1.3.4 非副本视图的子数组

Numpy数组切片返回的是数组数据的视图,而不是像python列表切片是值的副本。因此直接修改切片出来的子数组,会看到原始数组也被修改。

In [43]: x2
Out[43]: 
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 1]])

In [44]: x2_sub = x2[:2,:2]
# 切片
In [45]: print(x2_sub)
[[5 0]
 [7 9]]
# 从新赋值
In [46]: x2_sub[0,0] = 99

In [47]: x2
Out[47]: 
array([[99,  0,  3,  3],
       [ 7,  9,  3,  5],
       [ 2,  4,  7,  1]])

这种默认的处理方式实际上非常有用:它意味着在处理非常大的数据集时,可以获取或处理这些数据集的片段,而不用复制底层的数据缓存。

1.3.5 创建数组的副本

根据业务的不同,有时候复制数组里的数据或子数组也是非常有用的,通过copy()方法实现:

In [47]: x2
Out[47]: 
array([[99,  0,  3,  3],
       [ 7,  9,  3,  5],
       [ 2,  4,  7,  1]])

In [48]: x2_sub_copy = x2[:2,:2].copy()

In [49]: print(x2_sub_copy)
[[99  0]
 [ 7  9]]

如果修改这个子数组,原始的数组不会改变:

In [50]: x2_sub_copy[0,0] = 18

In [51]: print(x2_sub_copy)
[[18  0]
 [ 7  9]]

In [52]: x2
Out[52]: 
array([[99,  0,  3,  3],
       [ 7,  9,  3,  5],
       [ 2,  4,  7,  1]])

1.4 数组的变形

Numpy数组变形最灵活的实现方式是通过reshape()函数实现.

In [53]: grid = np.arange(1,10).reshape((3,3))

In [54]: print(grid)
[[1 2 3]
 [4 5 6]
 [7 8 9]]

需要注意:原始数组的大小必须和变形后数组的大小一致。

如果满足这个条件,reshape方法会用到原始数组的一个非副本视图,但实际情况是,在非连续的数据缓存的情况下,返回非副本视图往往不可能实现。

另外常见的模式:将一个一维数组转变为二维的行或列的矩阵

In [62]: x = np.array([1,2,3])
#通过变形获得的行向量
In [63]: x.reshape((1,3))
Out[63]: array([[1, 2, 3]])
#通过newaxis获得的行向量
In [64]: x[np.newaxis,:]
Out[64]: array([[1, 2, 3]])

#通过变形获得的列向量
In [66]: x.reshape((3,1))
Out[66]: 
array([[1],
       [2],
       [3]])
# 通过newaxis获得的列向量
In [67]: x[:,np.newaxis]
Out[67]: 
array([[1],
       [2],
       [3]])

1.4 数组拼接和分裂

1.4.1 数组的拼接

拼接或连接NumPy中的两个数组主要由np.concatenate、np.vstack和np.hstack实现。

np.concatenate将数组元组或数组列表作为第一个参数:

In [68]: x = np.array([1,2,3])

In [69]: y = np.array([3,2,1])
# 拼接2个数组
In [70]: np.concatenate([x,y])
Out[70]: array([1, 2, 3, 3, 2, 1])

In [71]: z = [6,6,6]
# 一次性拼接两个以上的数组
In [72]: print(np.concatenate([x,y,z]))
[1 2 3 3 2 1 6 6 6]

np.concatenate也可以用于二维数组的拼接:

In [73]: grid = np.array([[1,2,3],
                          [4,5,6]])
                  
# 沿着第一个轴拼接
In [74]: np.concatenate([grid,grid])
Out[74]: 
array([[1, 2, 3],
       [4, 5, 6],
       [1, 2, 3],
       [4, 5, 6]])
# 沿着第二个拼接(从0开始索引)
In [75]: np.concatenate([grid,grid],axis=1)
Out[75]: 
array([[1, 2, 3, 1, 2, 3],
       [4, 5, 6, 4, 5, 6]])

沿着固定维度处理数组时,使用np.vstack(垂直栈)和np.hstack(水平栈)函数会更简洁:

In [76]: x = np.array([1,2,3])

In [79]: grid = np.array([[9,8,7],
                          [6,5,4]])
                
# 垂直栈数组
In [80]: np.vstack([x,grid])
Out[80]: 
array([[1, 2, 3],
       [9, 8, 7],
       [6, 5, 4]])

In [81]: y = np.array([[99],
                       [99]])              
# 水平栈数组
In [82]: np.hstack([grid,y])
Out[82]: 
array([[ 9,  8,  7, 99],
       [ 6,  5,  4, 99]])

np.dstack将沿着第三个维度拼接数组

1.4.2 数组的分裂

分裂可以通过np.splitnp.hsplitnp.vsplit函数来实现,可以向以上函数传递一个索引列表作为参数,索引列表记录的是分裂点位置。

In [83]: x = [1,2,3,99,99,3,2,1]

In [84]: x1,x2,x3 = np.split(x,[3,5])

In [85]: print(x1,x2,x3)
[1 2 3] [99 99] [3 2 1]

注意:N分裂点会得到N+1个子数组

In [86]: grid = np.arange(16).reshape((4,4))

In [87]: grid
Out[87]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
# 垂直分裂数组
In [88]: upper,lower = np.vsplit(grid,[2])

In [90]: print(upper)
[[0 1 2 3]
 [4 5 6 7]]

In [91]: print(lower)
[[ 8  9 10 11]
 [12 13 14 15]]
# 水平分裂数组
In [92]: left,right = np.hsplit(grid,[3])

In [93]: print(left)
[[ 0  1  2]
 [ 4  5  6]
 [ 8  9 10]
 [12 13 14]]

In [94]: print(right)
[[ 3]
 [ 7]
 [11]
 [15]]

同样,np.dsplit将数组沿着第三个维度分裂。

你可能感兴趣的:(python数据分析)