2016-12-14 12 views
1

Где «отсутствует» может означать либо nan, либо np.masked, в зависимости от того, что проще всего реализовать с помощью.Как эффективно «растягивать» приведенные значения в массиве по отсутствующим

Например:

>>> from numpy import nan 
>>> do_it([1, nan, nan, 2, nan, 3, nan, nan, 4, 3, nan, 2, nan]) 
array([1, 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 2, 2]) 
# each nan is replaced with the first non-nan value before it 
>>> do_it([nan, nan, 2, nan]) 
array([nan, nan, 2, 2]) 
# don't care too much about the outcome here, but this seems sensible 

Я могу видеть, как вы могли бы сделать это с цикл:

def do_it(a): 
    res = [] 
    last_val = nan 
    for item in a: 
     if not np.isnan(item): 
      last_val = item 
     res.append(last_val) 
    return np.asarray(res) 

Есть ли более быстрый способ векторизации это?

ответ

1

Работа с @ удаленного решения Беньямина, все замечательно, если вы работаете с индексами

def do_it(data, valid=None, axis=0): 
    # normalize the inputs to match the question examples 
    data = np.asarray(data) 
    if valid is None: 
     valid = ~np.isnan(data) 

    # flat array of the data values 
    data_flat = data.ravel() 

    # array of indices such that data_flat[indices] == data 
    indices = np.arange(data.size).reshape(data.shape) 

    # thanks to benjamin here 
    stretched_indices = np.maximum.accumulate(valid*indices, axis=axis) 
    return data_flat[stretched_indices] 

Сравнение времени выполнения решения:

>>> import numpy as np 
>>> data = np.random.rand(10000) 

>>> %timeit do_it_question(data) 
10000 loops, best of 3: 17.3 ms per loop 
>>> %timeit do_it_mine(data) 
10000 loops, best of 3: 179 µs per loop 
>>> %timeit do_it_user(data) 
10000 loops, best of 3: 182 µs per loop 

# with lots of nans 
>>> data[data > 0.25] = np.nan 

>>> %timeit do_it_question(data) 
10000 loops, best of 3: 18.9 ms per loop 
>>> %timeit do_it_mine(data) 
10000 loops, best of 3: 177 µs per loop 
>>> %timeit do_it_user(data) 
10000 loops, best of 3: 231 µs per loop 

Так как это и решение @ user2357112 в взрывать решение в вопрос из воды, но это имеет небольшое преимущество над @ user2357112, когда имеется большое количество nan s

1

cumsum мина над массивом флагов обеспечивает хороший способ, чтобы определить, какие номера, чтобы написать над NaNs:

def do_it(x): 
    x = np.asarray(x) 

    is_valid = ~np.isnan(x) 
    is_valid[0] = True 

    valid_elems = x[is_valid] 
    replacement_indices = is_valid.cumsum() - 1 
    return valid_elems[replacement_indices] 
+0

Хм, это не работает, если x равно 2d, но я думаю, что это было не то, о чем я просил – Eric

+0

@ Эрик: Да, я не знаю, что вы даже хотели бы для двумерного ввода. – user2357112

+0

Я бы хотел, чтобы он обрабатывал каждую строку независимо, как если бы это был 1d – Eric

1

Предполагая, что нет нулей в данных (для того, чтобы использовать numpy.nan_to_num):

b = numpy.maximum.accumulate(numpy.nan_to_num(a)) 
>>> array([ 1., 1., 1., 2., 2., 3., 3., 3., 4., 4.]) 
mask = numpy.isnan(a) 
a[mask] = b[mask] 
>>> array([ 1., 1., 1., 2., 2., 3., 3., 3., 4., 3.]) 

EDIT: Как отметил Эрик, еще лучшим решением будет заменить с пренебрежимо малых -Inf:

mask = numpy.isnan(a) 
a[mask] = -numpy.inf 
b = numpy.maximum.accumulate(a) 
a[mask] = b[mask] 
+0

Nice! Замена 'nan' на' -inf' тоже будет работать здесь, верно? – Eric

+0

@ Эрик: Действительно, еще лучшее решение. – Benjamin

+0

Подождите, это не сработает. См. Мой обновленный тестовый пример. Вы предполагали, что значения всегда увеличиваются, а это не – Eric

 Смежные вопросы

  • Нет связанных вопросов^_^