1小时玩转numpy

Numpy
我们要开始接触高效计算库Numpy了,你要是之前在实验室用MATLAB之类的语法,你会发现Numpy和它们长得不要太像,爱MATLAB的同学,参考文档可以看这里
python里面调用一个包,用import对吧, 所以我们import numpy 包:

import numpy as np

Arrays/数组
看你数组的维度啦,我自己的话比较简单粗暴,一般直接把1维数组就看做向量/vector,2维数组看做2维矩阵,3维数组看做3维矩阵…

可以调用np.array去从list初始化一个数组:

a = np.array([1, 2, 3])  # 1维数组
print(type(a), a.shape, a[0], a[1], a[2])
a[0] = 5                 # 重新赋值
print(a)

b = np.array([[1,2,3],[4,5,6]])   # 2维数组
print(b.shape)

print(b.shape)  #可以看形状的(非常常用!!!)
print(b[0, 0], b[0, 1], b[1, 0])

# 有一些内置的创建数组的函数:
a = np.zeros((2,2))  # 创建2x2的全0数组
print(a)

b = np.ones((1,2))   # 创建1x2的全0数组
print(b)

c = np.full((2,2), 7) # 定值数组
print(c)

d = np.eye(2)        # 对角矩阵(对角元素为1print(d)

e = np.random.random((2,2)) # 2x2的随机数组(矩阵)
print(e)

输出:
<class 'numpy.ndarray'> (3,) 1 2 3
[5 2 3]
(2, 3)
(2, 3)
1 2 4
[[0. 0.]
 [0. 0.]]
[[1. 1.]]
[[7 7]
 [7 7]]
[[1. 0.]
 [0. 1.]]
[[0.13290724 0.48434   ]
 [0.03913866 0.81151244]]

Array indexing/数组取值
Numpy提供了蛮多种取值的方式的.

可以像list一样切片(多维数组可以从各个维度同时切片):

import numpy as np

# 创建一个如下格式的3x4数组
# [[ 1  2  3  4]
#  [ 5  6  7  8]
#  [ 9 10 11 12]]
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])

# 在两个维度上分别按照[:2]和[1:3]进行切片,取需要的部分
# [[2 3]
#  [6 7]]
b = a[:2, 1:3]
print(b)

# 虽然,怎么说呢,不建议你这样去赋值,但是你确实可以修改切片出来的对象,然后完成对原数组的赋值.
print(a[0, 1])
b[0, 0] = 77    # b[0, 0]改了,很遗憾a[0, 1]也被修改了
print(a[0, 1])

# 创建3x4的2维数组/矩阵
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
print(a)

# 你就放心大胆地去取你想要的数咯:
row_r1 = a[1, :]    # 第2行,但是得到的是1维输出(列向量)
row_r2 = a[1:2, :]  # 1x2的2维输出
row_r3 = a[[1], :]  # 同上
print(row_r1, row_r1.shape)
print(row_r2, row_r2.shape)
print(row_r3, row_r3.shape)

# 试试在第2个维度上切片也一样的:
col_r1 = a[:, 1]
col_r2 = a[:, 1:2]
print(col_r1, col_r1.shape)
print()
print(col_r2, col_r2.shape)

# 下面这个高级了,更自由地取值和组合,但是要看清楚一点:
a = np.array([[1,2], [3, 4], [5, 6]])

# 其实意思就是取(0,0),(1,1),(2,0)的元素组起来
print(a[[0, 1, 2], [0, 1, 0]])

# 下面这个比较直白啦
print(np.array([a[0, 0], a[1, 1], a[2, 0]]))

# 再来试试
print(a[[0, 0], [1, 1]])

# 还是一样
print(np.array([a[0, 1], a[0, 1]]))

# 再来熟悉一下
# 先创建一个2维数组
a = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
print(a)

# 用下标生成一个向量
b = np.array([0, 2, 0, 1])

# 你能看明白下面做的事情吗?
print(a[np.arange(4), b])  # Prints "[ 1  6  7 11]"

# 既然可以取出来,我们当然可以对这些元素操作咯
a[np.arange(4), b] += 10
print(a)

# 比较fashion的取法之一,用条件判定去取(但是很好用):
a = np.array([[1,2], [3, 4], [5, 6]])

bool_idx = (a > 2)  # 就是判定一下是否大于2

