У меня есть массив 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 различными способами, что не соответствует теме этого вопроса.






Вы можете использовать resize для изменения размера массива.
После изменения размера вы можете применить соответствующую логику для изменения порядка содержимого.
Проверьте ссылку ниже: https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.resize.html
При изменении размера нули заканчиваются, тогда как мне отделить его от других (возможных) нулей? Каков правильный способ использования изменения размера?
Этот подход размещает элементы, отличные от 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 образца между ними.
Используя 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 массивами с некоторыми изменениями.
Какова логика добавления NaN? Как они расположены?