关于python:利用-Python-进行数据分析-2-NumPy-基础

4次阅读

共计 10545 个字符,预计需要花费 27 分钟才能阅读完成。

什么是 NumPy?依据其官网文档的介绍:

NumPy 是 Python 中科学计算的根底包。它是一个 Python 库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组疾速操作的各种 API,有包含数学、逻辑、形态操作、排序、抉择、输入输出、离散傅立叶变换、根本线性代数,根本统计运算和随机模仿等等。

NumPy 的外围是一个非凡的数组对象——ndarray 对象。当运算波及到 ndarray 对象时,默认通过预编译的 C 代码对一一元素操作,在运算大量数据时,运算速度极快。此外,咱们还能够看到 NumPy 的语法更简略。

乍一看有些抽象。那么,就让咱们一点一点,揭开 NumPy 的面纱。

2.1 ndarray: 一种多维数组对象

想应用 NumPy,首先让咱们导入 NumPy 包:

import numpy as np

2.1.1 创立 ndarray 对象

NumPy 的所有运算简直都是围绕数组(ndarray 对象)开展的。那么如何创立数组呢?

简略的,咱们能够将一个序列转换为数组。

in : data1 = [6, 7.5, 8, 0, 1]
     arr1 = np.array(data1)  # np.array() 能够将所有序列型的对象转换为一个数组
     arr1
out: array([6. , 7.5, 8. , 0. , 1.])

in : data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
     arr2 = np.array(data2)  # 等长嵌套序列将被转换为多维数组
     arr2
out: array([[1, 2, 3, 4],
            [5, 6, 7, 8]])

也有其余的函数能帮忙咱们间接创立一些非凡数组。

in : np.zeros((2, 3))  # 创立指定规格的全 0 数组
out: array([[0., 0., 0.],
            [0., 0., 0.]])

in : np.ones(4)  # 创立指定规格的全 1 数组
out: array([1., 1., 1., 1.])

in : np.arange(10)  # 相似 range()
out: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

in : data = np.random.randn(2, 3)  # 创立指定规格的随机值数组(基于规范正态分布)data
out: array([[-0.22036948, -0.92073677,  0.38747356],
            [0.48489615, -0.74062949,  0.5220284]])

每个数组都有两个根本属性:shape (示意各维度大小的元组),dtype (用于阐明数组数据类型的对象)。

in : data.shape
out: (2, 3)

in : data.dtype
out: dtype('float64')

2.1.2 ndarray 的数据类型

NumPy 能主动为输出的序列设定一个适合的数据类型。

in : arr1 = np.array([1, 2, 3])
     arr1.dtype
out: dtype('int32')

in : arr2 = np.array([1.2, 0.78, 5])
     arr2.dtype
out: dtype('float64')

也能够本人手动设置数组的数据类型。

arr3 = np.array([1, 2, 3], dtype=np.float64)  # 留神这里的类型名称是“np.float64”

如果你想对一个数组进行类型转换,能够应用 astype()。然而留神,astype() 并非在原数组上进行批改,而是会建设一个新数组。

in : arr3.astype(np.int32)
out: array([1, 2, 3])

in : arr3.dtype
out: dtype('float64')  # 能够看到,原数组 arr3 的数据类型并未被批改 

2.1.3 雷同大小数组的运算

大小相等的数组之间,任何算术运算都会将运算利用到元素级。

in : arr = np.array([[1., 2., 3.], [4., 5., 6.]])
     arr
out: array([[1., 2., 3.],
            [4., 5., 6.]])

in : arr * arr
out: array([[1.,  4.,  9.],
            [16., 25., 36.]])

in : arr + arr
out: array([[2.,  4.,  6.],
            [8., 10., 12.]])

in : arr ** 2  # 幂运算
out: array([[1.,  4.,  9.],
            [16., 25., 36.]])

数组之间进行比拟,会生成布尔值数组。

in : arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
     arr2
out: array([[0.,  4.,  1.],
            [7.,  2., 12.]])

in : arr2 > arr
out: array([[False,  True, False],
            [True, False,  True]])

2.1.4 根本的索引和切片

咱们先来看一维数组。首先建设一个一维数组:

in : arr = np.arange(10)
     arr
