我有一个包含一些nan
值的2D Numpy数组。简化示例:
arr = np.array([[3, 5, np.nan, 2, 4],
[9, 1, 3, 5, 1],
[8, np.nan, 3, np.nan, 7]])
在控制台输出中看起来像这样:
array([[ 3., 5., nan, 2., 4.],
[ 9., 1., 3., 5., 1.],
[ 8., nan, 3., nan, 7.]])
我正在寻找一种将现有nan
值右侧的所有值设置为nan
的好方法。换句话说,我需要将示例数组转换为:
array([[ 3., 5., nan, nan, nan],
[ 9., 1., 3., 5., 1.],
[ 8., nan, nan, nan, nan]])
我知道如何使用循环完成此操作,但我认为只使用Numpy矢量化操作的方法会更有效。有没有人可以帮我找到这样的方法?
答案 0 :(得分:5)
arr[np.isnan(arr).cumsum(1)>0] = np.nan
为了提高性能,最好使用np.maximum.accumulate
-
arr[np.maximum.accumulate(np.isnan(arr),axis=1)] = np.nan
使用broadcasting
-
n = arr.shape[1]
mask = np.isnan(arr)
idx = mask.argmax(1)
idx[~mask.any(1)] = n
arr[idx[:,None] <= np.arange(n)] = np.nan
示例运行 -
In [96]: arr
Out[96]:
array([[ 3., 5., nan, 2., 4.],
[ 9., 1., 3., 5., 1.],
[ 8., nan, 3., nan, 7.]])
In [97]: arr[np.maximum.accumulate(np.isnan(arr),axis=1)] = np.nan
In [98]: arr
Out[98]:
array([[ 3., 5., nan, nan, nan],
[ 9., 1., 3., 5., 1.],
[ 8., nan, nan, nan, nan]])
<强>基准强>
方法 -
def func1(arr):
arr[np.isnan(arr).cumsum(1)>0] = np.nan
def func2(arr):
arr[np.maximum.accumulate(np.isnan(arr),axis=1)] = np.nan
def func3(arr): # @ MSeifert's suggestion
mask = np.isnan(arr);
accmask = np.cumsum(mask, out=mask, axis=1);
arr[accmask] = np.nan
def func4(arr):
mask = np.isnan(arr);
np.maximum.accumulate(mask,axis=1, out = mask)
arr[mask] = np.nan
def func5(arr):
n = arr.shape[1]
mask = np.isnan(arr)
idx = mask.argmax(1)
idx[~mask.any(1)] = n
arr[idx[:,None] <= np.arange(n)] = np.nan
计时 -
In [201]: # Setup inputs
...: arr = np.random.rand(5000,5000)
...: arr.ravel()[np.random.choice(range(arr.size), 10000, replace=0)] = np.nan
...: arr1 = arr.copy()
...: arr2 = arr.copy()
...: arr3 = arr.copy()
...: arr4 = arr.copy()
...: arr5 = arr.copy()
...:
In [202]: %timeit func1(arr1)
...: %timeit func2(arr2)
...: %timeit func3(arr3)
...: %timeit func4(arr4)
...: %timeit func5(arr5)
...:
10 loops, best of 3: 149 ms per loop
10 loops, best of 3: 90.5 ms per loop
10 loops, best of 3: 88.8 ms per loop
10 loops, best of 3: 88.5 ms per loop
10 loops, best of 3: 75.3 ms per loop
基于广播的广播似乎做得很好!
答案 1 :(得分:4)
使用布尔索引和某种累加器(我在这里使用np.cumsum
):
>>> mask = np.cumsum(np.isnan(arr), axis=1).astype(bool)
>>> arr[mask] = np.nan
>>> arr
array([[ 3., 5., nan, nan, nan],
[ 9., 1., 3., 5., 1.],
[ 8., nan, nan, nan, nan]])
正如使用out
- 参数的评论中已经指出的那样,可能会加快速度并避免创建另一个临时数组:
def put_nans_right_of_nans(arr):
mask = np.isnan(arr)
mask = np.cumsum(mask, out=mask, axis=1)
arr[mask] = np.nan
鉴于我是一个非常numba的狂热分子,我想展示一个直接实现的解决方案,并在性能和内存使用方面击败所有其他方法:
import numba as nb
import math
@nb.njit
def nan_items_rightofnans(arr):
x, y = arr.shape[0], arr.shape[1]
for row_no in range(x):
nanfound = False
for col_no in range(y):
if nanfound:
arr[row_no, col_no] = np.nan
elif math.isnan(arr[row_no, col_no]):
nanfound = True
return arr