第4章 Numpy基础(1)

以下内容主要学习自《利用Python进行数据分析》

第4章 NumPy基础(1)

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

本章将介绍NumPy数组的基础操作。虽然深入理解NumPy对于大部分数据分析应用并不是必需的,但是精通基于数组的编程和思维是成为Python科学计算专家的第一步

NumPy之所以如此重要,其中一个原因就是它的设计对于含有大量数组的数据非常有效,此外还有如下原因:

  • NumPy是用C语言写的,因此是强类型的,运算时省却了类型检查等管理操作;
  • NumPy将数据存储在连续的内存块上,对内存的消耗小于Python内建的序列;
  • NumPy可以针对全量数组进行复杂计算而不需要写Python循环。
In [1]: import numpy as np

In [2]: my_arr = np.arange(1000000)

In [3]: my_list = list(range(1000000))

In [4]: %time for _ in range(10): my_arr2 = my_arr * 2
Wall time: 19 ms

In [5]: %time for _ in range(10): my_list2 = [x*2 for x in my_list]
Wall time: 823 ms

上面的示例中,对100万个整数乘2,并循环10次,我们看到Numpy比传统的Python快了40多倍。

第4章 Numpy基础(1)_第1张图片
NumPy-1

ndarray多维数组对象

NumPy的核心特征之一就是N维数据对象:ndarray,它是一个快速、灵活的大型数据集容器,允许你在整块数据上进行计算。

数据类型

一个ndarray对象中包含的每一个元素均为相同类型的。NumPy支持的数据类型有:

类型 类型代码 描述
int8, uint8 i1, u1 有符号和无符号的8位整数
int16, uint16 i2, u2 有符号和无符号的16位整数
int32, uint32 i4, u4 有符号和无符号的32位整数
int64, uint64 i8, u8 有符号和无符号的64位整数
float16 f2 半精度浮点数
float32 f4或f 单精度浮点数
float64 f8或d 双精度浮点数
float128 f16或g 拓展精度浮点数
complex64 c8 基于32位浮点数的复数
complex128 c16 基于64位浮点数的复数
complex256 c32 基于128位浮点数的复数
bool 布尔值,True或Flase
object O Python object类型
string_ S ASCII字符串,‘S10’表示长度为10的字符串
unicode_ U Unicode字符串,‘U10’表示长度为10的字符串

NumPy创建ndarray对象时,若不明确指定数据类型,会设置默认的数据类型:整数默认为int32,小数默认为float64(双精度浮点数)

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

In [2]: arr.dtype
Out[3]: dtype('int32')

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

In [4]: arr.dtype
Out[4]: dtype('float64')

使用dtype参数显式指定数据类型:

In [5]: arr = np.array([1, 2, 3], dtype=np.float64)

In [6]: arr.dtype
Out[6]: dtype('float64')

In [7]: arr = np.array([1, 2, 3], dtype='f8')

In [8]: arr.dtype
Out[8]: dtype('float64')

可以用astype方法显式的转换数据的数据类型:

In [9]: arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])

In [10]: arr.dtype
Out[10]: dtype('float64')

In [11]: arr.astype('i2')  # 转换为 int16 类型
Out[12]: array([ 3, -1, -2,  0, 12, 10], dtype=int16)

从上例可以看出:从浮点数转换为整数,将丢失小数部分的内容。另外,还可以把数字组成的字符串转换为数值,但如果转换失败会报告ValueError异常

ndarray属性

ndarray对象的常用属性如下:

属性 说明
ndarray.ndim 秩,即轴的数量或维度的数量。
ndarray.shape 数组的维度(m, n),相当于 m行 n列。
ndarray.size 数组元素的总个数,相当于 .shape 中 m*n 的值。
ndarray.dtype ndarray 对象的元素类型。
In [1]: import numpy as np

In [2]: arr = np.array([[1, 2, 3], [4 ,5 ,6 ]])  # 创建了一个二维的ndarray对象

