6

我有一个一维数据集,其中一些没有数据值设置为 9999。这是一个摘录,因为它很长:

this_array = [   4,    4,    1, 9999, 9999, 9999,   -5,   -4, ... ]

我想用两边最接近的值的平均值替换无数据值,但是由于一些无数据值也具有最接近的值作为无数据值,替换它们有点困难。即我希望将三个无数据值替换为-2。我创建了一个循环来遍历数组中的每个标量并测试没有数据:

for k in this_array:
    if k == 9999:
        temp = np.where(k == 9999, (abs(this_array[k-1]-this_array[k+1])/2), this_array[k])
    else:
        pass
this_array[k] = temp

但是,如果它也等于 9999,我需要添加一个 if 函数或方法来获取 k-1 之前或 k+1 之后的值,例如:

if np.logical_or(k+1 == 9999, k-1 == 9999):
    temp = np.where(k == 9999, (abs(this_array[k-2]-this_array[k+2])/2), this_array[k])

可以看出,这段代码会变得混乱,因为最终可能会采用错误的值或以嵌套的 if 函数告终。有谁知道实现这一点的更简洁的方法,因为它在整个数据集中变化很大?

根据要求:如果第一个和/或最后一个点没有数据,最好将它们替换为最近的数据点。

4

4 回答 4

3

使用 numpy 函数可能有一种更有效的方法,但这里有一个使用itertools 模块的解决方案:

from itertools import groupby

for k, g in groupby(range(len(this_array)), lambda i: this_array[i] == 9999):
    if k:
        indices = list(g)
        new_v = (this_array[indices[0]-1] + this_array[indices[-1]+1]) / 2
        this_array[indices[0]:indices[-1]+1].fill(new_v)

如果最后一个元素或第一个元素可以是9999,则使用以下内容:

from itertools import groupby

for k, g in groupby(range(len(this_array)), lambda i: this_array[i] == 9999):
    if k:
        indices = list(g)
        prev_i, next_i = indices[0]-1, indices[-1]+1
        before = this_array[prev_i] if prev_i != -1 else this_array[next_i]
        after = this_array[next_i] if next_i != len(this_array) else before
        this_array[indices[0]:next_i].fill((before + after) / 2)

使用第二个版本的示例:

>>> from itertools import groupby
>>> this_array = np.array([9999, 4, 1, 9999, 9999, 9999, -5, -4, 9999])
>>> for k, g in groupby(range(len(this_array)), lambda i: this_array[i] == 9999):
...     if k:
...         indices = list(g)
...         prev_i, next_i = indices[0]-1, indices[-1]+1
...         before = this_array[prev_i] if prev_i != -1 else this_array[next_i]
...         after = this_array[next_i] if next_i != len(this_array) else before
...         this_array[indices[0]:next_i].fill((before + after) / 2)
...
>>> this_array
array([ 4,  4,  1, -2, -2, -2, -5, -4, -4])
于 2012-12-18T22:02:40.383 回答
1

我会按照以下方式做一些事情:

import numpy as np

def fill(arr, fwd_fill):
  out = arr.copy()
  if fwd_fill:
    start, end, step = 0, len(out), 1
  else:
    start, end, step = len(out)-1, -1, -1
  cur = out[start]
  for i in range(start, end, step):
    if np.isnan(out[i]):
      out[i] = cur
    else:
      cur = out[i]
  return out

def avg(arr):
  fwd = fill(arr, True)
  back = fill(arr, False)
  return (fwd[:-2] + back[2:]) / 2.

arr = np.array([   4,    4,    1, np.nan, np.nan, np.nan,   -5,   -4])
print arr
print avg(arr)

第一个函数可以进行前向或后向填充,将每个 NaN 替换为最近的非 NaN。

一旦你有了这个,计算平均值就很简单了,由第二个函数完成。

你没有说你希望如何处理第一个和最后一个元素,所以代码只是将它们砍掉。

最后,值得注意的是,如果输入数组的第一个或最后一个元素丢失(在这种情况下,没有数据可以计算某些平均值),该函数可以返回 NaN。

于 2012-12-18T22:15:56.570 回答
0

这是一个递归解决方案,其中第一个和最后一个不是 9999。您可能可以使用生成器清理它,因为递归可能会变得很深。这是一个合理的开始

def a(list, first, depth):    
  if ([] == list):
    return []
  car = list[0]
  cdr = list[1:]
  if (9999 ==  car):        
      return a(cdr, first, depth+1)
  if (depth != 0):
      avg = [((first + car) /2)] * depth
      return  avg + [car] + a(cdr, car, 0)
  else:
      return [car] + a(cdr, car, 0)



print a([1,2,9999, 4, 9999,9999, 12],0,0)
# => [1, 2, 3, 4, 8, 8, 12]
于 2012-12-18T22:45:34.040 回答
0

好的,恐怕我必须自己编写它,您可以使用np.interp或等效(可能更好,功能更多)的 scipy 函数,您可以在scipy.interpolate.

好的,重读......我猜你不想要线性插值?在这种情况下,这当然不起作用......虽然我确信有一些矢量化方法。

imort numpy as np
# data is the given array.
data = data.astype(float) # I cast to float, if you don't want that badly...
valid = data != 9999
x = np.nonzero(valid)[0]
replace = np.nonzero(~valid)[0]
valid_data = data[x]

# using np.interp, but I think you will find better things in scipy.interpolate
# if you don't mind using scipy.
data[replace] = np.interp(replace, x, valid_data,
                                   left=valid_data[0], right=valid_data[-1])
于 2012-12-18T22:58:59.957 回答