Подтвердить что ты не робот

Как вернуть представление нескольких столбцов в многоуровневом структурированном массиве

Я могу сразу увидеть несколько столбцов (fields) в структурированном массиве numpy путем индексации со списком имен полей, например

import numpy as np

a = np.array([(1.5, 2.5, (1.0,2.0)), (3.,4.,(4.,5.)), (1.,3.,(2.,6.))],
        dtype=[('x',float), ('y',float), ('value',float,(2,2))])

print a[['x','y']]
#[(1.5, 2.5) (3.0, 4.0) (1.0, 3.0)]

print a[['x','y']].dtype
#[('x', '<f4') ('y', '<f4')])

Но проблема в том, что это скорее копия, чем представление:

b = a[['x','y']]
b[0] = (9.,9.)

print b
#[(9.0, 9.0) (3.0, 4.0) (1.0, 3.0)]

print a[['x','y']]
#[(1.5, 2.5) (3.0, 4.0) (1.0, 3.0)]

Если я выбираю только один столбец, это вид:

c = x['y']
c[0] = 99.

print c
#[ 99.  4.   3. ]

print a['y']
#[ 99.  4.   3. ]

Можно ли каким-либо образом получить представление для более чем одного столбца одновременно?

У меня есть два метода обхода, один - просто пропустить столбцы, другой - создать иерархический dtype, так что один столбец фактически возвращает структурированный массив с двумя (или более) полями, которые я хочу. К сожалению, zip также возвращает копию, поэтому я не могу:

x = a['x']; y = a['y']
z = zip(x,y)
z[0] = (9.,9.)
4b9b3361

Ответ 1

Вы можете создать объект dtype, который содержит только те поля, которые вы хотите, и использовать numpy.ndarray() для создания представления исходного массива:

import numpy as np
strc = np.zeros(3, dtype=[('x', int), ('y', float), ('z', int), ('t', "i8")])

def fields_view(arr, fields):
    dtype2 = np.dtype({name:arr.dtype.fields[name] for name in fields})
    return np.ndarray(arr.shape, dtype2, arr, 0, arr.strides)

v1 = fields_view(strc, ["x", "z"])
v1[0] = 10, 100

v2 = fields_view(strc, ["y", "z"])
v2[1:] = [(3.14, 7)]

v3 = fields_view(strc, ["x", "t"])

v3[1:] = [(1000, 2**16)]

print(strc)

вот результат:

[(10, 0.0, 100, 0L) (1000, 3.14, 7, 65536L) (1000, 3.14, 7, 65536L)]

Ответ 2

Основываясь на ответе @HYRY, вы также можете использовать метод ndarray getfield:

def fields_view(array, fields):
    return array.getfield(numpy.dtype(
        {name: array.dtype.fields[name] for name in fields}
    ))

Ответ 3

Я не думаю, что есть простой способ добиться того, чего вы хотите. В общем, вы не можете взять произвольный вид в массив. Попробуйте следующее:

>>> a
array([(1.5, 2.5, [[1.0, 2.0], [1.0, 2.0]]),
       (3.0, 4.0, [[4.0, 5.0], [4.0, 5.0]]),
       (1.0, 3.0, [[2.0, 6.0], [2.0, 6.0]])], 
      dtype=[('x', '<f8'), ('y', '<f8'), ('value', '<f8', (2, 2))])
>>> a.view(float)
array([ 1.5,  2.5,  1. ,  2. ,  1. ,  2. ,  3. ,  4. ,  4. ,  5. ,  4. ,
        5. ,  1. ,  3. ,  2. ,  6. ,  2. ,  6. ])

Поплавковый вид вашего массива записей показывает, как фактические данные хранятся в памяти. Взгляд на эти данные должен быть выражен как комбинация формы, шага и смещения в вышеупомянутые данные. Поэтому, если вы хотите, например, просмотреть только 'x' и 'y', вы можете сделать следующее:

>>> from numpy.lib.stride_tricks import as_strided
>>> b = as_strided(a.view(float), shape=a.shape + (2,),
                   strides=a.strides + a.view(float).strides)
>>> b
array([[ 1.5,  2.5],
       [ 3. ,  4. ],
       [ 1. ,  3. ]])

as_strided делает то же самое, что, возможно, проще понять:

>>> bb = a.view(float).reshape(a.shape + (-1,))[:, :2]
>>> bb
array([[ 1.5,  2.5],
       [ 3. ,  4. ],
       [ 1. ,  3. ]])

Любой из них представляет собой представление в a:

>>> b[0,0] =0
>>> a
array([(0.0, 2.5, [[0.0, 2.0], [1.0, 2.0]]),
       (3.0, 4.0, [[4.0, 5.0], [4.0, 5.0]]),
       (1.0, 3.0, [[2.0, 6.0], [2.0, 6.0]])], 
      dtype=[('x', '<f8'), ('y', '<f8'), ('value', '<f8', (2, 2))])
>>> bb[2, 1] = 0
>>> a
array([(0.0, 2.5, [[0.0, 2.0], [1.0, 2.0]]),
       (3.0, 4.0, [[4.0, 5.0], [4.0, 5.0]]),
       (1.0, 0.0, [[2.0, 6.0], [2.0, 6.0]])], 
      dtype=[('x', '<f8'), ('y', '<f8'), ('value', '<f8', (2, 2))])

Было бы неплохо, если бы любой из них мог быть преобразован в массив записей, но numpy отказывается это делать, причина не в том, чтобы все это мне было ясно:

>>> b.view([('x',float), ('y',float)])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: new type not compatible with array.

Конечно, что работает (сорт) для 'x' и 'y' не будет работать, например, для 'x' и 'value', так что в целом ответ таков: это невозможно.

Ответ 4

Начиная с версии 1.13 для Numpy, код, который вы предлагаете, вернет представление. См. "Замечания по выпуску NumPy 1.12.0- > Future Changes- > Многоадресная обработка структурированных массивов" на этой странице:

https://docs.scipy.org/doc/numpy-dev/release.html