print(bool_idx)  # 返回一个布尔型的3x2数组

# 用刚才的布尔型数组作为下标就可以去除符合条件的元素啦
print(a[bool_idx])

# 其实一句话也可以完成是不是?
print(a[a > 2])

输出:
[[2 3]
 [6 7]]
2
77
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
[5 6 7 8] (4,)
[[5 6 7 8]] (1, 4)
[[5 6 7 8]] (1, 4)
[ 2  6 10] (3,)

[[ 2]
 [ 6]
 [10]] (3, 1)
[1 4 5]
[1 4 5]
[2 2]
[2 2]
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]
[ 1  6  7 11]
[[11  2  3]
 [ 4  5 16]
 [17  8  9]
 [10 21 12]]
[[False False]
 [ True  True]
 [ True  True]]
[3 4 5 6]
[3 4 5 6]

那个,真的,其实还有很多细节,其他的方式去取值,你可以看看官方文档。

我们一起来来总结一下,看下面切片取值方式(对应颜色是取出来的结果):
Datatypes
我们可以用dtype来看numpy数组中元素的类型:

x = np.array([1, 2])  # numpy构建数组的时候自己会确定类型
y = np.array([1.0, 2.0])
z = np.array([1, 2], dtype=np.int64)# 指定用int64构建

print(x.dtype, y.dtype, z.dtype)

输出:
int32 float64 int64

数学运算
下面这些运算才是你在科学运算中经常经常会用到的,比如逐个元素的运算如下:

x = np.array([[1,2],[3,4]], dtype=np.float64)
y = np.array([[5,6],[7,8]], dtype=np.float64)

# 逐元素求和有下面2种方式
print(x + y)
print(np.add(x, y))
# 逐元素作差
print(x - y)
print(np.subtract(x, y))

# 逐元素相乘
print(x * y)
print(np.multiply(x, y))

# 逐元素相除
# [[ 0.2         0.33333333]
#  [ 0.42857143  0.5       ]]
print(x / y)
print(np.divide(x, y))

# 逐元素求平方根!!!
# [[ 1.          1.41421356]
#  [ 1.73205081  2.        ]]
print(np.sqrt(x))
# 那如果我要做矩阵的乘法运算怎么办!!!恩,别着急,照着下面写就可以了:
x = np.array([[1,2],[3,4]])
y = np.array([[5,6],[7,8]])

v = np.array([9,10])
w = np.array([11, 12])

# 求向量内积
print(v.dot(w))
print(np.dot(v, w))

# 矩阵的乘法
print(x.dot(v))
print(np.dot(x, v))

# 矩阵的乘法
# [[19 22]
#  [43 50]]
print(x.dot(y))
print(np.dot(x, y))

# 你猜你做科学运算会最常用到的矩阵内元素的运算是什么?对啦,是求和,用 sum可以完成:
x = np.array([[1,2,5],[3,4,6]])
print(x.shape)
print(np.sum(x))  # 数组/矩阵中所有元素求和; prints "10"
print(np.sum(x, axis=0))  # 按列去求和; prints "[4 6]"
print(np.sum(x, axis=1)) # 按行去求和; prints "[3 7]"

输出:
[[ 6.  8.]
 [10. 12.]]
[[ 6.  8.]
 [10. 12.]]
[[-4. -4.]
 [-4. -4.]]
[[-4. -4.]
 [-4. -4.]]
[[ 5. 12.]
 [21. 32.]]
[[ 5. 12.]
 [21. 32.]]
[[0.2        0.33333333]
 [0.42857143 0.5       ]]
[[0.2        0.33333333]
 [0.42857143 0.5       ]]
[[1.         1.41421356]
 [1.73205081 2.        ]]
219
219
[29 67]
[29 67]
[[19 22]
 [43 50]]
[[19 22]
 [43 50]]
(2, 3)
21
[ 4  6 11]
[ 8 13]

我想说最基本的运算就是上面这个样子,更多的运算可能得查查文档.

其实除掉基本运算,我们经常还需要做一些操作,比如矩阵的变形,转置和重排等等:

# 转置和数学公式一直,简单粗暴
print(x)
print(x.T)

# 需要说明一下,1维的vector转置还是自己
v = np.array([1,2,3])
print(v)
print(v.T)

# 2维的就不一样了
w = np.array([[1,2,3]])
print(w)
print(w.T)

