在带NaN的numpy数组中计算移动平均值

6

我正在尝试计算一个包含NaN的大型numpy数组的移动平均值。目前我正在使用:

import numpy as np

def moving_average(a,n=5):
      ret = np.cumsum(a,dtype=float)
      ret[n:] = ret[n:]-ret[:-n]
      return ret[-1:]/n

当使用掩码数组进行计算时:

x = np.array([1.,3,np.nan,7,8,1,2,4,np.nan,np.nan,4,4,np.nan,1,3,6,3])
mx = np.ma.masked_array(x,np.isnan(x))
y = moving_average(mx).filled(np.nan)

print y

>>> array([3.8,3.8,3.6,nan,nan,nan,2,2.4,nan,nan,nan,2.8,2.6])

我想要的结果(如下)应该只在原始数组 x 中有 NaN 的位置上具有 NaN,并且平均值应该在分组中的非 NaN 元素数量上进行计算(我需要某种方法来更改函数中 n 的大小)。
y = array([4.75,4.75,nan,4.4,3.75,2.33,3.33,4,nan,nan,3,3.5,nan,3.25,4,4.5,3])

我可以遍历整个数组并逐个检查索引,但我使用的数组非常大,这将需要很长时间。是否有一种numpy式的方法来做到这一点?


那么,[4.75,4.75,nan,4.4,3.75,2.33,3.33,4,nan,nan,3,3.5,nan,3.25] 是期望的输出吗?如果是,为什么第三个元素是 NaN - Divakar
@Divakar 这是预期输出。在原始数组(x)中,第三个条目为nan - krakenwagon
那么为什么我们在预期输出的倒数第二个条目中有一个NaN呢? - Divakar
Pandas默认会处理缺失数据。 - U2EF1
1
@Divakar 的 np.cumsum 方法的答案在我的实际数据中给出了最快的结果(我已更改采纳的答案)。所有的答案都给出了我想要的结果。 - krakenwagon
显示剩余2条评论
6个回答

2
Pandas在这方面有很多非常好的功能。例如:
最初的回答
x = np.array([np.nan, np.nan, 3, 3, 3, np.nan, 5, 7, 7])

# requires three valid values in a row or the resulting value is null

print(pd.Series(x).rolling(3).mean())

#output
nan,nan,nan, nan, 3, nan, nan, nan, 6.333

# only requires 2 valid values out of three for size=3 window

print(pd.Series(x).rolling(3, min_periods=2).mean())

#output
nan, nan, nan, 3, 3, 3, 4, 6, 6.3333

你可以在一行代码中玩弄窗口/最小周期,并考虑填充所有的空值。"Original Answer"翻译成"最初的回答"。

1
我只是想补充之前的优秀回答,你仍然可以使用cumsum来实现这一点。
import numpy as np

def moving_average(a, n=5):
    ret = np.cumsum(a.filled(0))
    ret[n:] = ret[n:] - ret[:-n]
    counts = np.cumsum(~a.mask)
    counts[n:] = counts[n:] - counts[:-n]
    ret[~a.mask] /= counts[~a.mask]
    ret[a.mask] = np.nan

    return ret

x = np.array([1.,3,np.nan,7,8,1,2,4,np.nan,np.nan,4,4,np.nan,1,3,6,3])
mx = np.ma.masked_array(x,np.isnan(x))
y = moving_average(mx)

0
你可以创建一个临时数组,并使用np.nanmean()函数(如果我没记错的话,它是在1.8版中新加入的):
import numpy as np
temp = np.vstack([x[i:-(5-i)] for i in range(5)]) # stacks vertically the strided arrays
means = np.nanmean(temp, axis=0)

并使用means[np.isnan(x[:-5])] = np.nan将原始的nan放回原处。

然而,从内存(堆叠相同的数组步幅为5)和计算的角度来看,这看起来是冗余的。


np.nanmean() 在输出数组中任何地方都不会返回 nan - krakenwagon
@krakenwagon,是的,你可以使用我在你评论之前编辑的那行代码将它们添加回去。 - P. Camilleri

0

如果我理解正确,您想创建一个移动平均值,并将结果元素填充为nan,如果它们在原始数组中的索引是nan

import numpy as np

>>> inc = 5 #the moving avg increment 

>>> x = np.array([1.,3,np.nan,7,8,1,2,4,np.nan,np.nan,4,4,np.nan,1,3,6,3])
>>> mov_avg = np.array([np.nanmean(x[idx:idx+inc]) for idx in range(len(x))])

# Determine indices in x that are nans 
>>> nan_idxs = np.where(np.isnan(x))[0]

# Populate output array with nans
>>> mov_avg[nan_idxs] = np.nan
>>> mov_avg
array([ 4.75, 4.75, nan, 4.4, 3.75, 2.33333333, 3.33333333, 4., nan, nan, 3., 3.5, nan, 3.25, 4., 4.5, 3.])

0

这里有一种使用步幅的方法 -

w = 5 # Window size
n = x.strides[0]      
avgs = np.nanmean(np.lib.stride_tricks.as_strided(x, \
                        shape=(x.size-w+1,w), strides=(n,n)),1)

x_rem = np.append(x[-w+1:],np.full(w-1,np.nan))
avgs_rem = np.nanmean(np.lib.stride_tricks.as_strided(x_rem, \
                               shape=(w-1,w), strides=(n,n)),1)
avgs = np.append(avgs,avgs_rem)                               
avgs[np.isnan(x)] = np.nan

0

目前 bottleneck 包应该可以相当可靠且快速地完成任务。这里是稍作调整的示例,来自https://kwgoodman.github.io/bottleneck-doc/reference.html#bottleneck.move_mean:

>>> import bottleneck as bn
>>> a = np.array([1.0, 2.0, 3.0, np.nan, 5.0])
>>> bn.move_mean(a, window=2)
array([ nan,  1.5,  2.5,  nan,  nan])
>>> bn.move_mean(a, window=2, min_count=1)
array([ 1. ,  1.5,  2.5,  3. ,  5. ])

请注意,结果意味着对应于窗口的最后一个索引。
该软件包可从Ubuntu仓库、pip等获取。它可以在numpy数组的任意轴上操作等。此外,据称在许多情况下比纯numpy实现更快。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接