From e612e44199fc6b14323099d7425e163e29cc75c1 Mon Sep 17 00:00:00 2001 From: Lu Weizheng Date: Tue, 5 Mar 2024 19:29:44 +0800 Subject: [PATCH] numpy ops --- _static/custom.css | 3 + _toc.yml | 1 + ch-numpy/binary-ops.ipynb | 1532 ------------------------ ch-numpy/linalg.ipynb | 403 ------- ch-numpy/ops.ipynb | 2337 ++++--------------------------------- ch-numpy/unary-ops.md | 1 - conf.py | 3 +- logo.png | Bin 9854 -> 61251 bytes 8 files changed, 249 insertions(+), 4031 deletions(-) create mode 100644 _static/custom.css delete mode 100644 ch-numpy/binary-ops.ipynb delete mode 100644 ch-numpy/linalg.ipynb delete mode 100644 ch-numpy/unary-ops.md diff --git a/_static/custom.css b/_static/custom.css new file mode 100644 index 0000000..194630d --- /dev/null +++ b/_static/custom.css @@ -0,0 +1,3 @@ +html[data-theme="light"] { + --sbt-color-announcement: rgb(125, 125, 125); +} \ No newline at end of file diff --git a/_toc.yml b/_toc.yml index 534b1ff..f32c694 100644 --- a/_toc.yml +++ b/_toc.yml @@ -8,6 +8,7 @@ subtrees: - file: ch-numpy/ndarray - file: ch-numpy/ndarray-slicing-index - file: ch-numpy/random + - file: ch-numpy/ops - file: ch-pandas/index entries: - file: ch-pandas/pandas-intro diff --git a/ch-numpy/binary-ops.ipynb b/ch-numpy/binary-ops.ipynb deleted file mode 100644 index c1ac148..0000000 --- a/ch-numpy/binary-ops.ipynb +++ /dev/null @@ -1,1532 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 二元函数" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "二元运算是指作用于两个对象(例如数组)进行的运算:\n", - "给定集合 `A` ,二元函数 `F` : $A\\times A$ → $A$ 称为集合 `A` 上的二元运算。\n", - "需要注意的是,二元运算的运算结果跟两个输入值必须是同种东西。例如,整数的加法是二元运算,因为整数相加后仍然是整数。" - ] - }, - { - "cell_type": "code", - "execution_count": 87, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 简单操作\n", - "将两个数组各元素进行对应运算" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在正式开始前,我们先生成数组 `x` 和 `y` 用作演示练习使用。" - ] - }, - { - "cell_type": "code", - "execution_count": 88, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1, 2, 3, 4])" - ] - }, - "execution_count": 88, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = np.array([1,2,3,4])\n", - "x" - ] - }, - { - "cell_type": "code", - "execution_count": 89, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1.2, 1.4, 1.5, 1.7])" - ] - }, - "execution_count": 89, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = np.array([-1.2,1.4,1.5,1.7])\n", - "y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 基础运算\n", - "对于两个数组个元素对应进行加减乘运算。" - ] - }, - { - "cell_type": "code", - "execution_count": 90, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-0.2, 3.4, 4.5, 5.7])" - ] - }, - "execution_count": 90, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x+y" - ] - }, - { - "cell_type": "code", - "execution_count": 91, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([2.2, 0.6, 1.5, 2.3])" - ] - }, - "execution_count": 91, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x-y" - ] - }, - { - "cell_type": "code", - "execution_count": 92, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1.2, 2.8, 4.5, 6.8])" - ] - }, - "execution_count": 92, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x*y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 最值计算\n", - "对两个数组间进行元素级最值计算" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 1.最大值计算\n", - "我们可以使用 `np.fmax` 和 `np.maximum` 函数对两个数组 `x` 和 `y` 进行元素级最大值计算" - ] - }, - { - "cell_type": "code", - "execution_count": 93, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 2., 3., 4.])" - ] - }, - "execution_count": 93, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a1=np.fmax(x,y)\n", - "a1" - ] - }, - { - "cell_type": "code", - "execution_count": 94, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 2., 3., 4.])" - ] - }, - "execution_count": 94, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a2=np.maximum(x,y)\n", - "a2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 2.最小值计算\n", - "我们可以使用 `np.fmin` 和 `np.minimum` 函数对两个数组 `x` 和 `y` 进行元素级最大值计算" - ] - }, - { - "cell_type": "code", - "execution_count": 95, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1.2, 1.4, 1.5, 1.7])" - ] - }, - "execution_count": 95, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a1=np.fmin(x,y)\n", - "a1" - ] - }, - { - "cell_type": "code", - "execution_count": 96, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1.2, 1.4, 1.5, 1.7])" - ] - }, - "execution_count": 96, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a2=np.minimum(x,y)\n", - "a2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 元素及模运算\n", - "我们可以使用 `np.mod` 函数对两个数组元素进行模运算。" - ] - }, - { - "cell_type": "code", - "execution_count": 97, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-0.2, 0.6, 0. , 0.6])" - ] - }, - "execution_count": 97, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.mod(x,y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "除此之外,我们可以使用 `np.copysign` 函数,将数组 `y` 中各元素的符号赋值给数组 `x` 的对应元素。" - ] - }, - { - "cell_type": "code", - "execution_count": 98, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1., 2., 3., 4.])" - ] - }, - "execution_count": 98, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.copysign(x,y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 数组算数比较\n", - "我们可以对两个数组进行算数比较,产生布尔型数组。\n", - "这里我们使用的函数符号有:\n", - "`<` (小于),`>` (大于), `>=` (大于等于), `<=` (小于等于), `==` (等于),`!=` (不等于)。\n", - "需要注意的是,我们常用 `=` 表示赋值功能,因此在判定数组是否相等中,使用 `==` 符号。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面用一个例子作为示范,使用 `==` 对数组 `x` , `y` 元素级进行比较是否相等,相等输出结果 `True` ,若元素对应不相等,则输出结果 `False`。" - ] - }, - { - "cell_type": "code", - "execution_count": 99, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, False, False, False])" - ] - }, - "execution_count": 99, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x==y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "由此我们可以知道,对于该两个数组 `x` 和 `y` ,每一个对应元素值都不相等。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 按元素位操作(Elementwise bit operations)\n", - "\n", - "### 按位与(&)\n", - "\n", - "按位与(`&`)运算符是,在两个操作数对应的二进制位都为 `1` 时,该位的结果值才为 `1`,将运算符应用于每对位,然后按位构造结果。\n", - "| a \t| b \t| a AND b|\n", - "|:---:\t|:---:\t|:---:|\n", - "| 0 \t| 0 \t| 0 |\n", - "| 0 \t| 1 | 0 |\n", - "| 1 \t| 0 | 0 |\n", - "| 1 | 1 |1 | \n", - "\n", - "性质:任何数组 `x` 与 `0` 进行按位与运算都会得到数字 `0`。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`bitwise_and` 对两个数组按元素计算按位与(bit-wise AND)。\n", - "让我们通过一个例子实践一下,数字 `9` (十进制)可以用二进制表示为 `0000 1001` 。数字 `23` 同样可以用二进制表示为 `0001 0111`。则 `9` 与`23` 的按位与(bit-wise AND)就是 `0000 0001` ,或者说1。" - ] - }, - { - "cell_type": "code", - "execution_count": 100, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 100, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and(9,23)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "更进一步,`21` (base 10) = `0001 0101`。\n", - "则 `23` 与`21` 做按位与(bit-wise AND)计算 `0001 0101`, 或者说21." - ] - }, - { - "cell_type": "code", - "execution_count": 101, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "21" - ] - }, - "execution_count": 101, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and(21,23)" - ] - }, - { - "cell_type": "code", - "execution_count": 102, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'10101'" - ] - }, - "execution_count": 102, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(21) #使用二进制表示" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "接下来,我们可以使用 `bitwise_and` 对数组(array)进行按位与运算,下面是一些例子。" - ] - }, - { - "cell_type": "code", - "execution_count": 103, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1, 21], dtype=int32)" - ] - }, - "execution_count": 103, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and([9,21],23)" - ] - }, - { - "cell_type": "code", - "execution_count": 104, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1, 4])" - ] - }, - "execution_count": 104, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and([9,21],[23,14])" - ] - }, - { - "cell_type": "code", - "execution_count": 105, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 2, 4, 16])" - ] - }, - "execution_count": 105, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and(np.array([2,5,255]), np.array([3,14,16]))" - ] - }, - { - "cell_type": "code", - "execution_count": 106, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, False])" - ] - }, - "execution_count": 106, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and([True, False], [False, False])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `&` :" - ] - }, - { - "cell_type": "code", - "execution_count": 107, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 2, 4, 16])" - ] - }, - "execution_count": 107, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,255])\n", - "x2 = np.array([3,14,16])\n", - "x1 & x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "除了按位与(`&`)外,位操作函数还有:\n", - "`bitwise_or` 或\n", - "`bitwise_xor` 异或" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 按位或(|)\n", - "\n", - "\n", - "| a \t| b \t| a AND b|\n", - "|:---:\t|:---:\t|:---:|\n", - "| 0 \t| 0 \t| 0 |\n", - "| 0 \t| 1 | 1 |\n", - "| 1 \t| 0 | 1 |\n", - "| 1 | 1 | 1 | \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`bitwise_or` 对两个数组按元素计算按位或(bit-wise OR)。\n", - "让我们通过一个例子实践一下,数字 `9` (十进制)可以用二进制表示为 `0000 1001` 。数字 `23` 同样可以用二进制表示为 `0001 0111`。则 `9` 与`23` 的按位或(bit-wise OR)就是 `0001 1111` ,或者说 `31`。" - ] - }, - { - "cell_type": "code", - "execution_count": 108, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "31" - ] - }, - "execution_count": 108, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or(9,23)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "接下来,我们可以使用 `bitwise_or` 对数组(array)进行按位与运算,下面是更多的一些例子。" - ] - }, - { - "cell_type": "code", - "execution_count": 109, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([31, 23], dtype=int32)" - ] - }, - "execution_count": 109, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or([9,21],23)" - ] - }, - { - "cell_type": "code", - "execution_count": 110, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([31, 31])" - ] - }, - "execution_count": 110, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or([9,21],[23,14])" - ] - }, - { - "cell_type": "code", - "execution_count": 111, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 3, 15, 255])" - ] - }, - "execution_count": 111, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or(np.array([2,5,255]), np.array([3,14,16]))" - ] - }, - { - "cell_type": "code", - "execution_count": 112, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ True, False])" - ] - }, - "execution_count": 112, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or([True, False], [False, False])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `|` :" - ] - }, - { - "cell_type": "code", - "execution_count": 113, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 3, 15, 255])" - ] - }, - "execution_count": 113, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,255])\n", - "x2 = np.array([3,14,16])\n", - "x1 | x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 异或(^)\n", - "| a \t| b \t| a AND b|\n", - "|:---:\t|:---:\t|:---:|\n", - "| 0 \t| 0 \t| 0 |\n", - "| 0 \t| 1 | 1 |\n", - "| 1 \t| 0 | 1 |\n", - "| 1 | 1 | 0 | " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "让我们通过一个例子实践一下,数字 `9` (十进制)可以用二进制表示为 `0000 1001` 。数字 `23` 同样可以用二进制表示为 `0001 0111`。则 `9` 与`23` 的按位或(bit-wise XOR)就是 `0001 1110` ,或者说 `30`。" - ] - }, - { - "cell_type": "code", - "execution_count": 114, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "30" - ] - }, - "execution_count": 114, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_xor(9,23)" - ] - }, - { - "cell_type": "code", - "execution_count": 115, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([30, 27])" - ] - }, - "execution_count": 115, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_xor([9,21], [23,14])" - ] - }, - { - "cell_type": "code", - "execution_count": 116, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ True, False])" - ] - }, - "execution_count": 116, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_xor([True, True], [False, True])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `^` :" - ] - }, - { - "cell_type": "code", - "execution_count": 117, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1, 11, 239])" - ] - }, - "execution_count": 117, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,255])\n", - "x2 = np.array([3,14,16])\n", - "x1 ^ x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 按位取否(bit-wise NOT)\n", - "`invert()` 函数计算输入数组中整数的二进制按位取否的结果。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面举例来看 `invert()` 函数的作用。\n", - "`18` (base 10) = `0001 0010` (base 2), 那么对 `18` 按位取否(bit-wise NOT)得到:" - ] - }, - { - "cell_type": "code", - "execution_count": 118, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "237" - ] - }, - "execution_count": 118, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = np.invert(np.array(18, dtype=np.uint8))\n", - "x" - ] - }, - { - "cell_type": "code", - "execution_count": 119, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'11101101'" - ] - }, - "execution_count": 119, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(x, width=8)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "需要注意的是,输出的结果取决于 `bit_width` 。\n", - "接着上面的例子,当 `dtype` 取 `16` 时,结果会变得不同:" - ] - }, - { - "cell_type": "code", - "execution_count": 120, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "65517" - ] - }, - "execution_count": 120, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = np.invert(np.array(18, dtype=np.uint16))\n", - "y" - ] - }, - { - "cell_type": "code", - "execution_count": 121, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'1111111111101101'" - ] - }, - "execution_count": 121, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(y, width=16)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "布尔型也可以使用:" - ] - }, - { - "cell_type": "code", - "execution_count": 122, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, True])" - ] - }, - "execution_count": 122, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.invert(np.array([True, False]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `~` :" - ] - }, - { - "cell_type": "code", - "execution_count": 123, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([253, 250, 247], dtype=uint8)" - ] - }, - "execution_count": 123, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,8], dtype=np.uint8)\n", - "~x1" - ] - }, - { - "cell_type": "code", - "execution_count": 124, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, True])" - ] - }, - "execution_count": 124, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x2 = np.array([True, False])\n", - "~x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 移位\n", - "将输入数组的整数按二进制的位向左/向右移位。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 向左移位\n", - "`letf_shift` 将输入数组的整数按二进制的位向左移位。\n", - "具体的操作是,通过在 `x1` 的右边附加 `x2 ` 个 `0` ,从而向左移位。\n", - "由于数字是二进制格式表示,所以该运算等价于 `x1×2^x2` 。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面通过举例来看 `left_shift` 具体作用。\n", - "`5` (base 10) = `0101` (base 2):" - ] - }, - { - "cell_type": "code", - "execution_count": 125, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "20" - ] - }, - "execution_count": 125, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.left_shift(5, 2)" - ] - }, - { - "cell_type": "code", - "execution_count": 126, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'10100'" - ] - }, - "execution_count": 126, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(20)" - ] - }, - { - "cell_type": "code", - "execution_count": 127, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([10, 20, 40], dtype=int32)" - ] - }, - "execution_count": 127, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.left_shift(5, [1,2,3])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "需要注意的是,第二个参数的 `dtype` 可能会改变结果的 `dtype`:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "例如,`255` (base 10) = `1111 1111` (base 2),`254` (base 10) = `1111 1110` (base 2)。\n", - "我们希望将 `255` 向左移动 `1` 位得到 `254` :" - ] - }, - { - "cell_type": "code", - "execution_count": 128, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "510 \n" - ] - } - ], - "source": [ - "a = np.left_shift(np.uint8(255), 1) \n", - "print(a, type(a))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "得到结果510,并不是我们期望的结果,现在我们限定第二个参数的 `dtype`:" - ] - }, - { - "cell_type": "code", - "execution_count": 129, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "254 \n" - ] - } - ], - "source": [ - "b = np.left_shift(np.uint8(255), np.uint8(1))\n", - "print(b, type(b))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "以上便得到了我们所希望移位后得到的结果。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "该函数操作可以简化为 `<<`:" - ] - }, - { - "cell_type": "code", - "execution_count": 130, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([10, 20, 40], dtype=int32)" - ] - }, - "execution_count": 130, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = 5\n", - "x2 = np.array([1, 2, 3])\n", - "x1 << x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 向右移位\n", - "`right_shift` 将输入数组的整数按二进制的位向左移位。\n", - "具体的操作是,将 `x1` 的向右按位移动 `x2 ` ,从而向右移位。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面通过举例来看 `right_shift` 具体作用。\n", - "`10` (base 10) = `1010` (base 2):" - ] - }, - { - "cell_type": "code", - "execution_count": 131, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5" - ] - }, - "execution_count": 131, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.right_shift(10, 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 132, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'101'" - ] - }, - "execution_count": 132, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5)" - ] - }, - { - "cell_type": "code", - "execution_count": 133, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([5, 2, 1], dtype=int32)" - ] - }, - "execution_count": 133, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.right_shift(10, [1,2,3])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "该函数操作可以简化为 `>>`:" - ] - }, - { - "cell_type": "code", - "execution_count": 134, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([5, 2, 1], dtype=int32)" - ] - }, - "execution_count": 134, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = 10\n", - "x2 = np.array([1,2,3])\n", - "x1 >> x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 按位压缩存储(Bit packing)\n", - "\n", - "`packbits()` 函数,将二进制数值数组的元素打包成一个 `unit8` 数组中的位。\n", - "具体操作是通过在数组中的整数末尾插入 `0` ,使输出结果被填充为完整字节。" - ] - }, - { - "cell_type": "code", - "execution_count": 135, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[[160],\n", - " [ 64]],\n", - "\n", - " [[192],\n", - " [ 32]]], dtype=uint8)" - ] - }, - "execution_count": 135, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = np.array([[[1,0,1],\n", - " [0,1,0]],\n", - " [[1,1,0],\n", - " [0,0,1]]])\n", - "b = np.packbits(a, axis=-1)\n", - "b" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`160` (base 10) = `1010 0000` ;\n", - "`64` (base 10) = `0100 0000` ;\n", - "`192` (base 10) = `1100 0000` ;\n", - "`32` (base 10) = `0010 0000` 。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`unpackbits()` 函数,将 `uint8` 数组的元素解压缩到二进制值的输出数组中。\n", - "\n", - "每个元素都表示一个应解压缩到二进制值输出数组中的位字段。输出数组的形状可以是一维的(如果 `axis=None` ),也可以是与输入数组相同的形状,并沿着指定的轴进行解压。" - ] - }, - { - "cell_type": "code", - "execution_count": 136, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0, 0, 0, 0, 0, 0, 1, 0],\n", - " [0, 0, 0, 0, 0, 1, 1, 1],\n", - " [0, 0, 0, 1, 0, 1, 1, 1]], dtype=uint8)" - ] - }, - "execution_count": 136, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = np.array([[2], [7], [23]], dtype=np.uint8)\n", - "b = np.unpackbits(a, axis=1)\n", - "b" - ] - }, - { - "cell_type": "code", - "execution_count": 137, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0, 0, 0, 0, 0, 0],\n", - " [0, 0, 0, 0, 0, 1],\n", - " [0, 0, 0, 1, 0, 1]], dtype=uint8)" - ] - }, - "execution_count": 137, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c = np.unpackbits(a, axis=1, count=6)\n", - "c" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 输出格式\n", - "`binary_repr(num, width=None)` 函数用于将输入数组的整数以二进制格式输出。\n" - ] - }, - { - "cell_type": "code", - "execution_count": 138, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'101'" - ] - }, - "execution_count": 138, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5)" - ] - }, - { - "cell_type": "code", - "execution_count": 139, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'-101'" - ] - }, - "execution_count": 139, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(-5)" - ] - }, - { - "cell_type": "code", - "execution_count": 140, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'00101'" - ] - }, - "execution_count": 140, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5, width=5)" - ] - }, - { - "cell_type": "code", - "execution_count": 141, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'0101'" - ] - }, - "execution_count": 141, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5, width=4)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.11" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/ch-numpy/linalg.ipynb b/ch-numpy/linalg.ipynb deleted file mode 100644 index 4a6c954..0000000 --- a/ch-numpy/linalg.ipynb +++ /dev/null @@ -1,403 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 线性代数" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 矩阵与向量的乘积\n", - "### 点积\n", - "`dot(a,b,out=None)` 计算两个数组的点积:\n", - " 1)如果 `a` 和 `b` 都是一维数组,那么点积即为内积(没有共轭);" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "12" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.dot(3, 4)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(-12+0j)" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.dot(3j, 4j)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 2)如果`a` 和 `b` 都是二维数组,那么使用矩阵乘法 `a @ b` ;" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[4, 1],\n", - " [2, 2]])" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = [[1, 0], [0, 1]]\n", - "b = [[4, 1], [2, 2]]\n", - "np.dot(a, b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 3)如果`a` 或 `b` 是标量(维数为`0`),那么其点积等价于乘,可以使用 `numpy.multiply(a,b)` 或 `a*b` ;" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[16, 4],\n", - " [ 8, 8]])" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = 4\n", - "b = [[4,1],[2,2]]\n", - "np.dot(a,b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 4)如果`a` 是一维数组, `b` 是`N`维数组,那么点积是 `a` 与 `b` 的最后一个轴的积的和;" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([54, 66, 78])" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = [3,4,5]\n", - "b = [[1,2,3],[4,5,6],[7,8,9]]\n", - "np.dot(a,b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 5)如果`a` 是`N`维数组, `b` 是`M-d`维数组(M≥2),那么点积为 `a` 的最后一个轴与 `b` 的倒数第二条轴的乘机的和:\n", - " `dot(a,b)[i,j,k,m] = sum(a[i,j,:]*b[k,:,m])`" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 70, 83, 96],\n", - " [ 60, 72, 84],\n", - " [ 90, 108, 126],\n", - " [106, 128, 150]])" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = [[1,5,7],[2,4,6],[3,6,9],[4,8,10]]\n", - "b = [[1,2,3],[4,5,6],[7,8,9]]\n", - "np.dot(a,b)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`linalg.multi_dot(arrays, *, out=None)`函数,在单个函数调用中计算两个或多个数组的点积,同时自动选择最快的求值顺序。\n", - "使用该函数时需要注意的是,如果第一个参数是一维数组,它被视为行向量。如果最后一个参数是一维数组,则将其视为列向量。\n", - "其他参数必须是二维的。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面我们通过一个例子学习使用 `linalg.multi_dot(arrays, *, out=None)` 函数:" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "from numpy.linalg import multi_dot" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "A = np.random.random((10000, 100))\n", - "B = np.random.random((100, 1000))\n", - "C = np.random.random((1000, 5))\n", - "D = np.random.random((5, 333))" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[29258.56199495, 39185.76250743, 24831.33335922, ...,\n", - " 32218.47313706, 49684.46713456, 22935.23821755],\n", - " [27709.83193917, 37113.69763102, 23518.20177806, ...,\n", - " 30512.01252248, 47060.32181675, 21714.06820096],\n", - " [30923.66123875, 41400.43468792, 26235.57546294, ...,\n", - " 34030.73408076, 52492.73599524, 24230.36608396],\n", - " ...,\n", - " [30037.91721463, 40225.74401239, 25493.36791348, ...,\n", - " 33065.16482832, 51002.93827961, 23539.62618817],\n", - " [32787.66087443, 43892.23116514, 27817.26221112, ...,\n", - " 36072.93205314, 55650.19508135, 25687.60971541],\n", - " [32347.16027985, 43328.5110721 , 27457.63882012, ...,\n", - " 35623.51338682, 54936.40902548, 25354.31087954]])" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "_ = multi_dot([A, B, C, D])\n", - "_" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们也可以使用 `vdot(a,b,/)` 函数,返回两个向量的点积。\n", - "需要注意的是,`vdot` 处理多维数组的方式与 `dot` 不同:`vdot`不执行矩阵乘积,而是首先将输入参数扁平化为一维向量。\n", - "因此,`vdot`只能用于向量。" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(70-8j)" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = np.array([1+2j,3+4j])\n", - "b = np.array([5+6j,7+8j])\n", - "np.vdot(a, b)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(70+8j)" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.vdot(b, a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "当 `a` 和 `b` 是更高维度的数组时,输出结果依旧是扁平化的一维向量:" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = np.array([[1, 2], [3, 4]])\n", - "b = np.array([[5, 6], [7, 8]])\n", - "np.vdot(a, b)" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.vdot(b, a)" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "1*5 + 2*6 + 3*7 + 4*8" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.11" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/ch-numpy/ops.ipynb b/ch-numpy/ops.ipynb index 070ff4c..0bc73e3 100644 --- a/ch-numpy/ops.ipynb +++ b/ch-numpy/ops.ipynb @@ -4,20 +4,24 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# 矩阵运算" + "# 数学运算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "本章将对创建的高维数组(矩阵)进行一些常规的数学操作,例如一元函数、二元函数以及线性代数。" + "本节将对创建的高维数组进行一些数学运算,包括一元函数、二元函数、统计函数以及线性代数。这些运算操作大部分都比较直观,通过函数名或数学运算符的名字,我们就可以知道其要进行的数学计算。" ] }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, + "execution_count": 60, + "metadata": { + "tags": [ + "hide-cell" + ] + }, "outputs": [], "source": [ "import numpy as np" @@ -32,65 +36,13 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 61, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1, 2, 3, 4])" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "x = np.array([1,2,3,4])\n", - "x" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1.2, 1.4, 1.5, 1.7])" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ "y = np.array([-1.2,1.4,1.5,1.7])\n", - "y" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1.2, 1.4, 1.5, 1.7])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "z = np.array([-1.2,1.4,1.5,1.7])\n", - "z" + "z = np.array([-1.2,1.4,1.5,1.7])" ] }, { @@ -98,2048 +50,321 @@ "metadata": {}, "source": [ "## 一元函数\n", - "对一个数组执行元素级运算。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 绝对值计算\n", - "下面,我们将对数组中各元素进行绝对值的计算。" + "\n", + "一元函数对一个数组执行元素级运算。下面以对数 $\\log$ 和取整两个计算来演示 NumPy 的一元运算,其他运算可参考其官方文档。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们可以使用 `np.abs()` 和 `np.fabs()` 函数进行数组元素的绝对值计算,其中,使用 \n", - "`np.fabs()` 函数时,计算元绝对值时,元素类型为 `float` 。" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1, 2, 3, 4])" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.abs(x)" + "### 对数\n", + "\n", + "对数函数是数学中的一种常见函数,如果 $a^x=N$,那么 $x = \\log_{a} N$,其中 $a$叫做底数,$N$ 叫真数。通常以 `10` 为底的对数叫做常用对数,以 $e$ 为底的对数称为自然对数。\n", + "\n", + "`np.log()` 是以 $e$ 为底的自然对数。" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([1., 2., 3., 4.])" + "array([0. , 0.69314718, 1.09861229, 1.38629436])" ] }, - "execution_count": 6, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "np.fabs(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 指数运算" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 1.计算平方根\n", - "我们可以使用 `np.sqrt()` 函数对数组进行元素级开方运算。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```{note}\n", - "开方运算要求根号下元素非负.\n", - "```" + "np.log(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "由于元素非负性的要求,我们使用数组 `y` 用于练习。" + "`np.log10()` 对数组中元素求常用对数。" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 63, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\TNT\\AppData\\Local\\Temp\\ipykernel_26416\\3801561550.py:1: RuntimeWarning: invalid value encountered in sqrt\n", - " np.sqrt(y)\n" - ] - }, { "data": { "text/plain": [ - "array([ nan, 1.18321596, 1.22474487, 1.30384048])" + "array([0. , 0.30103 , 0.47712125, 0.60205999])" ] }, - "execution_count": 7, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "np.sqrt(y)" + "np.log10(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.平方运算\n", - "我们可以使用 `np.square()` 函数对数组中各元素进行平方运算。" + "### 取整运算\n", + "\n", + "`np.ceil()` 对数组各元素向上取整;`np.floor()` 对数组中各个元素向下取整;`np.rint()` 对数组各元素四舍五入取整。" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([ 1, 4, 9, 16])" + "array([-2., 1., 1., 1.])" ] }, - "execution_count": 8, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "np.square(x)" - ] - }, - { - "attachments": { - "image.png": { - "image/png": "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" - } - }, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 3.对数运算\n", - "对数函数是数学中的一种常见函数,如果 `a^x=N` ( `a` > `0` ,且 `a` ≠ `1` ),则 `x` 叫做以 `a` 为底 `N` 的对数,记做 `x=logaN` ,其中 `a` 要写于 `log` 右下。\n", - "其中 `a` 叫做对数的底, `N` 叫做真数。\n", - "![image.png](attachment:image.png)\n", - "通常以 `10` 为底的对数叫做常用对数,以 `e` 为底的对数称为自然对数。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### (1)自然对数\n", - "以 `e` 为底的对数称为自然对数。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " \n", - " ```{note}\n", - " `e` 是自然常数,是一个无限不循环小数,且为超越数,其值约为 `2.718281828459045` 。\n", - " ```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们可以使用 `np.log()` 函数对数组中元素求自然对数。\n", - "由于数组 `x` 中含有负数元素,而对数要求真数为正,因此,我们使用数组 `y` 进行实践。" + "np.floor(z)" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 65, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\TNT\\AppData\\Local\\Temp\\ipykernel_26416\\3455148303.py:1: RuntimeWarning: invalid value encountered in log\n", - " np.log(y)\n" - ] - }, { "data": { "text/plain": [ - "array([ nan, 0.33647224, 0.40546511, 0.53062825])" + "array([-1., 1., 2., 2.])" ] }, - "execution_count": 9, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "np.log(y)" + "np.rint(z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "##### (2) 常用对数\n", - "通常以 `10` 为底的对数叫做常用对数。" + "除去以上一元函数,还有以下函数较为常用:\n", + "\n", + "|类型| 函数 | 用途 |\n", + "|:---:|:---:|:---|\n", + "|普通型三角函数|[`np.sin()` ]|计算正弦函数|\n", + "|普通型三角函数|[`np.cos()` ]|计算余弦函数|\n", + "|普通型三角函数|[`np.tan()` ]|计算正切函数|\n", + "|双曲型三角函数|[`np.sinh()` ]|计算双曲正弦函数|\n", + "|双曲型三角函数|[`np.cosh()` ]|计算双曲余弦函数|\n", + "|双曲型三角函数|[`np.tanh()` ]|计算双曲正切函数|\n", + "|指数函数|[`np.exp()` ]|计算数组各元素的指数值|\n", + "|符号函数|[`np.sign()` ]|计算数组各元素的符号值|" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们可以使用 `np.log10()` 函数对数组中元素求自然对数。\n", - "由于数组 `x` 中含有负数元素,而对数要求真数为正,因此,我们使用数组 `y` 进行实践。" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\TNT\\AppData\\Local\\Temp\\ipykernel_26416\\4048949146.py:1: RuntimeWarning: invalid value encountered in log10\n", - " np.log10(y)\n" - ] - }, - { - "data": { - "text/plain": [ - "array([ nan, 0.14612804, 0.17609126, 0.23044892])" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.log10(y)" + "## 二元函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "##### (3)以2为底的对数" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\TNT\\AppData\\Local\\Temp\\ipykernel_26416\\3578401648.py:1: RuntimeWarning: invalid value encountered in log2\n", - " np.log2(y)\n" - ] - }, - { - "data": { - "text/plain": [ - "array([ nan, 0.48542683, 0.5849625 , 0.76553475])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.log2(y)" + "二元运算是指对两个对象(操作数)进行的运算:给定集合 $A$,二元函数 $F$ : $A\\times A$ → $A$ 称为集合 $A$ 上的二元运算。需要注意的是,二元运算的运算结果跟两个输入值必须是同种类型的数据。例如,加法的两个操作数都是整数,得到的运算结果也是整数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### 4.取整运算" + "### 简单数学操作" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "##### (1)向上舍入\n", - "我们可以使用 `np.ceil()` 函数计算数组各元素的 `ceiling` 值,即每个元素向上舍入" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1., 2., 2., 2.])" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.ceil(z)" + "两个数组的元素加减乘比较直观,使用常用的数学符号即可。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "##### (2)向下舍入\n", - "我们可以使用 `np.floor()` 函数计算数组各元素的 `floor` 值,即每个元素向下舍入。" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-2., 1., 1., 1.])" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.floor(z)" + "|函数|含义|示例|\n", + "|:---:|:---:|:---|\n", + "|[`+`]|对两个数组进行加法运算|x+y|\n", + "|[`-`]|对两个数组进行减法运算|x-y|\n", + "|[`*`]|对两个数组进行乘法运算|x*y|\n", + "|[`/`]|对两个数组进行乘法运算|x/y|" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "##### (3)四舍五入\n", - "我们可以使用 `np.rint()` 函数计算数组各元素的四舍五入值。" + "下面我们以两个数组元素的加法运算为例:" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([-1., 1., 2., 2.])" + "array([-0.83333333, 1.42857143, 2. , 2.35294118])" ] }, - "execution_count": 14, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "np.rint(z)" + "x / y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们可以使用 `np.modf()` 函数将数组各元素的小数和整数部分以两个独立数组形式返回。" + "`np.fmax`、`np.maximum` 和 `np.fmin`、`np.minimum`函数对两个数组进行元素级最大/小值计算。" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "(array([-0.2, 0.4, 0.5, 0.7]), array([-1., 1., 1., 1.]))" + "array([1., 2., 3., 4.])" ] }, - "execution_count": 15, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "np.modf(z)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "除去以上常用的 `NumPy` 库中的一元函数,还有以下函数较为常用:\n", - "|类型| 函数 | 用途 |\n", - "|:---:|:---:|:---|\n", - "|普通型三角函数|[`np.sin()` ]|计算正弦函数|\n", - "|普通型三角函数|[`np.cos()` ]|计算余弦函数|\n", - "|普通型三角函数|[`np.tan()` ]|计算正切函数|\n", - "|双曲型三角函数|[`np.sinh()` ]|计算双曲正弦函数|\n", - "|双曲型三角函数|[`np.cosh()` ]|计算双曲余弦函数|\n", - "|双曲型三角函数|[`np.tanh()` ]|计算双曲正切函数|\n", - "|指数函数|[`np.exp()` ]|计算数组各元素的指数值|\n", - "|符号函数|[`np.sign()` ]|计算数组各元素的符号值|" + "np.maximum(x,y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 二元函数" + "### 比较操作\n", + "\n", + "我们可以对两个数组逐元素进行算数比较,产生布尔型数组:\n", + "\n", + "|符号|含义|\n", + "|:---:|:---|\n", + "|[`<` ]|小于|\n", + "|[`>`]|大于|\n", + "|[`>=`]|大于等于|\n", + "|[ `<=`]|小于等于|\n", + "|[`==` ]|等于|\n", + "|[`!=` ]|不等于|\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "二元运算是指作用于两个对象(例如数组)进行的运算:\n", - "给定集合 `A` ,二元函数 `F` : $A\\times A$ → $A$ 称为集合 `A` 上的二元运算。\n", - "需要注意的是,二元运算的运算结果跟两个输入值必须是同种东西。例如,整数的加法是二元运算,因为整数相加后仍然是整数。" + "```{note}\n", + "需要注意的是,我们常用 `=` 表示赋值功能,不能用来进行数组的比较。判定数组是否相等时,使用 `==` 符号。\n", + "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### 1.简单操作\n", - "将两个数组各元素进行对应运算" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (1)基础运算\n", - "对于两个数组的元素对应进行加减乘运算。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "|函数|含义|示例|\n", - "|:---:|:---:|:---|\n", - "|[`+`]|对两个数组进行加法运算|x+y|\n", - "|[`-`]|对两个数组进行减法运算|x-y|\n", - "|[`*`]|对两个数组进行乘法运算|x*y|" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面我们以两个数组元素的加法运算为例:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-0.2, 3.4, 4.5, 5.7])" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x+y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (2)最值计算\n", - "对两个数组间进行元素级最值计算" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### 1)最大值计算\n", - "我们可以使用 `np.fmax` 和 `np.maximum` 函数对两个数组 `x` 和 `y` 进行元素级最大值计算" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 2., 3., 4.])" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a1=np.fmax(x,y)\n", - "a1" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 2., 3., 4.])" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a2=np.maximum(x,y)\n", - "a2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### 2)最小值计算\n", - "同样地,我们可以使用 `np.fmin` 和 `np.minimum` 函数对两个数组 `x` 和 `y` 进行元素级最大值计算" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (2)元素及模运算\n", - "我们可以使用 `np.mod` 函数对两个数组元素进行模运算。" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-0.2, 0.6, 0. , 0.6])" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.mod(x,y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "除此之外,我们可以使用 `np.copysign` 函数,将数组 `y` 中各元素的符号赋值给数组 `x` 的对应元素。" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-1., 2., 3., 4.])" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.copysign(x,y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (3)数组算数比较\n", - "我们可以对两个数组进行算数比较,产生布尔型数组。\n", - "这里我们使用的函数符号有:\n", - "|符号|含义|\n", - "|:---:|:---|\n", - "|[`<` ]|小于|\n", - "|[`>`]|大于|\n", - "|[`>=`]|大于等于|\n", - "|[ `<=`]|小于等于|\n", - "|[`==` ]|等于|\n", - "|[`!=` ]|不等于|\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```{note}\n", - "需要注意的是,我们常用 `=` 表示赋值功能,因此在判定数组是否相等中,使用 `==` 符号。\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面用一个例子作为示范,使用 `==` 对数组 `x` , `y` 元素级进行比较是否相等,相等输出结果 `True` ,若元素对应不相等,则输出结果 `False`。" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, False, False, False])" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x==y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "由此我们可以知道,对于该两个数组 `x` 和 `y` ,每一个对应元素值都不相等。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2.按元素位操作(Elementwise bit operations)\n", - "\n", - "#### (1)按位与(&)\n", - "\n", - "按位与(`&`)运算符是,在两个操作数对应的二进制位都为 `1` 时,该位的结果值才为 `1`,将运算符应用于每对位,然后按位构造结果。\n", - "| a \t| b \t| a AND b|\n", - "|:---:\t|:---:\t|:---:|\n", - "| 0 \t| 0 \t| 0 |\n", - "| 0 \t| 1 | 0 |\n", - "| 1 \t| 0 | 0 |\n", - "| 1 | 1 |1 | \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```{note}\n", - "性质:任何数组 x 与 0 进行按位与运算都会得到数字 0。\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`bitwise_and` 对两个数组按元素计算按位与(bit-wise AND)。\n", - "让我们通过一个例子实践一下,数字 `9` (十进制)可以用二进制表示为 `0000 1001` 。数字 `23` 同样可以用二进制表示为 `0001 0111`。则 `9` 与`23` 的按位与(bit-wise AND)就是 `0000 0001` ,或者说1。" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and(9,23)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "更进一步,`21` (base 10) = `0001 0101`。\n", - "则 `23` 与`21` 做按位与(bit-wise AND)计算 `0001 0101`, 或者说21." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "21" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and(21,23)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'10101'" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(21) #使用二进制表示" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "接下来,我们可以使用 `bitwise_and` 对数组(array)进行按位与运算,下面是一些例子。" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1, 21], dtype=int32)" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and([9,21],23)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 2, 4, 16])" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and(np.array([2,5,255]), np.array([3,14,16]))" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, False])" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_and([True, False], [False, False])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `&` :" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 2, 4, 16])" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,255])\n", - "x2 = np.array([3,14,16])\n", - "x1 & x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "除了按位与(`&`)外,位操作函数还有:\n", - "`bitwise_or` 或\n", - "`bitwise_xor` 异或" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (2)按位或(|)\n", - "\n", - "\n", - "| a \t| b \t| a AND b|\n", - "|:---:\t|:---:\t|:---:|\n", - "| 0 \t| 0 \t| 0 |\n", - "| 0 \t| 1 | 1 |\n", - "| 1 \t| 0 | 1 |\n", - "| 1 | 1 | 1 | \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`bitwise_or` 对两个数组按元素计算按位或(bit-wise OR)。\n", - "让我们通过一个例子实践一下,数字 `9` (十进制)可以用二进制表示为 `0000 1001` 。数字 `23` 同样可以用二进制表示为 `0001 0111`。则 `9` 与`23` 的按位或(bit-wise OR)就是 `0001 1111` ,或者说 `31`。" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "31" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or(9,23)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "接下来,我们可以使用 `bitwise_or` 对数组(array)进行按位与运算,下面是更多的一些例子。" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([31, 23], dtype=int32)" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or([9,21],23)" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 3, 15, 255])" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or(np.array([2,5,255]), np.array([3,14,16]))" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ True, False])" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_or([True, False], [False, False])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `|` :" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 3, 15, 255])" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,255])\n", - "x2 = np.array([3,14,16])\n", - "x1 | x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (3)异或(^)\n", - "| a \t| b \t| a AND b|\n", - "|:---:\t|:---:\t|:---:|\n", - "| 0 \t| 0 \t| 0 |\n", - "| 0 \t| 1 | 1 |\n", - "| 1 \t| 0 | 1 |\n", - "| 1 | 1 | 0 | " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "让我们通过一个例子实践一下,数字 `9` (十进制)可以用二进制表示为 `0000 1001` 。数字 `23` 同样可以用二进制表示为 `0001 0111`。则 `9` 与`23` 的按位或(bit-wise XOR)就是 `0001 1110` ,或者说 `30`。" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "30" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_xor(9,23)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([30, 27])" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_xor([9,21], [23,14])" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ True, False])" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.bitwise_xor([True, True], [False, True])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `^` :" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1, 11, 239])" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,255])\n", - "x2 = np.array([3,14,16])\n", - "x1 ^ x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (4)按位取否(bit-wise NOT)\n", - "`invert()` 函数计算输入数组中整数的二进制按位取否的结果。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面举例来看 `invert()` 函数的作用。\n", - "`18` (base 10) = `0001 0010` (base 2), 那么对 `18` 按位取否(bit-wise NOT)得到:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "237" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = np.invert(np.array(18, dtype=np.uint8))\n", - "x" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'11101101'" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(x, width=8)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "需要注意的是,输出的结果取决于 `bit_width` 。\n", - "接着上面的例子,当 `dtype` 取 `16` 时,结果会变得不同:" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "65517" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = np.invert(np.array(18, dtype=np.uint16))\n", - "y" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'1111111111101101'" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(y, width=16)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "布尔型也可以使用:" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, True])" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.invert(np.array([True, False]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在给定数组的情况下,该函数操作可以简化为 `~` :" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([253, 250, 247], dtype=uint8)" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = np.array([2,5,8], dtype=np.uint8)\n", - "~x1" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([False, True])" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x2 = np.array([True, False])\n", - "~x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (5)移位\n", - "将输入数组的整数按二进制的位向左/向右移位。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### 1)向左移位\n", - "`letf_shift` 将输入数组的整数按二进制的位向左移位。\n", - "具体的操作是,通过在 `x1` 的右边附加 `x2 ` 个 `0` ,从而向左移位。\n", - "由于数字是二进制格式表示,所以该运算等价于 `x1×2^x2` 。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面通过举例来看 `left_shift` 具体作用。\n", - "`5` (base 10) = `0101` (base 2):" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "20" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.left_shift(5, 2)" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'10100'" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(20)" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([10, 20, 40], dtype=int32)" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.left_shift(5, [1,2,3])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "需要注意的是,第二个参数的 `dtype` 可能会改变结果的 `dtype`:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "例如,`255` (base 10) = `1111 1111` (base 2),`254` (base 10) = `1111 1110` (base 2)。\n", - "我们希望将 `255` 向左移动 `1` 位得到 `254` :" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "510 \n" - ] - } - ], - "source": [ - "a = np.left_shift(np.uint8(255), 1) \n", - "print(a, type(a))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "得到结果 `510` ,并不是我们期望的结果,现在我们限定第二个参数的 `dtype`:" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "254 \n" - ] - } - ], - "source": [ - "b = np.left_shift(np.uint8(255), np.uint8(1))\n", - "print(b, type(b))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "以上便得到了我们所希望移位后得到的结果。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "该函数操作可以简化为 `<<`:" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([10, 20, 40], dtype=int32)" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = 5\n", - "x2 = np.array([1, 2, 3])\n", - "x1 << x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### 2)向右移位\n", - "`right_shift` 将输入数组的整数按二进制的位向左移位。\n", - "具体的操作是,将 `x1` 的向右按位移动 `x2 ` ,从而向右移位。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "下面通过举例来看 `right_shift` 具体作用。\n", - "`10` (base 10) = `1010` (base 2):" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.right_shift(10, 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'101'" - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5)" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([5, 2, 1], dtype=int32)" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.right_shift(10, [1,2,3])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "该函数操作可以简化为 `>>`:" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([5, 2, 1], dtype=int32)" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x1 = 10\n", - "x2 = np.array([1,2,3])\n", - "x1 >> x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 3.按位压缩存储(Bit packing)\n", - "\n", - "`packbits()` 函数,将二进制数值数组的元素打包成一个 `unit8` 数组中的位。\n", - "具体操作是通过在数组中的整数末尾插入 `0` ,使输出结果被填充为完整字节。" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[[160],\n", - " [ 64]],\n", - "\n", - " [[192],\n", - " [ 32]]], dtype=uint8)" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = np.array([[[1,0,1],\n", - " [0,1,0]],\n", - " [[1,1,0],\n", - " [0,0,1]]])\n", - "b = np.packbits(a, axis=-1)\n", - "b" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`160` (base 10) = `1010 0000` ;\n", - "`64` (base 10) = `0100 0000` ;\n", - "`192` (base 10) = `1100 0000` ;\n", - "`32` (base 10) = `0010 0000` 。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`unpackbits()` 函数,将 `uint8` 数组的元素解压缩到二进制值的输出数组中。\n", - "\n", - "每个元素都表示一个应解压缩到二进制值输出数组中的位字段。输出数组的形状可以是一维的(如果 `axis=None` ),也可以是与输入数组相同的形状,并沿着指定的轴进行解压。" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0, 0, 0, 0, 0, 0, 1, 0],\n", - " [0, 0, 0, 0, 0, 1, 1, 1],\n", - " [0, 0, 0, 1, 0, 1, 1, 1]], dtype=uint8)" - ] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = np.array([[2], [7], [23]], dtype=np.uint8)\n", - "b = np.unpackbits(a, axis=1)\n", - "b" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0, 0, 0, 0, 0, 0],\n", - " [0, 0, 0, 0, 0, 1],\n", - " [0, 0, 0, 1, 0, 1]], dtype=uint8)" - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c = np.unpackbits(a, axis=1, count=6)\n", - "c" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.输出格式\n", - "`binary_repr(num, width=None)` 函数用于将输入数组的整数以二进制格式输出。" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'101'" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5)" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'-101'" - ] - }, - "execution_count": 59, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(-5)" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'00101'" - ] - }, - "execution_count": 60, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5, width=5)" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'0101'" - ] - }, - "execution_count": 61, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.binary_repr(5, width=4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 统计函数\n", - "不同于一元函数和二元函数,对数组进行元素级运算,统计函数对一个数组行、列及整体运算。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```{note}\n", - "axis=0/1 ,该参数不指定时默认求整体;axis=0 对列计算; axis=1 对行计算 (下面不再演示)。\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "|函数|用途|\n", - "|:---:|:---|\n", - "|[`_.mean()`]|对数组整体求平均值|\n", - "|[`_.sum()`]|数组求和|\n", - "|[`_.max()`]|求数组中最大值|\n", - "|[`_.min()`]|求数组中最小值|\n", - "|[`_.var()`]|数组整体求方差(默认情况下,对数组整体求解)|\n", - "|[`_.np.std()`]|对数组整体求标准误|" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 线性代数" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 1.矩阵与向量的乘积\n", - "#### 点积\n", - "`dot(a,b,out=None)` 计算两个数组的点积:\n", - " 1)如果 `a` 和 `b` 都是一维数组,那么点积即为内积(没有共轭);" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "12" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.dot(3, 4)" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(-12+0j)" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.dot(3j, 4j)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 2)如果`a` 和 `b` 都是二维数组,那么使用矩阵乘法 `a @ b` ;" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[4, 1],\n", - " [2, 2]])" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = [[1, 0], [0, 1]]\n", - "b = [[4, 1], [2, 2]]\n", - "np.dot(a, b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 3)如果`a` 或 `b` 是标量(维数为`0`),那么其点积等价于乘,可以使用 `numpy.multiply(a,b)` 或 `a*b` ;" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[16, 4],\n", - " [ 8, 8]])" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = 4\n", - "b = [[4,1],[2,2]]\n", - "np.dot(a,b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 4)如果`a` 是一维数组, `b` 是`N`维数组,那么点积是 `a` 与 `b` 的最后一个轴的积的和;" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([54, 66, 78])" - ] - }, - "execution_count": 66, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = [3,4,5]\n", - "b = [[1,2,3],[4,5,6],[7,8,9]]\n", - "np.dot(a,b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " 5)如果`a` 是`N`维数组, `b` 是`M-d`维数组(M≥2),那么点积为 `a` 的最后一个轴与 `b` 的倒数第二条轴的乘机的和:\n", - " `dot(a,b)[i,j,k,m] = sum(a[i,j,:]*b[k,:,m])`" + "下面用一个例子作为示范,使用 `==` 对数组 `x` , `y` 元素级进行比较是否相等,相等输出结果 `True` ,若元素对应不相等,则输出结果 `False`。" ] }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([[ 70, 83, 96],\n", - " [ 60, 72, 84],\n", - " [ 90, 108, 126],\n", - " [106, 128, 150]])" + "array([False, False, False, False])" ] }, - "execution_count": 67, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "a = [[1,5,7],[2,4,6],[3,6,9],[4,8,10]]\n", - "b = [[1,2,3],[4,5,6],[7,8,9]]\n", - "np.dot(a,b)" + "x==y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "`linalg.multi_dot(arrays, *, out=None)`函数,在单个函数调用中计算两个或多个数组的点积,同时自动选择最快的求值顺序。\n", - "使用该函数时需要注意的是,如果第一个参数是一维数组,它被视为行向量。如果最后一个参数是一维数组,则将其视为列向量。\n", - "其他参数必须是二维的。" + "## 统计函数\n", + "\n", + "一元函数和二元函数对数组进行元素级运算,统计函数对高维数组的某个维度(行或列)及整体进行统计运算。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "下面我们通过一个例子学习使用 `linalg.multi_dot(arrays, *, out=None)` 函数:" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": {}, - "outputs": [], - "source": [ - "from numpy.linalg import multi_dot" + "```{note}\n", + "`axis` 参数用来指定对高维数组的哪个维度(或者说是轴)进行操作,不指定时默认求整体;`axis=0` 对高维数组的第一个维度进行计算,二维数组的 `axis=0` 指的是列; `axis=1` 对高维数组的第二个维度进行计算,二维数组的 `axis=1` 指的是行。\n", + "```" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0, 1, 2],\n", + " [3, 4, 5]])" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "A = np.random.random((10000, 100))\n", - "B = np.random.random((100, 1000))\n", - "C = np.random.random((1000, 5))\n", - "D = np.random.random((5, 333))" + "np_array_2d = np.arange(0, 6).reshape([2,3])\n", + "np_array_2d" ] }, { @@ -2150,19 +375,7 @@ { "data": { "text/plain": [ - "array([[35131.61158413, 18613.23337998, 29691.28900539, ...,\n", - " 43075.11560213, 40250.13158911, 39312.73602514],\n", - " [36036.48526667, 19110.88903417, 30466.94168399, ...,\n", - " 44205.74956408, 41292.42642951, 40333.49163259],\n", - " [34243.44514877, 18141.41363515, 28936.18893771, ...,\n", - " 41983.05410692, 39230.89345756, 38314.28093744],\n", - " ...,\n", - " [37884.92498048, 20081.63437724, 32022.58975952, ...,\n", - " 46459.14260604, 43406.86342706, 42398.11184878],\n", - " [40065.5968624 , 21243.13668627, 33867.12057961, ...,\n", - " 49142.06395326, 45907.32213681, 44839.35873359],\n", - " [35720.22531001, 18934.20066418, 30191.67932084, ...,\n", - " 43809.94871673, 40930.89160805, 39985.77263399]])" + "array([3, 5, 7])" ] }, "execution_count": 70, @@ -2171,17 +384,7 @@ } ], "source": [ - "_ = multi_dot([A, B, C, D])\n", - "_" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们也可以使用 `vdot(a,b,/)` 函数,返回两个向量的点积。\n", - "需要注意的是,`vdot` 处理多维数组的方式与 `dot` 不同:`vdot`不执行矩阵乘积,而是首先将输入参数扁平化为一维向量。\n", - "因此,`vdot`只能用于向量。" + "np.sum(np_array_2d, axis = 0)" ] }, { @@ -2192,7 +395,7 @@ { "data": { "text/plain": [ - "(70-8j)" + "15" ] }, "execution_count": 71, @@ -2201,9 +404,46 @@ } ], "source": [ - "a = np.array([1+2j,3+4j])\n", - "b = np.array([5+6j,7+8j])\n", - "np.vdot(a, b)" + "np.sum(np_array_2d)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "其他统计函数还有:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "|函数|用途|\n", + "|:---:|:---|\n", + "|[`np.mean()`]|求平均值|\n", + "|[`np.sum()`]|求和|\n", + "|[`np.max()`]|求最大值|\n", + "|[`np.min()`]|求最小值|\n", + "|[`np.var()`]|求方差|\n", + "|[`np.std()`]|求标准误|" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 线性代数" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 矩阵与向量的乘积\n", + "\n", + "`dot(a,b)` 计算两个数组的点积:\n", + "\n", + "1)如果 `a` 和 `b` 都是一维数组,那么点积即为内积(没有共轭);" ] }, { @@ -2214,7 +454,7 @@ { "data": { "text/plain": [ - "(70+8j)" + "12" ] }, "execution_count": 72, @@ -2223,14 +463,7 @@ } ], "source": [ - "np.vdot(b, a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "当 `a` 和 `b` 是更高维度的数组时,输出结果依旧是扁平化的一维向量:" + "np.dot(3, 4)" ] }, { @@ -2241,7 +474,7 @@ { "data": { "text/plain": [ - "70" + "(-12+0j)" ] }, "execution_count": 73, @@ -2250,9 +483,14 @@ } ], "source": [ - "a = np.array([[1, 2], [3, 4]])\n", - "b = np.array([[5, 6], [7, 8]])\n", - "np.vdot(a, b)" + "np.dot(3j, 4j)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " 2)如果 `a` 和 `b` 都是二维数组,`np.dot()` 等价于矩阵乘法,也可以用 `a @ b` 来表示;" ] }, { @@ -2263,7 +501,8 @@ { "data": { "text/plain": [ - "70" + "array([[4, 1],\n", + " [2, 2]])" ] }, "execution_count": 74, @@ -2272,7 +511,16 @@ } ], "source": [ - "np.vdot(b, a)" + "a = [[1, 0], [0, 1]]\n", + "b = [[4, 1], [2, 2]]\n", + "np.dot(a, b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " 3)如果 `a` 或 `b` 是标量(维数为 0),那么其点积等价于乘,可以使用 `numpy.multiply(a,b)` 或 `a*b` 来表示;" ] }, { @@ -2283,7 +531,8 @@ { "data": { "text/plain": [ - "70" + "array([[16, 4],\n", + " [ 8, 8]])" ] }, "execution_count": 75, @@ -2292,115 +541,105 @@ } ], "source": [ - "1*5 + 2*6 + 3*7 + 4*8" - ] - }, - { - "attachments": { - "1707065960872.png": { - "image/png": "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" - } - }, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2.矩阵特征值\n", - "假设我们有一个 `n` 阶的矩阵 `A` 以及一个实数 `λ` ,使得我们可以找到一个非零向量 `x` ,满足:\n", - "\n", - "![1707065960872.png](attachment:1707065960872.png)\n", - "\n", - "如果能够找到的话,我们就称 `λ` 是矩阵 `A` 的特征值,非零向量 `x` 是矩阵 `A` 的特征向量。" + "a = 4\n", + "b = [[4,1],[2,2]]\n", + "np.dot(a,b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们可以使用 `linalg.eig(a)` 函数计算方阵的特征值(eigenvalues)和特征向量(eigenvectors)。" + " 4)如果`a` 是一维数组, `b` 是`N`维数组,那么点积是 `a` 与 `b` 的最后一个轴的积的和;" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, - "outputs": [], - "source": [ - "from numpy import linalg as LA" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([1., 2., 3.])" + "array([54, 66, 78])" ] }, - "execution_count": 77, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "eigenvalues, eigenvectors = LA.eig(np.diag((1, 2, 3)))\n", - "eigenvalues" + "a = [3,4,5]\n", + "b = [[1,2,3],[4,5,6],[7,8,9]]\n", + "np.dot(a,b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " 5)如果`a` 是`N`维数组, `b` 是`M-d`维数组(M≥2),那么点积为 `a` 的最后一个轴与 `b` 的倒数第二条轴的乘机的和:\n", + " `dot(a,b)[i,j,k,m] = sum(a[i,j,:]*b[k,:,m])`" ] }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([[1., 0., 0.],\n", - " [0., 1., 0.],\n", - " [0., 0., 1.]])" + "array([[ 70, 83, 96],\n", + " [ 60, 72, 84],\n", + " [ 90, 108, 126],\n", + " [106, 128, 150]])" ] }, - "execution_count": 78, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "eigenvectors" + "a = [[1,5,7],[2,4,6],[3,6,9],[4,8,10]]\n", + "b = [[1,2,3],[4,5,6],[7,8,9]]\n", + "np.dot(a,b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们还可以使用 `linalg.eigvals(a)` 函数计算一般矩阵的特征值。\n", - "与 `eig` 函数不同的是, `eigvals` 不返回特征向量。" + "### 矩阵特征值\n", + "\n", + "假设我们有一个 `n` 阶的矩阵 `A` 以及一个实数 `λ` ,使得我们可以找到一个非零向量 `x` ,满足:$Ax = \\lambda x$,如果能够找到的话,我们就称 `λ` 是矩阵 `A` 的特征值,非零向量 `x` 是矩阵 `A` 的特征向量。我们可以使用 `np.linalg.eig(a)` 函数计算方阵的特征值和特征向量。" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 83, "metadata": {}, + "outputs": [], "source": [ - "### 3.范数(norm)和秩(rank)\n", - "在线性代数中, 范数(norm)是一个表示“长度”概念的函数,为向量空间内的所有向量赋予非零的正长度或大小。" + "eigenvalues, eigenvectors = np.linalg.eig(np.diag((1, 2, 3)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们可以使用 `linalg.norm(x)` 函数计算矩阵或者向量的范数。" + "我们还可以使用 `np.linalg.eigvals(a)` 函数计算一般矩阵的特征值。\n", + "与 `np.linalg.eig()` 函数不同的是, `np.linalg.eigvals()` 不返回特征向量。" ] }, { - "cell_type": "code", - "execution_count": 79, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "from numpy import linalg as LA" + "### 范数(norm)和秩(rank)\n", + "在线性代数中, 范数(norm)是一个表示“长度”概念的函数,为向量空间内的所有向量赋予非零的正长度或大小。" ] }, { @@ -2412,7 +651,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 84, "metadata": {}, "outputs": [ { @@ -2423,7 +662,7 @@ " [6, 7, 8]])" ] }, - "execution_count": 80, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -2436,7 +675,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 85, "metadata": {}, "outputs": [ { @@ -2445,13 +684,13 @@ "14.2828568570857" ] }, - "execution_count": 81, + "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "LA.norm(a)" + "np.linalg.norm(a)" ] }, { @@ -2459,28 +698,12 @@ "metadata": {}, "source": [ "在线性代数中,一个矩阵 `A` 的列秩是 `A` 的线性独立的纵列的极大数,通常表示为 `r(A)` ,`rk(A)` 或 `rank A` 。\n", - "行秩是 `A` 的线性无关的横行的极大数目。即如果把矩阵看成一个个行向量或者列向量,秩就是这些行向量或者列向量的秩,也就是极大无关组中所含向量的个数。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们可以使用 `linalg.matrix_rank(A)` 函数,使用 SVD 方法计算并返回数组的矩阵秩。" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "metadata": {}, - "outputs": [], - "source": [ - "from numpy.linalg import matrix_rank" + "行秩是 `A` 的线性无关的横行的极大数目。即如果把矩阵看成一个个行向量或者列向量,秩就是这些行向量或者列向量的秩,也就是极大无关组中所含向量的个数。我们可以使用 `np.linalg.matrix_rank(A)` 函数,使用 SVD 方法计算并返回数组的矩阵秩。" ] }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 86, "metadata": {}, "outputs": [ { @@ -2489,13 +712,13 @@ "2" ] }, - "execution_count": 83, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "matrix_rank(a) " + "np.linalg.matrix_rank(a) " ] }, { @@ -2507,7 +730,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 87, "metadata": {}, "outputs": [ { @@ -2519,7 +742,7 @@ " [0., 0., 0., 1.]])" ] }, - "execution_count": 84, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -2529,74 +752,23 @@ "b" ] }, - { - "cell_type": "code", - "execution_count": 85, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "4" - ] - }, - "execution_count": 85, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "matrix_rank(b) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4.解方程与逆" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (1)方程求解" - ] - }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们可以使用 `linalg.solve(a, b)` 函数求解线性矩阵方程或线性标量方程组。" + "### 解方程与逆" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "首先,我们生成矩阵 `a` 和 `b` 用于求解练习:" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "metadata": {}, - "outputs": [], - "source": [ - "a = np.array([[1, 2], [7, 9]])\n", - "b = np.array([1, 2])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "根据生成的矩阵中元素,我们可以列出所求解的方程组为 `x0+2*x1=1` 和 `7*x0+9*x1=2` 。" + "我们可以使用 `np.linalg.solve(a, b)` 函数求解线性矩阵方程或线性标量方程组。" ] }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 88, "metadata": {}, "outputs": [ { @@ -2605,12 +777,14 @@ "array([-1., 1.])" ] }, - "execution_count": 87, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "a = np.array([[1, 2], [7, 9]])\n", + "b = np.array([1, 2])\n", "x = np.linalg.solve(a, b)\n", "x" ] @@ -2619,26 +793,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### (2)矩阵求逆\n", - "设 `A` 是数域上的一个 `n` 阶方阵,若在相同数域上存在另一个 `n` 阶矩 `B` ,使得: `AB=BA=E` 。 \n", - "则我们称 `B` 是 `A` 的逆矩阵, `A` 则被称为可逆矩阵。\n", - "其中,`E` 为单位矩阵" + "根据生成的矩阵中元素,我们可以列出所求解的方程组为 `x0+2*x1=1` 和 `7*x0+9*x1=2` 。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "我们可以使用 `linalg.inv(a)` 函数对矩阵 `a` 求逆。" + "设 `A` 是数域上的一个 `n` 阶方阵,若在相同数域上存在另一个 `n` 阶矩 `B` ,使得: `AB=BA=E` 。则我们称 `B` 是 `A` 的逆矩阵, `A` 则被称为可逆矩阵。\n", + "其中,`E` 为单位矩阵" ] }, { - "cell_type": "code", - "execution_count": 88, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "from numpy.linalg import inv" + "我们可以使用 `np.linalg.inv(a)` 函数对矩阵 `a` 求逆。" ] }, { @@ -2649,8 +819,8 @@ { "data": { "text/plain": [ - "array([[1, 2],\n", - " [3, 4]])" + "array([[-2. , 1. ],\n", + " [ 1.5, -0.5]])" ] }, "execution_count": 89, @@ -2661,28 +831,7 @@ "source": [ "b = np.arange(4) +1\n", "a = b.reshape((2,2))\n", - "a" - ] - }, - { - "cell_type": "code", - "execution_count": 90, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[-2. , 1. ],\n", - " [ 1.5, -0.5]])" - ] - }, - "execution_count": 90, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ainv = inv(a)\n", + "ainv = np.linalg.inv(a)\n", "ainv" ] } @@ -2703,7 +852,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/ch-numpy/unary-ops.md b/ch-numpy/unary-ops.md deleted file mode 100644 index 39a4913..0000000 --- a/ch-numpy/unary-ops.md +++ /dev/null @@ -1 +0,0 @@ -# 一元函数 \ No newline at end of file diff --git a/conf.py b/conf.py index bbf9bed..8597296 100644 --- a/conf.py +++ b/conf.py @@ -40,7 +40,8 @@ "toc_title": "本节目录", } html_title = 'Python 数据科学实战' - +html_static_path = ["_static"] +html_css_files = ["custom.css"] html_js_files = [ "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js", ] diff --git a/logo.png b/logo.png index 06d56f40c838b64eb048a63e036125964a069a3a..6d6e5aeb000408ba324bee73f15ab5274cae0bb2 100644 GIT binary patch literal 61251 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 literal 9854 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 L>8MsHTOs~GC;Fmw