shape  形状
本文最后更新于4 天前,其中的信息可能已经过时,如有错误请发送邮件到184874483@qq.com

先看整句代码到底在做什么

答案是:

import numpy

arr = list(map(int, input().split()))
my_array = numpy.array(arr)
print(numpy.reshape(my_array, (3, 3)))

这段代码其实是在做 3 件事:

  1. 读入一行数据
  2. 把这一行数据拆成 9 个整数
  3. 把这 9 个整数变成一个 3 行 3 列的 NumPy 数组

现在卡住的两个点很典型:

  • input().split() 到底是什么意思
  • reshape() 里面几个参数到底分别表示什么

下面把这两个地方拆开讲清楚。


input().split() 到底是什么意思

input() 是什么

input() 的作用是:

从键盘读入一整行内容,并且结果默认是字符串 str 类型。

比如你输入:

1 2 3 4 5 6 7 8 9

那么:

x = input()
print(x)
print(type(x))

得到的会是:

1 2 3 4 5 6 7 8 9
<class 'str'>

也就是说,input() 读到的是一整行文本,不是数字列表。


split() 是什么

split() 的作用是:

把一个字符串按照分隔符切开,得到一个列表。

如果什么都不写,默认按“空白字符”来切分,也就是:

  • 空格
  • 多个空格
  • Tab
  • 换行前的空白

例如:

s = "1 2 3 4 5"
print(s.split())

结果是:

['1', '2', '3', '4', '5']

注意,这里得到的还是字符串列表,不是整数列表。

也就是说:

input().split()

整体意思就是:

读入一整行字符串,再按空格拆开,变成一个个小字符串组成的列表。

比如用户输入:

1 2 3 4 5 6 7 8 9

那么:

input().split()

得到的就是:

['1', '2', '3', '4', '5', '6', '7', '8', '9']

为什么还要 map(int, input().split())

因为 split() 拆出来的每一项都是字符串,比如 '1''2''3'

但题目需要的是整数,所以要把这些字符串一个个转成 int

map(int, ...) 是什么意思

map 的作用可以先用最直白的话理解成:

把某个操作,依次作用到一组数据的每一个元素上。

这里写的是:

map(int, input().split())

意思就是:

input().split() 得到的每个字符串,都拿去做一次 int(...) 转换。

比如:

['1', '2', '3']

经过:

map(int, ...)

就相当于变成:

1, 2, 3

不过在 Python 3 里,map(...) 返回的不是直接可见的列表,而是一个可迭代对象,所以通常再套一个 list(...)

arr = list(map(int, input().split()))

最后就得到真正的列表:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

这一整句如何一步一步理解

这句代码:

arr = list(map(int, input().split()))

可以拆成 3 小步来看。

第一步:先输入一整行

text = input()

如果输入:

1 2 3 4 5 6 7 8 9

那么:

text == "1 2 3 4 5 6 7 8 9"

第二步:按空格切开

parts = text.split()

这时:

parts == ['1', '2', '3', '4', '5', '6', '7', '8', '9']

第三步:把每个字符串转成整数

arr = list(map(int, parts))

这时:

arr == [1, 2, 3, 4, 5, 6, 7, 8, 9]

等价写法是什么

你可以把它写得更展开一点,这样更适合初学者理解:

text = input()
parts = text.split()
arr = []

for x in parts:
    arr.append(int(x))

print(arr)

这个写法和下面这句本质上做的是同一件事:

arr = list(map(int, input().split()))

只是前者更慢、更清楚;后者更简洁。


reshape() 到底是什么意思

reshape 是干什么的

reshape 的作用是:

在不改变数据内容的前提下,重新安排数组的形状。

比如原来是一维:

[1 2 3 4 5 6 7 8 9]

可以重新排成二维:

[[1 2 3]
 [4 5 6]
 [7 8 9]]

注意,它不是改数字,而是改“摆放方式”。


numpy.reshape(my_array, (3, 3)) 里面的两个参数分别是什么

这句代码:

numpy.reshape(my_array, (3, 3))

可以这样理解:

第 1 个参数:my_array

表示:

你要对哪个数组进行重塑。

也就是原始数组。

比如:

my_array = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9])

第 2 个参数:(3, 3)

表示:

你想把它变成什么形状。

这里 (3, 3) 是一个元组,意思是:

  • 3 行
  • 3 列

所以:

numpy.reshape(my_array, (3, 3))

就是:

my_array 重新排成 3 行 3 列。


为什么这里要写成 (3, 3),而不是 3, 3

因为 reshape 需要一个“形状说明”,这个形状通常用元组来表示。

例如:

  • (3, 3) 表示 3 行 3 列
  • (1, 9) 表示 1 行 9 列
  • (9, 1) 表示 9 行 1 列

