Растянуть массив и заполнить nan

У меня есть массив 1-d numpy с длиной n, и я хочу растянуть его до m (n<m) и систематически добавлять numpy.nan.

Например:

>>> arr = [4,5,1,2,6,8] # take this
>>> stretch(arr,8)
[4,5,np.nan,1,2,np.nan,6,8] # convert to this

Требования: 1. Никаких нан с обоих концов (если возможно) 2. Работайте на любой длине

я пробовал

>>> def stretch(x,to,fill=np.nan):
...     step = to/len(x)
...     output = np.repeat(fill,to)
...     foreign = np.arange(0,to,step).round().astype(int)
...     output[foreign] = x
...     return output

>>> arr = np.random.rand(6553)
>>> stretch(arr,6622)

  File "<ipython-input-216-0202bc39278e>", line 2, in <module>
    stretch(arr,6622)

  File "<ipython-input-211-177ee8bc10a7>", line 9, in stretch
    output[foreign] = x

ValueError: shape mismatch: value array of shape (6553,) could not be broadcast to indexing result of shape (6554,)

Кажется, он не работает должным образом (для массива длиной 6553 нарушает требование 2 и не гарантирует 1), какие-либо подсказки, чтобы преодолеть это?

Какова логика добавления NaN? Как они расположены?

cs95 22.01.2019 09:34

«Систематически» объясняет это, другими словами, равномерно (насколько это возможно). Я буду обрабатывать NaN различными способами, что не соответствует теме этого вопроса.

mccandar 22.01.2019 09:44
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
2
752
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете использовать resize для изменения размера массива.

После изменения размера вы можете применить соответствующую логику для изменения порядка содержимого.

Проверьте ссылку ниже: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.resize.html

При изменении размера нули заканчиваются, тогда как мне отделить его от других (возможных) нулей? Каков правильный способ использования изменения размера?

mccandar 22.01.2019 09:52

Этот подход размещает элементы, отличные от nan, на границах, оставляя значения nan в центре, хотя значения nan распределяются неравномерно.

arr = [4,5,1,2,6,8]   
stretch_len = 8    

def stretch(arr, stretch_len):
    stretched_arr = np.empty(stretch_len)   
    stretched_arr.fill(np.nan)
    arr_len = len(arr)

    if arr_len % 2 == 0:
        mid = int(arr_len/2)
        stretched_arr[:mid] = arr[:mid]
        stretched_arr[-mid:] = arr[-mid:]
    else:
        mid = int(np.floor(arr_len/2))
        stretched_arr[:mid] = arr[:mid]
        stretched_arr[-mid-1:] = arr[-mid-1:]

    return stretched_arr

Вот несколько тестовых случаев, которые я тестировал:

Тестовые примеры:

In [104]: stretch(arr, stretch_len)   
Out[104]: array([ 4.,  5.,  1., nan, nan,  2.,  6.,  8.])

In [105]: arr = [4, 5, 1, 2, 6, 8, 9]    

In [106]: stretch(arr, stretch_len)  
Out[106]: array([ 4.,  5.,  1., nan,  2.,  6.,  8.,  9.])

In [107]: stretch(arr, 9)  
Out[107]: array([ 4.,  5.,  1., nan, nan,  2.,  6.,  8.,  9.])

Два последовательных нана не удовлетворяют условию равномерного распределения. Я бы ожидал, по крайней мере, 2 образца между ними.

mccandar 22.01.2019 10:16
Ответ принят как подходящий

Используя roundrobin из рецептов itertools:

from itertools import cycle, islice

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

def stretch(x, to, fill=np.nan):
    n_gaps = to - len(x)
    return np.hstack([*roundrobin(np.array_split(x, n_gaps+1), np.repeat(fill, n_gaps))])

arr = [4,5,1,2,6,8]
stretch(arr, 8)
# array([ 4.,  5., nan,  1.,  2., nan,  6.,  8.])

arr2 = np.random.rand(655)
stretched_arr2 = stretch(arr,662)
np.diff(np.argwhere(np.isnan(stretched_arr2)), axis=0)
# nans are evenly spaced    
array([[83],
       [83],
       [83],
       [83],
       [83],
       [83]])

Логика позади

n_gaps: вычисляет, сколько пробелов нужно заполнить (желаемая длина - текущая длина)

np_array_split: с n_gaps+1 он разбивает входной массив на максимально возможную длину

roundrobin: поскольку np_array_split генерирует на один массив больше, чем пробелы, циклический перебор (то есть альтернативная итерация) гарантирует, что np.nan никогда не окажется ни на одном из концов результата.

Хотя Крис решил проблему, я нашел более короткий ответ, который может быть полезен,

def stretch2(x,to,fill=np.nan):
    output  = np.repeat(fill,to)
    foreign = np.linspace(0,to-1,len(x)).round().astype(int)
    output[foreign] = x
    return output

очень похоже на мою первую попытку. Тайминги:

>>> x = np.random.rand(1000)
>>> to = 1200
>>> %timeit stretch(x,to) # Chris' version
>>> %timeit stretch2(x,to)

996 µs ± 22.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
32.2 µs ± 339 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Проверьте, правильно ли он работает:

>>> aa = stretch2(x,to)
>>> np.diff(np.where(np.isnan(aa))[0])
array([6, 6, 6, ... , 6])
>>> np.sum(aa[~np.isnan(aa)] - x)
0.0

Проверить граничные условия:

>>> aa[:5]
array([0.78581616, 0.1630689 , 0.52039993,        nan, 0.89844404])
>>> aa[-5:]
array([0.7063653 ,        nan, 0.2022172 , 0.94604503, 0.91201897])

все довольны. Работает для всех 1-d массивов и может быть обобщен для работы с n-d массивами с некоторыми изменениями.

Другие вопросы по теме