out: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

创立数组切片的形式与创立序列切片的形式雷同。

in : arr[5:8]
out: array([5, 6, 7])

对切片赋值会被利用到每个元素之上。

in : arr[5:8] = 12  # 等式左边输出 1 个值,即可对切片蕴含的所有值赋值
     arr
out: array([0,  1,  2,  3,  4, 12, 12, 12,  8,  9])

in : arr = 12  # 这样赋值的话,arr 就被赋值为一个整数了,并不能放弃原来的数组构造
     arr
out: 12

# 如果想在原来的数组构造上,将所有值赋值为一个整数,能够这样:in : arr[:] = 12  
     arr
out: array([12, 12, 12, 12, 12, 12, 12, 12, 12, 12])

即便将切片保留为一个新的数组,对新数组的任何批改也会被利用到原数组上。这是因为 NumPy 为了解决大量数据,须要防止复制数组造成的占用运算性能问题。能够说, 咱们并没失去原数组切片的一个正本,而是失去了原数组切片的一个视图。

in : arr_slice = arr[5:8]  # 创立一个数组切片的视图
     arr_slice[0] = 100
     arr
out: array([12,  12,  12,  12,  12, 100,  12,  12,  12,  12])  # 能够看到原数组也被扭转了 

以上个性是和 Python 根底的序列对象不同的。以 list 为例:

in : list1 = list(range(10))
     list1
out: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

in : list1[5:8] = 12  # 一般的 python 列表则不能间接以这样的形式赋值给每个元素(NumPy 数组能够)out: Traceback (most recent call last):
       File "<input>", line 1, in <module>
     TypeError: can only assign an iterable

in : list1[5:8] = [12, 12, 12]  # 能够通过这样的办法赋值
     list1
out: [0, 1, 2, 3, 4, 12, 12, 12, 8, 9]

in : list1_slice = list1[5:8]
     list1_slice[0] = 100
     list1
out: [0, 1, 2, 3, 4, 12, 12, 12, 8, 9]  # 能够看到原列表没有被扭转 

如果想得到数组切片的正本(而不是视图),能够采纳 copy()。

in : arr_slice_copy = arr[5:8].copy()  # 创立一个数组切片的正本
     arr_slice_copy[0] = 123456
     arr
out: array([12,  12,  12,  12,  12, 100,  12,  12,  12,  12])  # 能够看到原数组就没有被扭转了 

那么如何对二维数组进行切片呢?

in : arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
     arr2d
out: array([[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]])

in : arr2d[0, 2]
     arr2d[0][2]  # 这两种索引形式是一样的成果
out: 3

in : arr2d[:2]  # 对第 0 轴切片
out: array([[1, 2, 3],
            [4, 5, 6]])  # 切片后,数组仍是个二维数组

in : arr2d[:2, :1]  # 对第 0 轴、第 1 轴切片
out: array([[1],
            [4]])  # 切片后,数组仍是个二维数组

# 注:能够看到,切片不扭转数组的维度 

2.1.5 布尔型索引

咱们先建设两个数组用于举例:

in : names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
     data = np.random.randn(7, 4)
     names
     data
out: array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')
     array([[0.05817461,  0.82783485,  0.46839737,  0.50975782],
            [1.15212531,  1.4481847 , -0.4201631 , -0.30265817],
            [1.05658084, -0.38813849,  0.16464375,  1.48137849],
            [-0.64680744, -0.90829029, -0.74018895,  0.17760956],
            [1.96177662,  0.747304  ,  0.08973106,  1.37109694],
            [-0.46767001, -0.50060317,  0.07396933,  0.8385746],
            [-1.71710491, -0.58035244,  2.19566878,  1.33896025]])

逻辑表达式能够用于生成布尔型数组,这个布尔型数组能够用于索引。

in : names == 'Bob'  # 用逻辑表达式生成布尔型数组
out: array([True, False, False,  True, False, False, False])

in : data_bob = data[names == 'Bob']  # 布尔型数组用于索引
                                   # 留神:布尔型数组的长度必须跟被索引的轴长度统一
     data_bob
out: array([[0.05817461,  0.82783485,  0.46839737,  0.50975782],
            [-0.64680744, -0.90829029, -0.74018895,  0.17760956]])