这几个形状都用了 9 个元素,所以都可以。

例如:

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9])

print(numpy.reshape(a, (3, 3)))
print(numpy.reshape(a, (1, 9)))
print(numpy.reshape(a, (9, 1)))

reshape 为什么能成功

因为你的数据正好有 9 个元素,而目标形状 (3, 3) 也需要:

3 * 3 = 9

元素个数完全一致,所以可以重塑成功。

一个非常重要的规则

reshape 前后,元素总数必须相同。

比如:

  • 9 个元素可以变成 (3, 3)
  • 9 个元素可以变成 (1, 9)
  • 9 个元素可以变成 (9, 1)

但是不能变成:

  • (2, 2),因为只需要 4 个元素
  • (2, 5),因为需要 10 个元素

这都会报错。


这道题的完整执行过程

下面我们把整段代码完整模拟一遍。

原代码

import numpy

arr = list(map(int, input().split()))
my_array = numpy.array(arr)
print(numpy.reshape(my_array, (3, 3)))

假设输入

1 2 3 4 5 6 7 8 9

第一步

input().split()

得到:

['1', '2', '3', '4', '5', '6', '7', '8', '9']

第二步

map(int, ...)

把每个字符串转成整数。

第三步

list(...)

得到列表:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

所以:

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]

第四步

my_array = numpy.array(arr)

把 Python 列表变成 NumPy 数组。

第五步

numpy.reshape(my_array, (3, 3))

把一维数组重新排成 3 行 3 列,结果就是:

[[1 2 3]
 [4 5 6]
 [7 8 9]]

从思路到代码,应该怎么想

做这种题时,不要一上来就盯着函数名,而是先想“数据怎么变化”。

这题的思路其实是这样的:

第一步:题目给我什么

题目给的是:

一行里有 9 个整数,中间用空格隔开。

所以你要立刻想到:

  • 先读一整行
  • 再按空格切开

对应代码:

input().split()

第二步:切开后是什么

切开后得到的是:

字符串列表

不是整数,所以还不能直接当数字用。

因此还要:

  • 把每个字符串转成整数

对应代码:

map(int, ...)

第三步:最终想要什么

题目要的是:

一个 3×3 的 NumPy array

所以先变成 NumPy 数组:

numpy.array(arr)

再改成 3 行 3 列:

numpy.reshape(my_array, (3, 3))

最小可运行例子

你可以先单独跑这个小例子,专门观察 split()reshape()

import numpy

text = "1 2 3 4 5 6 7 8 9"
parts = text.split()
print(parts)

arr = list(map(int, parts))
print(arr)

my_array = numpy.array(arr)
print(my_array)

new_array = numpy.reshape(my_array, (3, 3))
print(new_array)

你运行后,会一步步看到数据怎么变。


初学者最容易混淆的几个点

1. input() 读到的默认是字符串

这是最常见错误。

比如输入:

1 2 3

不是直接得到数字列表,而是得到一个字符串:

"1 2 3"

2. split() 得到的还是字符串列表

比如:

['1', '2', '3']

注意有引号,说明还是字符串。

如果不转成 int,后面很多数值操作都会不对。


3. reshape() 不是随便改形状

它必须满足:

新形状需要的元素总数 = 原来数组的元素总数

这是硬规则。


4. reshape() 改的是形状,不是内容

它不会把 1 变成别的数,也不会排序。

它只是重新排列显示方式。


再补一个你很容易遇到的写法

除了:

numpy.reshape(my_array, (3, 3))

你以后还会看到这种写法:

my_array.reshape(3, 3)

这两个意思很接近。

对于初学阶段,你可以先这样记:

  • numpy.reshape(my_array, (3, 3)):把数组传进去再重塑
  • my_array.reshape(3, 3):直接让这个数组自己重塑

在这道题里,用哪种都可以。


本节小结

这道题最核心的两部分可以这样记:

input().split()

表示:

读入一整行字符串,再按空格拆开,得到字符串列表。

例如:

"1 2 3".split()

得到:

['1', '2', '3']

list(map(int, input().split()))

表示:

把拆出来的每个字符串都转成整数,最后得到整数列表。

例如输入:

1 2 3

得到:

[1, 2, 3]

numpy.reshape(my_array, (3, 3))

表示:

把数组 my_array 改成 3 行 3 列的形状。

其中:

  • my_array 是要被改形状的数组
  • (3, 3) 是目标形状,意思是 3 行 3 列

而且必须满足:

原元素总数 = 新形状所需元素总数

练习

请你自己试着写一个小程序:

把输入的一行 6 个整数,转换成一个 2 x 3 的 NumPy 数组并打印出来。

