Скаляр, вектор и матрица в линейной алгебре: определения, операции и NumPy

Скаляр, вектор и матрица в линейной алгебре: определения, операции и NumPy Полезное

Скаляр — это одно число: в линейной алгебре элемент поля, над которым построено векторное пространство (для практики — обычно вещественное число). Вектор — упорядоченный набор из n чисел, элемент пространства R^n. Матрица — прямоугольная таблица чисел m x n, которая задает линейное отображение из R^n в R^m. Ниже — как эти три объекта связаны, какие операции между ними определены и как они выглядят в NumPy (примеры проверены на Python 3.14.7 и NumPy 2.5.3, сентябрь 2026).

Мини-словарь: четыре термина, которые путают

Слово «скаляр» встречается в нескольких близких, но разных смыслах. Разведем их сразу:

Термин Что это Результат Пример
Скаляр одно число число 3, -0.5
Умножение на скаляр число умножается на каждую координату вектора вектор 3 * (2, -1) = (6, -3)
Скалярное произведение сумма попарных произведений координат двух векторов скаляр (1, 2) . (3, 4) = 11
Скалярная величина (физика) величина без направления число с единицей масса 2 кг, температура 20 °C

Главная ловушка: «скалярное произведение» названо так не потому, что в нем участвует скаляр, а потому, что его результат — скаляр. А «умножение на скаляр» возвращает вектор.

Скаляр, вектор, матрица: сравнение по размерности

Удобная сквозная модель — смотреть на число индексов, которые нужны, чтобы достать одно значение. Это упрощение, принятое в программировании (NumPy, PyTorch): в строгой математике вектор определяется через аксиомы векторного пространства, а не через «массив чисел».

Объект Индексов Форма в NumPy (shape) Геометрический смысл
Скаляр 0 () точка на числовой прямой, коэффициент растяжения
Вектор 1 (n,) направленный отрезок от начала координат
Матрица 2 (m, n) линейное преобразование пространства
Тензор (в смысле ML) 3 и больше (k, m, n) набор матриц, например пакет изображений

Геометрически вектор (2, -1) — инструкция «сдвинуться на 2 вправо по X и на 1 вниз по Y». Скаляр 3 растягивает этот вектор в три раза, отрицательный скаляр еще и разворачивает его в противоположную сторону. Матрица поворачивает, растягивает или проецирует сразу все векторы пространства.

Минимальный пример: все три объекта в NumPy

Сначала полный рабочий пример, потом разбор по строкам.

import numpy as np

s = 3.0                                    # скаляр: одно число
v = np.array([2.0, -1.0, 2.0])             # вектор из R^3
A = np.array([1.0, 0.0, 2.0,
              0.0, 1.0, -1.0]).reshape(2, 3)  # матрица 2x3

print("ndim:", np.ndim(s), v.ndim, A.ndim)
print("shape:", np.shape(s), v.shape, A.shape)

print("s * v =", s * v)                 # умножение вектора на скаляр
print("v + v =", v + v)                 # сложение векторов
print("|v| =", np.linalg.norm(v))       # длина (евклидова норма) - скаляр
print("v . v =", v @ v)                 # скалярное произведение - скаляр
print("A @ v =", A @ v)                 # матрица 2x3 на вектор из R^3 -> R^2

Вывод:

ndim: 0 1 2
shape: () (3,) (2, 3)
s * v = [ 6. -3.  6.]
v + v = [ 4. -2.  4.]
|v| = 3.0
v . v = 9.0
A @ v = [ 6. -3.]

Что здесь произошло:

  • ndim показывает число индексов: 0 у скаляра, 1 у вектора, 2 у матрицы.
  • s * v умножает каждую координату на 3 — результат остается вектором той же длины.
  • Норма |v| = sqrt(2^2 + (-1)^2 + 2^2) = sqrt(9) = 3 — это длина вектора, скаляр.
  • v @ v для двух одномерных массивов — скалярное произведение: 4 + 1 + 4 = 9. Оно равно квадрату длины, это полезная проверка.
  • A @ v: матрица 2×3 переводит вектор из R^3 в R^2. Первая координата: 12 + 0(-1) + 22 = 6, вторая: 02 + 1(-1) + (-1)2 = -3.

Операции и их результаты

