Нашли ошибку?
Скачать чистый
урок 2 из 5≈ 20 минупражнения: 0 из 3

Матричное умножение

Сумма попарных произведений — скалярное произведение np.dot; оператор @ для таблиц — заказы × цены, оценки × веса; правило форм (m, n) @ (n, k) → (m, k).

Python запустится при первом выполнении

Покупатель взял 2 пачки кофе по 150 ₽, 1 кружку за 220 ₽ и 3 печенья по 90 ₽. Сколько он заплатил? Нужно перемножить количества на цены попарно и сложить: 2·150 + 1·220 + 3·90 = 790. Эта операция — «умножить попарно и сложить» — встречается в анализе данных постоянно, и у неё есть своё имя.

Скалярное произведение

Сумму попарных произведений двух массивов одинаковой длины называют скалярным произведением (dot product). Его можно собрать из знакомых частей — (q * p).sum() — или получить сразу: функцией np.dot(q, p) или оператором @:

[ ]Три способа посчитать чек
import numpy as np

q = np.array([2, 1, 3])          # сколько штук
p = np.array([150, 220, 90])     # цена штуки
print(q * p)
print((q * p).sum())
print(np.dot(q, p))
print(q @ p)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
[300 220 270]
790
790
790
[ ]Упражнение 1Взвешенная оценка решено

Итоговая оценка за курс складывается так: экзамен — 50 %, проект — 30 %, домашние задания — 20 %. В marks — оценки одного студента в этом порядке, в weights — веса. Сохраните в final итоговую оценку.

marks = np.array([78, 92, 85])
weights = np.array([0.5, 0.3, 0.2])
final = ...

Таблица на вектор

Настоящая сила @ — в таблицах. Пусть в orders пять заказов (строки) и четыре товара (столбцы): число — сколько штук товара в заказе. Цены — массив из четырёх чисел. orders @ prices считает скалярное произведение каждой строки на цены — сумму каждого заказа — за одну операцию:

[ ]Суммы пяти заказов
orders = np.array([
    [2, 1, 0, 3],
    [0, 2, 1, 1],
    [1, 0, 4, 0],
    [3, 3, 0, 2],
    [0, 1, 2, 5],
])
prices = np.array([180, 250, 90, 60])
print(orders.shape, prices.shape)
print(orders @ prices)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
(5, 4) (4,)
[ 790  650  540 1410  730]

Формы: таблица (5, 4) @ вектор (4,) → (5,). Число столбцов таблицы должно совпасть с длиной вектора — каждому товару своя цена. Если они не совпадают, NumPy сообщает об ошибке:

[ ]Цен меньше, чем товаровошибка: ValueError
orders @ np.array([180, 250, 90])
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 3 is different from 4)
[ ]Упражнение 2Итоги группы решено

В scores — оценки четырёх студентов (строки) за экзамен, проект и домашние задания (столбцы). Сохраните в finals итоговые оценки всех студентов с весами weights из прошлого упражнения, а в best — номер строки студента с лучшим итогом.

scores = np.array([
    [78, 85, 90],
    [62, 70, 58],
    [91, 88, 95],
    [55, 80, 72],
])
finals = ...
best = ...

Таблица на таблицу

Если справа не вектор, а таблица, @ считает скалярное произведение каждой строки левой таблицы на каждый столбец правой. Добавим к ценам второй столбец — вес товара в граммах. Тогда orders @ price_weight даст сразу два итога по каждому заказу — сумму и вес посылки:

[ ]Сумма и вес каждого заказа
price_weight = np.array([
    [180, 250],    # цена, ₽; вес, г
    [250, 300],
    [90, 100],
    [60, 50],
])
print(orders.shape, price_weight.shape)
print(orders @ price_weight)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
(5, 4) (4, 2)
[[ 790  950]
 [ 650  750]
 [ 540  650]
 [1410 1750]
 [ 730  750]]

Например, число в строке 0, столбце 1 — вес первого заказа: строка 0 orders (2, 1, 0, 3) на столбец 1 price_weight (250, 300, 100, 50): 2·250 + 1·300 + 0·100 + 3·50 = 950.

Правило форм: (m, n) @ (n, k) → (m, k). Внутренние размеры (n) должны совпасть — их «съедает» сумма, внешние (m и k) остаются.

Вопрос

a формы (5, 3), b формы (3, 2). Какая форма у a @ b?

[ ]Упражнение 3Прибыль заказов решено

В price_cost — цена и себестоимость каждого из четырёх товаров (строка — товар). Посчитайте для заказов orders таблицу rc — выручку и затраты каждого заказа, затем сохраните в profit прибыль каждого заказа (выручка минус затраты), а в total_profit — общую прибыль.

price_cost = np.array([
    [180, 70],     # цена, себестоимость
    [250, 110],
    [90, 30],
    [60, 15],
])
rc = ...
profit = ...
total_profit = ...

Итоги

  • Скалярное произведение — сумма попарных произведений: np.dot(a, b), a @ b или (a * b).sum().
  • * — поэлементно, @ — «умножить попарно и сложить».
  • Таблица (m, n) @ вектор (n,) → (m,): итог по каждой строке.
  • Таблица (m, n) @ таблица (n, k) → (m, k): внутренние размеры совпадают и исчезают.

Подробнее в справочнике

Нашли ошибку?