Scott's Blog

学则不固, 知则不惑

0%

NumPy 操作数组

对数组的操作是 NumPy 中很重要的一部分,了解这些内置的方法,可以让你的效率事半功倍。

操作数组

np.meshgrid方法

如果你给meshgrid两个一位数组,A像这样:

1
[𝑎1,𝑎2,𝑎3]

B像这样:

1
[𝑏1,𝑏2,𝑏3]

如果你运行 np.meshgrid(A,B),那么你会得到一个嵌套数组,里面分别是:

1
[[𝑎1,𝑎1,𝑎1],[𝑎2,𝑎2,𝑎2],[𝑎3,𝑎3,𝑎3]]

1
[[𝑏1,𝑏1,𝑏1],[𝑏2,𝑏2,𝑏2],[𝑏3,𝑏3,𝑏3]]

By adding these two arrays together, we can create the 2D array containing, as its elements, every combination of sums between the numbers in the original elements. Arrays such as linspace and arange are typically used to constuct N-D arrays used to plot in 3 dimensions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import plotly.plotly as py
import plotly.graph_objs as go

x = np.arange(-10, 10, 0.4)
y = np.arange(-10, 10, 0.4)
XX, YY = np.meshgrid(x, y)
ZZ = np.sin(XX**2 + YY**2) / (XX**2 + YY**2)

lines = []
line_marker = dict(color='#0066FF', width=2)
for i, j, k in zip(XX, YY, ZZ):
lines.append(go.Scatter3d(x=i, y=j, z=k, mode='lines', line=line_marker))

layout = go.Layout(
title='Wireframe with Meshgrid',
showlegend=False
)

fig = go.Figure(data=lines, layout=layout)
py.iplot(fig, filename='numpy-arange-to-meshgrid')
绘图请配合plotly

np.where方法

假设你有三个数组,第三个数组存的是布尔值,你想要按照条件去前两个数组中选值,如果第三个数组中的值是真,那么就去第一个数组中拿对应的值,否则就拿第二个数组中的值。

我们可以利用zip 函数实现这一需求:

1
2
3
4
5
6
7
8
9
10
import numpy as np

xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])

yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])

cond = np.array([True, False, True, True, False])

result = [(x if contidion else y) for x,y,contidion in zip(xarr,yarr,cond)]
result

虽然这样可以实现,但是会有一些问题:

  1. 如果数组很大就会有性能问题
  2. 多重数组无法这样操作

利用 np.where 方法,就可以简单的实现:

1
result = np.where(cond,xarr,yarr)

np.where 会返回一个新的数组,其中包含了你需要的值。

再举一个例子,将一堆数中的值进行替换,正数变成2,负数变成-2:

1
2
3
4
5
6
7
8
9
10
11
12
13
arr = np.random.randn(2, 10)
arr

# out
array([[-0.81110748, 0.68433723, 0.52473733, -0.41852741, -2.84401244,
-1.68747418, -0.11169401, -1.10101844, -0.8750002 , 1.18884515],
[-0.30515971, 0.60605408, 0.16320091, -0.99053775, -0.76669183,
-0.29594746, 0.33500369, 0.28612921, -0.93350166, -0.67301814]])

np.where(arr<0,-2,2)
# out
array([[-2, 2, 2, -2, -2, -2, -2, -2, -2, 2],
[-2, 2, 2, -2, -2, -2, 2, 2, -2, -2]])

通过将arr本身传进去,可以指修改某一部分的值,比如下面是指将负数修改为-2,正数不变:

1
2
3
4
5
6
7
np.where(arr<0,-2,arr)
# out

array([[-2. , 0.68433723, 0.52473733, -2. , -2. ,
-2. , -2. , -2. , -2. , 1.18884515],
[-2. , 0.60605408, 0.16320091, -2. , -2. ,
-2. , 0.33500369, 0.28612921, -2. , -2. ]])

数学方法

Numpy 支持很多常用的数学方法,比如对数组求和,算平均值,等等,可以直接对数组对象调用,也可以通过 np.method 的方式调用。