提示:

  1. 先用 input().split() 把一行拆开
  2. 再把每个字符串转成整数
  3. numpy.array() 变成数组
  4. reshape 改成 (2, 3)

补充:为什么 split() 后得到的是字符串,而不是整数

这个问题非常关键。很多初学者第一次看到:

input().split()

都会下意识觉得,既然我输入的是 1 2 3,那分开以后应该就是数字 1, 2, 3。但 Python 并不会这样做。

原因很简单:

input() 读进来的,本质上就是一整行文本。

也就是说,当你在键盘输入:

1 2 3

程序眼里看到的不是三个数字,而是一串字符:

"1 2 3"

既然 split() 是对字符串做操作,那么它做的事情只是:

把这串文本按空格切成几段文本。

所以:

"1 2 3".split()

得到的是:

['1', '2', '3']

这里每一项仍然是字符串,不是整数。

为什么 Python 不自动把它变成整数

因为 Python 不会替你“猜”。

比如下面这些输入:

"1 2 3"
"001 002 003"
"1.5 2.8 3.1"
"hello world"

如果 split() 自动转数字,就会出现很多问题:

  • 到底该转成 int 还是 float
  • 如果里面有单词怎么办
  • 如果既有数字又有字母怎么办

所以 Python 采取的规则很统一:

split() 只负责“切开字符串”,不负责“理解内容类型”。

类型转换这一步,要你自己明确写出来。

这也是为什么我们后面要写:

list(map(int, input().split()))

意思就是:

  • 先拆开
  • 再明确告诉 Python:这些内容我要按整数来处理

map 到底是怎么逐个处理数据的

先用一句最直白的话理解

map 的作用可以理解成:

把同一个操作,依次应用到一组数据的每一个元素上。

比如:

parts = ['1', '2', '3']

如果你写:

map(int, parts)

意思就是:

  • '1'int('1')
  • '2'int('2')
  • '3'int('3')

最后就变成整数:

1, 2, 3

不过在 Python 3 里,map(...) 返回的不是现成列表,而是一个“可迭代对象”,所以通常再用 list(...) 把结果真正拿出来:

arr = list(map(int, parts))

最后得到:

[1, 2, 3]

map 翻译成最朴素的 for 循环

下面这两种写法,本质是一样的。

写法 1:用 map

parts = ['1', '2', '3']
arr = list(map(int, parts))
print(arr)

写法 2:用 for 循环展开

parts = ['1', '2', '3']
arr = []

for x in parts:
    arr.append(int(x))

print(arr)

所以你可以把:

list(map(int, input().split()))

脑子里翻译成:

  1. 先得到一个字符串列表
  2. 再把列表里的每个元素都做一次 int()
  3. 最后收集成列表

map 是“马上全部处理完”吗

从初学角度,你可以先把它理解成“逐个处理”。

更准确地说,在 Python 3 里,map 是一种“按需要一个一个产出结果”的对象。它不是一开始就把所有结果都摆在你面前。

但在你写:

list(map(int, input().split()))

的时候,list(...) 会把这些结果全部取出来,所以你最后还是得到一个完整列表。

对于入门阶段,你可以先这样记:

  • map(int, ...):让每个元素做一次 int
  • list(...):把处理后的结果真正装进列表里

这就足够了。


如果我修改 split() 里面的参数,会怎么样

情况 1:什么都不写

text = "1 2 3 4"
print(text.split())

结果:

['1', '2', '3', '4']

这表示按空白字符分开,是最常见用法。


情况 2:指定分隔符

比如字符串里不是空格,而是逗号:

text = "1,2,3,4"
print(text.split(','))

结果:

['1', '2', '3', '4']

这说明:

你传给 split() 什么分隔符,它就按什么切。

如果你分隔符写错了,就切不开。

例如:

text = "1,2,3,4"
print(text.split())

结果会是:

['1,2,3,4']

因为这个字符串里没有空格,所以默认按空格切时,整个字符串就还是一整块。


情况 3:限制切几次

split() 还可以控制最多切几次,例如:

text = "a b c d"
print(text.split(maxsplit=1))

结果:

['a', 'b c d']

意思是:

  • 只切一次
  • 前面切出 'a'
  • 后面剩下的全部作为一个整体

这个在初学阶段不是最常用,但你以后会见到。


如果我修改 map() 里的参数,会怎么样

map 的基本结构是:

map(函数, 可迭代对象)

也就是说,map 最核心的两个部分是:

  • 你想对每个元素做什么操作
  • 你想处理哪一组数据

例 1:把字符串转成整数

parts = ['1', '2', '3']
print(list(map(int, parts)))

结果:

[1, 2, 3]

例 2:把字符串转成浮点数

parts = ['1.5', '2.8', '3.2']
print(list(map(float, parts)))

结果:

[1.5, 2.8, 3.2]

例 3:把每个字符串变成大写

parts = ['apple', 'banana']
print(list(map(str.upper, parts)))

结果:

['APPLE', 'BANANA']

所以你可以看到:

map 本身不决定做什么,真正决定行为的是你传进去的那个函数。

  • int,就转整数
  • float,就转浮点数
  • 传别的函数,就做别的处理

如果输入里有不能转换的内容,会怎么样

比如:

parts = ['1', '2', 'hello']
print(list(map(int, parts)))

这会报错,因为:

int('hello')

是做不到的。

所以 map(int, ...) 有一个前提:

列表里的每一项都必须能成功转成整数。


再看 reshape:它的参数改了以后会怎么样

你原题中写的是:

numpy.reshape(my_array, (3, 3))

这里第二个参数 (3, 3) 表示目标形状,也就是:

  • 3 行
  • 3 列

如果你换成别的形状,只要总元素数对得上,就都可以。

假设原数组有 6 个元素:

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6])

那么下面这些都可以:

print(a.reshape(2, 3))
print(a.reshape(3, 2))
print(a.reshape(1, 6))
print(a.reshape(6, 1))

因为它们需要的元素总数分别是:

  • 2 * 3 = 6
  • 3 * 2 = 6
  • 1 * 6 = 6
  • 6 * 1 = 6

都刚好等于原来的 6 个元素。


什么叫“平分”或者“均分”

这里说的“平分”“均分”,放到 reshape 里,实际可以这样理解:

你想把一串元素,平均安排到若干行或若干列中。

比如有 6 个元素:

[1, 2, 3, 4, 5, 6]

如果你想排成 2 行,那么每行就要放 3 个:

[[1 2 3]
 [4 5 6]]

如果你想排成 3 行,那么每行就要放 2 个:

[[1 2]
 [3 4]
 [5 6]]

这就是一种“整齐均分”。


如果不能均分,会怎么样

这是 reshape 里最重要的规则之一:

总元素个数必须正好等于目标形状所需的格子数。

例如你有 7 个元素:

[1, 2, 3, 4, 5, 6, 7]

你想把它变成 3 x 3

a.reshape(3, 3)

这是不行的,因为:

3 * 3 = 9

但你只有 7 个元素,不够填满 9 个位置。

NumPy 不会帮你自动补空,也不会偷偷丢掉元素,它会直接报错。

通常会看到类似这样的错误:

ValueError: cannot reshape array of size 7 into shape (3,3)

意思就是:

  • 原数组大小是 7
  • 你想变成 (3,3),需要 9 个位置
  • 对不上,所以失败

不能形成完整数组时,NumPy 会怎么做

标准二维数组要求每一行长度一致,也就是必须是规则矩阵。

所以如果你想把一维数据重排成二维,NumPy 要求必须“排得整整齐齐”。

它不会这样做:

  • 不会自动补 0
  • 不会自动补空值
  • 不会自动舍弃多出来的元素

而是直接报错,让你自己决定该怎么处理。

这其实是很好的设计,因为它避免程序悄悄做错事。


如果长度不合适,通常怎么处理

方法 1:改目标形状

比如你有 8 个元素:

[1, 2, 3, 4, 5, 6, 7, 8]

不能变成 (3, 3),但可以变成:

  • (2, 4)
  • (4, 2)
  • (1, 8)
  • (8, 1)

只要乘起来等于 8 就行。


方法 2:补齐数据

如果你就是想要 3 x 3,那你得自己补到 9 个元素。

例如:

import numpy

arr = [1, 2, 3, 4, 5, 6, 7, 8]
arr.append(0)

a = numpy.array(arr)
print(a.reshape(3, 3))

这样才行。


方法 3:截断数据

如果元素太多,也可以手动截掉一部分,再重塑。

例如有 10 个元素,但你只想做 3 x 3,那就先只取前 9 个。


用具体例子看“能不能形成完整数组”

可以成功的例子

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6])
print(a.reshape(2, 3))

结果:

[[1 2 3]
 [4 5 6]]

因为 2 * 3 = 6,刚好。


失败的例子

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6, 7])
print(a.reshape(2, 3))

会报错,因为 2 * 3 = 6,但你有 7 个元素,多了一个,也不行。

很多初学者只注意“不够不行”,其实“多了也不行”。

规则不是“差不多就行”,而是:

必须完全相等。


这几部分合在一起,再完整看一遍

看下面这段代码:

import numpy

arr = list(map(int, input().split()))
my_array = numpy.array(arr)
print(my_array.reshape(3, 3))

假设输入是:

1 2 3 4 5 6 7 8 9

程序会这样走:

第一步:input()

得到整行字符串:

"1 2 3 4 5 6 7 8 9"

