《Python数据分析基础教程:NumPy学习指南(第2版)》所有章节阅读笔记+代码
70 道 NumPy 面试题
1. 将 NumPy 导入为 np,并查看版本
问题:将 NumPy 导入为 np,并输出版本号。
1
2
import numpy as np
np.__version__
2.如何创建 1 维数组?
问题:创建数字从 0 到 9 的 1 维数组。
1
2
n = np.arange(10)
n
3.如何创建 boolean 数组?
问题:创建所有 True 的 \(3×3\) NumPy 数组。
1
2
n = np.full((3, 3), True)
n
4. 如何从 1 维数组中提取满足给定条件的项?
问题:从 arr 中提取所有奇数。
输入:
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])`
期望输出:
#> array([1, 3, 5, 7, 9])
方法1:np.where
1
2
n = np.arange(10)
np.where(n % 2 == 1)
方法2:使用布尔索引
注:布尔索引 是最常用且效率最高的方法。它利用 NumPy 数组的广播特性,直接通过条件选择满足条件的元素
1
2
n = np.arange(10)
n[n % 2 == 1]
当然也可以用np.mod()
方法:
1
2
3
n = np.arange(10)
oddNum = n[np.mod(n, 2) == 1]
oddNum
方法3:条件语句和循环(不推荐)
1
2
3
4
5
6
n = np.arange(10)
odd_num = [] # 列表是可变的数据类型
for i in n:
if i % 2 == 1:
odd_num.append(i)
odd_num
方法4:filter()
函数与 lambda 表达式
1
2
3
n = np.arange(10)
odd = list(filter(lambda x: x % 2 == 1, n))
odd
5. 如何将 NumPy 数组中满足给定条件的项替换成另一个数值?
问题:将 arr 中的所有奇数替换成 -1。
输入:
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
期望输出:
#> array([ 0, -1, 2, -1, 4, -1, 6, -1, 8, -1])
和第四题思想类似,只是多了一步赋值。
1
2
3
n = np.arange(10)
n[n % 2 == 1] = -1
n
6.如何在不影响原始数组的前提下替换满足给定条件的项?
问题:将 arr 中所有奇数替换成 -1,且不改变 arr。
输入:
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
期望输出:
out #> array([ 0, -1, 2, -1, 4, -1, 6, -1, 8, -1]) arr #> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
方法1:使用copy()
函数
1
2
3
n = np.arange(10)
n1 = n.copy()
n1[n1 % 2 == 1] = -1
方法2:np.where()
1
2
3
4
n = np.arange(10)
out = np.where(n % 2 == 1, -1, n)
display(out)
display(n)
方法3:np.vectorize
是一种高效的方式来对数组元素进行逐个操作,实际上它是对每个元素应用一个函数。
1
2
3
4
5
6
7
8
9
10
arr = np.arange(10)
def replace_minus_1_if_odd(x):
return -1 if x % 2 == 1 else x
vectorized_replace_minus_1_if_odd = np.vectorize(replace_minus_1_if_odd)
out_arr = vectorized_replace_minus_1_if_odd(arr)
display(arr)
display(out_arr)
7. 如何重塑(reshape)数组?
问题:将 1 维数组转换成 2 维数组(两行)。
输入:
np.arange(10) #> array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
期望输出
#> array([[0, 1, 2, 3, 4], #> [5, 6, 7, 8, 9]])
1
2
n = np.arange(10)
n.reshape(2, -1)
8. 如何垂直堆叠两个数组?
问题:垂直堆叠数组 a 和 b。
输入:
a = np.arange(10).reshape(2,-1) b = np.repeat(1, 10).reshape(2,-1)
期望输出:
#> array([[0, 1, 2, 3, 4], #> [5, 6, 7, 8, 9], #> [1, 1, 1, 1, 1], #> [1, 1, 1, 1, 1]])
注:本题不适合用
np.stack
,np.stack
主要是用于堆叠数组在新的轴上(【增加维度】),但也可以通过指定axis=0
来实现垂直堆叠。这个方法与np.concatenate
类似,但会返回一个新的维度。(会导致返回的是3维)
方法一:使用np.concatenate
:
1
2
3
a = np.arange(10).reshape(2, -1)
b = np.repeat(1, 10).reshape(2, -1)
np.concatenate((a, b)) # 默认axis就是0
方法2:np.vstack
1
2
3
a = np.arange(10).reshape(2, -1)
b = np.repeat(1, 10).reshape(2, -1)
np.vstack((a, b))
方法3:np.row_stack
1
np.row_stack((a, b))
方法4:np.append
也可以用于堆叠数组,不过它通常用于将一个数组的元素添加到另一个数组的末尾。要垂直堆叠,必须设置 axis=0
。
1
np.append(a, b, axis = 0)
方法5:np.insert
允许你将一个数组插入到另一个数组的指定位置。我们可以利用它将 b
插入到 a
的末尾,达到垂直堆叠的效果。
注:
a.shape[0]
是a
的行数,表示插入到a
的最后。
1
np.insert(a, a.shape[0], b, axis = 0)
9. 如何水平堆叠两个数组?
问题:水平堆叠数组 a 和 b。
输入:
a = np.arange(10).reshape(2,-1) b = np.repeat(1, 10).reshape(2,-1)
期望输出:
#> array([[0, 1, 2, 3, 4, 1, 1, 1, 1, 1], #> [5, 6, 7, 8, 9, 1, 1, 1, 1, 1]])
本题和上一题类似:
方法1:
1
np.concatenate((a, b), axis = 1)
方法2:
1
np.hstack((a, b))
方法3:
1
np.column_stack((a, b))
方法4:
1
np.append(a, b, axis = 1)
本题insert无法完成需求。
10. 在不使用硬编码的前提下,如何在 NumPy 中生成自定义序列?
问题:在不使用硬编码的前提下创建以下模式。仅使用 NumPy 函数和以下输入数组 a。
输入
a = np.array([1,2,3])`
期望输出:
#> array([1, 1, 1, 2, 2, 2, 3, 3, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3])
1
2
3
4
5
a = np.array([1, 2, 3])
b = np.tile(a, (3, 1)) # 重复三行
display(b)
out = np.append(b.T.flatten(),b.flatten())
out
11. 如何获得两个 Python NumPy 数组中共同的项?
问题:获取数组 a 和 b 中的共同项。
输入:
a = np.array([1,2,3,2,3,4,3,4,5,6]) b = np.array([7,2,10,2,7,4,9,4,9,8])
期望输出:
array([2, 4])
方法1:
1
2
3
a = np.array([1,2,3,2,3,4,3,4,5,6])
b = np.array([7,2,10,2,7,4,9,4,9,8])
np.intersect1d(a, b)
方法2:
1
np.array(list(set(a) & set(b)))
方法3:
1
np.unique(a[np.isin(a, b)]) # np.unique去重
注:
a[np.isin(a, b)]
返回的是array([2, 2, 4, 4])
12. 如何从一个数组中移除与另一个数组重复的项?
问题:从数组 a 中移除出现在数组 b 中的所有项。
输入:
a = np.array([1,2,3,4,5]) b = np.array([5,6,7,8,9])
期望输出:
array([1,2,3,4])
方法1:使用 np.isin()
函数和布尔索引
1
2
3
4
5
a = np.array([1,2,3,4,5])
b = np.array([5,6,7,8,9])
out = a[~np.isin(a, b)]
out
方法2:numpy.setdiff1d()
返回 a
中存在但不在 b
中的元素,这个方法也是专门为这种差集操作设计的。
1
2
res = np.setdiff1d(a, b)
res
方法3:使用纯 Python,也可以通过列表推导式来实现:
1
2
out_res = np.array([x for x in a if x not in b])
out_res
13. 如何获取两个数组匹配元素的位置?
问题:获取数组 a 和 b 中匹配元素的位置。
输入:
a = np.array([1,2,3,2,3,4,3,4,5,6]) b = np.array([7,2,10,2,7,4,9,4,9,8])
期望输出:
#> (array([1, 3, 5, 7]),)
1
2
3
a = np.array([1,2,3,2,3,4,3,4,5,6])
b = np.array([7,2,10,2,7,4,9,4,9,8])
np.where(np.isin(a, b))
14.如何从 NumPy 数组中提取给定范围内的所有数字?
问题:从数组 a 中提取 5 和 10 之间的所有项。
输入:
a = np.arange(15)
期望输出:
(array([ 5, 6, 7, 8, 9, 10]),)
1
2
a = np.arange(15)
np.where((a >= 5) & (a <= 10))
15.如何创建一个 Python 函数以对 NumPy 数组执行元素级的操作?
问题:转换函数 maxx,使其从只能对比标量而变为对比两个数组。
输入:
def maxx(x, y): “"”Get the maximum of two items””” if x >= y: return x else: return y
maxx(1, 5) #> 5
期望输出:
a = np.array([5, 7, 9, 8, 6, 4, 5]) b = np.array([6, 3, 4, 8, 9, 7, 1]) pair_max(a, b) #> array([ 6., 7., 9., 8., 9., 7., 5.])
1
2
3
4
5
6
7
8
9
10
11
12
13
def maxx(x, y):
"""Get the maximum of two items"""
if x >= y:
return x
else:
return y
pair_max = np.vectorize(maxx)
a = np.array([5, 7, 9, 8, 6, 4, 5])
b = np.array([6, 3, 4, 8, 9, 7, 1])
pair_max(a, b)
16.如何在 2d NumPy 数组中交换两个列?
问题:在数组 arr 中交换列 1 和列 2。
arr = np.arange(9).reshape(3,3) arr
1
2
3
4
arr = np.arange(9).reshape(3,3)
display(arr)
arr[:, [0, 1]] = arr[:, [1, 0]]
display(arr)
注:
arr[:, [0, 1]]
表示选择所有行的第一列和第二列。
arr[:, [1, 0]]
表示选择所有行的第二列和第一列。
17.如何在 2d NumPy 数组中交换两个行?
问题:在数组 arr 中交换行 1 和行 2。
arr = np.arange(9).reshape(3,3) arr
1
2
3
4
arr = np.arange(9).reshape(3,3)
display(arr)
out = arr[[1, 0, 2]]
display(out)
18.如何反转 2D 数组的所有行?
问题:反转 2D 数组 arr 中的所有行。
# Input arr = np.arange(9).reshape(3,3)
1
2
3
arr = np.arange(9).reshape(3,3)
display(arr)
display(arr[::-1])
19.如何反转 2D 数组的所有列?
问题:反转 2D 数组 arr 中的所有列。
# Input arr = np.arange(9).reshape(3,3)
1
2
3
arr = np.arange(9).reshape(3,3)
display(arr)
arr[::,::-1]
20.如何创建一个包含 5 和 10 之间随机浮点的 2 维数组?
问题:创建一个形态为 5×3 的 2 维数组,包含 5 和 10 之间的随机十进制小数。
方法1:np.random.uniform(low, high, size)
可以生成在 low
和 high
之间均匀分布的随机小数。
1
2
# 创建一个形态为 5×3 的 2 维数组,包含 5 和 10 之间的随机十进制小数。
np.random.uniform(5, 10, (5, 3))
方法2:
1
2
3
5 + (10 - 5)*np.random.rand(5, 3)
或者
5 + (10 - 5)*np.random.random((5, 3))
21.如何在 Python NumPy 数组中仅输出小数点后三位的数字?
问题:输出或显示 NumPy 数组 rand_arr
中小数点后三位的数字。
输入:
rand_arr = np.random.random((5,3))
注:np.round
不合适,因为会四舍五入。
1
2
3
4
5
6
rand_arr = np.random.random((5,3))
display(rand_arr)
# 使用列表推导和字符串格式化来控制小数点后三位
formatted_arr = [[f"{val:.3f}" for val in row] for row in rand_arr]
print(formatted_arr)
22.如何通过禁用科学计数法(如 1e10)打印 NumPy 数组?
问题:通过禁用科学计数法(如 1e10)打印 NumPy 数组 rand_arr
。
输入:
# Create the random array np.random.seed(100) rand_arr = np.random.random([3,3])/1e3 rand_arr
#> array([[ 5.434049e-04, 2.783694e-04, 4.245176e-04], #> [ 8.447761e-04, 4.718856e-06, 1.215691e-04], #> [ 6.707491e-04, 8.258528e-04, 1.367066e-04]])
期望输出:
#> array([[ 0.000543, 0.000278, 0.000425], #> [ 0.000845, 0.000005, 0.000122], #> [ 0.000671, 0.000826, 0.000137]])
补充:NumPy有一个
set_printoptions
函数,可以用来设置打印选项。这个函数可以控制浮点数的显示格式。比如,可以通过设置suppress参数为True,来禁用科学记数法。或者,也可以设置precision参数来指定小数点后的位数。需要在打印数组之前,调用这个
set_printoptions
函数。比如,先设置打印选项,然后打印数组。或者,直接在打印的时候,使用格式化字符串来控制显示方式。
1
2
np.set_printoptions(suppress=True)
rand_arr
注,如果要指定小数点后的位数,可以
np.set_printoptions(suppress=True, precision=6)
23. 如何限制 NumPy 数组输出中项的数目?
问题:将 Python NumPy 数组 a 输出的项的数目限制在最多 6 个元素(输出是只显示前6个和最后6个元素,中间用省略号表示)。
输入:
a = np.arange(15) #> array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
期望输出:
#> array([ 0, 1, 2, …, 12, 13, 14])
另外补充:(与本题无关)NumPy提供了一个函数
np.set_printoptions
,可以用来设置数组打印时的各种选项,比如抑制科学计数法、设置精度(参考上一题)等。其中,有一个参数叫做’linewidth’,可以控制每行显示的字符数,但这可能不是直接解决元素数量的问题。补充:另一个参数’edgeitems’,这个参数控制在打印数组时,显示的最前面和最后面的元素数目。默认情况下,edgeitems的值是3,所以会显示前面3个和后面3个元素,中间用省略号连接。如果我将edgeitems设置为6,那么应该可以显示前面6个和后面6个元素,中间用省略号表示。
但是我设置了
edgeitems
,却没有生效。通过打印np.get_printoptions()
,内容如下,发现问题:
edgeitems
控制的是数组两端显示的元素数,通常是在数组元素数目超过threshold
时才会生效。当数组的元素总数小于等于threshold
时,edgeitems
设置不起作用。也就是说,edgeitems
只有在数组超过threshold
元素时才会起作用。所以,这道题最终应该这么解决:
1
2
np.set_printoptions(threshold = 6, edgeitems = 3)
a
24. 如何在不截断数组的前提下打印出完整的 NumPy 数组?
问题:在不截断数组的前提下打印出完整的 NumPy 数组 a。
输入:
np.set_printoptions(threshold=6) a = np.arange(15) a #> array([ 0, 1, 2, …, 12, 13, 14])
期望输出:
a #> array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
要打印出完整的 NumPy 数组,同时不截断其中的元素,最简单的办法是将 threshold
设置为一个非常大的值(例如1000),确保 NumPy 在打印数组时不会对元素进行省略。
1
2
np.set_printoptions(threshold=np.inf)
a
25.如何向 Python NumPy 导入包含数字和文本的数据集,同时保持文本不变?
问题:导入 iris 数据集,保持文本不变。
要导入 iris
数据集,并保持文本不变,你可以使用 sklearn.datasets
中的 load_iris
方法来加载数据集。load_iris()
会返回一个包含 iris
数据集的字典对象,其中包括特征数据、目标值以及数据集的描述信息。
1
2
3
4
5
6
7
from sklearn.datasets import load_iris
# 加载 Iris 数据集
iris = load_iris()
# 打印文本描述保持不变
print(iris['DESCR'])
数据集内容:
- 该数据集包含 150 条记录,每条记录有 4 个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)。
- 目标变量有 3 个类别:
setosa
、versicolor
和virginica
,对应的数字标签分别是 0、1 和 2。
26.如何从 1 维元组数组中提取特定的列?
问题:从前一个问题导入的 1 维 iris 中提取文本列 species。
输入:
1
2
url = https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data
iris_1d = np.genfromtxt(url, delimiter= , , dtype=None)
首先注意iris_1d
:
1
2
3
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_1d = np.genfromtxt(url, delimiter=",", dtype=None, encoding=None)
iris_1d
文本列就是最后一列,所以:
1
2
3
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_1d = np.genfromtxt(url, delimiter=",", dtype=None, encoding=None, usecols=[4])
iris_1d
usecols=[4]
:指定提取数据集中的第 5 列(索引从 0 开始,第四列的索引为 4),即 species
列。
dtype=None
:让 numpy
自动推断每列的类型,特别是最后一列 species
,它应该被正确识别为字符串。
27. 如何将 1 维元组数组转换成 2 维 NumPy 数组?
问题:忽略 species 文本字段,将 1 维 iris 转换成 2 维数组 iris_2d
。
输入:
1
2
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_1d = np.genfromtxt(url, delimiter= , , dtype=None)
参考:
1
2
3
4
5
# 使用 np.loadtxt 导入 iris 数据集,确保按列解析
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.loadtxt(url, delimiter=',', dtype='str')
iris_2d
但是有第5列。
所以:
1
2
3
4
5
# 使用 np.loadtxt 导入 iris 数据集,确保按列解析
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.loadtxt(url, delimiter=',', dtype='str', usecols=range(4))
iris_2d
28.如何计算 NumPy 数组的平均值、中位数和标准差?
问题:找出 iris sepallength(第一列)的平均值、中位数和标准差。
1
2
3
4
5
6
col1 = iris_2d[:,0] # 第一列
col1 = col1.astype(float) # 转换成浮点数
# 找出 iris sepallength(第一列)的平均值、中位数和标准差。
print("均值:", np.mean(col1))
print("中位数:", np.median(col1))
print("标准差:", np.std(col1))
29.如何归一化数组,使值的范围在 0 和 1 之间?
问题:创建 iris sepallength 的归一化格式,使其值在 0 到 1 之间。
要将 iris
数据集中的 sepal length
列(即第一列)归一化到 0 到 1 的范围,可以使用 最小-最大归一化 方法。这个方法的公式为:\(Normalized \ Value = \frac{X-min(X)}{max(X)-min(X)}\)
1
2
3
4
iris_sepallength = iris_2d[:,0].astype(float)
min_ = np.min(iris_sepallength)
max_ = np.max(iris_sepallength)
(iris_sepallength - min_)/(max_ - min_)
30.如何计算 softmax 分数?
问题:计算 sepallength 的 softmax 分数。
补充:Softmax 是一种常用于分类任务的激活函数,通常用于将一个向量转换为一个概率分布,输出的值都在 [0, 1]
之间,并且总和为 1。Softmax 的计算公式如下:\(Softmax(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}}\)
1
2
3
4
5
6
def softmax(x):
exp = np.exp(x - np.max(x)) # 数值防止溢出
return exp/np.sum(exp)
iris_sepallength = iris_2d[:,0].astype(float)
softmax(iris_sepallength)
np.exp(x - np.max(x))
:为了防止计算中的数值溢出(即计算指数时可能遇到极大的值),我们通常减去 x
中的最大值。这是常见的数值稳定性技巧,它不会改变 softmax 输出的结果。
31.如何找到 NumPy 数组的百分数?
问题:找出 iris sepallength(第一列)的第 5 个和第 95 个百分数。
1
2
np.percentile(iris_2d[:,0].astype(float), 0.05)
np.percentile(iris_2d[:,0].astype(float), 0.95)
32.如何在数组的随机位置插入值?
问题:在 iris_2d
数据集中的 20 个随机位置插入 np.nan
值。
1
2
3
4
5
6
7
8
9
10
11
12
# 确定我们要插入 NaN 的位置:随机选择 20 个位置
np.random.seed(0) # 设置随机种子,确保结果可重复
random_indices = np.random.choice(iris_2d.size, 20, replace=False)
# 将这些位置的元素设置为 np.nan
iris_2d_flat = iris_2d.flatten() # 将二维数组展平成一维数组
iris_2d_flat[random_indices] = np.nan # 设置为 'nan' 字符串,因为数据是字符串类型
# 将一维数组重新转回二维数组
iris_2d = iris_2d_flat.reshape(iris_2d.shape)
iris_2d
33.如何在 NumPy 数组中找出缺失值的位置?
问题:在 iris_2d
的 sepallength(第一列)中找出缺失值的数目和位置。
查找 sepallength(第一列)中的缺失值(’nan’):(布尔索引)
1
2
sepal_length = (iris_2d=='nan')
sepal_length
然后统计总数:(因为True=1,False=0
)
1
np.sum(sepal_length)
最后要得到nan的位置:
1
2
index = np.where(sepal_length)[0]
index
34.如何基于两个或以上条件过滤 NumPy 数组?
问题:过滤 iris_2d
中满足 petallength(第三列)> 1.5 和 sepallength(第一列)< 5.0 的行。
1
2
iris_2d = iris_2d.astype(float)
iris_2d[(iris_2d[:, 2] > 1.5) & (iris_2d[:, 0] < 5.0)]
35.如何在 NumPy 数组中删除包含缺失值的行?
问题:选择 iris_2d
中不包含 nan 值的行。
首先注意:
1
np.isnan(iris_2d)
会返回一个布尔数组:
和刚在的逻辑相同:
1
np.where(bool_arr)[0]
那么这道题,需要对布尔数组取反。
1
2
notNan_arr = ~(np.isnan(iris_2d))
notNan_arr.all(axis = 1)
所以:
1
iris_2d[notNan_arr.all(axis = 1)]
36.如何找出 NumPy 数组中两列之间的关联性?
问题:找出 iris_2d
中 SepalLength(第一列)和 PetalLength(第三列)之间的关联性。
可以通过计算这两列之间的 相关系数 来衡量它们的关系。常用的相关系数是 Pearson 相关系数,它衡量了两者之间的线性关系。
Pearson 相关系数:值的范围是 -1 到 1,表示两个变量之间的线性关系。
- 1 表示完全正相关
- -1 表示完全负相关
- 0 表示没有线性相关性
使用 np.corrcoef
来计算两列之间的 Pearson 相关系数。
1
2
3
4
5
6
# 提取SepalLength(第一列)和 PetalLength(第三列)
sepalLength = iris_2d[0]
petalLength = iris_2d[2]
matrix = np.corrcoef(sepalLength, petalLength)
display(matrix)
print("相关系数是:", matrix[0, 1])
有非常强的正线性关系。
37.如何确定给定数组是否有空值?
问题:确定 iris_2d
是否有缺失值。
1
np.isnan(iris_2d).any()
只要有一个True(即有一个np.nan
)就会返回True.(这就是any()
的意义)
38.如何在 NumPy 数组中将所有缺失值替换成 0?
问题:在 NumPy 数组中将所有 nan 替换成 0。
首先做一份iris_2d
的拷贝
1
2
# 做一份拷贝,防止后续还要用到iris_2d
iris_2d_copy = iris_2d.copy()
然后将nan
变成0(赋值运算)
1
iris_2d[np.isnan(iris_2d)] = 0
39.如何在 NumPy 数组中找出唯一值的数量?
问题:在 iris 的 species 列中找出唯一值及其数量。
补充:在 iris
数据集中,species
列通常是数据集的 最后一列,它包含了每个样本的类别标签(即 Iris-setosa
、Iris-versicolor
、Iris-virginica
)。
此时因为一开始导入的时候没有导入最后一列,所以重新加载数据。(导入的是二维数组)
1
2
3
4
# 使用 np.loadtxt 导入 iris 数据集,确保按列解析
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris = np.loadtxt(url, delimiter=',', dtype='str')
iris
np.unique()
函数,它会返回唯一值和它们出现的次数。
1
np.unique(iris[:, -1])
但是想要统计数量(每个唯一值的出现次数),就要加上参数return_counts=True
1
np.unique(iris[:, -1], return_counts=True)
40.如何将一个数值转换为一个类别(文本)数组?
问题:将 iris_2d
的 petallength(第三列)转换以构建一个文本数组,按如下规则进行转换:
- Less than 3 –> ‘small’
- 3-5 –> medium
- >=5 –> large
1
2
3
4
# Input
url = https%3A//archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data
iris = np.genfromtxt(url, delimiter= , , dtype= object )
names = ( sepallength , sepalwidth , petallength , petalwidth , species )
首先注意:
1
2
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris = np.genfromtxt(url, delimiter=",", dtype=object )
使用 np.genfromtxt(url, delimiter=",", dtype=object)
导入数据时,数据前面带有一个 b
的原因是:指定了 dtype=object
,这会导致 NumPy 将所有的文本数据以字节串(bytes
)的形式加载,而不是普通的字符串(str
)。在 Python 中,字节串前面会有一个 b
前缀,表示该数据是字节类型(bytes
),而不是标准的 Unicode 字符串(str
)。
更加推荐dtype='str'
的读取方法。
1
2
3
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris = np.genfromtxt(url, delimiter=",", dtype='str')
iris
iris数据集的格式就是
1
<sepallength> <sepalwidth> <petallength> <petalwidth> <species>
现在需要对PetalLength:花瓣的长度
进行处理。
1
2
3
4
5
6
7
petal_length = iris[:, 2].astype(float)
display(petal_length)
petal_arr = np.empty(petal_length.size, dtype = 'U6') # dtype='str' 这里因为是空的,默认分配一个字符的大小
petal_arr[petal_length < 3] = 'small'
petal_arr[(3 <= petal_length) & (petal_length < 5)] = 'medium'
petal_arr[petal_length >= 5] = 'large'
petal_arr
41.如何基于 NumPy 数组现有列创建一个新的列?
问题:为 iris_2d
中的 volume 列创建一个新的列,volume 指 (pi x petallength x sepal_length^2)/3
。
注:iris数据集的格式是
1
<sepallength> <sepalwidth> <petallength> <petalwidth> <species>
解决代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 获取iris_2d
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.genfromtxt(url, delimiter=",", dtype='str')
# 提取 petallength 和 sepal_length 这2列
petallength = iris_2d[:, 2].astype(float)
sepal_length = iris_2d[:, 0].astype(float)
# 计算 volume = pi x petallength x sepal_length^2)/3
volume = (np.pi * petallength * (sepal_length ** 2))/3
# 添加一列
iris_2d = np.column_stack((iris_2d, volume))
display(iris_2d)
42.如何在 NumPy 中执行概率采样?
问题:随机采样 iris 数据集中的 species 列,使得 setose 的数量是 versicolor 和 virginica 数量的两倍。
思路是:
- 提取
species
列:从iris_2d
数据集提取species
列。 - 分组数据:将
species
列按照每个类别(setosa
,versicolor
,virginica
)分组。 - 控制采样数量:保证
setosa
的数量是versicolor
和virginica
的数量的两倍。具体来说,首先找到versicolor
和virginica
样本的数量,然后将setosa
样本的数量调整为它们的两倍。 - 随机抽样:从
setosa
、versicolor
和virginica
中随机抽样,保证满足所需的数量关系。 - 合并样本:将采样后的样本合并为一个新的数据集。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 提取 species 列:从 iris_2d 数据集提取 species 列。
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.genfromtxt(url, delimiter=",", dtype='str')
species = iris_2d[:, -1]
# 分组数据:将 species 列按照每个类别(setosa, versicolor, virginica)分组。
versicolor_index = np.where(species == "Iris-versicolor")[0]
virginica_index = np.where(species == "Iris-virginica")[0]
setosa_index = np.where(species == "Iris-setosa")[0]
print(len(versicolor_index)) # 50
print(len(virginica_index)) # 50
print(len(setosa_index)) # 50
# 控制采样数量:保证 setosa 的数量是 versicolor 和 virginica 的数量的两倍。具体来说,首先找到 versicolor 和 virginica 样本的数量,然后将 setosa 样本的数量调整为它们的两倍。
setosa_num = 2*(len(versicolor_index)+len(virginica_index))
# 随机抽样:从 setosa、versicolor 和 virginica 中随机抽样,保证满足所需的数量关系。
setosa_sample = np.random.choice(setosa_index, setosa_num, replace = True ) # replace = True就是允许重复采样
versicolor_sample = np.random.choice(versicolor_index, len(versicolor_index), replace= False)
virginica_sample = np.random.choice(virginica_index, len(virginica_index), replace = False)
# 合并样本:将采样后的样本合并为一个新的数据集。
index_all = np.concatenate([setosa_sample, versicolor_sample, virginica_sample])
sample_all = iris_2d[index_all]
display(sample_all)
43.如何在多维数组中找到一维的第二最大值?
问题:在 species setosa 的 petallength 列中找到第二最大值。
注:iris数据集的格式是
1
<sepallength> <sepalwidth> <petallength> <petalwidth> <species>
numpy.sort()
排序可以完成这个任务,但是numpy.partition()
可能会更高效。
下面我分别写这两种方法并且统计哪个更快。
方法1:numpy.sort()
排序
1
2
3
4
5
6
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.genfromtxt(url, delimiter=",", dtype='str')
setosa_index = np.where(iris_2d[:, -1] == "Iris-setosa")[0]
setosa = iris_2d[setosa_index]
sorted_setosa = np.sort(setosa[:, 2])
sorted_setosa[-2]
方法2:numpy.partition()
1
np.partition(setosa[:, 2], -2)[-2]
统计哪个更快?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
import time
# 假设数据已经加载
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.genfromtxt(url, delimiter=",", dtype='str')
# 获取 setosa 样本
setosa_index = np.where(iris_2d[:, -1] == "Iris-setosa")[0]
setosa = iris_2d[setosa_index]
# 方法1: 使用 np.sort()
start_time = time.time()
sorted_setosa = np.sort(setosa[:, 2].astype(float)) # 排序并提取花瓣长度
method1_result = sorted_setosa[-2] # 获取倒数第二个值
method1_time = time.time() - start_time
# 方法2: 使用 np.partition()
start_time = time.time()
method2_result = np.partition(setosa[:, 2].astype(float), -2)[-2] # 获取第二大值
method2_time = time.time() - start_time
method1_result, method1_time, method2_result, method2_time
很显然partition更快。
44.如何用给定列将 2 维数组排序?
问题:基于 sepallength 列将 iris 数据集排序。
注:iris数据集的格式是
1
<sepallength> <sepalwidth> <petallength> <petalwidth> <species>
1
2
3
4
5
6
7
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.genfromtxt(url, delimiter=",", dtype='str')
sepallength = iris_2d[:, 0].astype(float)
sorted_index = np.argsort(sepallength)
out_arr = iris_2d[sorted_index]
out_arr
注:numpy.argsort()
返回的是排序后数组的索引,而 numpy.sort()
会返回排序后的数组。
45.如何在 NumPy 数组中找到最频繁出现的值?
问题:在 iris 数据集中找到 petallength(第三列)中最频繁出现的值。
1
2
3
4
5
6
7
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.genfromtxt(url, delimiter=",", dtype='str')
petallength = iris_2d[:, 2]
print(petallength)
print(np.unique(petallength, return_counts=True)[1])
petallength[np.argmax(np.unique(petallength, return_counts=True)[1])]
46.如何找到第一个大于给定值的数的位置?
问题:在 iris 数据集的 petalwidth(第四列)中找到第一个值大于 1.0 的数的位置。
1
2
3
4
5
6
7
8
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris_2d = np.genfromtxt(url, delimiter=",", dtype='str')
petalwidth = iris_2d[:, 3].astype(float)
print(petalwidth)
argmax = np.where(petalwidth > 1.0)[0][0]
print("最大值位置:", argmax)
print("最大值:", petalwidth[argmax])
47.如何将数组中所有大于给定值的数替换为给定的 cutoff 值?
问题:对于数组 a,将所有大于 30 的值替换为 30,将所有小于 10 的值替换为 10。
输入:
1
2
np.random.seed(100)
np.random.uniform(1,50, 20)
1
2
3
4
5
6
np.random.seed(100)
n = np.random.uniform(1,50, 20)
display(n)
n[n > 30] = 30
n[n < 10] = 10
display(n)
48.如何在 NumPy 数组中找到 top-n 数值的位置?
问题:在给定数组 a 中找到 top-5 最大值的位置。
输入:
1
2
np.random.seed(100)
a = np.random.uniform(1,50, 20)
注:用partition获取的 top-5 最大值,但是得不到位置。所以还是要用np.argsort()
1
2
3
4
5
6
7
8
np.random.seed(100)
a = np.random.uniform(1,50, 20)
# 找到 top-5 最大值的位置
sorted_index = np.argsort(a)
top5_index = sorted_index[-5:]
print("top 5 位置:", top5_index)
print("top 5 是:", a[top5_index])
49.如何逐行计算数组中所有值的数量?
问题:逐行计算唯一值的数量。
输入:
1
2
3
4
5
6
7
8
9
np.random.seed(100)
arr = np.random.randint(1,11,size=(6, 10))
arr
\> array([[ 9, 9, 4, 8, 8, 1, 5, 3, 6, 3],
\> [ 3, 3, 2, 1, 9, 5, 1, 10, 7, 3],
\> [ 5, 2, 6, 4, 5, 5, 4, 8, 2, 2],
\> [ 8, 8, 1, 3, 10, 10, 4, 3, 6, 9],
\> [ 2, 1, 8, 7, 3, 1, 9, 3, 6, 2],
\> [ 9, 2, 6, 5, 3, 9, 4, 6, 1, 10]])
期望输出:
1
2
3
4
5
6
\> [[1, 0, 2, 1, 1, 1, 0, 2, 2, 0],
\> [2, 1, 3, 0, 1, 0, 1, 0, 1, 1],
\> [0, 3, 0, 2, 3, 1, 0, 1, 0, 0],
\> [1, 0, 2, 1, 0, 1, 0, 2, 1, 2],
\> [2, 2, 2, 0, 0, 1, 1, 1, 1, 0],
\> [1, 1, 1, 1, 1, 2, 0, 0, 2, 1]]
输出包含 10 个列,表示从 1 到 10 的数字。这些数值分别代表每一行的计数数量。例如,Cell(0,2) 中有值 2,这意味着,数字 3 在第一行出现了两次。
1
2
3
4
5
6
7
8
np.random.seed(100)
arr = np.random.randint(1,11,size=(6, 10))
out = np.zeros(arr.shape, dtype = int)
display(arr)
for i in range(len(arr[:, 0])): # 遍历每一行
for j in range(len(np.unique(arr[i], return_counts=True)[1])):
out[i][np.unique(arr[i], return_counts=True)[0][j] -1 ] = np.unique(arr[i], return_counts=True)[1][j]
display(out)
有没有更快的方法?上面的方法是我一开始写的,但是每次调用 np.unique(arr[i], return_counts=True)
都会重新计算唯一值及其计数,这样效率较低,特别是当数组的大小较大时。
可以通过以下方法优化:
- 缓存唯一值和计数:对每一行的唯一值和计数计算一次,而不是重复计算。
- 简化代码:避免重复的数组操作,直接将结果填充到输出数组
out
中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np
# 随机生成数据
np.random.seed(100)
arr = np.random.randint(1, 11, size=(6, 10))
display(arr)
# 初始化输出数组
out = np.zeros((arr.shape[0], 10), dtype=int)
# 遍历每一行,计算唯一值及其数量
for i in range(arr.shape[0]):
unique_vals, counts = np.unique(arr[i], return_counts=True)
out[i, unique_vals - 1] = counts # 填充结果
# 输出结果
print(out)
50.如何将 array_of_arrays 转换为平面 1 维数组?
问题:将 array_of_arrays
转换为平面线性 1 维数组。
# Input: arr1 = np.arange(3) arr2 = np.arange(3,7) arr3 = np.arange(7,10) array_of_arrays = np.array([arr1, arr2, arr3]) array_of_arrays#> array([array([0, 1, 2]), array([3, 4, 5, 6]), array([7, 8, 9])], dtype=object)
期望输出:
#> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
很难受,题目就是错的:报错信息如下
1
2
3
4
5
6
7
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[254], line 4
2 arr2 = np.arange(3,7)
3 arr3 = np.arange(7,10)
----> 4 array_of_arrays = np.array([arr1, arr2, arr3])
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (3,) + inhomogeneous part.
这个错误发生是因为尝试将不同长度的数组(arr1, arr2, arr3)直接组合成一个统一的NumPy数组,而NumPy要求数组的形状必须一致。
1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
arr1 = np.arange(3)
arr2 = np.arange(3, 7)
arr3 = np.arange(7, 10)
display(arr1)
display(arr2)
display(arr3)
# 使用np.hstack来堆叠这些数组
array_of_arrays = np.hstack([arr1, arr2, arr3])
display(array_of_arrays)
51.如何为 NumPy 数组生成 one-hot 编码?
问题:计算 one-hot 编码。
输入:
np.random.seed(101) arr = np.random.randint(1,4, size=6) arr #> array([2, 3, 2, 2, 2, 1])
输出:
#> array([[ 0., 1., 0.], #> [ 0., 0., 1.], #> [ 0., 1., 0.], #> [ 0., 1., 0.], #> [ 0., 1., 0.], #> [ 1., 0., 0.]])
补充:One-hot编码(也称为独热编码)是一种将分类变量转换为数值表示的方法,其中每个类别被转换为一个二进制向量。具体来说,对于一个具有N个类别的变量,每个类别用一个N维的向量表示,其中只有一个元素为1(对应于该类别),其他所有元素都为0。
假设我们有以下分类数据:
- 颜色:[‘红’, ‘绿’, ‘蓝’, ‘红’, ‘绿’]
这些颜色可以编码为:
- 红:[1, 0, 0]
- 绿:[0, 1, 0]
- 蓝:[0, 0, 1]
看下面这个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import numpy as np
# 假设我们有以下类别数据
categories = np.array(['红', '绿', '蓝', '红', '绿'])
# 找到所有独特的类别
unique_categories = np.unique(categories)
# 创建一个函数来生成one-hot编码
def one_hot_encode(categories, unique_categories):
# 初始化编码数组
one_hot = np.zeros((categories.shape[0], unique_categories.shape[0]))
# 为每个类别设置相应的1
for i, cat in enumerate(categories):
one_hot[i, np.where(unique_categories == cat)[0][0]] = 1
return one_hot
# 执行one-hot编码
encoded = one_hot_encode(categories, unique_categories)
print("原数据:", categories)
print("独特类别:", unique_categories)
print("One-Hot编码:")
print(encoded)
注:enumerate 是一个非常有用的Python内置函数,允许你在遍历一个可迭代对象(如列表、字符串或其他可迭代的集合)时,同时获取到索引和元素值。这对于需要同时使用元素的位置和值的场景非常方便。
来解决这道题:(和上面这个例子的思想差不多)
1
2
3
4
5
6
7
8
9
10
11
12
np.random.seed(101)
arr = np.random.randint(1,4, size=6)
unique_arr = np.unique(arr)
print("原数据:", arr)
print("独特类别:", unique_arr)
out = np.zeros((len(arr), len(unique_arr)))
for i,value in enumerate(arr):
out[i, np.where(value == unique_arr)] = 1
print("One-Hot编码:")
display(out)
(难)52.如何创建由类别变量分组确定的一维数值?
问题:创建由类别变量分组的行数。使用以下来自 iris species 的样本作为输入。
输入:
1 2 3 4 url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data" species = np.genfromtxt(url, delimiter=",", dtype=None, encoding=None, usecols=[4]) species_small = np.sort(np.random.choice(species, size=20)) species_small
species_small
:通过np.random.choice()
随机从species
中抽取 20 个样本,并将它们按升序排序。然后你需要计算这些样本在不同类别中的分布情况。
期望输出:
#> [0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7]
参考:
1
2
3
4
5
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
species = np.genfromtxt(url, delimiter=",", dtype=None, encoding=None, usecols=[4])
species_small = np.sort(np.random.choice(species, size=20))
print(species_small)
print([i for a in np.unique(species_small) for i, b in enumerate(species_small[species_small == a])])
53.如何基于给定的类别变量创建分组 id?
难度:L4
问题:基于给定的类别变量创建分组 id。使用以下来自 iris species 的样本作为输入。
输入:
1
2
3
4
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
species = np.genfromtxt(url, delimiter=",", dtype=None, encoding=None, usecols=[4])
species_small = np.sort(np.random.choice(species, size=20))
species_small
期望输出:
#> [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2]
这题和上面一题类似。
1
2
3
4
5
6
7
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
species = np.genfromtxt(url, delimiter=",", dtype=None, encoding=None, usecols=[4])
species_small = np.sort(np.random.choice(species, size=20))
print(species_small)
uni_species = np.unique(species_small)
print([i for i, a in enumerate(uni_species) for j, item in enumerate(species_small[species_small == a]) ])
54.如何使用 NumPy 对数组中的项进行排序?
问题:为给定的数值数组 a 创建排序。
输入:
np.random.seed(10) a = np.random.randint(20, size=10)print(a)#> [ 9 4 15 0 17 16 17 8 9 0]
期望输出:
[4 2 6 0 8 7 9 3 5 1]
参考答案给的是:
1
2
3
4
5
np.random.seed(10)
a = np.random.randint(20, size=10)
print(a)
a.argsort().argsort()
莫名其妙的排序两次。。。
参考我自己写的:(如果想要跟上面结果一样,可以这么写,这么写下来 我就理解为什么上面是a.argsort().argsort()
了)
1
2
3
4
5
6
7
8
9
10
11
12
13
np.random.seed(10)
a = np.random.randint(20, size=10)
print(a) # [ 9 4 15 0 17 16 17 8 9 0]
i = len(a) - 1
index_sort = np.argsort(a) # [3 9 1 7 8 0 2 5 6 4]
out = np.empty(a.size, dtype = int)
# 期待输出: [4 2 6 0 8 7 9 3 5 1]
for i in range(len(a)):
index = index_sort[i]
out[index] = i
print(out)
55.如何使用 NumPy 对多维数组中的项进行排序?
问题:给出一个数值数组 a,创建一个形态相同的排序数组。
输入:
np.random.seed(10) a = np.random.randint(20, size=[2,5])print(a)#> [[ 9 4 15 0 17]#> [16 17 8 9 0]]
期望输出:
#> [[4 2 6 0 8] #> [7 9 3 5 1]]
1
2
3
4
5
6
7
8
9
10
11
np.random.seed(10)
a = np.random.randint(20, size=[2,5])
print(a)
a_fla = a.flatten()
display(a_fla)
# 期待输出:
#> [[4 2 6 0 8]
#> [7 9 3 5 1]]
o = a_fla.argsort().argsort().reshape(2, 5)
print(o)
56.如何在 2 维 NumPy 数组中找到每一行的最大值?
问题:在给定数组中找到每一行的最大值。
np.random.seed(100) a = np.random.randint(1,10, [5,3]) a #> array([[9, 9, 4], #> [8, 8, 1], #> [5, 3, 6], #> [3, 3, 3], #> [2, 1, 9]])
1
2
3
4
5
np.random.seed(100)
a = np.random.randint(1,10, [5,3])
display(a)
np.max(a, axis = 1)
57.如何计算 2 维 NumPy 数组每一行的 min-by-max?
问题:给定一个 2 维 NumPy 数组,计算每一行的 min-by-max。
np.random.seed(100) a = np.random.randint(1,10, [5,3]) a #> array([[9, 9, 4], #> [8, 8, 1], #> [5, 3, 6], #> [3, 3, 3], #> [2, 1, 9]])
这道题应该是计算min/max
吧。
1
2
3
4
5
6
7
8
9
10
np.random.seed(100)
a = np.random.randint(1,10, [5,3])
display(a)
max_ = np.max(a, axis = 1)
min_ = np.min(a, axis = 1)
print("每一行的最小值", min_)
print("每一行的最大值", max_)
print("min-by-max: ", np.divide(min_, max_))
58.如何在 NumPy 数组中找到重复条目?
问题:在给定的 NumPy 数组中找到重复条目(从第二次出现开始),并将其标记为 True。第一次出现的条目需要标记为 False。
# Input np.random.seed(100) a = np.random.randint(0, 5, 10) print( Array: , a) #> Array: [0 0 3 0 2 4 2 2 2 2]
期望输出:
#> [False True False True False False True True True True]
1
2
3
4
5
6
7
8
np.random.seed(100)
a = np.random.randint(0, 5, 10)
display(a)
v, i = np.unique(a, return_index = True) # return_index返回第一次出现的索引
out = np.full(a.size, True)
out[i] = False
out
59.如何找到 NumPy 的分组平均值?
问题:在 2 维 NumPy 数组的类别列中找到数值的平均值。
输入:
1
2
3
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris = np.genfromtxt(url, delimiter=",", dtype= object )
names = ( sepallength , sepalwidth , petallength , petalwidth , species )
期望解:
#> [[b Iris-setosa , 3.418], #> [b Iris-versicolor , 2.770], #> [b Iris-virginica , 2.974]]
这道题没说清楚是算哪一列的均值。(看期望解猜测是 第二列[:, 1]
)
1
2
3
4
5
6
7
8
9
10
11
12
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
iris = np.genfromtxt(url, delimiter=",", dtype= object )
# display(iris)
species = iris[:, -1]
species_class = np.unique(species)
out = np.zeros(len(species_class))
for v, index in enumerate(species_class.astype(str)):
out[v] = np.mean(iris[iris[:, -1].astype(str) == index][:, 1].astype(float))
res = np.column_stack((species_class, out))
display(res)
60.如何将 PIL 图像转换成 NumPy 数组?
问题:从以下 URL 中导入图像,并将其转换成 NumPy 数组。
1
URL = "https://upload.wikimedia.org/wikipedia/commons/8/8b/Denali_Mt_McKinley.jpg"
关于在jupyter notebook中阅读图像,我曾经总结了4点(及基本操作),参考博客
这道题可以采用思路:利用Pillow读图片,并且用np.array
将其转化成ndarray
但是因为 Image.open()
本身不支持直接传入 URL,而是需要本地文件路径或文件对象(如字节流)。为此,可以结合 requests 库下载图片,然后用 BytesIO 将字节数据转换为文件对象,再交给Image.open()
处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from PIL import Image
import requests
from io import BytesIO
url = "https://upload.wikimedia.org/wikipedia/commons/8/8b/Denali_Mt_McKinley.jpg"
response = requests.get(url)
response.raise_for_status()
image_data = BytesIO(response.content)
pic = Image.open(image_data)
# 展示图片
pic.show()
# 图片转化成ndarray
pic_arr = np.array(pic)
display(pic_arr)
图片是窗口弹出的。
也可以不窗口弹出,如下:需要注释掉pic.show()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from PIL import Image
import requests
from io import BytesIO
url = "https://upload.wikimedia.org/wikipedia/commons/8/8b/Denali_Mt_McKinley.jpg"
response = requests.get(url)
response.raise_for_status()
image_data = BytesIO(response.content)
pic = Image.open(image_data)
# 展示图片
# pic.show()
display(pic)
# 图片转化成ndarray
pic_arr = np.array(pic)
display(pic_arr)
初次之外可以查看图片的大小和模式:
1
2
print("大小", pic_arr.size)
print("模式", pic.mode)
61.如何删除 NumPy 数组中所有的缺失值?
问题:从 1 维 NumPy 数组中删除所有的 nan 值。
输入:
np.array([1,2,3,np.nan,5,6,7,np.nan])
期望输出:
array([ 1., 2., 3., 5., 6., 7.])
1
2
3
4
a = np.array([1,2,3,np.nan,5,6,7,np.nan])
display(a)
a_cleaned = a[~np.isnan(a)]
a_cleaned
62.如何计算两个数组之间的欧几里得距离?
问题:计算两个数组 a 和 b 之间的欧几里得距离。
输入:
a = np.array([1,2,3,4,5]) b = np.array([4,5,6,7,8])
补充1:欧几里得距离:
对于两个 n维向量 \(a=[a_1,a_2,...,a_n]\)和 \(b=[b_1,b_2,...,b_n]\),欧几里得距离定义为:\(d = \sqrt{(a_1-b_1)^2+(a_2-b_2)^2+...+(a_n-b_n)^2}\)
补充2:np.linalg.norm()
的使用方法
函数签名:
1
numpy.linalg.norm(x, ord=None, axis=None, keepdims=False)
【参数说明】
- x(必需)
- 类型:数组或类似数组的对象(如 NumPy 数组、列表)。
- 描述:输入向量或矩阵,用于计算范数。
- 示例:
np.array([1, 2, 3])
或np.array([[1, 2], [3, 4]])
。
- ord(可选)
- 类型:整数、浮点数或字符串,默认 None。
- 描述:指定范数的类型。对于向量,默认是 2(L2 范数,即欧几里得范数)。
- 常见值:
- None 或 2:L2 范数(\(\sqrt{\sum x_i^2}\))
- 1:L1 范数(\(\sum \vert x_i \vert\))
- 0:非零元素个数。
np.inf
:最大值范数(\(max \vert x_i \vert\))。- ‘fro’:Frobenius 范数(矩阵默认,\(\sqrt{\sum x_{ij}^2}\))。
- ‘nuc’:核范数(矩阵的奇异值之和)。
- axis(可选)
- 类型:整数或元组,默认 None。
- 描述:指定沿哪个轴计算范数。
- None:展平数组后计算整个范数。
- 0:沿列计算。
- 1:沿行计算。
- 示例:axis=0 对矩阵每列单独计算。
- keepdims(可选)
- 类型:布尔值,默认 False。
- 描述:是否保留输入维度。
- False:返回标量或降维数组。
- True:保持维度(如返回形状为 (1,) 的数组)。
- 示例:keepdims=True 返回数组而非标量。
【返回值】
- 类型:浮点数或 NumPy 数组。
- 描述:根据 ord 和 axis 计算的范数值。
- 向量默认返回标量(L2 范数)。
- 矩阵按指定轴返回数组或标量。
看一个例子:
1
2
3
4
5
import numpy as np
arr = np.array([1, 2, 3])
norm = np.linalg.norm(arr)
print(norm) # 输出:3.7416573867739413
即计算的是\(\sqrt{1^2+2^2+3^2}=\sqrt{14}\)
回到这道题:
1
2
3
4
a = np.array([1,2,3,4,5])
b = np.array([4,5,6,7,8])
np.linalg.norm(a - b)
63.如何在一个 1 维数组中找到所有的局部极大值(peak)?
问题:在 1 维数组 a 中找到所有的 peak,peak 指一个数字比两侧的数字都大。
输入:
a = np.array([1, 3, 7, 1, 2, 6, 0, 1])
期望输出:
#> array([2, 5])
方法1:python实现:
1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
a = np.array([1, 3, 7, 1, 2, 6, 0, 1])
display(a)
def find_peaks_index(a):
peaks = []
for i in range(1, len(a)-1):
if a[i] > a[i - 1] and a[i] > a[i + 1]:
peaks.append(i)
return np.array(peaks)
find_peaks_index(a)
方法2:scipy.signal.find_peaks
专门用于信号处理,其中峰值检测是一个常见的需求。
1
2
3
4
5
6
7
from scipy.signal import find_peaks
a = np.array([1, 3, 7, 1, 2, 6, 0, 1])
display(a)
out = find_peaks(a)
display(out)
display(out[0]) # 答案要的是这个
64.如何从 2 维数组中减去 1 维数组,从 2 维数组的每一行分别减去 1 维数组的每一项?
问题:从 2 维数组 a_2d
中减去 1 维数组 b_1d
,即从 a_2d 的每一行分别减去 b_1d
的每一项。
输入:
a_2d = np.array([[3,3,3],[4,4,4],[5,5,5]]) b_1d = np.array([1,1,1])
期望输出:
#> [[2 2 2] #> [2 2 2] #> [2 2 2]]
额,如果我的理解没有问题,不应该输出
1
2
3
array([[2, 2, 2],
[3, 3, 3],
[4, 4, 4]])
吗?
这道题就是很简单,了解广播机制就可以。关于广播机制,参考博客
1
2
3
4
5
a_2d = np.array([[3,3,3],[4,4,4],[5,5,5]])
b_1d = np.array([1,1,1])
display(a_2d)
display(b_1d)
a_2d - b_1d
65.如何在数组中找出某个项的第 n 个重复索引?
问题:找到数组 x 中数字 1 的第 5 个重复索引。
x = np.array([1, 2, 1, 1, 3, 4, 3, 1, 1, 2, 1, 1, 2])
1
2
3
4
5
x = np.array([1, 2, 1, 1, 3, 4, 3, 1, 1, 2, 1, 1, 2])
display(x)
index = np.where(x == 1)[0]
index[4]
感觉这道题就是考察np.where
当然也可以再加层判断,是不是有5个1:
1
2
3
4
5
6
7
8
x = np.array([1, 2, 1, 1, 3, 4, 3, 1, 1, 2, 1, 1, 2])
display(x)
index = np.where(x == 1)[0]
if len(index) >= 5:
print(index[4])
else:
print("数组中1的数字没有5个")
66.如何将 NumPy 的 datetime64 对象(object)转换为 datetime 的 datetime 对象?
问题:将 NumPy 的 datetime64 对象(object)转换为 datetime 的 datetime 对象。
# Input: a numpy datetime64 object dt64 = np.datetime64(“2018-02-25 22:10:10 “)
尼玛这题真的有问题,这道题原题引号都没有,误人子弟。
补充:NumPy 的 datetime64 对象和datetime.datetime
首先:NumPy 的 datetime64 对象和 Python 的 datetime.datetime
对象都是用于表示日期和时间的(这句话属于是废话)
但是:
-
datetime.datetime 是 Python 标准库 datetime 的一部分,它是一个 Python 对象,用 Python 代码实现;numpy.datetime64 是 NumPy 库的一部分,它是一个 NumPy 的数据类型,用 C 代码实现,旨在提供高效的数值运算,特别是对于日期和时间序列。
- datetime.datetime作为 Python 对象,它具有 Python 对象的开销,例如引用计数、类型检查等,在处理大量日期时间数据时,性能可能不如
numpy.datetime64
;numpy.datetime64NumPy 数组存储相同类型的元素,datetime64 对象在内存中紧密排列,允许向量化操作,从而提高了性能,对于处理大型日期时间数据集,numpy.datetime64 通常更快。 - 精度差异:datetime.datetime微秒级的精度;numpy.datetime64精度是可配置的,最多达到
datetime64[ps]
(皮秒) 或datetime64[fs]
(飞秒)
说多了还不如看看代码:
创建对象和算术运算:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
import datetime
# 创建
dt_py = datetime.datetime(2025, 2, 21, 10, 30, 0)
dt_np = np.datetime64("2025-02-21T10:30:00")
display(dt_py)
display(dt_np)
# 加法
dt_py_plus = dt_py + datetime.timedelta(days = 1)
print("datetime.datetime加1天:", dt_py_plus)
# numpy.datetime64支持向量化操作
dt_arr = np.array(["2025-02-21", "2025-02-22", "2025-02-23"], dtype = "datetime64[D]")
dt_arr_plus_a_week = dt_arr + np.timedelta64(7, "D")
display(dt_arr)
print("np.datetime64加1周(向量化操作):", dt_arr_plus_a_week)
当然numpy.datetime64
可以指定精度:
1
2
3
# 指定精度的 numpy.datetime64 对象
dt_numpy_ms = np.datetime64('2023-10-27T10:30:00.123', 'ms') # 毫秒
print(f"numpy.datetime64 object (milliseconds): {dt_numpy_ms}")
访问属性:特别注意:numpy.datetime64
的属性 (需要先转换为 datetime 对象才能访问)
1
2
3
4
5
6
7
8
9
10
11
dt_py = datetime.datetime(2025, 2, 21, 10, 30, 0)
dt_np = np.datetime64("2025-02-21T10:30:00")
print("datetime.datetime 年", dt_py.year)
print("datetime.datetime 月", dt_py.month)
print("datetime.datetime 日", dt_py.day)
# numpy.datetime64 的属性 (需要先转换为 datetime 对象才能访问)
dt_np2py = dt_np.astype(datetime.datetime)
print("numpy.datetime64转换成datetime.datetime后 年", dt_np2py.year)
简单的性能比较:创建 10 万个日期对象或数组元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import datetime
import numpy as np
import time
n = 100000
# datetime.datetime 性能
start = time.time()
dt_python_list = [datetime.datetime(2023, 10, 27) + datetime.timedelta(days=i) for i in range(n)]
end = time.time()
print(f"datetime.datetime list creation time: {end - start:.4f} seconds")
# numpy.datetime64 性能
start = time.time()
start_date = np.datetime64('2023-10-27')
dt_numpy_array = np.arange(start_date, start_date + np.timedelta64(n, 'D'), np.timedelta64(1, 'D'), dtype='datetime64[D]')
end = time.time()
print(f"numpy.datetime64 array creation time: {end - start:.4f} seconds")
显然,在创建和处理大量日期时间数据时,NumPy 通常比 Python 的标准 datetime 库更有效率
回到本来的题目,就很简单了:
1
2
3
dt64 = np.datetime64("2018-02-25 22:10:10 ")
dt64_2_dtdt = dt64.astype(datetime.datetime)
dt64_2_dtdt
67.如何计算 NumPy 数组的移动平均数?
问题:给定 1 维数组,计算 window size 为 3 的移动平均数。
输入:
np.random.seed(100) Z = np.random.randint(10, size=10)
关于移动平均线这个在博客中讨论了好几种方法。可以简单回顾下。
1
2
3
4
5
6
7
8
9
10
np.random.seed(100)
Z = np.random.randint(10, size=10)
display(Z)
# 计算SMA
N = 3 # window的大小
weights = np.ones(N)/N # 生成等权重窗口[1/N, 1/N, ..., 1/N]
print("权重:", weights)
sma = np.convolve(Z, weights, mode = "valid")
print("sma", sma)
注:np.convolve
函数的 mode 参数决定了卷积操作如何处理边界。 它有三个可选值:
- ‘full’ (默认值): 返回完整卷积。 输出数组的大小为 len(Z) + len(weights) - 1。 在 ‘full’ 模式下,卷积操作会从 weights 与 Z 完全不重叠的位置开始,逐步移动 weights 直到完全不重叠为止。 这意味着输出数组的开头和结尾的值是基于较少的数据点计算的。
- ‘valid’: 仅返回完全重叠的部分。 输出数组的大小为 len(Z) - len(weights) + 1。 在 ‘valid’ 模式下,卷积操作只计算 weights 与 Z 完全重叠的部分。 这意味着输出数组只包含基于完整窗口大小计算的平均值。 这是计算移动平均的常用模式。
- ‘same’: 返回与输入数组 Z 相同大小的数组。 卷积的结果会进行裁剪或填充,以使输出数组与输入数组具有相同的形状。 如果 len(weights) 是奇数,则输出将以 Z 的中心对齐。如果 len(weights) 是偶数,则输出将略微偏向右侧。
mode="valid"
表示我们只想计算完全重叠的移动平均值。 由于窗口大小 N 是 3,这意味着输出数组 sma 将包含 len(Z) - len(weights) + 1 = 10 - 3 + 1 = 8
个元素。
Z = [8, 8, 3, 7, 7, 0, 4, 2, 5, 2]
并且 weights = [1/3, 1/3, 1/3]
1
2
3
4
1. 第一个移动平均值:(8 + 8 + 3) / 3 = 19 / 3 = 6.33333333
2. 第二个移动平均值:(8 + 3 + 7) / 3 = 18 / 3 = 6.0
3. 第三个移动平均值:(3 + 7 + 7) / 3 = 17 / 3 = 5.66666667
4. 以此类推,直到计算出所有 8 个移动平均值。
如果没有设置mode ,默认就是full,那么计算过程就是:
- weights 位于 Z 的左侧,部分不重叠:
- weights = [1/3, 1/3, 1/3]
- Z = 8, 8, 3, 7, 7, 0, 4, 2, 5, 2
- 计算:(0 * 1/3 + 0 * 1/3 + 8 * 1/3) = 8/3 = 2.66666667 (这里假设 Z 数组左侧用 0 填充)
- weights 仍然位于 Z 的左侧,部分不重叠:
- weights = 1/3, 1/3, 1/3
- Z = 8, 8, 3, 7, 7, 0, 4, 2, 5, 2
- 计算:(0 * 1/3 + 8 * 1/3 + 8 * 1/3) = 16/3 = 5.33333333 (这里假设 Z 数组左侧用 0 填充)
- weights 与 Z 完全重叠:
- weights = 1/3, 1/3, 1/3
- Z = 8, 8, 3, 7, 7, 0, 4, 2, 5, 2
- 计算:(8 * 1/3 + 8 * 1/3 + 3 * 1/3) = 19/3 = 6.33333333
- 继续移动 weights,直到 weights 位于 Z 的右侧,部分不重叠:
- weights = 1/3, 1/3, 1/3
- Z = 8, 8, 3, 7, 7, 0, 4, 2, 5, 2
- 计算:(2 * 1/3 + 0 * 1/3 + 0 * 1/3) = 2/3 = 0.66666667 (这里假设 Z 数组右侧用 0 填充)
- weights 完全位于 Z 的右侧,不重叠: 不进行计算 (已经到末尾)。
如果mode 设置为same,计算过程就是:
- 填充(Padding):
- 计算需要填充的数量: 为了使输出大小与输入 Z 相同,np.convolve 会在 Z 的两侧添加填充。 填充的数量取决于 weights 的长度。 一般来说,填充的数量是 (len(weights) - 1) // 2。 对于我们的例子,len(weights) = 3,所以填充的数量是
(3 - 1) // 2 = 1
。 - 创建填充后的 Z:
Z_padded = [0, 8, 8, 3, 7, 7, 0, 4, 2, 5, 2, 0]
(在原始 Z 的开头和结尾各添加一个 0)。
- 计算需要填充的数量: 为了使输出大小与输入 Z 相同,np.convolve 会在 Z 的两侧添加填充。 填充的数量取决于 weights 的长度。 一般来说,填充的数量是 (len(weights) - 1) // 2。 对于我们的例子,len(weights) = 3,所以填充的数量是
- 卷积(Convolution):
- 对填充后的 Z_padded 和 weights 执行卷积操作。 因为
Z_padded
有 12 个元素,weights 有 3 个元素,所以卷积结果(如果使用 mode=’full’)将有12 + 3 - 1 = 14
个元素。
- 对填充后的 Z_padded 和 weights 执行卷积操作。 因为
- 裁剪(Cropping):
- 从卷积结果中裁剪一个与原始数组 Z 大小相同的子数组。 裁剪的位置取决于 len(weights) 是奇数还是偶数。
- 如果 len(weights) 是奇数 (就像我们的例子),则输出将以 Z 的中心对齐。 这意味着我们将从卷积结果的中心位置向前和向后裁剪,以获得与 Z 大小相同的数组。
- 如果 len(weights) 是偶数,则输出将略微偏向右侧。
- 由于输出需要和Z一样是10个元素,因此需要裁剪掉头尾各2个元素。
- 从卷积结果中裁剪一个与原始数组 Z 大小相同的子数组。 裁剪的位置取决于 len(weights) 是奇数还是偶数。
68.给定起始数字、length 和步长,如何创建一个 NumPy 数组序列?
问题:从 5 开始,创建一个 length 为 10 的 NumPy 数组,相邻数字的差是 3。
归纳成通用的表达式就是:
1
2
3
4
start = 5
length = 10
step = 3
np.arange(start, start + length * step, step)
69.如何在不规则 NumPy 日期序列中填充缺失日期?
问题:给定一个非连续日期序列的数组,通过填充缺失的日期,使其变成连续的日期序列。
输入:
# Input dates = np.arange(np.datetime64( 2018-02-01 ), np.datetime64( 2018-02-25 ), 2) print(dates) #> [ 2018-02-01 2018-02-03 2018-02-05 2018-02-07 2018-02-09 #> 2018-02-11 2018-02-13 2018-02-15 2018-02-17 2018-02-19 #> 2018-02-21 2018-02-23 ]
已经有的是:
也就是因为步长是2,需要把中间缺的日子都补上。
1
2
3
4
5
6
7
dates = np.arange(np.datetime64( "2018-02-01" ), np.datetime64( "2018-02-25"), 2)
display(dates)
min_day = dates.min()
max_day = dates.max()
full_days = np.arange(min_day, max_day + np.timedelta64(1, "D"))
display(full_days)
70.如何基于给定的 1 维数组创建 strides?
问题:给定 1 维数组 arr,使用 strides 生成一个 2 维矩阵,其中 window length 等于 4,strides 等于 2,例如 [[0,1,2,3], [2,3,4,5], [4,5,6,7]..]
。
输入:
arr = np.arange(15) arr #> array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
期望输出:
#> [[ 0 1 2 3] #> [ 2 3 4 5] #> [ 4 5 6 7] #> [ 6 7 8 9] #> [ 8 9 10 11] #> [10 11 12 13]]
补充:as_strided
函数是 numpy.lib.stride_tricks
模块的一部分。 它允许我们创建一个新的 NumPy 数组,该数组是现有数组的视图,但具有不同的形状和步长。 重要的是,它不会复制数据。
1
2
3
4
5
6
7
8
9
10
11
12
from numpy.lib.stride_tricks import as_strided
arr = np.arange(15)
display(arr)
def rolling_window(arr, window_length, strides ):
shape = (arr.size - window_length) // strides + 1, window_length
strides = (strides * arr.itemsize, arr.itemsize)
return as_strided(arr, shape=shape, strides=strides)
window_length = 4
strides = 2
rolling_window(arr, window_length, strides)