繁体   English   中英

Python - 如何用相邻元素的中位数替换 numpy 数组中的 None 值

[英]Python - How to replace None values in a numpy array with the median of adjacent elements

我生成了一个缺少数据的数组

A = np.zeros((80,80))
for i in range(80):
    if i%2 == 0:
        for j in range(80):
            A[i,j] = None
            if j%2 == 0:
                A[i,j] = 50+i+j
    else:
        for j in range(80):
            A[i,j] = None
            if j%2 != 0:
                A[i,j] = 50+i+j

这给了我下面的截图。

在此处输入图片说明

我想要做的是用不是 None 值的相邻元素的中位数替换所有“无”值。 有没有一种简单的方法可以在不遍历循环中的每个元素的情况下做到这一点?

我已经基于numba写了这样的东西(即使它是用于掩码数组,但我很快将它改编为 NaN )。

正如 Ami Tavory 所说,您也可以使用一些 numpy 技巧来做到这一点,但我发现如果您自己编写循环然后对其进行优化,它会更清晰(如果没有内置)。 我选择 numba 是因为尽管它在某些方面受到限制,但它非常适合加速此类for循环。

import numba as nb
import numpy as np

@nb.njit
def median_filter_2d(array, filtersize):
    x = array.shape[0]
    y = array.shape[1]
    filter_half = filtersize // 2
    # Create an empty result
    res = np.zeros_like(array)
    # Loop through each pixel
    for i in range(x):
        for j in range(y):
            # If it's not NaN just let it stay
            if not np.isnan(array[i, j]):
                res[i, j] = array[i, j]
            else:
                # We don't want to go outside the image:
                start_x = max(0, i - filter_half)
                end_x = min(x, i + filter_half+1)
                start_y = max(0, j - filter_half)
                end_y = min(x, j + filter_half+1)

                # If you want to use nanmedian uncomment this line and comment everything else following
                #res[i, j] = np.nanmedian(array[start_x:end_x, start_y:end_y])      

                # Create a temporary array.
                tmp = np.zeros(filtersize*filtersize)
                counter = 0 # Counter because we want to know how many not-NaNs are present.
          
                # Get all adjacent pixel that are not NaN and insert them
                for ii in range(start_x, end_x):
                    for jj in range(start_y, end_y):
                        if not np.isnan(array[ii, jj]):
                            tmp[counter] = array[ii, jj]
                            counter += 1
                
                # Either do it with np.median but it will be slower
                #res[i, j] = np.median(tmp[0:counter])
                # or use some custom median-function
                res[i, j] = numba_median_insertionsortbased(tmp[0:counter])
    return res

辅助中值函数只是一个基于插入排序的排序,然后返回中间元素或两个中间元素的平均值。

@nb.njit
def numba_median_insertionsortbased(items):
    # Insertion sort
    for i in range(1, len(items)):
        j = i
        while j > 0 and items[j] < items[j-1]:
            items[j], items[j-1] = items[j-1], items[j]
            j -= 1
    # Median is the middle element (odd length) or the mean of the two middle elements (even length)
    if items.size % 2 == 0:
        return 0.5 * (items[(items.size // 2)-1] + items[(items.size // 2)])
    else:
        return items[(items.size // 2)]

如果您不想使用numba或不能使用,您可以删除@nb.njit行并在纯 python 中使用它。 它会慢很多,但它仍然可以工作。

对于 80x80 A我使用 numba 进行计时:

1000 个循环,最好的 3 个:每个循环 1.63 毫秒(自定义中位数)

100 个循环,最好的 3 个:每个循环 4.88 毫秒(numpy 中值)

但是对于大型过滤器,numpy 中值会快一点,因为它们(希望)比插入排序具有更先进的方法。 但是对于您的数组,元素将在临时数组中排序,因此基于插入排序的中位数显然最适合。 对于真实图像,它可能会比 numpy 中值慢。

和纯蟒蛇:

1 个循环,3 个最佳:每个循环 406 毫秒(自定义中位数)

1 个循环,最好的 3 个:每个循环 707 毫秒(没有内部循环和临时数组的 numpy nanmedian)

1 个循环,最好的 3 个:每个循环 832 毫秒(临时数组的 numpy 中位数)

顺便说一句:我总是觉得令人惊讶的是,即使没有 numba 的自定义中位数也比小 1D 输入的 numpy 中位数更快(好吧,它们已经排序,因此它是插入排序的最佳情况 :-) ):

%timeit numba_median_insertionsortbased(np.arange(9)) # without the @nb.njit
10000 loops, best of 3: 21.7 µs per loop
%timeit np.median(np.arange(9))
10000 loops, best of 3: 123 µs per loop

并且这些更快的解决方案可以通过 numba 加速:

%timeit numba_median_insertionsortbased(np.arange(9)) # WITH the @nb.njit
100000 loops, best of 3: 8.93 µs per loop

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM