Из данного массива numy [1,2,3,4] и окна wz = 2 (два элемента до и два элемента после каждого элемента) я должен получить пары (центральный элемент el из окна) . Пары с несуществующими элементами могут быть пропущены или заменены на ноль. Итак, на этом примере я должен получить это:

[[1., 0.]
 [2., 1.]
 [3., 2.]
 [4., 3.]
 [1., 2.]
 [2., 3.]
 [3., 4.]
 [4., 0.]
 [1., 0.]
 [2., 0.]
 [3., 1.]
 [4., 2.]
 [1., 3.]
 [2., 4.]
 [3., 0.]
 [4., 0.]]

Моя реализация крайне неэффективна и выглядит так:

x = np.array([1,2,3,4])
l = x.shape[0]
for i in range(1, m):
    init = np.empty((x.shape[0]*2,2))
    init[:,0] = np.append(x, x)
    init[:l,1] = np.pad(x, (i,0), mode='constant')[:l]
    init[-l:,1] = np.pad(x, (0,i), mode='constant')[-l:]
    corpus.extend(init)

Может ли кто-нибудь помочь с гораздо более эффективным решением? На других простых тестовых данных и вариантах, которые я реализовал, у меня есть:

285 µs ± 19.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
379 µs ± 7.68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3
Ivan Telnov 24 Фев 2018 в 21:53

3 ответа

Лучший ответ

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

Заменено нулевой версией

x = np.array([1,2,3,4])
wz = 2
zero = 0

Построим матрицу индексации.

ri = np.arange(-wz,wz+1)+np.arange(x.shape[0]).reshape(-1,1)
print(ri) 

< Ет > Вывод :

  [[-2, -1,  0,  1,  2],
   [-1,  0,  1,  2,  3],
   [ 0,  1,  2,  3,  4],
   [ 1,  2,  3,  4,  5]

Теперь, если мы добавим ноль к x как последний элемент, мы можем заменить неправильные индексы на его индекс.

np.place(ri,(ri<0)|(ri>x.shape[0]),x.shape[0]) #replace wrong indexes
np.vstack((
    np.hstack((x,[zero]))[ri].reshape(1,-1),#extending x with zero and reindexing 
    np.tile(x,2*wz+1)) #repeating basic `x` to each window position
    )#.T #uncomment .T to make it vertical   

Выход:

 ([[0, 0, 1, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 4, 0, 2, 3, 4, 0, 0],
   [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]])

Пропущенная версия

Та же идея, но в несколько ином порядке: создать полную индексную матрицу [window_index,x_index], затем исключить неправильные пары и, наконец, переиндексировать 'x'.

x = np.array([1,2,3,4])
wz = 2
ri = np.vstack((
    (np.arange(-wz,wz+1)+np.arange(x.shape[0]).reshape(-1,1)).ravel(),#same index matrix flaten 
    np.tile(np.arange(x.shape[0]),2*wz+1) #repeating `x` indexes to each window position
    )) 
x[ri[:,(ri[0]>=0)&(ri[0]<x.shape[0])]]#.T #uncomment .T to make it vertical   

Выход:

 [[1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 2, 3, 4],
  [3, 4, 1, 3, 4, 1, 2, 3, 4, 1, 2, 4, 1, 2]]

Обновление 1 (исправление ошибки) исключить ноль из окна, чтобы избежать дублирования пар.

x = np.array([1,2,3,4])
wz = 2
ri = np.vstack(((
        np.hstack(( np.arange(-wz,0), #remove zero from window
                    np.arange(1,wz+1)))+
        np.arange(x.shape[0]).reshape(-1,1)).ravel(), #same index matrix flaten 
    np.tile(np.arange(x.shape[0]),2*wz) #repeating `x` indexes to each window position
    )) 
x[ri[:,(ri[0]>=0)&(ri[0]<x.shape[0])]]#.T #uncomment .T to make it vertical   

Выход:

  [[2, 3, 1, 3, 4, 1, 2, 4, 2, 3],
   [3, 4, 2, 3, 4, 1, 2, 3, 1, 2]]

Проверьте документацию по используемым функциям np.arange, np.reshape, np.place, np.hstack, правила трансляции и индексации.

1
ilia timofeev 25 Фев 2018 в 14:43

Вот Numpythonic подход:

In [23]: a = np.array([1,2,3,4])
In [24]: arr = np.hstack((a-1, a+1, a - 2, a+ 2))
In [25]: mask = ~np.in1d(arr, a)
In [26]: arr[mask] = 0
In [27]: np.column_stack((np.tile(a, 4), arr))
Out[27]: 
array([ [1, 0],
        [2, 1],
        [3, 2],
        [4, 3],
        [1, 2],
        [2, 3],
        [3, 4],
        [4, 0],
        [1, 0],
        [2, 0],
        [3, 1],
        [4, 2],
        [1, 3],
        [2, 4],
        [3, 0],
        [4, 0]])
2
Kasramvd 24 Фев 2018 в 19:01

Numpy подход выгоден, но вот функциональный подход для заинтересованных:

< Сильный > Учитывая

import functools as ft


# Helper function
def curry(f):
    @ft.wraps(f)
    def wrapped(arg):
        try:
            return f(arg)
        except TypeError:
            return curry(ft.wraps(f)(ft.partial(f, arg)))
    return wrapped

< Сильный > Код

lst = [1, 2, 3, 4]
c = curry(lambda x, y: x + y)
funcs = [c(-1), c(1), c(-2), c(2)]
set_ = set(lst)


[[x, 0] if fn(x) not in set_ else [x, fn(x)] for fn in funcs for x in lst]

Выход

[[1, 0],
 [2, 1],
 [3, 2],
 [4, 3],
 [1, 2],
 [2, 3],
 [3, 4],
 [4, 0],
 [1, 0],
 [2, 0],
 [3, 1],
 [4, 2],
 [1, 3],
 [2, 4],
 [3, 0],
 [4, 0]]

< Сильный > Подробнее

В двойных for циклах понимания списка список карри функций повторяется, и каждая функция применяется к каждому элементу первичного списка (lst). Карринг позволяет вам вычислять новые значения, передавая некоторый аргумент (например, 1, -1, -2, 2), а затем передавая элемент из первичного списка.

Создаются кортежи, например, (основной элемент, вычисляемый элемент) . Условная часть понимания списка заменяет 0 вычисляемых элементов, не найденных в первичном списке.

Смотрите также эту реализацию функции curry.

0
pylang 24 Фев 2018 в 22:02