NumPy 70题

NumPy 70道习题(题目+答案)

Posted by Hilda on February 21, 2025

《Python数据分析基础教程:NumPy学习指南(第2版)》所有章节阅读笔记+代码


70 道 NumPy 面试题

1. 将 NumPy 导入为 np,并查看版本

问题:将 NumPy 导入为 np,并输出版本号。

1
2
import numpy as np
np.__version__

image-20250214184453067

2.如何创建 1 维数组?

问题:创建数字从 0 到 9 的 1 维数组。

1
2
n = np.arange(10)
n

image-20250214184612480

3.如何创建 boolean 数组?

问题:创建所有 True 的 \(3×3\) NumPy 数组。

1
2
n = np.full((3, 3), True)
n

image-20250214184751712

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)

image-20250214184920764

方法2:使用布尔索引

注:布尔索引 是最常用且效率最高的方法。它利用 NumPy 数组的广播特性,直接通过条件选择满足条件的元素

1
2
n = np.arange(10)
n[n % 2 == 1]

image-20250214185109401

当然也可以用np.mod() 方法:

1
2
3
n = np.arange(10)
oddNum = n[np.mod(n, 2) == 1]
oddNum

image-20250214185601819

方法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

image-20250214185252397

方法4:filter() 函数与 lambda 表达式

1
2
3
n = np.arange(10)
odd = list(filter(lambda x: x % 2 == 1, n))
odd

image-20250214185436309

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

image-20250214185816158

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

image-20250215140129820

方法2:np.where()

1
2
3
4
n = np.arange(10)
out = np.where(n % 2 == 1, -1, n)
display(out)
display(n)

image-20250215140347723

方法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)

image-20250215140638008

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)

image-20250215140818729

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.stacknp.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

image-20250215141052743

方法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))

image-20250215141218412

方法3:np.row_stack

1
np.row_stack((a, b))

image-20250215141313663

方法4:np.append 也可以用于堆叠数组,不过它通常用于将一个数组的元素添加到另一个数组的末尾。要垂直堆叠,必须设置 axis=0

1
np.append(a, b, axis = 0)

image-20250215141403566

方法5:np.insert 允许你将一个数组插入到另一个数组的指定位置。我们可以利用它将 b 插入到 a 的末尾,达到垂直堆叠的效果。

注:a.shape[0]a 的行数,表示插入到 a 的最后。

1
np.insert(a, a.shape[0], b, axis = 0)

image-20250215141515671

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)

image-20250215141804415

方法2:

1
np.hstack((a, b))

image-20250215141812344

方法3:

1
np.column_stack((a, b))

image-20250215141909832

方法4:

1
np.append(a, b, axis = 1)

image-20250215141919235

本题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

image-20250215150901060

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)

image-20250215151320675

方法2:

1
np.array(list(set(a) & set(b)))

image-20250215151443003

方法3:

1
np.unique(a[np.isin(a, b)])   # np.unique去重

image-20250215151600610

注: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

image-20250216172202992

方法2:numpy.setdiff1d() 返回 a 中存在但不在 b 中的元素,这个方法也是专门为这种差集操作设计的。

1
2
res = np.setdiff1d(a, b)
res

image-20250216172223281

方法3:使用纯 Python,也可以通过列表推导式来实现:

1
2
out_res = np.array([x for x in a if x not in b])
out_res

image-20250216172338756

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))

image-20250217140429658

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))

image-20250217140818753

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)

image-20250217141034996

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)

image-20250217141357596

注: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)

image-20250217141729865

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])

image-20250217141958928

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]

image-20250217142054903

20.如何创建一个包含 5 和 10 之间随机浮点的 2 维数组?

问题:创建一个形态为 5×3 的 2 维数组,包含 5 和 10 之间的随机十进制小数。

方法1:np.random.uniform(low, high, size) 可以生成在 lowhigh 之间均匀分布的随机小数。

1
2
# 创建一个形态为 5×3 的 2 维数组,包含 5 和 10 之间的随机十进制小数。
np.random.uniform(5, 10, (5, 3))

image-20250217142404128

方法2:

1
2
3
5 + (10 - 5)*np.random.rand(5, 3)
或者
5 + (10 - 5)*np.random.random((5, 3))

image-20250217142506326

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)

image-20250217160900044

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

image-20250219102148186

注,如果要指定小数点后的位数,可以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(),内容如下,发现问题:

image-20250219103524665

edgeitems 控制的是数组两端显示的元素数,通常是在数组元素数目超过 threshold 时才会生效。当数组的元素总数小于等于 threshold 时,edgeitems 设置不起作用。也就是说,edgeitems 只有在数组超过 threshold 元素时才会起作用。

所以,这道题最终应该这么解决:

1
2
np.set_printoptions(threshold = 6, edgeitems = 3)
a

image-20250219103641437

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

image-20250219103833631

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'])

image-20250219104037437

数据集内容:

  • 该数据集包含 150 条记录,每条记录有 4 个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)。
  • 目标变量有 3 个类别:setosaversicolorvirginica,对应的数字标签分别是 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

image-20250219104412433

文本列就是最后一列,所以:

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

image-20250219104605970

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

image-20250219105245766

但是有第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

image-20250219105332322

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))

image-20250219105736546

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_)

image-20250219110154167

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)

image-20250219110631215

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)

image-20250219124531145

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

image-20250219124825559

33.如何在 NumPy 数组中找出缺失值的位置?

问题:在 iris_2d 的 sepallength(第一列)中找出缺失值的数目和位置。


查找 sepallength(第一列)中的缺失值(’nan’):(布尔索引)

1
2
sepal_length = (iris_2d=='nan')
sepal_length

image-20250219125423191

然后统计总数:(因为True=1,False=0

1
np.sum(sepal_length)

image-20250219140942542

最后要得到nan的位置:

1
2
index = np.where(sepal_length)[0]
index

image-20250219141943919

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)]

image-20250219142359521

35.如何在 NumPy 数组中删除包含缺失值的行?

问题:选择 iris_2d 中不包含 nan 值的行。


首先注意:

1
np.isnan(iris_2d)

会返回一个布尔数组:

image-20250219142801022

和刚在的逻辑相同:

1
np.where(bool_arr)[0]

image-20250219142925160

那么这道题,需要对布尔数组取反。

1
2
notNan_arr = ~(np.isnan(iris_2d))
notNan_arr.all(axis = 1)

image-20250219143329891

所以:

1
iris_2d[notNan_arr.all(axis = 1)]

image-20250219143408763

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])

image-20250219143848098

有非常强的正线性关系。

37.如何确定给定数组是否有空值?

问题:确定 iris_2d 是否有缺失值。


1
np.isnan(iris_2d).any()

image-20250219144103518

只要有一个True(即有一个np.nan)就会返回True.(这就是any()的意义)

38.如何在 NumPy 数组中将所有缺失值替换成 0?

问题:在 NumPy 数组中将所有 nan 替换成 0。


首先做一份iris_2d的拷贝

1
2
# 做一份拷贝,防止后续还要用到iris_2d
iris_2d_copy = iris_2d.copy()

image-20250219144359208

然后将nan变成0(赋值运算)

1
iris_2d[np.isnan(iris_2d)] = 0

image-20250219144502186

39.如何在 NumPy 数组中找出唯一值的数量?

问题:在 iris 的 species 列中找出唯一值及其数量。


补充:在 iris 数据集中,species 列通常是数据集的 最后一列,它包含了每个样本的类别标签(即 Iris-setosaIris-versicolorIris-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])

image-20250219144943345

但是想要统计数量(每个唯一值的出现次数),就要加上参数return_counts=True

1
np.unique(iris[:, -1], return_counts=True)

image-20250219145151471

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 )

image-20250219145523157

使用 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

image-20250219145639452

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

image-20250219150517328

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)

image-20250219160812239

42.如何在 NumPy 中执行概率采样?

问题:随机采样 iris 数据集中的 species 列,使得 setose 的数量是 versicolor 和 virginica 数量的两倍。


思路是:

  1. 提取 species:从 iris_2d 数据集提取 species 列。
  2. 分组数据:将 species 列按照每个类别(setosa, versicolor, virginica)分组。
  3. 控制采样数量:保证 setosa 的数量是 versicolorvirginica 的数量的两倍。具体来说,首先找到 versicolorvirginica 样本的数量,然后将 setosa 样本的数量调整为它们的两倍。
  4. 随机抽样:从 setosaversicolorvirginica 中随机抽样,保证满足所需的数量关系。
  5. 合并样本:将采样后的样本合并为一个新的数据集。
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)