输出:
[[1 2 5]
 [3 4 6]]
[[1 3]
 [2 4]
 [5 6]]
[1 2 3]
[1 2 3]
[[1 2 3]]
[[1]
 [2]
 [3]]

Broadcasting
这个没想好哪个中文词最贴切,我们暂且叫它“传播吧”:
作用是什么呢,我们设想一个场景,如果要用小的矩阵去和大的矩阵做一些操作,但是希望小矩阵能循环和大矩阵的那些块做一样的操作,那急需要Broadcasting啦

# 我们要做一件事情,给x的每一行都逐元素加上一个向量,然后生成y
x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
y = np.empty_like(x)   # 生成一个和x维度一致的空数组/矩阵

# 比较粗暴的方式是,用for循环逐个相加
for i in range(4):
    y[i, :] = x[i, :] + v

print(y)

输出:
[[ 2  2  4]
 [ 5  5  7]
 [ 8  8 10]
 [11 11 13]]

这种方法当然可以啦,问题是不高效嘛,如果你的x矩阵行数非常多,那就很慢的咯:
咱们调整一下,先生成好要加的内容

vv = np.tile(v, (4, 1))  # 重复4遍v,叠起来
print(vv)                 # Prints "[[1 0 1]
                         #          [1 0 1]
                         #          [1 0 1]
                         #          [1 0 1]]"

y = x + vv  # 这样求和大家都能看明白对吧
print(y)


import numpy as np

# 因为broadcasting的存在,你上面的操作可以简单地汇总成一个求和操作
x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
y = x + v  # Add v to each row of x using broadcasting
print(y)

输出:
[[1 0 1]
 [1 0 1]
 [1 0 1]
 [1 0 1]]
[[ 2  2  4]
 [ 5  5  7]
 [ 8  8 10]
 [11 11 13]]
[[ 2  2  4]
 [ 5  5  7]
 [ 8  8 10]
 [11 11 13]]

当操作两个array时,numpy会逐个比较它们的shape,在下述情况下,两arrays会兼容和输出broadcasting结果:

相等
其中一个为1,(进而可进行拷贝拓展已至,shape匹配)
比如求和的时候有:

Image (3d array): 256 x 256 x 3
Scale (1d array): 3
Result (3d array): 256 x 256 x 3

A (4d array): 8 x 1 x 6 x 1
B (3d array): 7 x 1 x 5
Result (4d array): 8 x 7 x 6 x 5

A (2d array): 5 x 4
B (1d array): 1
Result (2d array): 5 x 4

A (2d array): 15 x 3 x 5
B (1d array): 15 x 1 x 5
Result (2d array): 15 x 3 x 5
下面是一些 broadcasting 的例子:

# 我们来理解一下broadcasting的这种用法
v = np.array([1,2,3])  # v 形状是 (3,)
w = np.array([4,5])    # w 形状是 (2,)
# 先把v变形成3x1的数组/矩阵,然后就可以broadcasting加在w上了:
print(np.reshape(v, (3, 1)) * w)

# 那如果要把一个矩阵的每一行都加上一个向量呢
x = np.array([[1,2,3], [4,5,6]])
v = np.array([1,2,3])
# 恩,其实是一样的啦
print(x + v)

x = np.array([[1,2,3], [4,5,6]]) # 2x3的
w = np.array([4,5])    # w 形状是 (2,)

# 自己算算看?
print((x.T + w).T)

# 上面那个操作太复杂了,其实我们可以直接这么做嘛
print(x + np.reshape(w, (2, 1)))

# broadcasting当然可以逐元素运算了
print(x * 2)

输出:
[[ 4  5]
 [ 8 10]
 [12 15]]
[[2 4 6]
 [5 7 9]]
[[ 5  6  7]
 [ 9 10 11]]
[[ 5  6  7]
 [ 9 10 11]]
[[ 2  4  6]
 [ 8 10 12]]

个人微信公众号,专注于学习资源、笔记分享,欢迎关注。我们一起成长,一起学习。一直纯真着,善良着,温情地热爱生活,,如果觉得有点用的话,请不要吝啬你手中点赞的权力,谢谢我亲爱的读者朋友
五角钱的程序员,专注于学习资源、笔记分享。

2020年2月14日于重庆城口
好好学习,天天向上,终有所获

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值