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