布尔型数组用于索引时,能够用“~”反转条件。

in : data[~(names == 'Bob')]  #  ~ 用于反转条件
out: array([[1.15212531,  1.4481847 , -0.4201631 , -0.30265817],
            [1.05658084, -0.38813849,  0.16464375,  1.48137849],
            [1.96177662,  0.747304  ,  0.08973106,  1.37109694],
            [-0.46767001, -0.50060317,  0.07396933,  0.8385746],
            [-1.71710491, -0.58035244,  2.19566878,  1.33896025]])

创立布尔型索引时,Python 能辨认的 not, and, or 是有效的,须要应用 ! & |。

in : data[(names != 'Bob') & (names != 'Joe')]
out: array([[1.05658084, -0.38813849,  0.16464375,  1.48137849],
            [1.96177662,  0.747304  ,  0.08973106,  1.37109694]])

2.1.6 数组的构造重塑和转置

如果想把一个一维数组进行构造重塑,变成 3 × 5 的二维数组,该怎么做呢?

in : arr = np.arange(15).reshape((3, 5))  # reshape() 用于数组的构造重塑
     arr
out: array([[0,  1,  2,  3,  4],
            [5,  6,  7,  8,  9],
            [10, 11, 12, 13, 14]])

如果想把上述数组转置,该怎么做呢?

in : arr.T
out: array([[0,  5, 10],
            [1,  6, 11],
            [2,  7, 12],
            [3,  8, 13],
            [4,  9, 14]])

2.2 通用函数(ufunc)

通用函数(ufunc)是一种对 ndarray 中的数据执行元素级运算的函数。

ufunc 须要至多一个数组作为参数。输出一个数组的 ufunc,被称为一元 ufunc。

in : arr = np. arrange(10)
     arr
out: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

# 开平方
in : np.sqrt(arr)
out: array([0.        , 1.        , 1.41421356, 1.73205081, 2.        ,
            2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ])

# 天然底数 e 的 x 次方
in : np.exp(arr)
out: array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
            5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
            2.98095799e+03, 8.10308393e+03])

而输出两个数组的 ufunc 被称为 二元 ufunc。

in : x = np.random.randn(8)
     y = np.random.randn(8)
     x
     y
out: array([ 0.88741632, -0.47989197,  0.961642  ,  1.43875204, -1.28796969,
            -0.04834001,  0.27405319, -0.03876604])
     array([-2.25585454,  0.9791547 ,  2.72284215, -0.44911582,  0.39418769,
            -0.73406601, -0.77432083, -3.21548859])

# 返回两个数组中,对应地位更大的值组成的数组(与 x, y 雷同构造)in : np.maximum(x, y) 
out: array([ 0.88741632,  0.9791547 ,  2.72284215,  1.43875204,  0.39418769,
            -0.04834001,  0.27405319, -0.03876604])

也有能返回两个后果数组的函数。

in : arr = np.random.randn(7) * 5
     arr
out: array([-7.01330576, -0.40563694,  5.65876183, -6.69774849,  0.42850616,
             4.65646863,  4.64433757])

# 返回浮点数数组的小数和整数局部
in : remainder, whole_part = np.modf(arr)
     remainder
     whole_part
out: array([-0.01330576, -0.40563694,  0.65876183, -0.69774849,  0.42850616,
             0.65646863,  0.64433757])
     array([-7., -0.,  5., -6.,  0.,  4.,  4.])  # 尽管是整数局部,但数组类型仍是“float64”

通用函数都并非在原始数组上进行更改,而是构建了一个新的数组。如果想原地操作数组,能够明确指明 out 参数。

in : arr = np.random.randn(5)
     arr
out: array([-0.97691849,  0.31726746, -0.75027946,  0.51031132, -0.54012687])

in : np.sqrt(arr)
     arr
out: array([-0.97691849,  0.31726746, -0.75027946,  0.51031132, -0.54012687])  # 能够看到原数组没有被批改

in : np.sqrt(arr, arr)
out: array([nan, 0.563265  ,        nan, 0.71436078,        nan])

其余的通用函数列举于此:

2.3 利用数组进行数据处理