In [3]: arr.ndim  # 数组是2维的
Out[3]: 2

In [4]: arr.shape  # 数组是2X3的
Out[4]: (2, 3)

In [5]: arr.size  # 数组共有6个元素
Out[5]: 6

In [6]: arr.dtype
Out[6]: dtype('int32')

创建ndarray

从Python序列创建

使用nd.array()可以从已有的Python序列(列表或元组)生成ndarray对象:

In [1]: arr = np.array(((1, 2, 3), (4, 5, 6)), dtype=np.float64)

In [2]: arr
Out[2]:
array([[1., 2., 3.],
       [4., 5., 6.]])

In [3]: arr = np.array([['a', 'b', 'c'],['d', 'e', 'f']])

In [4]: arr
Out[4]:
array([['a', 'b', 'c'],
       ['d', 'e', 'f']], dtype='
用数值范围生成

使用nd.arange(start, stop, step, dtype)可以用数值范围生成ndarray对象:

In [5]: arr = np.arange(0, 10, 3, dtype=np.float32)

In [6]: arr
Out[6]: array([0., 3., 6., 9.], dtype=float32)
用函数生成

NumPy还提供了如下的函数,用于生成给定值的数组:

函数名 描述
ones 根据给定维度和数据类型生成全1数组
ones_like 根据给定数组生成一个维度一样的全1数组
zeros 根据给定维度和数据类型生成全0数组
zeros_like 根据给定数组生成一个维度一样的全0数组
empty 根据给定维度生成没有初始化值的空数组
empty_like 根据给定数组生成一个维度一样的空数组
full 根据给定维度和数据类型生成指定值的数组
full_like 根据给定数组生成一个维度一样的指定值的数组
eye, identity 生成一个N*N的特征矩阵(对角线是1,其余是0)
In [7]: arr1 = np.ones((2,3))

In [8]: arr1
Out[8]:
array([[1., 1., 1.],
       [1., 1., 1.]])

In [9]: arr2 = np.zeros_like(arr1, dtype=np.int32)

In [10]: arr2
Out[10]:
array([[0, 0, 0],
       [0, 0, 0]])

In [11]: arr3 = np.full((2,3), 'a')

In [12]: arr3
Out[12]:
array([['a', 'a', 'a'],
       ['a', 'a', 'a']], dtype='
用随机数生成

下面这段代码,生成了100万个正态分布的数值数组,但分别用了Python内置的random对象、以及NumPy的random对象。再次看出NumPy的高效,在性能上比Python快了20多倍:

In [1]: import numpy as np
In [2]: from random import normalvariate

In [3]: N = 1000000

In [4]: %timeit samples = [normalvariate(0, 1) for _ in range(N)]
820 ms ± 4.93 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit np.random.normal(size=N)
32.6 ms ± 321 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

出于科学计算经常要构建一些样本数据的考虑,NumPy提供了多种生成特定概率分布下样本数值的函数:

函数 描述
rand 均匀分布样本
randint 均匀分布整数样本
binomial 从二项分布中抽取样本
normal 正态分布样本
beta beta分布样本
chisquare 卡方分布样本
gamma 伽马分布样本
In [6]: np.random.rand(3, 2)  # 生成一个3X2的随机样本
Out[6]:
array([[0.11786151, 0.08833357],
       [0.22276668, 0.80334031],
       [0.8968275 , 0.85327754]])

# 生成一个2X4的随机整数样本,每个元素大于等于20,、且小于50
In [86]: np.random.randint(20, 50, size=(2, 4))
Out[86]:
array([[33, 48, 42, 36],
       [28, 36, 46, 43]])

# 生成10个正态分布的数值,以0为分布的中心值,5为标准偏差(扩散值)。
In [96]: np.random.normal(0, 5, size=(10))
Out[96]:
array([ 0.51643973, -2.83238426,  4.52948333,  3.56189259,  3.9595988 ,
        2.12536774,  2.96357585,  4.07909436, -2.13527795, -2.62903347])

正态分布(Normal distribution),又名高斯分布(Gaussian distribution),是一个在数学、物理及工程等领域都非常重要的概率分布,在统计学的许多方面有着重大的影响力。

符合正态分布的数据,如果用曲线表达,具有如下特征:

  • 集中性:正态曲线的高峰位于正中央,即均数所在的位置。
  • 对称性:正态曲线以均数为中心,左右对称,曲线两端永远不与横轴相交。
  • 均匀变动性:正态曲线由均数所在处开始,分别向左右两侧逐渐均匀下降。
从文件输入/输出

NumPy可以将数据以文本或二进制格式存入磁盘或由磁盘载入。

In [97]: arr = np.arange(10)

In [99]: np.save('c:\SomeArray', arr)  # 如果存放路径中没有后缀名,将自动加上.npy

In [100]: np.load('c:\SomeArray.npy')
Out[100]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

变换ndarray

修改数组形状

numpy.ndarray的reshape方法可以在不改变数据的前提下,修改数组的形状。

In [1]: arr = np.arange(8)

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

In[3]: arr.shape()
Out[3]: (8,)

In[4]: arr = arr.reshape(2, 4)

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

In[6]: arr = arr.reshape(4, 2)
Out[6]: array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7]])
行列转置

