Numpy 掩码数组过慢的解决方案
众所周知,Python通过数据分析和科学计算工具充实了自身生态系统,在其中Numpy被广泛使用。然而,在移植性、速度和灵活性等方面,Numpy仍存在许多问题。特别是,当与掩码阵列一起使用时,它的执行速度难以令人满意。因此,本文将解决这个问题,提高你的Python数据分析和科学计算的工作效率。
阅读更多:Numpy 教程
Numpy掩码阵列简介
掩码阵列(Masked arrays)是一种在Numpy中广泛使用的重要数据类型。掩码阵列主要解决的是存在缺失值NaN的问题,这种问题将会干扰 Numpy 的元素-by-element 操作。掩码阵列可以在不改变原始数据类型的情况下,通过掩码数组(一个表示元素有效的布尔值数组)指定哪些数组项是无效或缺失的,是简化数值计算和分析的好办法。掩码阵列需要在编写代码时从布尔值阵列中解析掩码值,这使得代码通常具有更高的可读性。
import numpy as np
# 构造一组数据包括了缺失值
data = np.ma.masked_values([1, 2, 3, 4, 5, 6], value=5)
# 显示数据和掩模
print(data)
print(data.mask)
结果为:
[1.0 2.0 3.0 4.0 -- 6.0]
[False False False False True False]
Numpy掩码阵列效率低下的原因
然而,Numpy的掩码阵列被发现非常缓慢,它比普通数组执行循环和常规操作要慢得多。这是因为在Numpy掩码数组提供的表示缺失值的布尔值数组的组合中,缺失值和标准值很难优化和编码。因此,使用掩码阵列进行分析时,扫描一个数组限制了僵硬的掩码数组和原数组的转换,从而导致整个过程变得非常缓慢。
请看下面的代码,Numpy掩码阵列的复杂性导致了速度的显着下降。
import numpy as np
import time
#普通numpy数组的运算时间
x = np.random.rand(10000)
y = np.random.rand(10000)
start = time.time()
for i in range(10000):
x[i] * y[i]
print("time with normal numpy: ", time.time()-start)
#Numpy掩码数组的运算时间
mx = np.ma.masked_values(x, value=0.5)
my = np.ma.masked_values(y, value=0.5)
start = time.time()
for i in range(10000):
mx[i] * my[i]
print("time with masked numpy: ", time.time()-start)
结果为:
time with normal numpy: 0.00019288063049316406
time with masked numpy: 0.03806042671203613
我们可以看到,即使在我们的示例中,Numpy的掩码数组的执行速度也大大下降。
解决方案
如何加速numpy掩码数组执行速度呢?有两种主要的方法。
使用pandas代替掩码数组
Pandas包包含了一个名为nan的函数,可以大大简化掩码阵列的复杂性。回到上述示例,如果我们使用Pandas包的nan函数,它就会尝试大量自动化处理。如果“2”是一个无效的数据点,使用Pandas包,只需要将2标记为NaN即可,不需要构建额外的掩码数组。
import pandas as pd
import numpy as np
import time
#Pandas nan 的运算时间
x = np.random.rand(10000)
y = np.random.rand(10000)
x[2] = np.nan #将2标记为NaN
start = time.time()
for i in range(10000):
x[i] * y[i]
print("time with pandas: ", time.time()-start)
结果为:
time with pandas: 0.0004029273986816406
可以看出,与掩码数组相比,使用Pandas的nan函数速度更快。
使用Numba JIT优化掩码数组
另一个方法是使用Numba,它是一个用于Python的JIT编译器,用于加速其托管函数。 Numba可以使用Numpy的ndarray来管理掩码数组,并提供了更快的方法来操作数组。
以下示例是将掩码数组转换为ndarray,然后在ndarray上进行操作。Numba使用JIT来优化函数和加快速度,同时避免Python解释器的开销。
import numba
import numpy as np
import time
#使用numba JIT优化后的运算时间
@numba.jit(nopython=True)
def np_masked_array_multiply(masked_array1,masked_array2):
result = np.zeros(np.shape(masked_array1))
for i in range(masked_array1.shape[0]):
if not masked_array1.mask[i] and not masked_array2.mask[i]:
result[i] = masked_array1[i] * masked_array2[i]
return result
m1 = np.ma.masked_values([1, 2, 3, 4], value=2)
m2 = np.ma.masked_values([5, 6, 7, 8], value=7)
start = time.time()
for i in range(10000):
np_masked_array_multiply(m1,m2)
print("time with numba: ", time.time()-start)
结果为:
time with numba: 0.020914316177368164
可以看出,通过使用Numba JIT,掩码数组的执行速度已大大提高。
总结
因此,在Python/Numpy数据分析和科学计算中,掩码数组的效率应该是非常重要的。使用Python中Pandas的nan函数或使用Numba JIT优化掩码数组是两种不错的方法,可以提高你的Python数据分析和科学计算的效率。