《利用python进行数据分析》Chapter 4

本章着重介绍NumPy基础知识。
由于NumPy提供了一个非常易用的C语言API,可以将数据传递给用底层语言编写的外部类库,再由外部类库将计算结果按照NumPy数组的方式返回。这个特征使得Python可以对存量C/C++/Fortran代码库进行封装,并为这些代码提供动态、易用的接口。
NumPy对含有大量数组的数据非常有效。

  1. NumPy在内部将数据存储在连续的内存块上,这与其他的Python内建数据结构时不同的。
  2. NumPy的算法库是用C语言写的,所以在操作数据内存时,不需要任何类型检查或者其他管理操作。
  3. NumPy数组使用的内存量也小于其他Python内建序列。
  4. NumPy可以针对全量数组进行复杂计算而不需要写Python循环。
    计算效率对比如下:
import numpy as np
my_arr = np.arange(1000000)
my_list = list(range(1000000))
%time for i in range(10) : my_arr2 = my_arr * 2
Wall time: 26 ms
%time for i in range(10) : my_list2 = [x * 2 for x in my_list]
Wall time: 998 ms
NumPy的方法比Python方法要快10到100倍,并且使用的内存也少。

1. NumPy ndarray—多维数组对象

一个ndarray是一个通用的多维同类数据容器,包含的每一个元素均为相同类型。

1.1 生成ndarray
  1. array函数接收任意的序列型对象生成一个ndarray数组。
data1 = [1,2,3,4]
arr1 = np.array(data1)
arr1
array([1, 2, 3, 4])
arr1.shape
(4,)
arr1.ndim
1
arr1.dtype
dtype('int32')
  1. 嵌套序列,例如同等长度的列表,将会自动转化为多维数组。
data2 = [[1,2,3,4], [5,6,7,8]]
arr2 = np.array(data2)
arr2
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])
  1. 其他函数zeros/ones/empty/ones_like/full_like/eye(特征矩阵)
np.zeros(10)
np.zeros((2,3))
np.ones_like(data)
np.full((2,3), 2)
np.full_like(data, 3)
  1. arange是range的数组版
np.arange(10)
1.2 ndarry的数据类型

arr1.dtype
arr2.astype

1.3 数组算术

数组可以进行批量操作而无需任何for循环,这种特性成为向量化。
广播机制

1.4 基础索引和切片

区别于Python的内建列表,数组的切片是原数组的视图。这意味着数据并不是被复制了,任何对于视图的修改都会反映到原数组上。
如果想要一份数组切片的拷贝而不是一份视图,可以如下:

arr[5:8].copy()

多维数组引入轴的概念,0轴是行方向,1轴是列方向。
索引与切片的区别,对于多维数组来说,索引定位的某一个具体位置的元素,某一行或者某一列或某一个元素,切片就算也只取一行或者一列仍然可能是多维的。

arr2d = np.arange(9).reshape(3,3)
arr2d
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
arr_slice = arr2d[:2, 2]
arr_slice
array([2, 5])
arr_slice.shape
(2,)
arr_slice = arr2d[2:, 2:]
arr_slice
array([[8]])
arr_slice.shape
(1, 1)
arr_slice = arr2d[2, 2]
arr_slice
8
arr_slice.shape
()
arr_slice = arr2d[:, 2]
arr_slice
array([2, 5, 8])
arr_slice.shape
(3,)
arr_slice = arr2d[:, 2:]
arr_slice
array([[2],
       [5],
       [8]])
arr_slice.shape
(3, 1)
1.5 布尔索引

布尔数组的长度必须和数组轴索引长度一致。
布尔索引选择数据时总是生成数据的拷贝。

data[names == 'Bob']
data[names != 'Bob']
data[~(names == 'Bob')] #对条件取反
mask = (names == 'Bob') | (names == 'Will') #python关键字and和or对布尔数组没用,必须使用&和|来代替。
data[mask]
1.6 神奇索引

指的是使用整数数组来进行数据索引,将数组依据索引位置重新排列,或者选出一个符合特定顺序的子集。

arr = np.empty((8,4))
for i in range(8):
    arr[i] = i
arr
array([[0., 0., 0., 0.],
       [1., 1., 1., 1.],
       [2., 2., 2., 2.],
       [3., 3., 3., 3.],
       [4., 4., 4., 4.],
       [5., 5., 5., 5.],
       [6., 6., 6., 6.],
       [7., 7., 7., 7.]])
  1. 传递一个包含指明所需顺序的列表或数组
arr[[4, 3, 0, 6]]
array([[4., 4., 4., 4.],
       [3., 3., 3., 3.],
       [0., 0., 0., 0.],
       [6., 6., 6., 6.]])
  1. 但多个索引数组时会根据每个索引元组对应的元素选出一个一维数组