第二步:split()

拆成字符串列表:

['1', '2', '3', '4', '5', '6', '7', '8', '9']

第三步:map(int, ...)

对每个元素做 int(...)

1, 2, 3, 4, 5, 6, 7, 8, 9

第四步:list(...)

收集成列表:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

第五步:numpy.array(...)

变成 NumPy 数组。

第六步:reshape(3, 3)

重排成 3 行 3 列,因为总元素正好是 9 个,所以成功。


最容易错的几个点

第一,split() 不会自动转数字

它只负责切开,不负责类型转换。


第二,map(int, ...) 里的每一项都必须能转成整数

只要有一个不能转,整个过程就会报错。


第三,reshape 必须严格匹配元素总数

不是“大概差不多”,而是必须完全相等。


第四,不能整齐排开时,NumPy 不会替你补齐

你必须自己决定:

  • 改形状
  • 补数据
  • 删数据

本节小结

这几个地方可以这样记:

split() 为什么得到字符串

因为 input() 本来读进来的就是文本,split() 只是把文本切开,所以结果还是文本,也就是字符串。


map 是怎么逐个处理的

map(int, data) 就是把 data 里的每个元素都依次做一次 int(...)

你可以把它想成一个自动化的 for 循环。


改参数会怎样

  • split() 的参数,就是改“按什么切”
  • map() 的函数,就是改“对每个元素做什么处理”
  • reshape() 的形状,就是改“想排成几行几列”

不能形成完整数组怎么办

reshape 时,元素总数必须和目标形状完全一致。

如果不一致,NumPy 会直接报错,不会自动补齐,也不会自动删除。


练习

请你自己试着判断下面每一行代码的结果,先不要运行,先靠脑子想:

text = "10 20 30"
print(text.split())
print(list(map(int, text.split())))

再判断下面这两句,哪一句会成功,哪一句会报错:

import numpy
a = numpy.array([1, 2, 3, 4, 5, 6])

print(a.reshape(2, 3))
print(a.reshape(4, 2))

提示:

  • 先看 split() 后每个元素是什么类型
  • 再看 reshape 时,目标形状需要几个位置

shapereshape 的区别到底是什么

这是 NumPy 里一个非常容易混淆的点,因为这两个单词长得很像,而且都和“形状”有关。

但它们的作用其实完全不同:

  • shape看形状
  • reshape改形状

你可以先把它们记成一句最直白的话:

shape 是“现在长什么样”,reshape 是“把它改成长什么样”。

下面把这个区别彻底讲清楚。


shape 是干什么的

shape 的作用是:

查看一个数组当前的形状。

它返回的是一个元组 tuple,里面记录了每个维度的长度。

一维数组的 shape

import numpy

a = numpy.array([1, 2, 3, 4, 5])
print(a.shape)

输出:

(5,)

这里的 (5,) 表示:

  • 这是一个一维数组
  • 里面有 5 个元素

注意这个逗号不能忽略。
(5,) 不是普通括号包个数字,它表示“一维元组”。


二维数组的 shape

import numpy

a = numpy.array([[1, 2, 3],
                 [4, 5, 6]])
print(a.shape)

输出:

(2, 3)

意思是:

  • 2 行
  • 3 列

所以 shape 的本质就是:

告诉你这个数组现在是几行几列,或者更一般地说,各个维度分别有多长。


reshape 是干什么的

reshape 的作用是:

把数组重新变成另一种形状。

例如:

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6])
b = a.reshape(2, 3)

print(b)

输出:

[[1 2 3]
 [4 5 6]]

原来 a 是一维的,现在通过 reshape(2, 3) 变成了二维数组。

所以:

  • shape 是“描述”
  • reshape 是“操作”

用一句类比来理解

你可以把数组想成一堆积木。

shape

是在问:

这些积木现在是怎么摆的?

比如是:

  • 1 排摆 6 个
  • 2 排,每排 3 个
  • 3 排,每排 2 个

这就是 shape 在做的事:看当前摆法。

reshape

是在做:

把这些积木重新摆一摆。

但前提是积木总数不能变。


最核心的区别

下面是最重要的区别,你要真正记住。

名称作用本质
shape查看当前形状属性
reshape()改变形状方法 / 函数

也就是说:

shape 不负责修改

它只是告诉你:

a.shape

相当于在问:

“你现在是什么形状?”


reshape 不负责查看当前形状

它是在说:

a.reshape(2, 3)

相当于在命令:

“请你变成 2 行 3 列。”


先看一个完整例子

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6])

print(a)
print(a.shape)

b = a.reshape(2, 3)
print(b)
print(b.shape)

输出逻辑是:

一开始的 a

[1 2 3 4 5 6]

它是一维数组,所以:

a.shape

结果是:

(6,)

经过 reshape(2, 3) 之后

[[1 2 3]
 [4 5 6]]

这时:

b.shape

结果是:

(2, 3)

所以你能看到:

  • shape 告诉你原来是 (6,)
  • reshape 把它改成 (2, 3)
  • 再用 shape 看,就变成 (2, 3)

shape 为什么不加括号,reshape 为什么要加括号

这也是初学者特别容易混淆的地方。

shape 是属性

写法是:

a.shape

它像是在读取“这个对象的某个信息”。

就像:

student.name
student.age

shape 是数组自带的一个属性,表示它当前的形状,所以不用加括号。


reshape 是方法

写法是:

a.reshape(2, 3)

因为它是一个动作,要告诉它“改成什么形状”,所以要传参数。

所以它需要括号。

你可以这样记:

  • shape:像“信息”
  • reshape:像“动作”

从执行过程来看,二者有什么不同

shape 的执行过程

比如:

a = numpy.array([[1, 2, 3],
                 [4, 5, 6]])
print(a.shape)

程序并没有改动数组,只是在读取:

  • 有几行
  • 有几列

最后返回:

(2, 3)

reshape 的执行过程

比如:

a = numpy.array([1, 2, 3, 4, 5, 6])
b = a.reshape(2, 3)

程序会做的事是:

  • 先看原数组有多少个元素
  • 再看目标形状 (2, 3) 需要多少个位置
  • 如果数量一致,就重新排列
  • 如果数量不一致,就报错

所以 reshape 是真正会参与“变形”的。


一个特别容易混淆的地方:a.shape = (2, 3) 是什么

你有时可能会看到这种写法:

a.shape = (2, 3)

这个在某些情况下也能改形状。

但对初学者来说,不建议把这个当主要写法。

你现在先把标准、最好理解的口径记牢:

  • a.shape:查看形状
  • a.reshape(2, 3):修改形状

这样最稳定,也最不容易混。


shapereshape 放在一起看

下面这段代码最能看出区别:

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6])

print("原来的数组:", a)
print("原来的形状:", a.shape)

b = a.reshape(2, 3)

print("变形后的数组:")
print(b)
print("变形后的形状:", b.shape)

你会发现:

  • shape 只是打印信息
  • reshape 才是把一维改成二维

如果 reshape 失败了,shape 还能看吗

当然可以。

例如:

import numpy

a = numpy.array([1, 2, 3, 4, 5])

print(a.shape)
print(a.reshape(2, 3))

这里:

  • a.shape 会先正常输出 (5,)
  • reshape(2, 3) 会报错

因为:

2 * 3 = 6

而数组只有 5 个元素。

这也说明:

  • shape 只是读取当前状态
  • reshape 要满足严格条件才能成功

reshape 的前提条件是什么

这个一定要再强调一次:

reshape 前后,元素总数必须一致。

例如:

可以

a = numpy.array([1, 2, 3, 4, 5, 6])
a.reshape(2, 3)

因为 2 * 3 = 6


不可以

a = numpy.array([1, 2, 3, 4, 5, 6])
a.reshape(4, 2)

因为 4 * 2 = 8,和原来的 6 不一致。


你可以怎样实际判断该用哪个

遇到题目时,可以这样判断。

如果题目在问“这个数组现在是什么形状”

你就应该想到:

a.shape

比如:

  • 它是几行几列
  • 它是一维还是二维
  • 它当前尺寸是多少

这些都属于“看形状”。


如果题目在要求“把数组变成某种形状”

你就应该想到:

a.reshape(...)

比如:

  • 变成 3 x 3
  • 变成 2 x 4
  • 变成一列
  • 变成一行

这些都属于“改形状”。


最小可运行对比例子

下面这个例子非常适合你自己跑一遍。

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6])

print("a =", a)
print("a.shape =", a.shape)

b = a.reshape(3, 2)
print("b =")
print(b)
print("b.shape =", b.shape)

你运行后会很直观地看到:

  • a.shape(6,)
  • b.shape(3, 2)

也就是说:

  • shape 是结果信息
  • reshape 是变形过程

常见错误与易混点

1. 把 shapereshape 当成同一种东西

不是。

  • shape 是“形状信息”
  • reshape 是“改变形状的方法”

2. 写成 a.shape()

这是错的。

因为 shape 不是函数,不需要括号。

正确写法是:

a.shape

3. 写成 a.reshape 却不给参数

这也不行。

因为 reshape 要知道你想改成什么形状。

正确写法应该像:

a.reshape(2, 3)

4. 忘记检查元素个数是否匹配

这是 reshape 最常见错误。

只要总数对不上,就一定会报错。


本节小结

可以把今天这一节压缩成下面 4 句话。