Сводная таблица «что с чем можно делать и что получится». Размеры записаны по правилам линейной алгебры, где вектор — столбец n x 1.

Операция Условие Результат
скаляр * вектор любой n вектор той же длины
вектор + вектор одинаковая длина вектор
вектор . вектор (скалярное произведение) одинаковая длина скаляр
норма вектора — скаляр >= 0
матрица m x n * вектор n число столбцов = длина вектора вектор длины m
матрица m x n * матрица n x k внутренние размеры совпадают матрица m x k

Скалярное произведение связано с углом между векторами: a . b = |a| * |b| * cos(угла). Отсюда правило: если скалярное произведение равно нулю, ненулевые векторы перпендикулярны.

import numpy as np

a = np.array([1.0, 0.0])
b = np.array([1.0, 1.0])

cos_ab = (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b))
angle = np.degrees(np.arccos(np.clip(cos_ab, -1.0, 1.0)))
print(round(float(cos_ab), 4), round(float(angle), 1))

p = np.array([3.0, 4.0])
print(p / np.linalg.norm(p))        # единичный вектор (орт)

Вывод:

0.7071 45.0
[0.6 0.8]

np.clip здесь страховка: из-за округления в числах с плавающей точкой косинус может чуть выйти за [-1, 1], и arccos вернет nan. Деление на норму дает единичный вектор (орт) того же направления. Граница: у нулевого вектора норма 0, и деление дает [nan nan] с предупреждением RuntimeWarning: invalid value encountered in divide — нулевой вектор нормировать нельзя, это надо проверять до деления.

Геометрия векторов подробнее (коллинеарность, сложение по правилу треугольника, координаты) — в статье о векторах и операциях над ними.

Скаляр в NumPy: 0-мерный массив, вектор длины 1 и матрица 1×1

На бумаге 5, (5) и матрица [5] выглядят одинаково. В коде это четыре разных объекта:

import numpy as np

x = np.float64(5.0)      # скаляр NumPy
z = np.array(5.0)        # 0-мерный массив
one = np.array([5.0])    # вектор длины 1
m = np.full((1, 1), 5.0) # матрица 1x1

for name, obj in [("x", x), ("z", z), ("one", one), ("m", m)]:
    print(name, type(obj).__name__, np.shape(obj), np.ndim(obj))

print(z.item(), one[0], m[0, 0])   # достать число как скаляр
print(float(z), type(z.item()).__name__)

Вывод:

x float64 () 0
z ndarray () 0
one ndarray (1,) 1
m ndarray (1, 1) 2
5.0 5.0 5.0
5.0 float

Скалярами в смысле формы являются только x и z (shape ()). Метод .item() возвращает обычное число Python. Разница важна, когда число из массива передается дальше: например, в float().

Неверно — превращать вектор длины 1 в число через float():

import numpy as np
one = np.array([5.0])
print(float(one))

Результат в NumPy 2.5.3:

TypeError: only 0-dimensional arrays can be converted to Python scalars

Исправление — явно взять элемент: float(one[0]) или one.item(). В NumPy с 1.25 по 2.3 такой вызов еще работал с предупреждением DeprecationWarning (проверено на 2.0.2 и 2.3.5), а начиная с 2.4 это ошибка (проверено на 2.4.6 и 2.5.3), поэтому старый код может сломаться только после обновления.

Две частые ошибки с векторами и матрицами

Ошибка 1: * вместо @ для матриц

В NumPy оператор * поэлементный, а матричное произведение — @ (или np.matmul).

import numpy as np

A = np.array([1, 2, 3, 4]).reshape(2, 2)   # строки: (1, 2) и (3, 4)
B = np.array([0, 1, 1, 0]).reshape(2, 2)   # строки: (0, 1) и (1, 0)

for title, M in [("A * B", A * B), ("A @ B", A @ B), ("A * 10", A * 10)]:
    print(title, "->", *M)   # печатаем матрицу построчно

Вывод (каждая пара скобок — строка матрицы):

A * B -> [0 2] [3 0]
A @ B -> [2 1] [4 3]
A * 10 -> [10 20] [30 40]

A * B — поэлементное произведение (произведение Адамара), A @ B — матричное.

