NumPy 基础(数组与向量化计算)——《利用Python进行数据分析》第四章阅读笔记

NumPy 基础(数组与向量化计算)——《利用Python进行数据分析》第四章阅读笔记

前言

上一次总结了该书第三章的内容,这次带来第四章,也就是NumPy 的基础部分,有关Numpy的历史发展等相关知识点,读者可直接参考该书的87、88页。从这两页,我们知道Numpy主要是在数组部分,其算法库是采用C编写,数据存储在连续内存块上,这一特性,使得Numpy数组方法在性能上要远优于传统的Python方法。
环境依旧是 Pycharm 2020.2.4 社区版, Anconda3

一 NumPy ndarray: 多维数组对象

作为NumPy的核心特征之一,ndarray即N维数组对象,是Python中一个快速、灵活的大型数据集容器。

用下面的随机NumPy数组代码,来感受一下

import numpy as np

data = np.random.randn(2, 3)
print(data)

print(data * 10)
print(data + data)

print(data.shape)

print(data.dtype)

1. 生成 ndarray

几个示例代码可以参考一下

import numpy as np

data1 = [6, 7.5, 8, 0, 1]

arr1 = np.array(data1)
print(arr1, arr1.dtype)

data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)
print(arr2, arr2.dtype)

print(arr2.ndim, arr1.ndim)
print(arr2.shape)

print(np.zeros((3, 6)))

print(np.empty((2, 3, 2)))

print(np.arange(12))

更多的数组生成函数可以参考该书的 表4-1

2. ndarray 的数据类型

数据类型,即dtype,是一个特殊对象,它包含了ndarray 需要为某一种类型数据申明的内存块信息(也称为元数据,即表示数据的数据)

import numpy as np

arr1 = np.array([1, 2, 3], dtype=np.float64)
arr2 = np.array([1, 2, 3], dtype=np.int32)
print(arr1.dtype)
print(arr2.dtype)

更多数组类型请参考该书的 表4-2

import numpy as np

arr1 = np.array([1, 2, 3], dtype=np.float64)

arr1 = arr1.astype(np.int32)
print(arr1.dtype)
numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
numeric_strings = numeric_strings.astype(np.float64)
print(numeric_strings)

empty_unit32 = np.empty(8, dtype='u4')
print(empty_unit32, empty_unit32.dtype)

使用 astype时 会生成一个新的数组,即使你传入的dtype 与之前一样。

3. NumPy 数组算术

数组之所以重要,是因为它允许进行批量操作而无须进行任何 for 循环。这一特性 也称为 向量化。

import numpy as np

arr = np.array([[1., 2., 3.], [4., 5., 6.]])
print(arr * arr)
print(arr - arr)

print(1 / arr)
print(arr ** 0.5)

arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
print(arr2 > arr)

4. 基础索引与切片

NumPy 数组索引 方式有很多种

import numpy as np

arr = np.arange(10)
print(arr)

print(arr[5])

print(arr[5:8])

arr[5:8] = 12

print(arr)

arr_slice = arr[5:8]

arr_slice[1] = 12345
print(arr)

arr_slice[:] = 13
print(arr)

copy = arr[5:8].copy()
copy[:] = 0
print(copy)
print(arr)

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2d[0])

print(arr2d[0][2], arr2d[0, 2])

arr2d[0][2] = 5
print(arr2d)

4.1 切片索引

其实这种索引方式在上面有所提及,对于高维数组,我们可以使用切片索引获取低维度的切片,并重新赋值

import numpy as np

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

5. 布尔索引

其实就是bool值去索引, 需注意的是,布尔值数组长度应该和数组轴索引长度一致, 且可能是因为用C编写的原因,and和or关键字对布尔值数组没有用,需要使用 & 和 | 替代。

import numpy as np

names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = np.random.randn(7, 4)
print(names == 'Bob')
print(data[names == 'Bob'])
print(data[~(names == 'Bob')])
mask = (names == 'Bob') | (names == 'Will')
print(data[mask])
data[data < 0] = 0
print(data)

6. 神奇索引

其实就是用整数数组进行索引

import numpy as np

arr = np.empty((8, 4))
print(arr[[4, 3, 0, -6]])

arr = np.arange(32).reshape((8, 4))
print(arr)

print(arr[[1, 5, 7, 2], [0, 3, 1, 2]])

print(arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]])

7. 数组转置和换轴

import numpy as np

arr = np.arange(15).reshape((3, 5))
print(arr)
print(arr.T)

print(np.dot(arr, arr.T))
arr1 = np.arange(16).reshape((2, 2, 4))

print(arr1.transpose((1, 0, 2)))
print(arr1)

print(arr1.swapaxes(1, 2))

以上方法,都是返回数据的底层视图,而不生成新的数组,也不对数据进行复制

二 通用函数

通用函数,ufunc,是一种在 ndarray 数据中 进行逐元素操作的函数,某些简单函数接收一个或多个标量差值, 并产生 一个或多个 标量结果, 而通用函数就是对这些函数是对这些简单函数的向量化封装

import numpy as np

arr = np.arange(10)
print(np.sqrt(arr))
print(np.exp(arr))

x = np.random.randn(8)
y = np.random.randn(8)
print(x, y)

print(np.maximum(x, y))

reminder, whole_part = np.modf(x)
print(reminder)
print(whole_part)
arr = np.random.randn(7) * 5
print(arr)

更多的通用函数请参考该书的表 4-3,4-4

三 使用数组进行面向数组编程

使用Numpy数组,可以使用简单的数组表达式代替显示循环的方法, 这种方法称为向量化,向量化的数组操作会比纯Python的等价实现在速度上更快

import numpy as np

points = np.arange(-5, 5, 0.01)

xs, ys = np.meshgrid(points, points)
print(ys)
z = np.sqrt(xs ** 2 + ys ** 2)
print(z)

import matplotlib.pyplot as plt
plt.imshow(z, cmap=plt.cm.gray)
plt.colorbar()
plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
plt.show()

1. 将条件逻辑作为数组操作

这里主要讲的是where函数, 其实就是三元表达式 x if condition else y 的向量化版本。如果直接使用三元表达式,其需要解释器解释python完成, 速度会很慢,且对高维数组需要修改代码,无法轻易复用。而使用哪np.where就可以非常简单的完成

import numpy as np

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])
result = [(x if c else y)
          for x, y, c in zip(xarr, yarr, cond)]
print(result)

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

arr = np.random.randn(4, 4)
print(arr)
print(arr > 0)

print(np.where(arr > 0, 2, -2))

print(np.where(arr > 0, 2, arr))

传递给 np.where 的数组既可以是同等大小的数组,也可以是标量

2. 数学和统计方法

import numpy as np

arr = np.random.randn(5, 4)
print(arr)
print(arr.mean())

print(np.mean(arr))

print(arr.mean(1))

print(arr.sum(axis=0))

arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
print(arr.cumsum())

arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(arr.cumsum(axis=0))
print(arr.cumprod(axis=1))

更多地请参考该书的表4-5,然后提一句,该书在该页有一个问题,axis=0 是表示每一行,axis=1 表示每一列

3. 布尔值数组的方法

布尔值会被强制为 1(True)和 0(False)

import numpy as np

arr = np.random.randn(100)
print((arr > 0).sum())

bools = np.array([False, False, True, True])

print(bools.any())

print(bools.all())

这些方法也适用非布尔值数组,所有非0的元素都按 True 处理

4. 排序

import numpy as np

arr = np.random.randn(6)
print(arr)
arr.sort()
print(arr)

arr = np.random.randn(5, 3)
print(arr)
arr.sort(1)
print(arr)

顶层的 np.sort 方法 返回的是已经排序好的数组拷贝,而不是对原数组继续拷贝

5. 唯一值与其他集合逻辑

NumPy 包含一些针对一维nadarray 的基础集合操作,一个常用的方法是 np.unique,返回的是数组中唯一值排序后形成的数组

import numpy as np

names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
print(np.unique(names))
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
print(np.unique(ints))

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

import numpy as np

values = np.array([6, 0, 0, 3, 2, 5, 6])
print(np.in1d(values, [2, 3, 6]))

更多的ndarray数组的集合操作,请参考表 4-6

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

import numpy as np

arr = np.arange(10)
np.save('som_array', arr)
print(np.load('som_array.npy'))

np.savez('array_archive.npz', a=arr, b=arr)
arch = np.load('array_archive.npz')
print(arch['b'])

np.savez_compressed('array_compressed.npz', a=arr, b=arr)
print(np.load('array_compressed.npz'))

五 线性代数

这部分函数大多集合在了numpy的linalg中

import numpy as np
x = np.array([[1., 2., 3.], [4., 5., 6.]])
y = np.array([[6., 23.], [-1, 7], [8, 9]])
print(x.dot(y))

print(np.dot(x, y))

print(x @ y)

from  numpy.linalg import inv, qr
X = np.random.randn(5, 5)
mat = X.T.dot(X)
print(inv(mat))
print(mat.dot(inv(mat)))
q, r = qr(mat)
print(r)

更多的线性代数操作方法,请参考表4-7

六 伪随机数生成

import numpy as np

samples = np.random.normal(size=(4, 4))
print(samples)

np.random.seed(1234)

rng = np.random.RandomState(1234)
print(rng.randn(10))

其他numpy.random 中的部分函数列表参考表 4-8

七 示例:随机漫步

一个简单的随机漫步

import matplotlib.pyplot as plt
import numpy as np
import random

position = 0
walk = [position]
steps = 1000
for i in range(steps):
    step = 1 if random.randint(0, 1) else -1
    position += step
    walk.append(position)

nsteps = 1000
draws = np.random.randint(0, 2, size =nsteps)
steps = np.where(draws > 0, 1, -1)
walk = steps.cumsum()

print(walk.min(), walk.max())

print((np.abs(walk) >= 10).argmax())

plt.plot(walk[:100])
plt.show()

1. 一次性模拟多次随机漫步

import matplotlib.pyplot as plt
import numpy as np
import random

nwalks = 5000
nsteps = 1000
draws = np.random.randint(0, 2, size=(nwalks, nsteps))
steps = np.where(draws > 0, 1, -1)
walks = steps.cumsum(1)
print(walks)

print(walks.max(), walks.min())

hits30 = (np.abs(walks) >= 30).any(1)
print(hits30)
print(hits30.sum())

crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1)
print(crossing_times)
print(crossing_times.mean())

小结

本书接下来的部分将会集中在打造 pandas 数据处理技能上,继续在基于数组的风格下处理数据

终于写完了,最近的效率真的低下
_

Original: https://blog.csdn.net/weixin_54891898/article/details/122792809
Author: 物联黄同学
Title: NumPy 基础(数组与向量化计算)——《利用Python进行数据分析》第四章阅读笔记

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/763447/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球