NumPy 数组能够让咱们在不编写循环的状况下,用简洁的数组表达式进行数学运算。这种用数组表达式代替循环的做法,通常被称为矢量化。

比方,咱们当初想对一个网格型数据计算:

$$
\sqrt{(x^2 + y^2)}
$$

首先,让咱们创立一个网格型数据。

in : points1 = np.arange(-5, 0)  # x 轴
     points2 = np.arange(0, 5)  # y 轴
     points1
     points2
out: array([-5, -4, -3, -2, -1])
     array([0, 1, 2, 3, 4])

# np.meshgrid() 能够承受两个一维数组,并产生两个二维矩阵。# 能够把下面建设的网格画进去,将 x 轴和 y 轴组成的网格中,每个 (x, y) 都写进去,就容易了解了。# xs 是这个网格上 (x, y) 中 x 组成的二维矩阵;ys 是这个网格上 (x, y) 中 y 组成的二维矩阵;in : xs, ys = np.meshgrid(points1, points2)
     xs
     ys
out: array([[-5, -4, -3, -2, -1],
            [-5, -4, -3, -2, -1],
            [-5, -4, -3, -2, -1],
            [-5, -4, -3, -2, -1],
            [-5, -4, -3, -2, -1]])
     array([[0, 0, 0, 0, 0],
            [1, 1, 1, 1, 1],
            [2, 2, 2, 2, 2],
            [3, 3, 3, 3, 3],
            [4, 4, 4, 4, 4]])

# 当初,咱们就能够来计算下面的公式了
in : z = np.sqrt(xs ** 2 + ys ** 2)  # NumPy 让咱们能够像计算浮点数一样,编写计算数组的代码
     z
out: array([[5.        , 4.        , 3.        , 2.        , 1.],
            [5.09901951, 4.12310563, 3.16227766, 2.23606798, 1.41421356],
            [5.38516481, 4.47213595, 3.60555128, 2.82842712, 2.23606798],
            [5.83095189, 5.        , 4.24264069, 3.60555128, 3.16227766],
            [6.40312424, 5.65685425, 5.        , 4.47213595, 4.12310563]])

2.3.1 将条件逻辑表述为数组运算

这一节介绍一下 np.where(),它是三元表达式 x if condition else y 的矢量化。

假如咱们有一个布尔数组和两个值数组。

xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])

咱们想要依据 cond 中的值选取 xarr 和 yarr 的值:当 cond 中的值为 True 时,选取 xarr 的值,否则从 yarr 中选取。

如果用列表推导式编写:

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

但如果用 np.where() 编写,代码会简洁很多:

in : result = np.where(cond, xarr, yarr)
     result
out: array([1.1, 2.2, 1.3, 1.4, 2.5])

np.where() 的第一个参数是逻辑型数组,第二、三个参数则无需是数组。该函数的逻辑是:依据第一个逻辑型数组参数进行断定,若是,把数据替换为参数 2,;若不是,把数据替换为参数 3。

比方,咱们想把一个二维数组中,负数赋值为 2,正数放弃不变。

in : arr = np.random.randn(4,4)
     arr
out: array([[-1.14810005, -1.26020793, -0.88391974,  0.92820162],
            [-0.37743466,  0.72906491, -2.08884266,  1.02572131],
            [-0.18377037,  0.92768921,  0.80025967,  1.63431864],
            [2.05365743, -0.13690236, -0.84462678,  1.96340251]])
     
in : result = np.where(arr > 0, 2, arr)
out: array([[-1.14810005, -1.26020793, -0.88391974,  2.],
            [-0.37743466,  2.        , -2.08884266,  2.],
            [-0.18377037,  2.        ,  2.        ,  2.],
            [2.        , -0.13690236, -0.84462678,  2.]])

2.3.2 数学和统计办法

NumPy 能够对整个数组进行数学统计,举例如下:

in : arr = np.random.randn(3,2)
     arr
out: array([[0.39769436, -0.8714971],
            [-1.98139244, -0.91648828],
            [-0.61709608,  0.82630001]])

in : arr.mean()  # 求均值
     np.mean(arr)  # 和上式是等价的
out: -0.527079921854901

in : arr.std()  # 求标准差
out: 0.9201661620576673

in : arr.sum()  # 求和
out: -3.1624795311294065

