Как уже отмечалось, выбор мультиполя находится в состоянии изменения. Недавно я обновился до 1.14.2, и поведение вернулось к тому, что было до 1.14.0.
In [114]: data = np.array([(1.0, 2.0, 0), (3.0, 4.0, 1)],
...: dtype=[('feature_1', float), ('feature_2', float), ('resul
...: t', int)])
...:
In [115]: data
Out[115]:
array([(1., 2., 0), (3., 4., 1)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8'), ('result', '<i8')])
In [116]: features = data[['feature_1', 'feature_2']]
In [117]: features
Out[117]:
array([(1., 2.), (3., 4.)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8')])
(я пропускаю дополнительный слой recarray
преобразования.)
В 1.14.0 этот dtype будет включать значение offset
, указывающее, что features
было представлением, а не копией.
Я могу изменить значения features
без изменения data
:
In [124]: features['feature_1']
Out[124]: array([1., 3.])
In [125]: features['feature_1'] = [4,5]
In [126]: features
Out[126]:
array([(4., 2.), (5., 4.)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8')])
In [127]: data
Out[127]:
array([(1., 2., 0), (3., 4., 1)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8'), ('result', '<i8')])
Но, не углубляясь в обсуждение разработки, я не могу сказать, каким будет долгосрочное решение. В идеале он должен иметь возможность извлекать view
(который поддерживает ссылку на исходный буфер данных) и копию, массив, который является независимым и свободно модифицируемым.
Я подозреваю, что версия copy
будет следовать практике recfunctions
построения нового массива с новым dtype, а затем копирования поля данных по полю.
In [132]: data.dtype.descr
Out[132]: [('feature_1', '<f8'), ('feature_2', '<f8'), ('result', '<i8')]
In [133]: dt = data.dtype.descr[:-1]
In [134]: dt
Out[134]: [('feature_1', '<f8'), ('feature_2', '<f8')]
In [135]: arr = np.zeros(data.shape, dtype=dt)
In [136]: arr
Out[136]:
array([(0., 0.), (0., 0.)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8')])
In [137]: for name in arr.dtype.fields:
...: arr[name] = data[name]
...:
In [138]: arr
Out[138]:
array([(1., 2.), (3., 4.)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8')])
или другая функция повторного вызова:
In [159]: rf.drop_fields(data, 'result')
Out[159]:
array([(1., 2.), (3., 4.)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8')])
recfunctions
имеет код, который может копировать сложные dtypes, с вложенными dtypes и тому подобное. Но для простого однослойного dtype, подобного этому, достаточно простой итерации имени поля.
Как правило, структурированные массивы (и повторные массивы) имеют много записей и ограниченное количество полей. Поэтому копирование полей по имени является относительно эффективным.
In [150]: import numpy.lib.recfunctions as rf
In [154]: arr = np.zeros(data.shape, dtype=dt)
In [155]: rf.recursive_fill_fields(data, arr)
Out[155]:
array([(1., 2.), (3., 4.)],
dtype=[('feature_1', '<f8'), ('feature_2', '<f8')])
но обратите внимание, что его код заканчивается:
output = np.empty(base.shape, dtype=newdtype)
output = recursive_fill_fields(base, output)
Замечания по разработке в какой-то момент ссылались на функцию recfunctions.compress_fields
, но она, по-видимому, фактически никогда не добавлялась.