Создание массивов в NumPy: arange, linspace, zeros, ones и другие способы
Все конструкторы NumPy на одной странице: arange против linspace, заготовки zeros и ones, матрица eye — и шпаргалка, когда какой способ брать.
Редакция Питоники
Перед вами задача: проанализировать температуру за 365 дней. Писать триста шестьдесят пять чисел руками? А если нужен массив из миллиона точек для графика? В прошлом уроке про первый массив ndarray мы создавали массивы из готовых списков через np.array — этого мало. У NumPy есть десяток конструкторов, которые генерируют данные за вас: последовательности, заготовки нужной формы, диагональные матрицы. В этом уроке разберём все способы создания массива NumPy и главное — научимся выбирать правильный.
Как создать массив NumPy из списка
Начнём с повторения, потому что np.array — самый частый способ создания массива numpy на практике: данные у вас уже есть, нужно лишь превратить их в ndarray. Работает с кортежами, списками и списками списков — из последних получается двумерный массив.
import numpy as np
temps = np.array([12.5, 14.0, 11.8, 15.3]) # из плоского списка
week = np.array([[12.5, 14.0],
[11.8, 15.3]]) # из списка списков
print(temps)
print(week)
print(week.shape, week.ndim, week.size)
[12.5 14. 11.8 15.3] [[12.5 14. ] [11.8 15.3]] (2, 2) 2 4
Если данные приходят из чужого кода или файла — это ваш путь. А вот когда массива ещё нет и его нужно породить с нуля, вступают в игру генераторы, и первый из них — np.arange.
np.arange: арифметическая прогрессия с шагом
np.arange(start, stop, step) создаёт целые числа через равный шаг — это брат обычного range, который возвращает ndarray. Правило то же, что и у срезов: граница stop не включается. Аргументов можно передавать один, два или три.
import numpy as np
print(np.arange(5)) # от 0 до 4
print(np.arange(2, 11, 3)) # от 2 до 10 с шагом 3
print(np.arange(10, 0, -2)) # шаг бывает и отрицательным
[0 1 2 3 4] [2 5 8] [10 8 6 4 2]
Отрицательный шаг стоит запомнить отдельно: np.arange(10, 0, -2) — это обратный отсчёт от десяти до двойки. Так удобно строить обратные индексы и убывающие шкалы. Обратите внимание ещё на выравнивание в печати: NumPy подгоняет колонки по самому широкому числу, поэтому между элементами бывает по два пробела.
np.linspace: N точек между двумя краями
np.linspace(start, stop, num) решает задачу с другой стороны: вы говорите не «какой шаг», а «сколько точек нужно». NumPy сам вычислит шаг, а оба края будут включены — в отличие от arange. Именно linspace используют для осей графиков, сеток координат и любых шкал, где важно точное число точек.
import numpy as np
print(np.linspace(0, 1, 5)) # пять точек от 0 до 1
print(np.linspace(0, 100, 5)) # шаг NumPy посчитает сам
print(np.linspace(10, 20, 3, dtype=int)) # тип можно задать
[0. 0.25 0.5 0.75 1. ] [ 0. 25. 50. 75. 100.] [10 15 20]
Чем arange отличается от linspace в NumPy?
Разница arange и linspace — вечный источник путаницы, поэтому сведём её в таблицу. Запомните привязку: arange управляет шагом, linspace — количеством точек.
| np.arange | np.linspace | |
|---|---|---|
| Третий аргумент | шаг между элементами | число точек |
| Правая граница | не включается | включается |
| Дробный шаг | ненадёжен из-за ошибок float | шаг считается точно |
| Типичная задача | целые индексы, обратный отсчёт | оси графиков, сетки, шкалы |
Почему arange с шагом 0.1 теряет элементы
Обещанный разбор каприза. Число 0.1 в двоичной системе — бесконечная периодическая дробь, как одна треть в десятичной: компьютер хранит лишь приближение с примерно шестнадцатью знаками. Python показывает это честно, если складывать 0.1 подряд:
# складываем одну десятую десять раз подряд
x = 0.0
for i in range(1, 11):
x += 0.1
print(i, x)
1 0.1 2 0.2 3 0.30000000000000004 4 0.4 5 0.5 6 0.6 7 0.7 8 0.7999999999999999 9 0.8999999999999999 10 0.9999999999999999
Уже на третьем шаге вместо 0.3 вышло 0.30000000000000004, а десять сложений дали 0.9999999999999999. Старые версии NumPy строили arange накоплением start += step — хвост уплывал. Современный NumPy считает элементы как start + i * step, но число элементов по-прежнему вычисляется делением (stop - start) / step с округлением вверх — одной лишней единицы на шестнадцатом знаке хватает, чтобы округление съехало.
import numpy as np
a = np.arange(0, 0.7, 0.1) # ждём числа 0.0, 0.1, ... 0.7
print(len(a))
print(a)
b = np.arange(0, 1.1, 0.1) # здесь деление "сошлось"
print(len(b))
7 [0. 0.1 0.2 0.3 0.4 0.5 0.6] 11
Классический пример, где linspace незаменим, — тригонометрия. Построим таблицу синусов для привычных углов: сначала переведём градусы в радианы функцией np.deg2rad (NumPy считает синусы только в радианах — забытый перевод градусов — источник половины «у меня синус не тот» вопросов на форумах).
import numpy as np
degrees = np.array([0, 30, 45, 60, 90], dtype=float)
radians = np.deg2rad(degrees)
print(radians)
print(np.sin(radians)) # синусы знакомых углов
[0. 0.52359878 0.78539816 1.04719755 1.57079633] [0. 0.5 0.70710678 0.8660254 1. ]
В нижней строке легко узнать таблицу Брадиса: 0.5 — это синус 30 градусов, 0.7071 — сорока пяти, 1.0 — девяноста. Обратный перевод делает np.rad2deg. А если нужны точки именно на отрезке от нуля до двух пи, комбинация np.linspace(0, 2 * np.pi, 10) — самый короткий путь.
zeros, ones и full: массивы-заготовки
Часто массив нужен не с данными, а под данные: буфер под результат вычислений, заглушка для будущих значений, обнулённая таблица счётчиков. Для этого в NumPy есть три конструктора: np.zeros заполняет нулями, np.ones — единицами, np.full — любым заданным значением. Запомните синтаксическую ловушку: размер передаётся одним аргументом — кортежем.
import numpy as np
print(np.zeros(3)) # вектор из трёх нулей
print(np.zeros((2, 3))) # матрица 2 на 3 из нулей
print(np.ones(4, dtype=int)) # единицы, но целые
print(np.full((2, 2), 7)) # всё заполняем семёрками
[0. 0. 0.] [[0. 0. 0.] [0. 0. 0.]] [1 1 1 1] [[7 7] [7 7]]
Почему np.zeros(3) даёт нули с точкой — 0.? По умолчанию заготовки создаются в float64: вещественный тип — безопасный дефолт, в него влезают и целые значения, и дробные. Целые счётчики удобно делать сразу: np.zeros(10, dtype=int). Точка после числа в печати означает именно «это float», как мы разбирали в уроке про dtype.
np.empty: быстрый, но с мусором
Есть ещё np.empty((2, 2)) — он выделяет память, но не заполняет её, а отдаёт как есть: внутри окажутся байты, которые там лежали до вас. Работает чуть быстрее zeros на огромных массивах, но значения будут случайным мусором — у автора в браузере это оказались остатки предыдущих вычислений страницы. Практическое правило: используйте np.empty только если следующей же строкой заполните массив целиком. Если сомневаетесь — берите np.zeros, две лишние миллисекунды дешевле ночи отладки.
np.eye: единичная матрица
np.eye(n) строит квадратную матрицу n на n, где на диагонали единицы, а вокруг нули. Она называется единичной и играет роль единицы в матричном умножении: умножить матрицу на единичную — всё равно что ничего не менять. Встречается в линейной алгебре, тестах математического кода и методах вроде регуляризации.
import numpy as np
print(np.eye(3))
[[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]
Родственная функция np.diag(v) ставит ваш массив v на диагональ квадратной матрицы, а если ей передать уже готовую матрицу — наоборот, вытащит диагональ. Пригодится, когда дойдёте до матричных операций в уроке про reshape и оси.
Матрица переходов: eye как заготовка
Где eye встречается за пределами учебников? В матрицах переходов: строка — из какого состояния выходим, столбец — в какое приходим. Так моделируют погоду, очереди, поведение пользователей. Система, которая никогда не меняет состояние, описывается в точности единичной матрицей. Реальную матрицу получают из np.eye, заменяя часть единиц и нулей:
import numpy as np
# погода: состояния "ясно" и "дождь"
# старт - единичная матрица: из любого состояния остаёмся в нём же
P = np.eye(2)
P[0, 0], P[0, 1] = 0.8, 0.2 # из ясно: 80% ясно, 20% дождь
P[1, 0], P[1, 1] = 0.6, 0.4 # из дождя: 60% ясно, 40% дождь
print(P)
[[0.8 0.2] [0.6 0.4]]
Каждая строка в сумме даёт единицу: 0.8 + 0.2 и 0.6 + 0.4 — все возможные исходы из одного состояния. Заготовка np.eye(2) страхует от глупых ошибок: матрица гарантированно квадратная, диагональ уже занята «остаюсь на месте», остаётся разлить вероятности по клеткам. Запись P[0, 1] — двумерная индексация, правила те же, что у одномерной, — её разбирает индексация и срезы. Тот же приём «заготовка плюс точечная перезапись» работает и с np.zeros.
Практика: сетка температур и таблица умножения
Соберём конструкторы в два мини-упражнения. Первое — шкала температур: пять опорных точек от 10 до 30 градусов через linspace. Второе — таблица умножения на семь: возьмём arange и умножим весь массив сразу. Третьей строкой превратим числа от 1 до 9 в квадратную матрицу методом reshape — пока просто посмотрите на эффект, подробно его разберём в уроке про reshape и многомерные массивы.
import numpy as np
# шкала из пяти точек от 10 до 30 градусов
temps = np.linspace(10, 30, 5)
print(temps)
# таблица умножения на 7 для чисел 1..9
sevens = np.arange(1, 10) * 7
print(sevens)
# те же числа - квадратной матрицей
square = np.arange(1, 10).reshape(3, 3)
print(square)
[10. 15. 20. 25. 30.] [ 7 14 21 28 35 42 49 56 63] [[1 2 3] [4 5 6] [7 8 9]]
Строка np.arange(1, 10) * 7 — маленькая демонстрация векторизации, о которой подробно пойдёт речь в четвёртом уроке: одна операция применилась ко всем элементам сразу, без циклов. Заметьте также выравнивание: NumPy увидел двузначные числа и подвинул семёрку вправо.
Какой конструктор выбрать: шпаргалка
Девять способов создания массива numpy из этого урока закрывают девять задач из десяти. Сводная таблица для быстрого выбора:
| Задача | Функция | Пример |
|---|---|---|
| Данные уже есть в списке | np.array | np.array([1, 2, 3]) |
| Целые числа с шагом | np.arange | np.arange(0, 10, 2) |
| N равных точек на отрезке | np.linspace | np.linspace(0, 1, 11) |
| Массив нулей под буфер | np.zeros | np.zeros((3, 4)) |
| Массив единиц | np.ones | np.ones(5, dtype=int) |
| Одно значение везде | np.full | np.full((2, 2), 7) |
| Единичная матрица | np.eye | np.eye(3) |
| Матрица с заданной диагональю | np.diag | np.diag([0.8, 0.4]) |
| Быстрая память под заполнение | np.empty | np.empty((2, 3)) |
Частые ошибки при создании массивов
Две ошибки вы уже видели в предупреждениях — соберём их рядом, чтобы разобрать тексты дословно. NumPy пишет их довольно откровенно, нужно только привыкнуть читать до конца.
import numpy as np
# опечатка: размер передан двумя числами вместо кортежа
try:
print(np.zeros(2, 3))
except TypeError as e:
print("TypeError:", e)
# reshape под размер, которого нет
try:
np.arange(4).reshape(3, 2)
except ValueError as e:
print("ValueError:", e)
TypeError: Cannot interpret '3' as a data type ValueError: cannot reshape array of size 4 into shape (3,2)
Первая ошибка говорит про тип данных, хотя проблема в скобках: np.zeros(2, 3) воспринял тройку как попытку задать dtype. Вторая честно объясняет арифметику: в массиве 4 элемента, а форма (3, 2) требует шести — «cannot reshape». Читайте такие сообщения как уравнение: числа слева и справа должны сходиться. На эту тему есть отдельная ловушка про -1 в reshape, но ей время придёт в уроке про форму массивов.
Что дальше
Теперь вы умеете создавать массив numpy девятью способами и знаете, чем linspace лучше arange с дробным шагом. Следующий логичный вопрос — как доставать из массива отдельные элементы и куски: индексация и срезы с подвохом в виде view и copy. А после индексации — арифметика массивов, ради которой всё и затевалось: покажем, почему векторизация быстрее циклов в сотню раз.
Сначала предскажи ответ в голове — это главный навык программиста.
import numpy as np
print(np.arange(10, 0, -2))
import numpy as np
print(np.linspace(0, 1, 5))
1. Чем np.linspace отличается от np.arange?
2. Что выведет print(np.arange(2, 11, 3))?
3. Какой код создаст матрицу 2 на 3 из нулей?
4. Какой dtype у массива np.zeros(3) по умолчанию?
5. Что выведет print(np.linspace(10, 20, 3, dtype=int))?
6. Сколько элементов вернёт np.arange(0, 0.7, 0.1)?
Создайте массив numbers с целыми числами от 1 до 10 включительно через np.arange, возведите его в квадрат (поэлементно) и выведите результат одной строкой.
Как создать массив NumPy из списка?
Передайте список конструктору np.array: np.array([1, 2, 3]) вернёт одномерный массив, а np.array([[1, 2], [3, 4]]) — двумерный из списка списков. Если данные придут из внешнего источника, полезно сразу указать dtype, например np.array(values, dtype=float), чтобы одна строка в данных не превратила весь массив в текст.
Чем np.arange отличается от np.linspace?
arange строит числа с заданным шагом и не включает правую границу, а linspace строит заданное число точек на отрезке и включает оба края. Для целых шагов arange удобен и точен; с дробным шагом он ненадёжен из-за ошибок плавающей точки — в таких случаях берите linspace.
Как заполнить массив нулями и единицами в NumPy?
Функциями np.zeros и np.ones, передав размер одним кортежем: np.zeros((3, 4)) даст матрицу 3 на 4 из нулей. По умолчанию тип float64, целые нужны — добавьте dtype=int. Для заполнения произвольным значением есть np.full((2, 2), 7).
Как создать пустой массив NumPy?
np.empty((2, 3)) выделяет память без инициализации — внутри окажется случайный мусор, а не нули. Он чуть быстрее zeros на огромных объёмах, но значения нужно перезаписать до первого чтения. Если нужен «пустой» массив под будущие данные, почти всегда безопаснее np.zeros.
Почему np.arange с дробным шагом возвращает не все числа?
Из-за ошибок плавающей точки: шаг 0.1 не представим в двоичной памяти точно, поэтому число элементов, вычисляемое делением (stop − start) / step, может съехать на единицу. Например, np.arange(0, 0.7, 0.1) возвращает семь чисел вместо восьми. Надёжные альтернативы: целый arange с умножением на шаг (np.arange(7) * 0.1) или np.linspace, который гарантирует обе границы.
Как создать заготовку массива NumPy и заполнить её постепенно?
Создайте заготовку нужной формы через np.zeros((n,)) и перезаписывайте значения по индексам — тот же приём «заготовка плюс точечная перезапись», что и в матрице переходов из этого урока. Добавлять элементы по одному нельзя: размер массива фиксирован после создания, поэтому постепенное заполнение — это запись в заранее выделенную память, а не append.
Понравился урок? Сошлитесь на него
«Целочисленный шаг arange надёжен, дробный — нет.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
NumPy · Урок 1
Что такое NumPy и как установить через pip: первый массив ndarray
Первый массив ndarray: создаём, сравниваем со списком, разбираем dtype и shape — и ускоряем сумму миллиона чисел примерно в сто раз.
NumPy · Урок 3
Индексация и срезы массивов NumPy: как достать любой элемент
Индексы с нуля и с конца, срезы с шагом, строки и столбцы двумерных массивов — и главный подводный камень NumPy: срез возвращает view, а не копию. С примерами, разбором ошибок и шпаргалкой.
NumPy · Урок 4
Операции с массивами: векторизация и почему NumPy быстрее списков в 100 раз
Умножаем миллион цен одной строкой и замеряем время через time.perf_counter: почему векторизация NumPy обгоняет цикл for в десятки раз. Ufunc, np.dot, булевы сравнения и правило «никаких for там, где есть операция».
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
aiogram · Урок 1
Как создать телеграм-бота на Python: от BotFather до первого эхо-ответа
Регистрируем бота в BotFather, разбираемся, что такое токен и long polling, и собираем эхо-бота на aiogram — механику проверяем прямо в браузере, без токена.
как создать телеграм бота на pythonсоздание телеграм бота с нуля
openpyxl · Урок 2
Листы в openpyxl: создание, переименование и выбор листа
Книга с одним листом — это заметка. Строим настоящую книгу: create_sheet с позицией, переименование, выбор по имени, удаление и цветные ярлычки.
openpyxl создать лист
NumPy · Урок 7
Многомерные массивы и оси: reshape, axis и работа с матрицами
Форма массива — это взгляд на данные: reshape и магия -1, оси axis на пальцах, транспонирование T, матричное умножение @ и склейка vstack/hstack.
numpy reshapenumpy reshape как изменить размер массива