第一句

shape 是看当前形状的。

例如:

a.shape

第二句

reshape 是改目标形状的。

例如:

a.reshape(2, 3)

第三句

shape 是属性,不加括号。
reshape 是方法,要加括号并传参数。


第四句

reshape 只有在元素总数完全匹配时才能成功。


练习

请你自己判断下面每一步的结果,不要先运行,先自己想。

import numpy

a = numpy.array([1, 2, 3, 4, 5, 6, 7, 8])
print(a.shape)

b = a.reshape(2, 4)
print(b)
print(b.shape)

再想一想,下面这句为什么会报错:

a.reshape(3, 3)

提示:

  • 先看 a 一共有几个元素
  • 再看 (2, 4)(3, 3) 分别需要多少个位置

NumPy 一维数组、二维数组,到底该怎么从 shape 上判断出来

这是一个非常重要、也非常容易混淆的问题。

很多初学者看到:

a.shape

返回一个结果后,往往只会机械地读数字,但不知道这个结果到底说明了什么。

其实判断方法可以先记一句最核心的话:

shape 这个元组里有几个数字,就大致能判断它是几维数组。

也就是说:

  • shape 里有 1 个数字,通常就是一维数组
  • shape 里有 2 个数字,通常就是二维数组
  • shape 里有 3 个数字,通常就是三维数组

下面我们专门把“一维”和“二维”讲透。


先回顾:shape 到底表示什么

shape 表示的是:

数组在每一个维度上分别有多长。

它返回的是一个元组 tuple

例如:

import numpy

a = numpy.array([10, 20, 30, 40])
print(a.shape)

输出:

(4,)

这个 (4,) 的意思不是“4 行 0 列”,也不是“4×1”。

它真正表示的是:

这个数组只有 1 个维度,这个维度长度是 4。

所以这是一个一维数组。


怎么从 shape 判断是不是一维数组

一维数组的特征

如果 shape 长这样:

(5,)
(10,)
(3,)

也就是:

  • 只有一个数字
  • 后面通常带一个逗号

那么它就是一维数组

例子

import numpy

a = numpy.array([1, 2, 3, 4, 5])
print(a)
print(a.shape)

输出:

[1 2 3 4 5]
(5,)

说明:

  • 数组里有 5 个元素
  • 它只有一个方向上的长度
  • 所以它是一维数组

为什么 (5,) 是一维,而不是二维

这是初学者非常容易误解的地方。

很多人看到 (5,),会下意识觉得:

“是不是 5 行 1 列?”

不是。

因为如果是二维数组,shape 必须有两个数字,比如:

(5, 1)

这才表示:

  • 5 行
  • 1 列

(5,) 只有一个数字,所以它只是“一维长度为 5”。

也就是说:

(5,)

表示:

一维数组,长度是 5

(5, 1)

表示:

二维数组,5 行 1 列

这两个完全不是一回事。


怎么从 shape 判断是不是二维数组

二维数组的特征

如果 shape 长这样:

(2, 3)
(4, 5)
(3, 1)
(1, 6)

也就是:

  • 有两个数字
  • 第一个数字表示行数
  • 第二个数字表示列数

那么它就是二维数组

例子

import numpy

a = numpy.array([[1, 2, 3],
                 [4, 5, 6]])
print(a)
print(a.shape)

输出:

[[1 2 3]
 [4 5 6]]
(2, 3)

这个 (2, 3) 表示:

  • 2 行
  • 3 列

所以这是一个二维数组。


用最直白的判断规则来记

你可以先这样记。

shape(n,)

那就是一维数组。

例如:

  • (3,)
  • (8,)
  • (100,)

shape(行数, 列数)

那就是二维数组。

例如:

  • (2, 3)
  • (3, 2)
  • (1, 5)
  • (5, 1)

为什么“一维数组”和“只有一行的二维数组”不是同一个东西

这个地方特别关键。

看下面两个数组。

第一种:一维数组

a = numpy.array([1, 2, 3])
print(a.shape)

输出:

(3,)

这是一维数组


第二种:二维数组,1 行 3 列

b = numpy.array([[1, 2, 3]])
print(b.shape)

输出:

(1, 3)

这是二维数组

虽然它看起来也像“横着排了 3 个数”,但本质不同。

为什么不同?

因为:

  • a 只有一个维度
  • b 有两个维度:第一个维度是行,第二个维度是列

所以它们不是一回事。


再看一个更容易混的例子:列向量

import numpy

c = numpy.array([[1],
                 [2],
                 [3]])
print(c.shape)

输出:

(3, 1)

这表示:

  • 3 行
  • 1 列

这也是二维数组,不是一维数组。

所以你现在要能清楚地区分这三种情况:

一维数组