image-20250219162414887

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]

image-20250219163121873

方法2:numpy.partition()

1
np.partition(setosa[:, 2], -2)[-2]

image-20250219163235255

统计哪个更快?

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

image-20250219163427937

很显然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

image-20250219163800260

注: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])]

image-20250219164339155

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])

image-20250219164724939

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)

image-20250219164906206

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])

image-20250219165346902

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)

image-20250219171704309

有没有更快的方法?上面的方法是我一开始写的,但是每次调用 np.unique(arr[i], return_counts=True) 都会重新计算唯一值及其计数,这样效率较低,特别是当数组的大小较大时。

可以通过以下方法优化:

  1. 缓存唯一值和计数:对每一行的唯一值和计数计算一次,而不是重复计算。
  2. 简化代码:避免重复的数组操作,直接将结果填充到输出数组 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)

image-20250219171959842

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)

image-20250219172851682

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)

image-20250219173216019

注: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)

image-20250219173855305

(难)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

image-20250219202226059

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])])

image-20250219203246717

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]) ])

image-20250219204220131

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()

image-20250219205708279

莫名其妙的排序两次。。。


参考我自己写的:(如果想要跟上面结果一样,可以这么写,这么写下来 我就理解为什么上面是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)

image-20250219221030396

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)

image-20250220100620716

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)

image-20250220100931153

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_))

image-20250220101642304

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)

image-20250220112402328

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)

图片是窗口弹出的。

image-20250220195222036

image-20250220195310300

也可以不窗口弹出,如下:需要注释掉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)

image-20250220195426271


初次之外可以查看图片的大小和模式:

1
2
print("大小", pic_arr.size)
print("模式", pic.mode)

image-20250220195637868

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

image-20250220195935713

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)

【参数说明】

  1. x(必需)
    • 类型:数组或类似数组的对象(如 NumPy 数组、列表)。
    • 描述:输入向量或矩阵,用于计算范数。
    • 示例np.array([1, 2, 3]) np.array([[1, 2], [3, 4]])
  2. 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’:核范数(矩阵的奇异值之和)。
  3. axis(可选)
  • 类型:整数或元组,默认 None。
  • 描述:指定沿哪个轴计算范数。
    • None:展平数组后计算整个范数。
    • 0:沿列计算。
    • 1:沿行计算。
  • 示例:axis=0 对矩阵每列单独计算。
  1. 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

image-20250220224204334

即计算的是\(\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)

image-20250220224414994

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)

image-20250221105929670

方法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]) # 答案要的是这个

image-20250221110208253

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        

image-20250221110643964

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]

image-20250221111257175

感觉这道题就是考察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.datetime64numpy.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)

image-20250221114036697

当然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}")

image-20250221114048441

访问属性:特别注意: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)

image-20250221114406263

简单的性能比较:创建 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")

image-20250221114644285

显然,在创建和处理大量日期时间数据时,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

image-20250221114908987

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)

image-20250221115538021

注: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,那么计算过程就是:

image-20250221120105499

  1. 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 填充)
  2. 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 填充)
  3. 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
  4. 继续移动 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 填充)
  5. weights 完全位于 Z 的右侧,不重叠: 不进行计算 (已经到末尾)。

如果mode 设置为same,计算过程就是:

  1. 填充(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)。
  2. 卷积(Convolution):
    • 对填充后的 Z_padded 和 weights 执行卷积操作。 因为 Z_padded 有 12 个元素,weights 有 3 个元素,所以卷积结果(如果使用 mode=’full’)将有 12 + 3 - 1 = 14 个元素。
  3. 裁剪(Cropping):
    • 从卷积结果中裁剪一个与原始数组 Z 大小相同的子数组。 裁剪的位置取决于 len(weights) 是奇数还是偶数。
      • 如果 len(weights) 是奇数 (就像我们的例子),则输出将以 Z 的中心对齐。 这意味着我们将从卷积结果的中心位置向前和向后裁剪,以获得与 Z 大小相同的数组。
      • 如果 len(weights) 是偶数,则输出将略微偏向右侧。
    • 由于输出需要和Z一样是10个元素,因此需要裁剪掉头尾各2个元素。

image-20250221120257258

image-20250221120324409

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)

image-20250221120549581

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 ]


已经有的是:

image-20250221120736908

也就是因为步长是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)

image-20250221121018032

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)

image-20250221121711264