Numpy 掩码数组过慢的解决方案

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数据分析和科学计算的效率。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程