in : arr.mean(axis=0)  # 计算每列的均值
out: array([-0.73359805, -0.32056179])

in : arr.sum(axis=1)  # 计算每行的和
out: array([-0.47380273, -2.89788073,  0.20920393])

2.3.3 用于布尔型数组的办法

上一节的办法也能够利用到布尔型数组里,此时 True = 1,False = 0。

in : arr = np.random.randn(100)
     (arr > 0).sum()  # 能够利用 sum() 对布尔型数组中的 True 值计数
out: 52

对于布尔型数组而言,也有一些非凡的罕用办法。

in : bools = np.array([False, False, True, False])
     bools.any()  # 数组中是否存在一个或多个 True
out: True

in : bools.all()  # 数组中是否都是 True
out: False

2.3.4 排序

如何对数组中的值排序呢?

in : arr = np.random.randn(6)
     arr
out: array([-0.30308892, -1.18380195, -1.06029921, -0.92172841, -0.25920488, -0.53782583])

# 将数组原地排序
in : arr.sort()  
     arr
out: array([-1.18380195, -1.06029921, -0.92172841, -0.53782583, -0.30308892, -0.25920488])

对于多维数组来说,指明 sort() 中的轴参数,能够按轴对数组进行排序。

in : arr = np.array([[1, 6, 2], [8, 2, 0], [5, 7, 9]])
     arr
out: array([[1, 6, 2],
            [8, 2, 0],
            [5, 7, 9]])

in : arr.sort(1)
     arr
out: array([[1, 2, 6],
            [0, 2, 8],
            [5, 7, 9]])

in : arr.sort(0)
     arr
out: array([[0, 2, 6],
            [1, 2, 8],
            [5, 7, 9]])

2.3.5 惟一化和其余的汇合逻辑

在数据分析中,经常须要从一个蕴含反复值的数组中,提取出惟一值。这时,咱们能够应用 np.unique()。

in : names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
     np.unique(names)  # 返回不蕴含反复值、已排序的后果数组
out: array(['Bob', 'Joe', 'Will'], dtype='<U4')

in : ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
     np.unique(ints)
out: array([1, 2, 3, 4])

有时,还须要判断一个数组中的值,是否蕴含在另一个数组中。

in : values = np.array([6, 0, 0, 3, 2, 5, 6])
     np.in1d(values, [2, 3, 6])  # 返回一个布尔型后果数组
out: array([True, False, False,  True,  True, False,  True])

2.4 伪随机数生成

本节以上的局部,咱们应用了很屡次 np.random.randn() 函数来创立一个随机数构建的数组,它示意依照规范正态分布取随机值。NumPy 生成的随机数并非真正的随机数,而是依据一个随机种子通过算法计算失去的,被称为伪随机数。如果你违心,也能够更改随机种子。

其余的函数能够用于生成不同的伪随机数数组:

2.5 示例:随机散步

学习了很多零散的常识,接下来咱们来看一个综合使用的例子。

假如咱们的初始地位是 0,往前走一步记为 1,往后走一步记为 -1。咱们开始随机散步,即步长为 1 和 -1 呈现的概率相等,每次咱们都能够往前走一步,或者往后走一步,一共走 1000 步。

nsteps = 1000
draws = np.random.randint(0, 2, size=nsteps)  # np.random.randint() 是从给定上上限的范畴内随机选取整数
steps = np.where(draws > 0, 1, -1)
walk = steps.cumsum()  # 生成散步门路:累加,返回每一步的累加值形成的后果数组 

接下来,让咱们沿着散步门路做一些统计工作。

往后最远走到哪里?

walk.min()

往前最远走到哪里?

walk.max()

第一次走到第 10 步远是什么时候?

for i, walk_len in enumerate(np.abs(walk) >= 10):
    if walk_len == True:
        print(i)
        break

# 或者,用上面的形式更简洁:(np.abs(walk) >= 10).argmax()  # argmax() 返回第一个最大值的索引(True 就是布尔型数组中的最大值)

注:转载请注明出处。

本文属于《利用 Python 进行数据分析》读书笔记系列:

  • 利用 Python 进行数据分析 —— 1 数据结构、函数和文件
正文完
 0