A * B не ошибка синтаксиса, а другая операция — код молча вернет не то. Для скаляра * корректен: A * 10 умножает каждый элемент. Правило для чтения чужого кода: * со скаляром — растяжение, * между массивами одной формы — поэлементное произведение, @ — произведение из линейной алгебры.

Ошибка 2: несовпадение размеров

Матрицу 2×3 можно умножить только на вектор длины 3:

import numpy as np
A = np.array([1.0, 0.0, 2.0,
              0.0, 1.0, -1.0]).reshape(2, 3)
w = np.array([1.0, 2.0])
print(A @ w)

Результат:

ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)

Сообщение читается так: у матрицы k = 3 столбца, а у вектора 2 элемента. Исправление — согласовать размеры: вектор длины 3 или транспонированная матрица A.T @ w (3×2 на вектор длины 2 дает вектор длины 3), если по смыслу задачи нужна именно она.

Строка, столбец и почему .T у вектора ничего не делает

Одномерный массив NumPy формы (n,) — это не строка и не столбец, у него одна ось. Поэтому транспонирование его не меняет. Когда ориентация важна, форму задают явно:

import numpy as np

v = np.array([1, 2, 3])
print(v.T.shape)            # (3,) - транспонирование 1-D ничего не меняет
col = v.reshape(-1, 1)      # явный столбец 3x1
row = v.reshape(1, -1)      # явная строка 1x3
print(col.shape, row.shape)
inner = row @ col
print(inner.shape, inner.item())  # матрица 1x1, число берем через item()
outer = col @ row
print(outer.shape, *outer)        # внешнее произведение, построчно

Вывод:

(3,)
(3, 1) (1, 3)
(1, 1) 14
(3, 3) [1 2 3] [2 4 6] [3 6 9]

Строка на столбец дает матрицу 1×1 — по числу это скалярное произведение (1 + 4 + 9 = 14), но по форме нет. Столбец на строку дает внешнее произведение: матрицу 3×3. Одни и те же числа, разный порядок множителей — разный результат, потому что умножение матриц в общем случае некоммутативно.

Выводы

  • Скаляр — одно число (shape ()), вектор — упорядоченный набор чисел (shape (n,)), матрица — таблица m x n (shape (m, n)); удобно различать их по числу индексов.
  • Умножение на скаляр возвращает вектор, а скалярное произведение двух векторов возвращает скаляр — это разные операции.
  • В NumPy * поэлементный, @ — матричное произведение; A @ w требует, чтобы число столбцов матрицы совпадало с длиной вектора.
  • Вектор длины 1 и матрица 1×1 — не скаляры по форме: число из них берется через .item() или индекс, начиная с NumPy 2.4 float() от вектора длины 1 падает с TypeError (до 2.3 включительно — только предупреждение).
  • Нулевой вектор нельзя нормировать: деление на норму 0 дает nan.

Где применяется / связь с практикой

Скаляры, векторы и матрицы — базовый словарь анализа данных и машинного обучения. Объект выборки — вектор признаков, таблица данных — матрица, веса линейной модели — вектор, а learning rate — скаляр, на который умножается градиент. Предсказание линейной регрессии для всей выборки — это одно произведение X @ w, и ошибки из разделов выше (форма, * вместо @) — самые частые на первых задачах.

Освойте тему на практике

Если хочется системно пройти линейную алгебру, матанализ и теорию вероятностей с упором на применение в коде, посмотрите курс «Базовая математика для цифровых профессий». Попробовать формат и разобрать отдельные темы можно на бесплатных открытых уроках Otus.

FAQ

Может ли скаляр быть комплексным числом?
Да. Скаляры — элементы поля, над которым задано векторное пространство: в R^n это вещественные числа, в C^n — комплексные. В NumPy для этого есть тип complex128.

Чем np.dot отличается от @?
Для одномерных векторов и двумерных матриц результат совпадает. Различие проявляется на массивах с тремя и более осями: @ трактует их как пакет матриц, а np.dot сворачивает оси по другому правилу, поэтому для линейной алгебры обычно берут @.

Скаляр — это тензор нулевого ранга?
В терминах NumPy и PyTorch — да: тензор с формой () и нулем осей. В дифференциальной геометрии у слова «тензор» более строгое определение, и там скаляр — инвариант, не меняющийся при смене системы координат.

OTUS Журнал