arr = np.arange(32).reshape(8,4)
arr
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, 30, 31]])
arr[[1, 5, 7, 2], [0, 3, 1, 2]]
array([ 4, 23, 29, 10]) # 取出的分别时(1,0) (5,3)等位置的元素
  1. 想要达到通过选择矩阵中行列的子集所形成的矩形区域,可以如下来实现
arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])
1.7 数组转置和换轴

转置 arr.T
方法transpose可以接收包含轴编号的元组,用于置换轴
ndarray有一个swapaxes方法,该方法接收一对轴编号作为参数,并对轴进行调整来重整数组

arr.swapaxes(1,2)

2.通用函数——快速的逐元素数组函数

通用函数就是对一些简单函数的向量化封装。
sqrt/exp/maximum/add等
multiply 将数组的对应元素相乘,不是矩阵的乘法!

3.面向数组编程

利用数组表达式来代替显式循环的方法称为向量化。通常向量化的数组操作比纯python的等价实现在速度上快一到两个数量级(甚至更多)。

points = np.arange(-5,5,0.01)
xs, ys = np.meshgrid(points, points) #meshgrid函数接收两个一维数组,并根据两个数组的所有(x,y)对生成二维
xs
array([[-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       ...,
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
       [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99]])
z = np.sqrt(xs ** 2 + ys ** 2)
z
array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985,
        7.06400028],
       [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
        7.05692568],
       [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
        7.04985815],
       ...,
       [7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603,
        7.04279774],
       [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
        7.04985815],
       [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
        7.05692568]])
3.1 条件逻辑操作数组

numpy.where函数是三元表达式 x if condtion else y 的向量化版本。

result = np.where(cond, xarr, yarr)

等价于以下循环代码,但效率快很多

result = [(x if c else y) for x,y, c in zip(xarr, yarr, cond)]

where函数的第二和第三个参数也可以是标量,在数据分析中经常用于根据一个数组来生成一个新的数组。

np.where(arr > 0, 2, -2) #数组arr中正值替换为2,负值替换为 -2

where函数也支持标量和数组联合使用。

np.where(arr>0, 2, arr) #仅将正值替换为2
3.2 数学和统计方法

聚合函数的使用——sum mean std min max cumsum(累加)cumprod(累乘)
可以通过指定轴来确定聚合函数的方向,axis = 0表示按行的方向(横着计算),axis = 1表示按列的方向(竖着计算)。

arr = np.random.rand(5,4)
arr
array([[0.36089492, 0.96022134, 0.22585348, 0.51348969],
       [0.78062164, 0.37050394, 0.46274186, 0.33013851],
       [0.21334633, 0.93623858, 0.22104246, 0.44392382],
       [0.85695648, 0.14728384, 0.29550666, 0.49136345],
       [0.0505092 , 0.53460095, 0.6288425 , 0.65028889]])
arr.mean(axis=1)
array([0.51511486, 0.48600149, 0.4536378 , 0.44777761, 0.46606039]) #得到 5个值
array.sum(axis=0)
array([2.26232858, 2.94884866, 1.83398696, 2.42920436]) #得到4个值
3.3 布尔值数组的方法

布尔值True和Fasle会被强制为1和0,所以sum函数可以用于计算布尔值数组中的True个数。
any 检查数组中是否至少有一个True
all 检查数组中是否每个值都是True

3.4 排序

sort 方法支持按指定的轴进行排序。

3.5 唯一值及其他集合逻辑

np.unique 返回数组中唯一值排序后形成的数组。

np.unique(names)
sorted(set(names)) #纯python实现

np.inld 可以检查一个数组中的值是否存在于另一个数组中,并返回一个布尔值数组。

4. 使用数组进行文件输入和输出

np.save 和 np.load函数

arr = np,arange(10)
np.save('some_array', arr) # 默认为未压缩格式,后缀名为.npy
np.load('some_array.npy')
np.savez('array_archive.npz', a=arr, b=arr) #未压缩文件中保存多个数组
np.load('array_archive.npz') #载入.npz文件时得到一个字典型的对象,可通过键调用对应数组

5. 线性代数

注意numpy中*时矩阵的逐个元素乘积,而不是矩阵的点乘积,点乘操作需要dot函数,也可使用点乘操作符@

x.dot(y)
np.dot(x,y)
x @ np.ones(3)

numpy.linalg 是矩阵分解的标准函数集,可对矩阵求逆和行列式求解。常用的函数有:diag、dot、trace、det等。

6. 伪随机数生成

伪随机数是由具有确定性行为的算法根据随机数生成器中的随机数种子生成的。

np.random.seed(1234)

随机数支持根据各种分布来抽取样本,如均匀分布、正态分布、二项分布、高斯分布、卡方分布等,均有对象的函数。

7. 随机漫步

numpy使用实例。

发布了5 篇原创文章 · 获赞 0 · 访问量 313
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章