1
2
3
4
5
arr = np.random.rand(2,2)*10
arr
out:
array([[ 0.89779239, 8.62581317],
[ 3.48522587, 5.17990087]])

平均值

arr.mean()

1
4.5471830734485419

对横向数值求平均:

arr.mean(axis=1)

1
array([ 4.76180278,  4.33256337])

纵向数求平均:

arr.mean(axis=0)

1
2
array([ 2.19150913,  6.90285702])

求和

arr.sum()

1
18.188732293794168

求和函数也可以用 axis 来指定行或列求和。

更多方法请参考附录。

附录

math_funcs.jpg

布尔数组方法

可以对一个包含True和False的布尔数组执行 sum 运算:

1
2
3
4
arr = np.random.randn(100)
(arr > 0).sum()

49

bools.any() 测试数组中是否有 True 的:

1
2
3
4
5
bools = np.array([False])
bools.any()

# 输出
False

bools.all() 测试所有值是否都是 True.

排序

NumPy 的数组排序:

1
2
arr = np.random.randn(6)
arr.sort()

sortin-place 的, 也就是会修改原来数组的数据。

Unique函数

unique函数返回排序后的、数组中唯一的值。

1
2
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.unique(names)

它的作用和下面的代码差不多:

1
sorted(set(names))

in1d函数

in1d函数测试一个数组中的值,是否都在另一个数组中,如果在就返回True,不再返回False:

1
2
3
4
values = np.array([1,2,3])
np.in1d(values,[1])
#
arrray([True,False,False])

更多函数请参考下图:

array_set_operations.jpeg

NumPy数据持久化、随机数生成

NumPy可以从磁盘读取数据,也可以将数据保存到本地。这个部分只讲一些基本的内容,因为大家可能更喜欢用 Pandas 来导入数据。

分别介绍两个方法:

  • np.save
  • np.load

存储

1
2
arr = np.arrange(10)
np.save('/Users/wittyfans/Desktop/data',arr)

读取

1
2
3
4
5
saved = np.load('/Users/wittyfans/Desktop/data.npy')
saved
# 输出

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

打包储存

1
np.savez('path',a=arr,b=arr)

读取打包文件

1
2
3
arch = np.load('path.npz')
arch['a'] # 通过类似dict的方式来读取数据
arch['b']

随机生成

生成符合正态分布的4x4数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
samples = np.random.normal(size=(4, 4))
samples
# 输出

array([[ 0.5732,

0.1933, 0.4429, 1.2796],

[ 0.575 , [-0.5367, [ 0.1525,

0.4339, -0.7658, -1.237 ],

1.8545, -0.92 , -0.1082],

0.9435, -1.0953, -0.144 ]])

Python内置的 random 模块,相比于NumPy要慢一个数量级。

1
2
3
4
5
6
7
from random import normalvariate

N = 1000000

%timeit samples = [normalvariate(0, 1) for _ in range(N)] 1.77 s +- 126 ms per loop (mean +- std. dev. of 7 runs, 1 loop each)

%timeit np.random.normal(size=N) 61.7 ms +- 1.32 ms per loop (mean +- std. dev. of 7 runs, 10 loops each)

这些随机数都是根据种子即 seed 生成的,如果每次生成随机数的时候seed都不变,那么每次生成的随机数都会是一样的。默认NumPy会根据时间来更改seed,这样每次我们就可以拿到不一样的随机数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 随机产生一个数据,此随机数是变动的
print(np.random.random())
print('--分割线0--')
# 设置随机数种子
np.random.seed(1)
# 产生一组顺序的固定的数数据
for _ in range(5):
print(np.random.random())
# 此时,如果再生成一个数据,该数据也是固定的
print('--分割线1--')
print(np.random.random())
# 说明我们设置的种子会一直有效,除非我们再次设置
np.random.seed()
print('--分割线2--')
# 此随机数是变动的
print(np.random.random())

参考