使用ndarray.T属性可以得到转置的数组。在下面的例子演示的是对一个矩阵(二维数组)进行行列转置。

In[1]: arr = np.arange(8).reshape(2, 4)

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

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

如果数组是多维度的,如下三维数组,那么效果如下

In[1]: arr = np.arange(24).reshape(2, 3, 4)

In[2]: arr
out[2]: 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]]])

In[3]: arr = arr.T
Out[3]: array([[[ 0, 12],
        [ 4, 16],
        [ 8, 20]],

       [[ 1, 13],
        [ 5, 17],
        [ 9, 21]],

       [[ 2, 14],
        [ 6, 18],
        [10, 22]],

       [[ 3, 15],
        [ 7, 19],
        [11, 23]]])

In[4]: arr.shape
Out[4]: (4, 3, 2)

在上面的例子中,数组还是三维的,不同的是每个维度的数量发生了变化,即形状从(2,3,4)变为了(4,3,2)。

高维度数组换轴

实际上,使用.T进行转置是一个特例。如果要对指定的轴进行置换,那么可以使用transpose方法,该方法接收包含轴编号的元组。

In[1]: arr = np.arange(24).reshape(2, 3, 4)

In[2]: arr
Out[2]: 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]]])

In[3]: arr.transpose(1, 0, 2)
Out[3]: array([[[ 0,  1,  2,  3],
        [12, 13, 14, 15]],

       [[ 4,  5,  6,  7],
        [16, 17, 18, 19]],

       [[ 8,  9, 10, 11],
        [20, 21, 22, 23]]])

对于多维度数组的换轴,很容易引起误解。在上面的案例中,我们构建了一个三维的数组,每个轴的索引号分别是0,1,2。
接着我们调用了transpose(1, 0, 2)方法,这里面传输的参数,就是原轴的索引号。实际上就是把原来的1轴变为0轴、0轴变为1轴(0、1轴对换),而2轴没有改变。

swapaxes()

Numpy的transpose方法可以置换多个轴的数据。但如果只想对两个轴的数据进行交换,那么可以使用swapaxes方法,如下例的效果与上面是一致的。

In[4]: np.swapaxes(arr, 0, 1)
Out[4]: array([[[ 0,  1,  2,  3],
        [12, 13, 14, 15]],

       [[ 4,  5,  6,  7],
        [16, 17, 18, 19]],

       [[ 8,  9, 10, 11],
        [20, 21, 22, 23]]])

你可能感兴趣的:(第4章 Numpy基础(1))