Python numpy recarray: Можно ли получить представление о различных полях, используя арифметику указателей?

У меня есть NumPy структурированный массив следующей формы:

x = np.array([(1,2,3)]*2, [('t', np.int16), ('x', np.int8), ('y', np.int8)])

Теперь я хочу создать представления в этот массив, которые объединяются 't' либо с 'x' или же 'y', Обычный синтаксис создает копию:

v_copy = x[['t', 'y']]
v_copy
#array([(1, 3), (1, 3)], 
#     dtype=[('t', '<i2'), ('y', '|i1')])

v_copy.base is None
#True

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

Совсем не правда, что требуемые элементы не могут быть доступны в рамках модели памяти numpy. Глядя на отдельные байты в памяти:

x.view(np.int8)
#array([1, 0, 2, 3, 1, 0, 2, 3], dtype=int8)

можно выяснить необходимые шаги:

v = np.recarray((2,2), [('b', np.int8)], buf=x, strides=(4,3))
v
#rec.array([[(1,), (3,)],
#    [(1,), (3,)]], 
#    dtype=[('b', '|i1')])
v.base is x
#True

Очевидно, что v указывает на правильные места в памяти, не создавая копию. К сожалению, numpy не позволяет мне интерпретировать эти области памяти как исходные типы данных:

v_view = v.view([('t', np.int16), ('y', np.int8)])
#ValueError: new type not compatible with array.

Есть ли способ обмануть Numpy в выполнении этого приведения, чтобы массив v_view эквивалентно v_copy создан, но не сделав копию? Возможно, работает непосредственно на v.__array_interface__, как это делается в np.lib.stride_tricks.as_strided()?

2 ответа

Решение

Вы можете создать подходящий тип d, например, так

dt2 = np.dtype(dict(names=('t', 'x'), formats=(np.int16, np.int8), offsets=(0, 2)))

а затем сделать

y = np.recarray(x.shape, buf=x, strides=x.strides, dtype=dt2)

В будущих версиях Numpy (> 1.6) вы также можете сделать

dt2 = np.dtype(dict(names=('t', 'x'), formats=(np.int16, np.int8), offsets=(0, 2), itemsize=4))
y = x.view(dt2)

Это работает с numpy 1.6.x и позволяет избежать создания recarray:

dt2 = {'t': (np.int16, 0), 'y': (np.int8, 3)}
v_view = np.ndarray(x.shape, dtype=dt2, buffer=x, strides=x.strides)
v_view
#array([(1, 3), (1, 3)], 
#    dtype=[('t', '<i2'), ('', '|V1'), ('y', '|i1')])
v_view.base is x
#True

Можно обернуть это в перегрузку класса np.ndarray:

class arrayview(np.ndarray):
    def __new__(subtype, x, fields):
        dtype = {f: x.dtype.fields[f] for f in fields}
        return np.ndarray.__new__(subtype, x.shape, dtype,
                                  buffer=x, strides=x.strides)

v_view = arrayview(x, ('t', 'y'))
v_view
#arrayview([(1, 3), (1, 3)], 
#    dtype=[('t', '<i2'), ('', '|V1'), ('y', '|i1')])
v_view.base is x
#True
Другие вопросы по тегам