numpy.array([1, 2, 3])

shape 是:

(3,)

二维数组,1 行 3 列

numpy.array([[1, 2, 3]])

shape 是:

(1, 3)

二维数组,3 行 1 列

numpy.array([[1], [2], [3]])

shape 是:

(3, 1)

shape 看“有几个维度”,本质看什么

本质上,看的是:

shape 这个元组里有几个数字。

一个数字

例如:

(4,)

说明只有一个维度,所以是一维数组。


两个数字

例如:

(2, 3)

说明有两个维度,所以是二维数组。


三个数字

例如:

(2, 3, 4)

说明有三个维度,所以是三维数组。

你现在主要学一维和二维,所以先把前两个分清楚最重要。


把“一维”和“二维”放在一起对比

下面这组对比非常重要。

数组写法shape几维说明
numpy.array([1, 2, 3])(3,)一维只有一个方向,长度为 3
numpy.array([[1, 2, 3]])(1, 3)二维1 行 3 列
numpy.array([[1], [2], [3]])(3, 1)二维3 行 1 列
numpy.array([[1, 2], [3, 4]])(2, 2)二维2 行 2 列

这个表你最好多看几遍,因为它能解决很多初学阶段的混乱。


从“列表写法”也能辅助判断

虽然你这次问的是“怎么从 shape 判断”,但我还是顺手补一句,因为这两者经常一起看更容易懂。

一维数组常见写法

numpy.array([1, 2, 3, 4])

里面只有一层方括号。


二维数组常见写法

numpy.array([[1, 2, 3],
             [4, 5, 6]])

里面是“列表套列表”。

也就是说:

  • 外层表示整体
  • 里面每个小列表通常表示一行

这通常就会变成二维数组。

不过真正最稳的判断方式,还是看 shape


最小可运行例子

你可以直接运行下面这段代码,看它们的 shape 有什么不同。

import numpy

a = numpy.array([1, 2, 3])
b = numpy.array([[1, 2, 3]])
c = numpy.array([[1], [2], [3]])
d = numpy.array([[1, 2], [3, 4]])

print("a.shape =", a.shape)
print("b.shape =", b.shape)
print("c.shape =", c.shape)
print("d.shape =", d.shape)

你会看到:

a.shape = (3,)
b.shape = (1, 3)
c.shape = (3, 1)
d.shape = (2, 2)

这组例子非常有代表性。


从思路到判断,实际该怎么做

以后你拿到一个 shape,可以按下面的顺序判断。

第一步:先看有几个数字

例如:

  • (5,) 里面有 1 个数字
  • (2, 3) 里面有 2 个数字

这一步就是在判断有几个维度。


第二步:如果是一个数字,就是一维

例如:

(8,)

表示一维数组,长度是 8。


第三步:如果是两个数字,就是二维

例如:

(3, 4)

表示二维数组,3 行 4 列。


第四步:不要把 (n,) 看成 (n, 1)

这是非常常见的误判。

  • (3,) 是一维
  • (3, 1) 是二维

这两个差别很大。


常见错误与易混点

1. 看到 (3,) 就以为是 3 行 1 列

错。

(3,) 只有一个维度,所以是一维数组,不是二维数组。


2. 觉得 [1, 2, 3][[1, 2, 3]] 差不多

表面看很像,实际不一样。

  • [1, 2, 3] 对应一维数组
  • [[1, 2, 3]] 对应二维数组

3. 认为“只有一行”就不算二维

错。

只要 shape 里有两个数字,它就是二维。

所以:

(1, 3)

虽然只有一行,但仍然是二维数组。


4. 认为“只有一列”就不算二维

也错。

(3, 1)

虽然只有一列,但仍然是二维数组。


本节小结

这节内容可以压缩成下面几句最核心的话。

第一条

shape 里有几个数字,就能大致判断是几维数组。


第二条

shape(n,),说明它是一维数组。

例如:

(5,)

表示一维,长度是 5。


第三条

shape(行数, 列数),说明它是二维数组。

例如:

(2, 3)

表示二维,2 行 3 列。


第四条

一定要分清:

  • (3,) 是一维
  • (1, 3) 是二维
  • (3, 1) 也是二维

这三个非常容易混。


练习

请你自己判断下面每个数组是一维还是二维,并说出它的形状含义。

import numpy

a = numpy.array([10, 20, 30, 40])
b = numpy.array([[10, 20, 30, 40]])
c = numpy.array([[10], [20], [30], [40]])
d = numpy.array([[1, 2], [3, 4], [5, 6]])

提示:

  1. 先分别看它们的 shape
  2. 数一数 shape 里有几个数字
  3. 一个数字就是一维,两个数字就是二维
  4. 如果是两个数字,就再解释成“几行几列”

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