Коротко
- Представление (view) — новый объект-окно на те же данные. Изменения видны в обоих массивах.
- Копия (copy) — отдельные данные; изменения независимы.
- Представления дают: срезы
a[1:4],a[::2],reshape,ravel(если возможно),.T,transpose. - Копии дают: fancy- и булева индексация (
a[[0, 2]],a[a > 0]), арифметика,flatten,.copy(). - Проверка:
b.base is a— представлениеa;np.shares_memory(a, b)— есть ли общие данные.
Синтаксис
b = a[1:4] # представление
b = a[1:4].copy() # копия
b.base is a # True — b смотрит в данные a
b.base is None # True — у b свои данные
np.shares_memory(a, b)| Операция | Результат |
|---|---|
срез a[i:j], a[::k], a[:, 0] |
представление |
reshape, .T, transpose, ravel |
обычно представление |
a[[0, 2]], a[mask] |
копия |
a + 1, np.sqrt(a), flatten(), copy() |
копия (новый массив) |
Примеры
prices = np.array([100, 200, 300, 400, 500])part = prices[1:4] # срез — представление
part[0] = 0
prices # исходный массив изменилсяВывод
array([100, 0, 300, 400, 500])
part = prices[1:4].copy() # независимая копия
part[0] = 0
pricesВывод
array([100, 200, 300, 400, 500])
view = prices[::2]
fancy = prices[[0, 2, 4]]
print(view.base is prices) # у представления base — исходный массив
print(fancy.base is None) # у копии собственные данные
np.shares_memory(prices, view), np.shares_memory(prices, fancy)Вывод
True True (True, False)
grid = prices.reshape(5, 1)
grid[0, 0] = -1 # reshape тоже даёт представление
pricesВывод
array([ -1, 200, 300, 400, 500])
Подводные камни
Функция меняет массив вызывающего
Массив передаётся в функцию по ссылке. Операции на месте (+=, /=, запись по индексу) внутри функции меняют исходные данные. Если это не задумано — начинайте функцию с a = a.copy() или пишите a = a / a.max().
def normalize(a):
a /= a.max() # на месте: меняет массив вызывающего
return a
data = np.array([2.0, 4.0, 8.0])
normalize(data)
dataВывод
array([0.25, 0.5 , 1. ])
Маленький срез держит в памяти большой массив
Пока жив срез, жив и весь исходный массив. Если из большого массива нужна маленькая часть надолго, сделайте .copy().
big = np.arange(1_000_000)
first = big[:3] # держит ссылку на весь миллион элементов
print(first.base.size)
first = big[:3].copy() # после копии большой массив можно освободить
print(first.base)Вывод
1000000 None