Let us see how to compute matrix multiplication with NumPy. We will be using the numpy.dot() method to find the product of 2 matrices.
For example, for two matrices A and B. A = [[1, 2], [2, 3]] B = [[4, 5], [6, 7]] So, A.B = [[1*4 + 2*6, 2*4 + 3*6], [1*5 + 2*7, 2*5 + 3*7] So the computed answer will be: [[16, 26], [19, 31]]
In Python numpy.dot() method is used to calculate the dot product between two arrays.
Example 1 : Matrix multiplication of 2 square matrices.
import
numpy as np
p
=
[[
1
,
2
], [
2
,
3
]]
q
=
[[
4
,
5
], [
6
,
7
]]
print
(
"Matrix p :"
)
print
(p)
print
(
"Matrix q :"
)
print
(q)
result
=
np.dot(p, q)
print
(
"The matrix multiplication is :"
)
print
(result)
Output :
Matrix p : [[1, 2], [2, 3]] Matrix q : [[4, 5], [6, 7]] The matrix multiplication is : [[16 19] [26 31]]
Example 2 : Matrix multiplication of 2 rectangular matrices.
import
numpy as np
p
=
[[
1
,
2
], [
2
,
3
], [
4
,
5
]]
q
=
[[
4
,
5
,
1
], [
6
,
7
,
2
]]
print
(
"Matrix p :"
)
print
(p)
print
(
"Matrix q :"
)
print
(q)
result
=
np.dot(p, q)
print
(
"The matrix multiplication is :"
)
print
(result)
Output :
Matrix p : [[1, 2], [2, 3], [4, 5]] Matrix q : [[4, 5, 1], [6, 7, 2]] The matrix multiplication is : [[16 19 5] [26 31 8] [46 55 14]]
Last Updated :
02 Sep, 2020
Like Article
Save Article
Содержание
- NumPy: матрицы и операции над ними
- 1. Создание матриц
- 2. Индексирование
- 3. Векторы, вектор-строки и вектор-столбцы
- 4. Datatypes
- 5. Математические операции
- 6. Умножение матриц и столбцов
- 7. Объединение массивов
- Задания: (Блок 1)
- Задание 1:
- Задание 2:
- Задания: (Блок 1)
- 8. Транспонирование матриц
- 9. Определитель матрицы
- 10. Ранг матрицы
- 11. Системы линейных уравнений
- 12. Обращение матриц
- 13. Собственные числа и собственные вектора матрицы
- 14. Расстояния между векторами
- p-норма
- ℓ1 норма
- ℓ2 норма
- 15. Расстояния между векторами
- 16. Скалярное произведение и угол между векторами
- 17. Комплексные числа в питоне
- Задания: (Блок 2)
- Задание 3:
- Задания: (Блок 2)
NumPy: матрицы и операции над ними
Ссылка на jupyter notebook
В этом ноутбуке из сторонних библиотек нам понадобится только NumPy.
Для удобства импортируем ее под более коротким именем:
import numpy as np
1. Создание матриц
Приведем несколько способов создания матриц в NumPy.
Самый простой способ — с помощью функции
numpy.array(list, dtype=None, …).
В качестве первого аргумента ей надо передать итерируемый объект,
элементами которого являются другие итерируемые объекты одинаковой длины
и содержащие данные одинакового типа.
Второй аргумент является опциональным и определяет тип данных матрицы.
Его можно не задавать, тогда тип данных будет определен из типа
элементов первого аргумента. При задании этого параметра будет
произведена попытка приведения типов.
Например, матрицу из списка списков целых чисел можно создать следующим
образом:
a = np.array([1, 2, 3]) # Создаем одномерный массив print(type(a)) # Prints "<class 'numpy.ndarray'>" print(a.shape) # Prints "(3,)" - кортеж с размерностями print(a[0], a[1], a[2]) # Prints "1 2 3" a[0] = 5 # Изменяем значение элемента массива print(a) # Prints "[5, 2, 3]" b = np.array([[1,2,3],[4,5,6]]) # Создаем двухмерный массив print(b.shape) # Prints "(2, 3)" print(b[0, 0], b[0, 1], b[1, 0]) # Prints "1 2 4" print(np.arange(1, 5)) #Cоздает вектор с эелементами от 1 до 4
<class 'numpy.ndarray'> (3,) 1 2 3 [5 2 3] (2, 3) 1 2 4 [1 2 3 4]
matrix = np.array([[1, 2, 3], [2, 5, 6], [6, 7, 4]]) print ("Матрица:n", matrix)
Матрица: [[1 2 3] [2 5 6] [6 7 4]]
Второй способ создания — с помощью встроенных функций
numpy.eye(N, M=None, …), numpy.zeros(shape, …),
numpy.ones(shape, …).
Первая функция создает единичную матрицу размера N×M;
если M не задан, то M = N.
Вторая и третья функции создают матрицы, состоящие целиком из нулей или
единиц соответственно. В качестве первого аргумента необходимо задать
размерность массива — кортеж целых чисел. В двумерном случае это набор
из двух чисел: количество строк и столбцов матрицы.
Примеры:
b = np.eye(5) print ("Единичная матрица:n", b)
Единичная матрица: [[1. 0. 0. 0. 0.] [0. 1. 0. 0. 0.] [0. 0. 1. 0. 0.] [0. 0. 0. 1. 0.] [0. 0. 0. 0. 1.]]
c = np.ones((7, 5)) print ("Матрица, состоящая из одних единиц:n", c)
Матрица, состоящая из одних единиц: [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]]
d = np.full((2,2), 7) # Создает матрицу (1, 2) заполненую заданным значением print(d) # Prints "[[ 7. 7.] # [ 7. 7.]]" e = np.random.random((2,2)) # Создает еденичную матрицу (2, 2) заполненую случаными числами (0, 1) print(e) # Might print "[[ 0.91940167 0.08143941] # [ 0.68744134 0.87236687]]"
[[7 7] [7 7]] [[0.25744383 0.48056466] [0.13767881 0.40578168]]
Обратите внимание: размерность массива задается не двумя аргументами
функции, а одним — кортежем!
Вот так — np.ones(7, 5) — создать массив не получится, так как
функции в качестве параметра shape передается 7, а не кортеж
(7, 5).
И, наконец, третий способ — с помощью функции
numpy.arange([start, ]stop, [step, ], …), которая создает
одномерный массив последовательных чисел из промежутка
[start, stop) с заданным шагом step, и метода
array.reshape(shape).
Параметр shape, как и в предыдущем примере, задает размерность
матрицы (кортеж чисел). Логика работы метода ясна из следующего примера:
v = np.arange(0, 24, 2) print ("Вектор-столбец:n", v)
Вектор-столбец: [ 0 2 4 6 8 10 12 14 16 18 20 22]
d = v.reshape((3, 4)) print ("Матрица:n", d)
Матрица: [[ 0 2 4 6] [ 8 10 12 14] [16 18 20 22]]
Более подробно о том, как создавать массивы в NumPy, см.
документацию.
2. Индексирование
Для получения элементов матрицы можно использовать несколько способов.
Рассмотрим самые простые из них.
Для удобства напомним, как выглядит матрица d:
print ("Матрица:n", d)
Матрица: [[ 0 2 4 6] [ 8 10 12 14] [16 18 20 22]]
Элемент на пересечении строки i и столбца j можно
получить с помощью выражения array[i, j].
Обратите внимание: строки и столбцы нумеруются с нуля!
print ("Второй элемент третьей строки матрицы:", d[2, 1])
Второй элемент третьей строки матрицы: 18
Из матрицы можно получать целые строки или столбцы с помощью выражений
array[i, :] или array[:, j] соответственно:
print ("Вторая строка матрицы d:n", d[1, :]) print ("Четвертый столбец матрицы d:n", d[:, 3])
Вторая строка матрицы d: [ 8 10 12 14] Четвертый столбец матрицы d: [ 6 14 22]
Еще один способ получения элементов — с помощью выражения
array[list1, list2], где list1, list2 —
некоторые списки целых чисел. При такой адресации одновременно
просматриваются оба списка и возвращаются элементы матрицы с
соответствующими координатами. Следующий пример более понятно объясняет
механизм работы такого индексирования:
print ("Элементы матрицы d с координатами (1, 2) и (0, 3):n", d[[1, 0], [2, 3]])
Элементы матрицы d с координатами (1, 2) и (0, 3): [12 6]
# Slicing # Создадим матрицу (3, 4) # [[ 1 2 3 4] # [ 5 6 7 8] # [ 9 10 11 12]] a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]]) # Используя слайсинг, созадим матрицу b из элементов матрицы а # будем использовать 0 и 1 строку, а так же 1 и 2 столебц # [[2 3] # [6 7]] b = a[:2, 1:3] print(b) # ОБРАТИТЕ ВНИМАНИЕ НА ИЗМЕНЕНИЕ ИСХОДОЙ МАТРИЦЫ print(a[0, 1]) # Prints "2" b[0, 0] = 77 # b[0, 0] is the same piece of data as a[0, 1] print(a[0, 1]) # Prints "77"
[[2 3] [6 7]] 2 77
# Integer array indexing a = np.array([[1,2], [3, 4], [5, 6]]) print(a) print() # Пример Integer array indexing # В результате получится массив размерности (3,) # Обратите внимание, что до запятой идут индексы строк, после - столбцов print(a[[0, 1, 2], [0, 1, 0]]) # Prints "[1 4 5]" print() # По-другому пример можно записать так print(np.array([a[0, 0], a[1, 1], a[2, 0]])) # Prints "[1 4 5]"
[[1 2] [3 4] [5 6]] [1 4 5] [1 4 5]
Примеры использования слайсинга:
# Создадим новый маассив, из которого будем выбирать эллементы a = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]]) print(a) # prints "array([[ 1, 2, 3], # [ 4, 5, 6], # [ 7, 8, 9], # [10, 11, 12]])" # Создадим массив индексов b = np.array([0, 2, 0, 1]) # Выберем из каждой строки элемент с индексом из b (индекс столбца берется из b) print(a[np.arange(4), b]) # Prints "[ 1 6 7 11]" print() # Добавим к этим элементам 10 a[np.arange(4), b] += 10 print(a) # prints "array([[11, 2, 3], # [ 4, 5, 16], # [17, 8, 9], # [10, 21, 12]])
[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]] [ 1 6 7 11] [[11 2 3] [ 4 5 16] [17 8 9] [10 21 12]]
a = np.array([[1,2], [3, 4], [5, 6]]) bool_idx = (a > 2) # Найдем эллементы матрицы a, которые больше 2 # В результате получим матрицу b, такой же размерности, как и a print(bool_idx) # Prints "[[False False] print() # [ True True] # [ True True]]" # Воспользуемся полученным массивом для создания нового массива, ранга 1 print(a[bool_idx]) # Prints "[3 4 5 6]" # Аналогично print(a[a > 2]) # Prints "[3 4 5 6]"
[[False False] [ True True] [ True True]] [3 4 5 6] [3 4 5 6]
#Помните, что вы можете пользоваться сразу несколькими типами индексирования a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]]) row_r1 = a[1, :] row_r2 = a[1:2, :] print(row_r1, row_r1.shape) # Prints "[5 6 7 8] (4,)" print(row_r2, row_r2.shape) # Prints "[[5 6 7 8]] (1, 4)"
[5 6 7 8] (4,) [[5 6 7 8]] (1, 4)
Более подробно о различных способах индексирования в массивах см.
документацию.
3. Векторы, вектор-строки и вектор-столбцы
Следующие два способа задания массива кажутся одинаковыми:
a = np.array([1, 2, 3]) b = np.array([[1], [2], [3]])
Однако, на самом деле, это задание одномерного массива (то есть
вектора) и двумерного массива:
print ("Вектор:n", a) print ("Его размерность:n", a.shape) print ("Двумерный массив:n", b) print ("Его размерность:n", b.shape)
Вектор: [1 2 3] Его размерность: (3,) Двумерный массив: [[1] [2] [3]] Его размерность: (3, 1)
Обратите внимание: вектор (одномерный массив) и вектор-столбец
или вектор-строка (двумерные массивы) являются различными объектами в
NumPy, хотя математически задают один и тот же объект. В случае
одномерного массива кортеж shape состоит из одного числа и имеет
вид (n,), где n — длина вектора. В случае двумерных
векторов в shape присутствует еще одна размерность, равная
единице.
В большинстве случаев неважно, какое представление использовать, потому
что часто срабатывает приведение типов. Но некоторые операции не
работают для одномерных массивов. Например, транспонирование (о нем
пойдет речь ниже):
a = a.T b = b.T
print ("Вектор не изменился:n", a) print ("Его размерность также не изменилась:n", a.shape) print ("Транспонированный двумерный массив:n", b) print ("Его размерность изменилась:n", b.shape)
Вектор не изменился: [1 2 3] Его размерность также не изменилась: (3,) Транспонированный двумерный массив: [[1 2 3]] Его размерность изменилась: (1, 3)
4. Datatypes
Все элементы в массиве numpy принадлежат одному типу. В этом плане
массивы ближе к C, чем к привычным вам листам питона. Numpy имеет
множество встренных типов, подходящих для решения большинства задач.
x = np.array([1, 2]) # Автоматический выбор типа print(x.dtype) # Prints "int64" x = np.array([1.0, 2.0]) # Автоматический выбор типа print(x.dtype) # Prints "float64" x = np.array([1, 2], dtype=np.int64) # Принудительное выставление типа print(x.dtype) # Prints "int64"
int32 float64 int64
5. Математические операции
К массивам (матрицам) можно применять известные вам математические
операции. Следут понимать, что при этом у элементов должны быть схожие
размерности. Поведение в случае не совпадения размерностей хорошо
описанно в документации numpy.
x = np.array([[1,2],[3,4]], dtype=np.float64) y = np.array([[5,6],[7,8]], dtype=np.float64) arr = np.array([1, 2])
# Сложение происходит поэлеметно # [[ 6.0 8.0] # [10.0 12.0]] print(x + y) print() print(np.add(x, y)) print('С числом') print(x + 1) print('C массивом другой размерности') print(x + arr)
[[ 6. 8.] [10. 12.]] [[ 6. 8.] [10. 12.]] С числом [[2. 3.] [4. 5.]] C массивом другой размерности [[2. 4.] [4. 6.]]
# Вычитание print(x - y) print(np.subtract(x, y))
[[-4. -4.] [-4. -4.]] [[-4. -4.] [-4. -4.]]
# Деление # [[ 0.2 0.33333333] # [ 0.42857143 0.5 ]] print(x / y) print(np.divide(x, y))
[[0.2 0.33333333] [0.42857143 0.5 ]] [[0.2 0.33333333] [0.42857143 0.5 ]]
# Другие функции # [[ 1. 1.41421356] # [ 1.73205081 2. ]] print(np.sqrt(x))
[[1. 1.41421356] [1.73205081 2. ]]
6. Умножение матриц и столбцов
Напоминание теории. Операция умножения определена для двух
матриц, таких что число столбцов первой равно числу строк второй.
Пусть матрицы A и B таковы, что
A ∈ ℝn×k и
B ∈ ℝk×m. Произведением матриц
A и B называется матрица C, такая что
cij = ∑kr = 1airbrj, где cij —
элемент матрицы C, стоящий на пересечении строки с номером
i и столбца с номером j.
В NumPy произведение матриц вычисляется с помощью функции
numpy.dot(a, b, …) или с помощью метода
array1.dot(array2), где array1 и array2 —
перемножаемые матрицы.
a = np.array([[1, 0], [0, 1]]) b = np.array([[4, 1], [2, 2]]) r1 = np.dot(a, b) r2 = a.dot(b)
print ("Матрица A:n", a) print ("Матрица B:n", b) print ("Результат умножения функцией:n", r1) print ("Результат умножения методом:n", r2)
Матрица A: [[1 0] [0 1]] Матрица B: [[4 1] [2 2]] Результат умножения функцией: [[4 1] [2 2]] Результат умножения методом: [[4 1] [2 2]]
Матрицы в NumPy можно умножать и на векторы:
c = np.array([1, 2]) r3 = b.dot(c)
print ("Матрица:n", b) print ("Вектор:n", c) print ("Результат умножения:n", r3)
Матрица: [[4 1] [2 2]] Вектор: [1 2] Результат умножения: [6 6]
Обратите внимание: операция * производит над матрицами
покоординатное умножение, а не матричное!
r = a * b
print ("Матрица A:n", a) print ("Матрица B:n", b) print ("Результат покоординатного умножения через операцию умножения:n", r)
Матрица A: [[1 0] [0 1]] Матрица B: [[4 1] [2 2]] Результат покоординатного умножения через операцию умножения: [[4 0] [0 2]]
Более подробно о матричном умножении в NumPy см.
документацию.
7. Объединение массивов
Массивы можно Объединенять. Есть горизонтальное и вертикальное
объединение.
a = np.floor(10*np.random.random((2,2))) b = np.floor(10*np.random.random((2,2))) print(a) print(b) print() print(np.vstack((a,b))) print() print(np.hstack((a,b)))
[[4. 0.] [1. 4.]] [[9. 7.] [2. 6.]] [[4. 0.] [1. 4.] [9. 7.] [2. 6.]] [[4. 0. 9. 7.] [1. 4. 2. 6.]]
Массивы можно переформировать при помощи метода, который задает новый
многомерный массив. Следуя следующему примеру, мы переформатируем
одномерный массив из десяти элементов во двумерный массив, состоящий из
пяти строк и двух столбцов:
a = np.array(range(10), float) print(a) print() # Превратим в матрицу a = a.reshape((5, 2)) print(a) print() # Вернем обратно print(a.flatten()) # Другой вариант print(a.reshape((-1))) # Превратим в марицу (9, 1) print(a.reshape((-1, 1))) # Превратим в марицу (1, 9) print(a.reshape((1, -1)))
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] [[0. 1.] [2. 3.] [4. 5.] [6. 7.] [8. 9.]] [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] [[0.] [1.] [2.] [3.] [4.] [5.] [6.] [7.] [8.] [9.]] [[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]]
Задания: (Блок 1)
Задание 1:
Решите без использования циклов средставми NumPy (каждый пункт решается
в 1-2 строчки)
- Создайте вектор с элементами от 12 до 42
- Создайте вектор из нулей длины 12, но его пятый елемент должен быть равен 1
- Создайте матрицу (3, 3), заполненую от 0 до 8
- Найдите все положительные числа в np.array([1,2,0,0,4,0])
- Умножьте матрицу размерности (5, 3) на (3, 2)
- Создайте матрицу (10, 10) так, чтобы на границе были 0, а внтури 1
- Создайте рандомный вектор и отсортируйте его
- Каков эквивалент функции enumerate для numpy массивов?
- *Создайте рандомный вектор и выполните нормализацию столбцов (из каждого столбца вычесть среднее этого столбца, из каждого столбца вычесть sd этого столбца)
- *Для заданного числа найдите ближайший к нему элемент в векторе
- *Найдите N наибольших значений в векторе
# ваш код здесь
Задание 2:
Напишите полностью векторизованный вариант
Дан трёхмерный массив, содержащий изображение, размера (height, width,
numChannels), а также вектор длины numChannels. Сложить каналы
изображения с указанными весами, и вернуть результат в виде матрицы
размера (height, width). Считать реальное изображение можно при помощи
функции
scipy.misc.imread
(если изображение не в формате png,
установите пакет pillow:
conda install pillow
). Преобразуйте
цветное изображение в оттенки серого, использовав коэффициенты
np.array([0.299, 0.587, 0.114]).
# ваш код здесь
8. Транспонирование матриц
Напоминание теории. Транспонированной матрицей AT
называется матрица, полученная из исходной матрицы A заменой
строк на столбцы. Формально: элементы матрицы AT определяются
как aTij = aji, где aTij — элемент
матрицы AT, стоящий на пересечении строки с номером i
и столбца с номером j.
В NumPy транспонированная матрица вычисляется с помощью функции
numpy.transpose() или с помощью метода array.T, где
array — нужный двумерный массив.
a = np.array([[1, 2], [3, 4]]) b = np.transpose(a) c = a.T
print ("Матрица:n", a) print ("Транспонирование функцией:n", b) print ("Транспонирование методом:n", c)
Матрица: [[1 2] [3 4]] Транспонирование функцией: [[1 3] [2 4]] Транспонирование методом: [[1 3] [2 4]]
См. более подробно о
numpy.transpose()
и
array.T
в NumPy.
В следующих разделах активно используется модуль numpy.linalg,
реализующий некоторые приложения линейной алгебры. Более подробно о
функциях, описанных ниже, и различных других функциях этого модуля можно
посмотреть в его
документации.
9. Определитель матрицы
Напоминание теории. Для квадратных матриц существует понятие
определителя.
Пусть A — квадратная матрица. Определителем (или
детерминантом) матрицы A ∈ ℝn×n назовем
число
detA = ∑α1, α2, …, αn( − 1)N(α1, α2, …, αn)⋅aα11⋅⋅⋅aαnn,
где α1, α2, …, αn — перестановка
чисел от 1 до n,
N(α1, α2, …, αn) — число инверсий в
перестановке, суммирование ведется по всем возможным перестановкам длины
n.
Не стоит расстраиваться, если это определение понятно не до конца — в
дальнейшем в таком виде оно не понадобится.
Например, для матрицы размера 2×2 получается:
det⎛⎜⎝
a11
a12
a21
a22
⎞⎟⎠ = a11a22 − a12a21
Вычисление определителя матрицы по определению требует порядка
n! операций, поэтому разработаны методы, которые позволяют
вычислять его быстро и эффективно.
В NumPy определитель матрицы вычисляется с помощью функции
numpy.linalg.det(a), где a — исходная матрица.
a = np.array([[1, 2, 1], [1, 1, 4], [2, 3, 6]], dtype=np.float32) det = np.linalg.det(a)
print ("Матрица:n", a) print ("Определитель:n", det)
Матрица: [[1. 2. 1.] [1. 1. 4.] [2. 3. 6.]] Определитель: -1.0
Рассмотрим одно интересное свойство определителя. Пусть у нас есть
параллелограмм с углами в точках
(0, 0), (c, d), (a + c, b + d), (a, b) (углы даны в порядке обхода по
часовой стрелке). Тогда площадь этого параллелограмма можно вычислить
как модуль определителя матрицы
⎛⎜⎝
a
c
b
d
⎞⎟⎠.
Похожим образом можно выразить и объем параллелепипеда через
определитель матрицы размера 3×3.
10. Ранг матрицы
Напоминание теории. Рангом матрицы A называется
максимальное число линейно независимых строк (столбцов) этой матрицы.
В NumPy ранг матрицы вычисляется с помощью функции
numpy.linalg.matrix_rank(M, tol=None), где M — матрица,
tol — параметр, отвечающий за некоторую точность вычисления. В
простом случае можно его не задавать, и функция сама определит
подходящее значение этого параметра.
a = np.array([[1, 2, 3], [1, 1, 1], [2, 2, 2]]) r = np.linalg.matrix_rank(a)
print ("Матрица:n", a) print ("Ранг матрицы:", r)
Матрица: [[1 2 3] [1 1 1] [2 2 2]] Ранг матрицы: 2
С помощью вычисления ранга матрицы можно проверять линейную
независимость системы векторов.
Допустим, у нас есть несколько векторов. Составим из них матрицу, где
наши векторы будут являться строками. Понятно, что векторы линейно
независимы тогда и только тогда, когда ранг полученной матрицы совпадает
с числом векторов. Приведем пример:
a = np.array([1, 2, 3]) b = np.array([1, 1, 1]) c = np.array([2, 3, 5]) m = np.array([a, b, c])
print (np.linalg.matrix_rank(m) == m.shape[0])
True
11. Системы линейных уравнений
Напоминание теории. Системой линейных алгебраических уравнений
называется система вида Ax = b, где
A ∈ ℝn×m, x ∈ ℝm×1, b ∈ ℝn×1.
В случае квадратной невырожденной матрицы A решение системы
единственно.
В NumPy решение такой системы можно найти с помощью функции
numpy.linalg.solve(a, b), где первый аргумент — матрица
A, второй — столбец b.
a = np.array([[3, 1], [1, 2]]) b = np.array([9, 8]) x = np.linalg.solve(a, b)
print ("Матрица A:n", a) print ("Вектор b:n", b) print ("Решение системы:n", x)
Матрица A: [[3 1] [1 2]] Вектор b: [9 8] Решение системы: [2. 3.]
Убедимся, что вектор x действительно является решением системы:
print (a.dot(x))
[9. 8.]
Бывают случаи, когда решение системы не существует. Но хотелось бы все
равно “решить” такую систему. Логичным кажется искать такой вектор
x, который минимизирует выражение
‖Ax − b‖2 — так мы приблизим выражение
Ax к b.
В NumPy такое псевдорешение можно искать с помощью функции
numpy.linalg.lstsq(a, b, …), где первые два аргумента такие
же, как и для функции numpy.linalg.solve(). Помимо решения
функция возвращает еще три значения, которые нам сейчас не понадобятся.
a = np.array([[0, 1], [1, 1], [2, 1], [3, 1]]) b = np.array([-1, 0.2, 0.9, 2.1]) x, res, r, s = np.linalg.lstsq(a, b, rcond=None)
print ("Матрица A:n", a) print ("Вектор b:n", b) print ("Псевдорешение системы:n", x)
Матрица A: [[0 1] [1 1] [2 1] [3 1]] Вектор b: [-1. 0.2 0.9 2.1] Псевдорешение системы: [ 1. -0.95]
12. Обращение матриц
Напоминание теории. Для квадратных невырожденных матриц определено
понятие обратной матрицы.
Пусть A — квадратная невырожденная матрица. Матрица
A − 1 называется обратной матрицей к A, если
AA − 1 = A − 1A = I,
где I — единичная матрица.
В NumPy обратные матрицы вычисляются с помощью функции
numpy.linalg.inv(a), где a — исходная матрица.
a = np.array([[1, 2, 1], [1, 1, 4], [2, 3, 6]], dtype=np.float32) b = np.linalg.inv(a)
print ("Матрица A:n", a) print ("Обратная матрица к A:n", b) print ("Произведение A на обратную должна быть единичной:n", a.dot(b))
Матрица A: [[1. 2. 1.] [1. 1. 4.] [2. 3. 6.]] Обратная матрица к A: [[ 6. 9. -7.] [-2. -4. 3.] [-1. -1. 1.]] Произведение A на обратную должна быть единичной: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]
13. Собственные числа и собственные вектора матрицы
Напоминание теории. Для квадратных матриц определены понятия
собственного вектора и собственного числа.
Пусть A — квадратная матрица и
A ∈ ℝn×n. Собственным вектором матрицы
A называется такой ненулевой вектор
x ∈ ℝn, что для некоторого
λ ∈ ℝ выполняется равенство
Ax = λx. При этом λ называется
собственным числом матрицы A. Собственные числа и
собственные векторы матрицы играют важную роль в теории линейной алгебры
и ее практических приложениях.
В NumPy собственные числа и собственные векторы матрицы вычисляются
с помощью функции numpy.linalg.eig(a), где a — исходная
матрица. В качестве результата эта функция выдает одномерный массив
w собственных чисел и двумерный массив v, в котором по
столбцам записаны собственные вектора, так что вектор v[:, i]
соотвествует собственному числу w[i].
a = np.array([[-1, -6], [2, 6]]) w, v = np.linalg.eig(a)
print ("Матрица A:n", a) print ("Собственные числа:n", w) print ("Собственные векторы:n", v)
Матрица A: [[-1 -6] [ 2 6]] Собственные числа: [2. 3.] Собственные векторы: [[-0.89442719 0.83205029] [ 0.4472136 -0.5547002 ]]
Обратите внимание: у вещественной матрицы собственные значения или
собственные векторы могут быть комплексными.
14. Расстояния между векторами
Вспомним некоторые нормы, которые можно ввести в пространстве
ℝn, и рассмотрим, с помощью каких библиотек и
функций их можно вычислять в NumPy.
p-норма
p-норма (норма Гёльдера) для вектора
x = (x1, …, xn) ∈ ℝn вычисляется по
формуле:
‖x‖p = (n∑i = 1|xi|p)1 ⁄ p, p ≥ 1.
В частных случаях при: * p = 1 получаем ℓ1 норму
* p = 2 получаем ℓ2 норму
Далее нам понабится модуль numpy.linalg, реализующий некоторые
приложения линейной алгебры. Для вычисления различных норм мы используем
функцию numpy.linalg.norm(x, ord=None, …), где x —
исходный вектор, ord — параметр, определяющий норму (мы
рассмотрим два варианта его значений — 1 и 2). Импортируем эту функцию:
from numpy.linalg import norm
ℓ1 норма
ℓ1 норма (также известная как манхэттенское
расстояние)
для вектора x = (x1, …, xn) ∈ ℝn
вычисляется по формуле:
‖x‖1 = n∑i = 1|xi|.
Ей в функции numpy.linalg.norm(x, ord=None, …) соответствует
параметр ord=1.
a = np.array([1, 2, -3]) print('Вектор a:', a)
Вектор a: [ 1 2 -3]
print('L1 норма вектора a:n', norm(a, ord=1))
L1 норма вектора a: 6.0
ℓ2 норма
ℓ2 норма (также известная как евклидова норма) для вектора
x = (x1, …, xn) ∈ ℝn вычисляется по
формуле:
‖x‖2 = √(n∑i = 1(xi)2).
Ей в функции numpy.linalg.norm(x, ord=None, …) соответствует
параметр ord=2.
print ('L2 норма вектора a:n', norm(a, ord=2))
L2 норма вектора a: 3.7416573867739413
Более подробно о том, какие еще нормы (в том числе матричные) можно
вычислить, см.
документацию.
15. Расстояния между векторами
Для двух векторов x = (x1, …, xn) ∈ ℝn и
y = (y1, …, yn) ∈ ℝn ℓ1 и
ℓ2 раccтояния вычисляются по следующим формулам
соответственно:
ρ1(x, y) = ‖x − y‖1 = n∑i = 1|xi − yi|
ρ2(x, y) = ‖x − y‖2 = √(n∑i = 1(xi − yi)2).
a = np.array([1, 2, -3]) b = np.array([-4, 3, 8]) print ('Вектор a:', a) print ('Вектор b:', b)
Вектор a: [ 1 2 -3] Вектор b: [-4 3 8]
print ('L1 расстояние между векторами a и b:n', norm(a - b, ord=1)) print ('L2 расстояние между векторами a и b:n', norm(a - b, ord=2))
L1 расстояние между векторами a и b: 17.0 L2 расстояние между векторами a и b: 12.12435565298214
16. Скалярное произведение и угол между векторами
a = np.array([0, 5, -1]) b = np.array([-4, 9, 3]) print ('Вектор a:', a) print ('Вектор b:', b)
Вектор a: [ 0 5 -1] Вектор b: [-4 9 3]
Скалярное произведение в пространстве ℝn для двух
векторов x = (x1, …, xn) и
y = (y1, …, yn) определяется как:
⟨x, y⟩ = n∑i = 1xiyi.
Длиной вектора x = (x1, …, xn) ∈ ℝn
называется квадратный корень из скалярного произведения, то есть длина
равна евклидовой норме вектора:
|x| = √(⟨x, x⟩) = √(n∑i = 1x2i) = ‖x‖2.
Теперь, когда мы знаем расстояние между двумя ненулевыми векторами и их
длины, мы можем вычислить угол между ними через скалярное произведение:
⟨x, y⟩ = |x||y|cos(α) ⟹ cos(α) = (⟨x, y⟩)/(|x||y|),
где α ∈ [0, π] — угол между векторами x и
y.
cos_angle = np.dot(a, b) / norm(a) / norm(b) print ('Косинус угла между a и b:', cos_angle) print ('Сам угол:', np.arccos(cos_angle))
Косинус угла между a и b: 0.8000362836474323 Сам угол: 0.6434406336093618
17. Комплексные числа в питоне
Напоминание теории. Комплексными числами называются числа вида
x + iy, где x и y — вещественные числа, а
i — мнимая единица (величина, для которой выполняется равенство
i2 = − 1). Множество всех комплексных чисел обозначается
буквой ℂ (подробнее про комплексные числа см.
википедию).
В питоне комплескные числа можно задать следующим образом (j
обозначает мнимую единицу):
a = 3 + 2j b = 1j
print ("Комплексное число a:n", a) print ("Комплексное число b:n", b)
Комплексное число a: (3+2j) Комплексное число b: 1j
С комплексными числами в питоне можно производить базовые арифметические
операции так же, как и с вещественными числами:
c = a * a d = a / (4 - 5j)
print ("Комплексное число c:n", c) print ("Комплексное число d:n", d)
Комплексное число c: (5+12j) Комплексное число d: (0.0487804878048781+0.5609756097560976j)
Задания: (Блок 2)
Задание 3:
Рассмотрим сложную математическую функцию на отрезке [1, 15]:
f(x) = sin(x / 5) * exp(x / 10) + 5 * exp(-x / 2)
Она может описывать, например, зависимость оценок, которые выставляют
определенному сорту вина эксперты, в зависимости от возраста этого вина.
Мы хотим приблизить сложную зависимость с помощью функции из
определенного семейства. В этом задании мы будем приближать указанную
функцию с помощью многочленов.
Как известно, многочлен степени n (то есть w0 +
w1x + w2x2 + … + wnxn)
однозначно определяется любыми n + 1 различными точками, через которые
он проходит. Это значит, что его коэффициенты w0, … wn
можно определить из следующей системы линейных уравнений:
где через x1, …, xn, xn + 1 обозначены точки, через которые
проходит многочлен, а через f(x1), …, f(xn), f(xn + 1) —
значения, которые он должен принимать в этих точках.
Воспользуемся описанным свойством, и будем находить приближение функции
многочленом, решая систему линейных уравнений.
- Сформируйте систему линейных уравнений (то есть задайте матрицу
коэффициентов A и свободный вектор b) для многочлена первой степени,
который должен совпадать с функцией f в точках 1 и 15. Решите данную
систему с помощью функции scipy.linalg.solve. Нарисуйте функцию f и
полученный многочлен. Хорошо ли он приближает исходную функцию? - Повторите те же шаги для многочлена второй степени, который совпадает
с функцией f в точках 1, 8 и 15. Улучшилось ли качество
аппроксимации? - Повторите те же шаги для многочлена третьей степени, который
совпадает с функцией f в точках 1, 4, 10 и 15. Хорошо ли он
аппроксимирует функцию? Коэффициенты данного многочлена (четыре числа
в следующем порядке: w_0, w_1, w_2, w_3) являются ответом на задачу.
Округлять коэффициенты не обязательно, но при желании можете
произвести округление до второго знака (т.е. до числа вида 0.42)
Тема третьего урока: действия над матрицами. В рамках нее будут рассмотрены следующие вопросы: умножение матрицы на число, сложение и умножение матриц.
- Действия над матрицами
- Умножение матрицы на число
- Сложение матриц
- Умножение матриц
Действия над матрицами
Умножение матрицы на число
При умножении матрицы на число, все элементы матрицы умножаются на это число:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2 3; 4 5 6') >>> C = 3 * A >>> print(C) [[ 3 6 9] [12 15 18]]
Рассмотрим свойства операции умножения матрицы на число.
Свойство 1. Произведение единицы и любой заданной матрицы равно заданной матрице:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> L = 1 * A >>> R = A >>> print(L) [[1 2] [3 4]] >>> print(R) [[1 2] [3 4]]
Свойство 2. Произведение нуля и любой матрицы равно нулевой матрице, размерность которой равна исходной матрицы:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> Z = np.matrix('0 0; 0 0') >>> L = 0 * A >>> R = Z >>> print(L) [[0 0] [0 0]] >>> print(R) [[0 0] [0 0]]
Свойство 3. Произведение матрицы на сумму чисел равно сумме произведений матрицы на каждое из этих чисел:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> p = 2 >>> q = 3 >>> L = (p + q) * A >>> R = p * A + q * A >>> print(L) [[ 5 10] [15 20]] >>> print(R) [[ 5 10] [15 20]]
Свойство 4. Произведение матрицы на произведение двух чисел равно произведению второго числа и заданной матрицы, умноженному на первое число:
➣ Численный пример
➤Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> p = 2 >>> q = 3 >>> L = (p * q) * A >>> R = p * (q * A) >>> print(L) [[ 6 12] [18 24]] >>> print(R) [[ 6 12] [18 24]]
Свойство 5. Произведение суммы матриц на число равно сумме произведений этих матриц на заданное число:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> B = np.matrix('5 6; 7 8') >>> k = 3 >>> L = k * (A + B) >>> R = k * A + k * B >>> print(L) [[18 24] [30 36]] >>> print(R) [[18 24] [30 36]]
Сложение матриц
Складывать можно только матрицы одинаковой размерности — то есть матрицы, у которых совпадает количество столбцов и строк.
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 6 3; 8 2 7') >>> B = np.matrix('8 1 5; 6 9 12') >>> C = A + B >>> print(C) [[ 9 7 8] [14 11 19]]
Рассмотрим свойства сложения матриц.
Свойство 1. Коммутативность сложения. От перестановки матриц их сумма не изменяется:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> B = np.matrix('5 6; 7 8') >>> L = A + B >>> R = B + A >>> print(L) [[ 6 8] [10 12]] >>> print(R) [[ 6 8] [10 12]]
Свойство 2. Ассоциативность сложения. Результат сложения трех и более матриц не зависит от порядка, в котором эта операция будет выполняться:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> B = np.matrix('5 6; 7 8') >>> C = np.matrix('1 7; 9 3') >>> L = A + (B + C) >>> R = (A + B) + C >>> print(L) [[ 7 15] [19 15]] >>> print(R) [[ 7 15] [19 15]]
Свойство 3. Для любой матрицы существует противоположная ей , такая, что их сумма является нулевой матрицей :
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> Z = np.matrix('0 0; 0 0') >>> L = A + (-1)*A >>> print(L) [[0 0] [0 0]] >>> print(Z) [[0 0] [0 0]]
Умножение матриц
Умножение матриц это уже более сложная операция, по сравнению с рассмотренными выше. Умножать можно только матрицы, отвечающие следующему требованию: количество столбцов первой матрицы должно быть равно числу строк второй матрицы.
Для простоты запоминания этого правила можно использовать диаграмму умножения, представленную на рисунке 1.
Рисунок 1 — Диаграмма матричного умножения
Рассмотрим умножение матриц на примере.
➣ Численный пример
Каждый элемент cij новой матрицы является суммой произведений элементов i-ой строки первой матрицы и j-го столбца второй матрицы. Математически это записывается так:
➤Пример на Python
Решим задачу умножения матриц на языке Python. Для этого будем использовать функцию dot() из библиотеки Numpy:
>>> A = np.matrix('1 2 3; 4 5 6') >>> B = np.matrix('7 8; 9 1; 2 3') >>> C = A.dot(B) >>> print(C) [[31 19] [85 55]]
Ниже представлены свойства произведения матриц. Примеры свойств будут показаны для квадратной матрицы.
Свойство 1. Ассоциативность умножения. Результат умножения матриц не зависит от порядка, в котором будет выполняться эта операция:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> B = np.matrix('5 6; 7 8') >>> C = np.matrix('2 4; 7 8') >>> L = A.dot(B.dot(C)) >>> R = (A.dot(B)).dot(C) >>> print(L) [[192 252] [436 572]] >>> print(R) [[192 252] [436 572]]
Свойство 2. Дистрибутивность умножения. Произведение матрицы на сумму матриц равно сумме произведений матриц:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> B = np.matrix('5 6; 7 8') >>> C = np.matrix('2 4; 7 8') >>> L = A.dot(B + C) >>> R = A.dot(B) + A.dot(C) >>> print(L) [[35 42] [77 94]] >>> print(R) [[35 42] [77 94]]
Свойство 3. Умножение матриц в общем виде не коммутативно. Это означает, что для матриц не выполняется правило независимости произведения от перестановки множителей:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> B = np.matrix('5 6; 7 8') >>> L = A.dot(B) >>> R = B.dot(A) >>> print(L) [[19 22] [43 50]] >>> print(R) [[23 34] [31 46]]
Свойство 4. Произведение заданной матрицы на единичную равно исходной матрице:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> E = np.matrix('1 0; 0 1') >>> L = E.dot(A) >>> R = A.dot(E) >>> print(L) [[1 2] [3 4]] >>> print(R) [[1 2] [3 4]] >>> print(A) [[1 2] [3 4]]
Свойство 5. Произведение заданной матрицы на нулевую матрицу равно нулевой матрице:
➣ Численный пример
➤ Пример на Python
>>> A = np.matrix('1 2; 3 4') >>> Z = np.matrix('0 0; 0 0') >>> L = Z.dot(A) >>> R = A.dot(Z) >>> print(L) [[0 0] [0 0]] >>> print(R) [[0 0] [0 0]] >>> print(Z) [[0 0] [0 0]]
P.S.
Вводные уроки по “Линейной алгебре на Python” вы можете найти соответствующей странице нашего сайта. Все уроки по этой теме собраны в книге “Линейная алгебра на Python”.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. Для начала вы можете познакомиться с вводными уроками. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
Here’s some short and simple code for matrix/vector routines in pure Python that I wrote many years ago:
'''Basic Table, Matrix and Vector functions for Python 2.2
Author: Raymond Hettinger
'''
Version = 'File MATFUNC.PY, Ver 183, Date 12-Dec-2002,14:33:42'
import operator, math, random
NPRE, NPOST = 0, 0 # Disables pre and post condition checks
def iszero(z): return abs(z) < .000001
def getreal(z):
try:
return z.real
except AttributeError:
return z
def getimag(z):
try:
return z.imag
except AttributeError:
return 0
def getconj(z):
try:
return z.conjugate()
except AttributeError:
return z
separator = [ '', 't', 'n', 'n----------n', 'n===========n' ]
class Table(list):
dim = 1
concat = list.__add__ # A substitute for the overridden __add__ method
def __getslice__( self, i, j ):
return self.__class__( list.__getslice__(self,i,j) )
def __init__( self, elems ):
list.__init__( self, elems )
if len(elems) and hasattr(elems[0], 'dim'): self.dim = elems[0].dim + 1
def __str__( self ):
return separator[self.dim].join( map(str, self) )
def map( self, op, rhs=None ):
'''Apply a unary operator to every element in the matrix or a binary operator to corresponding
elements in two arrays. If the dimensions are different, broadcast the smaller dimension over
the larger (i.e. match a scalar to every element in a vector or a vector to a matrix).'''
if rhs is None: # Unary case
return self.dim==1 and self.__class__( map(op, self) ) or self.__class__( [elem.map(op) for elem in self] )
elif not hasattr(rhs,'dim'): # List / Scalar op
return self.__class__( [op(e,rhs) for e in self] )
elif self.dim == rhs.dim: # Same level Vec / Vec or Matrix / Matrix
assert NPRE or len(self) == len(rhs), 'Table operation requires len sizes to agree'
return self.__class__( map(op, self, rhs) )
elif self.dim < rhs.dim: # Vec / Matrix
return self.__class__( [op(self,e) for e in rhs] )
return self.__class__( [op(e,rhs) for e in self] ) # Matrix / Vec
def __mul__( self, rhs ): return self.map( operator.mul, rhs )
def __div__( self, rhs ): return self.map( operator.div, rhs )
def __sub__( self, rhs ): return self.map( operator.sub, rhs )
def __add__( self, rhs ): return self.map( operator.add, rhs )
def __rmul__( self, lhs ): return self*lhs
def __rdiv__( self, lhs ): return self*(1.0/lhs)
def __rsub__( self, lhs ): return -(self-lhs)
def __radd__( self, lhs ): return self+lhs
def __abs__( self ): return self.map( abs )
def __neg__( self ): return self.map( operator.neg )
def conjugate( self ): return self.map( getconj )
def real( self ): return self.map( getreal )
def imag( self ): return self.map( getimag )
def flatten( self ):
if self.dim == 1: return self
return reduce( lambda cum, e: e.flatten().concat(cum), self, [] )
def prod( self ): return reduce(operator.mul, self.flatten(), 1.0)
def sum( self ): return reduce(operator.add, self.flatten(), 0.0)
def exists( self, predicate ):
for elem in self.flatten():
if predicate(elem):
return 1
return 0
def forall( self, predicate ):
for elem in self.flatten():
if not predicate(elem):
return 0
return 1
def __eq__( self, rhs ): return (self - rhs).forall( iszero )
class Vec(Table):
def dot( self, otherVec ): return reduce(operator.add, map(operator.mul, self, otherVec), 0.0)
def norm( self ): return math.sqrt(abs( self.dot(self.conjugate()) ))
def normalize( self ): return self / self.norm()
def outer( self, otherVec ): return Mat([otherVec*x for x in self])
def cross( self, otherVec ):
'Compute a Vector or Cross Product with another vector'
assert len(self) == len(otherVec) == 3, 'Cross product only defined for 3-D vectors'
u, v = self, otherVec
return Vec([ u[1]*v[2]-u[2]*v[1], u[2]*v[0]-u[0]*v[2], u[0]*v[1]-u[1]*v[0] ])
def house( self, index ):
'Compute a Householder vector which zeroes all but the index element after a reflection'
v = Vec( Table([0]*index).concat(self[index:]) ).normalize()
t = v[index]
sigma = 1.0 - t**2
if sigma != 0.0:
t = v[index] = t<=0 and t-1.0 or -sigma / (t + 1.0)
v /= t
return v, 2.0 * t**2 / (sigma + t**2)
def polyval( self, x ):
'Vec([6,3,4]).polyval(5) evaluates to 6*x**2 + 3*x + 4 at x=5'
return reduce( lambda cum,c: cum*x+c, self, 0.0 )
def ratval( self, x ):
'Vec([10,20,30,40,50]).ratfit(5) evaluates to (10*x**2 + 20*x + 30) / (40*x**2 + 50*x + 1) at x=5.'
degree = len(self) / 2
num, den = self[:degree+1], self[degree+1:] + [1]
return num.polyval(x) / den.polyval(x)
class Matrix(Table):
__slots__ = ['size', 'rows', 'cols']
def __init__( self, elems ):
'Form a matrix from a list of lists or a list of Vecs'
Table.__init__( self, hasattr(elems[0], 'dot') and elems or map(Vec,map(tuple,elems)) )
self.size = self.rows, self.cols = len(elems), len(elems[0])
def tr( self ):
'Tranpose elements so that Transposed[i][j] = Original[j][i]'
return Mat(zip(*self))
def star( self ):
'Return the Hermetian adjoint so that Star[i][j] = Original[j][i].conjugate()'
return self.tr().conjugate()
def diag( self ):
'Return a vector composed of elements on the matrix diagonal'
return Vec( [self[i][i] for i in range(min(self.size))] )
def trace( self ): return self.diag().sum()
def mmul( self, other ):
'Matrix multiply by another matrix or a column vector '
if other.dim==2: return Mat( map(self.mmul, other.tr()) ).tr()
assert NPRE or self.cols == len(other)
return Vec( map(other.dot, self) )
def augment( self, otherMat ):
'Make a new matrix with the two original matrices laid side by side'
assert self.rows == otherMat.rows, 'Size mismatch: %s * %s' % (`self.size`, `otherMat.size`)
return Mat( map(Table.concat, self, otherMat) )
def qr( self, ROnly=0 ):
'QR decomposition using Householder reflections: Q*R==self, Q.tr()*Q==I(n), R upper triangular'
R = self
m, n = R.size
for i in range(min(m,n)):
v, beta = R.tr()[i].house(i)
R -= v.outer( R.tr().mmul(v)*beta )
for i in range(1,min(n,m)): R[i][:i] = [0] * i
R = Mat(R[:n])
if ROnly: return R
Q = R.tr().solve(self.tr()).tr() # Rt Qt = At nn nm = nm
self.qr = lambda r=0, c=`self`: not r and c==`self` and (Q,R) or Matrix.qr(self,r) #Cache result
assert NPOST or m>=n and Q.size==(m,n) and isinstance(R,UpperTri) or m<n and Q.size==(m,m) and R.size==(m,n)
assert NPOST or Q.mmul(R)==self and Q.tr().mmul(Q)==eye(min(m,n))
return Q, R
def _solve( self, b ):
'''General matrices (incuding) are solved using the QR composition.
For inconsistent cases, returns the least squares solution'''
Q, R = self.qr()
return R.solve( Q.tr().mmul(b) )
def solve( self, b ):
'Divide matrix into a column vector or matrix and iterate to improve the solution'
if b.dim==2: return Mat( map(self.solve, b.tr()) ).tr()
assert NPRE or self.rows == len(b), 'Matrix row count %d must match vector length %d' % (self.rows, len(b))
x = self._solve( b )
diff = b - self.mmul(x)
maxdiff = diff.dot(diff)
for i in range(10):
xnew = x + self._solve( diff )
diffnew = b - self.mmul(xnew)
maxdiffnew = diffnew.dot(diffnew)
if maxdiffnew >= maxdiff: break
x, diff, maxdiff = xnew, diffnew, maxdiffnew
#print >> sys.stderr, i+1, maxdiff
assert NPOST or self.rows!=self.cols or self.mmul(x) == b
return x
def rank( self ): return Vec([ not row.forall(iszero) for row in self.qr(ROnly=1) ]).sum()
class Square(Matrix):
def lu( self ):
'Factor a square matrix into lower and upper triangular form such that L.mmul(U)==A'
n = self.rows
L, U = eye(n), Mat(self[:])
for i in range(n):
for j in range(i+1,U.rows):
assert U[i][i] != 0.0, 'LU requires non-zero elements on the diagonal'
L[j][i] = m = 1.0 * U[j][i] / U[i][i]
U[j] -= U[i] * m
assert NPOST or isinstance(L,LowerTri) and isinstance(U,UpperTri) and L*U==self
return L, U
def __pow__( self, exp ):
'Raise a square matrix to an integer power (i.e. A**3 is the same as A.mmul(A.mmul(A))'
assert NPRE or exp==int(exp) and exp>0, 'Matrix powers only defined for positive integers not %s' % exp
if exp == 1: return self
if exp&1: return self.mmul(self ** (exp-1))
sqrme = self ** (exp/2)
return sqrme.mmul(sqrme)
def det( self ): return self.qr( ROnly=1 ).det()
def inverse( self ): return self.solve( eye(self.rows) )
def hessenberg( self ):
'''Householder reduction to Hessenberg Form (zeroes below the diagonal)
while keeping the same eigenvalues as self.'''
for i in range(self.cols-2):
v, beta = self.tr()[i].house(i+1)
self -= v.outer( self.tr().mmul(v)*beta )
self -= self.mmul(v).outer(v*beta)
return self
def eigs( self ):
'Estimate principal eigenvalues using the QR with shifts method'
origTrace, origDet = self.trace(), self.det()
self = self.hessenberg()
eigvals = Vec([])
for i in range(self.rows-1,0,-1):
while not self[i][:i].forall(iszero):
shift = eye(i+1) * self[i][i]
q, r = (self - shift).qr()
self = r.mmul(q) + shift
eigvals.append( self[i][i] )
self = Mat( [self[r][:i] for r in range(i)] )
eigvals.append( self[0][0] )
assert NPOST or iszero( (abs(origDet) - abs(eigvals.prod())) / 1000.0 )
assert NPOST or iszero( origTrace - eigvals.sum() )
return Vec(eigvals)
class Triangular(Square):
def eigs( self ): return self.diag()
def det( self ): return self.diag().prod()
class UpperTri(Triangular):
def _solve( self, b ):
'Solve an upper triangular matrix using backward substitution'
x = Vec([])
for i in range(self.rows-1, -1, -1):
assert NPRE or self[i][i], 'Backsub requires non-zero elements on the diagonal'
x.insert(0, (b[i] - x.dot(self[i][i+1:])) / self[i][i] )
return x
class LowerTri(Triangular):
def _solve( self, b ):
'Solve a lower triangular matrix using forward substitution'
x = Vec([])
for i in range(self.rows):
assert NPRE or self[i][i], 'Forward sub requires non-zero elements on the diagonal'
x.append( (b[i] - x.dot(self[i][:i])) / self[i][i] )
return x
def Mat( elems ):
'Factory function to create a new matrix.'
m, n = len(elems), len(elems[0])
if m != n: return Matrix(elems)
if n <= 1: return Square(elems)
for i in range(1, len(elems)):
if not iszero( max(map(abs, elems[i][:i])) ):
break
else: return UpperTri(elems)
for i in range(0, len(elems)-1):
if not iszero( max(map(abs, elems[i][i+1:])) ):
return Square(elems)
return LowerTri(elems)
def funToVec( tgtfun, low=-1, high=1, steps=40, EqualSpacing=0 ):
'''Compute x,y points from evaluating a target function over an interval (low to high)
at evenly spaces points or with Chebyshev abscissa spacing (default) '''
if EqualSpacing:
h = (0.0+high-low)/steps
xvec = [low+h/2.0+h*i for i in range(steps)]
else:
scale, base = (0.0+high-low)/2.0, (0.0+high+low)/2.0
xvec = [base+scale*math.cos(((2*steps-1-2*i)*math.pi)/(2*steps)) for i in range(steps)]
yvec = map(tgtfun, xvec)
return Mat( [xvec, yvec] )
def funfit( (xvec, yvec), basisfuns ):
'Solves design matrix for approximating to basis functions'
return Mat([ map(form,xvec) for form in basisfuns ]).tr().solve(Vec(yvec))
def polyfit( (xvec, yvec), degree=2 ):
'Solves Vandermonde design matrix for approximating polynomial coefficients'
return Mat([ [x**n for n in range(degree,-1,-1)] for x in xvec ]).solve(Vec(yvec))
def ratfit( (xvec, yvec), degree=2 ):
'Solves design matrix for approximating rational polynomial coefficients (a*x**2 + b*x + c)/(d*x**2 + e*x + 1)'
return Mat([[x**n for n in range(degree,-1,-1)]+[-y*x**n for n in range(degree,0,-1)] for x,y in zip(xvec,yvec)]).solve(Vec(yvec))
def genmat(m, n, func):
if not n: n=m
return Mat([ [func(i,j) for i in range(n)] for j in range(m) ])
def zeroes(m=1, n=None):
'Zero matrix with side length m-by-m or m-by-n.'
return genmat(m,n, lambda i,j: 0)
def eye(m=1, n=None):
'Identity matrix with side length m-by-m or m-by-n'
return genmat(m,n, lambda i,j: i==j)
def hilb(m=1, n=None):
'Hilbert matrix with side length m-by-m or m-by-n. Elem[i][j]=1/(i+j+1)'
return genmat(m,n, lambda i,j: 1.0/(i+j+1.0))
def rand(m=1, n=None):
'Random matrix with side length m-by-m or m-by-n'
return genmat(m,n, lambda i,j: random.random())
if __name__ == '__main__':
import cmath
a = Table([1+2j,2,3,4])
b = Table([5,6,7,8])
C = Table([a,b])
print 'a+b', a+b
print '2+a', 2+a
print 'a/5.0', a/5.0
print '2*a+3*b', 2*a+3*b
print 'a+C', a+C
print '3+C', 3+C
print 'C+b', C+b
print 'C.sum()', C.sum()
print 'C.map(math.cos)', C.map(cmath.cos)
print 'C.conjugate()', C.conjugate()
print 'C.real()', C.real()
print zeroes(3)
print eye(4)
print hilb(3,5)
C = Mat( [[1,2,3], [4,5,1,], [7,8,9]] )
print C.mmul( C.tr()), 'n'
print C ** 5, 'n'
print C + C.tr(), 'n'
A = C.tr().augment( Mat([[10,11,13]]).tr() ).tr()
q, r = A.qr()
assert q.mmul(r) == A
assert q.tr().mmul(q)==eye(3)
print 'q:n', q, 'nr:n', r, 'nQ.tr()&Q:n', q.tr().mmul(q), 'nQ*Rn', q.mmul(r), 'n'
b = Vec([50, 100, 220, 321])
x = A.solve(b)
print 'x: ', x
print 'b: ', b
print 'Ax: ', A.mmul(x)
inv = C.inverse()
print 'ninverse C:n', inv, 'nC * inv(C):n', C.mmul(inv)
assert C.mmul(inv) == eye(3)
points = (xvec,yvec) = funToVec(lambda x: math.sin(x)+2*math.cos(.7*x+.1), low=0, high=3, EqualSpacing=1)
basis = [lambda x: math.sin(x), lambda x: math.exp(x), lambda x: x**2]
print 'Func coeffs:', funfit( points, basis )
print 'Poly coeffs:', polyfit( points, degree=5 )
points = (xvec,yvec) = funToVec(lambda x: math.sin(x)+2*math.cos(.7*x+.1), low=0, high=3)
print 'Rational coeffs:', ratfit( points )
print polyfit(([1,2,3,4], [1,4,9,16]), 2)
mtable = Vec([1,2,3]).outer(Vec([1,2]))
print mtable, mtable.size
A = Mat([ [2,0,3], [1,5,1], [18,0,6] ])
print 'A:'
print A
print 'eigs:'
print A.eigs()
print 'Should be:', Vec([11.6158, 5.0000, -3.6158])
print 'det(A)'
print A.det()
c = Mat( [[1,2,30],[4,5,10],[10,80,9]] ) # Failed example from Konrad Hinsen
print 'C:n', c
print c.eigs()
print 'Should be:', Vec([-8.9554, 43.2497, -19.2943])
A = Mat([ [1,2,3,4], [4,5,6,7], [2,1,5,0], [4,2,1,0] ] ) # Kincaid and Cheney p.326
print 'A:n', A
print A.eigs()
print 'Should be:', Vec([3.5736, 0.1765, 11.1055, -3.8556])
A = rand(3)
q,r = A.qr()
s,t = A.qr()
print q is s # Test caching
print r is t
A[1][1] = 1.1 # Invalidate the cache
u,v = A.qr()
print q is u # Verify old result not used
print r is v
print u.mmul(v) == A # Verify new result
print 'Test qr on 3x5 matrix'
a = rand(3,5)
q,r = a.qr()
print q.mmul(r) == a
print q.tr().mmul(q) == eye(3)
Мы можем реализовать матрицу Python в форме 2-го списка или 2-го массива. Для выполнения операций с Python Matrix нам необходимо импортировать Python NumPy Module.
Matrix важен в области статистики, обработки данных, обработки изображений и т. д.
Матрицу Python можно создать одним из следующих способов:
- Используя списки
- Используя метод arange()
- и метода matrix()
1 С использованием списков
numpy.array()
можно использовать для создания массива, используя списки в качестве входных данных.
Пример:
import numpy input_arr = numpy.array([[ 10, 20, 30],[ 40, 50, 60]]) print(input_arr)
Выход:
[[10 20 30] [40 50 60]]
Как видно выше, выходные данные представляют собой двумерную матрицу с заданным набором входных данных в виде списка.
2 С помощью функции numpy.arange()
numpy.arange()
вместе со списком входов.
Пример:
import numpy print(numpy.array([numpy.arange(10,15), numpy.arange(15,20)]))
Выход:
[[10 11 12 13 14] [15 16 17 18 19]]
3 С помощью функции numpy.matrix().
Функция numpy.matrix()
, ее синтаксис:
numpy.matrix(input,dtype)
- input: элементы input для формирования матрицы.
- dtype: тип данных соответствующего вывода.
Пример:
import numpy as p matA = p.matrix([[10, 20], [30, 40]]) print('MatrixA:n', matA) matB = p.matrix('[10,20;30,40]', dtype=p.int32) # Setting the data-type to int print('nMatrixB:n', matB)
Выход:
MatrixA: [[10 20] [30 40]] MatrixB: [[10 20] [30 40]]
Сложение
Операцию сложения матриц можно выполнить следующими способами:
- Традиционный метод
- Используя оператор ‘+’
1 Традиционный метод
В этом традиционном методе мы в основном берем ввод от пользователя, а затем выполняем операцию сложения с использованием циклов for (для обхода элементов матрицы) и оператора ‘+’.
Пример:
import numpy as p ar1 = p.matrix([[11, 22], [33, 44]]) ar2 = p.matrix([[55, 66], [77, 88]]) res = p.matrix(p.zeros((2,2))) print('Matrix ar1 :n', ar1) print('nMatrix ar2 :n', ar2) # traditional code for x in range(ar1.shape[1]): for y in range(ar2.shape[0]): res[x, y] = ar1[x, y] + ar2[x, y] print('nResult :n', res)
Примечание. Matrix.shape
возвращает размеры конкретной матрицы.
Выход:
Matrix ar1 : [[11 22] [33 44]] Matrix ar2 : [[55 66] [77 88]] Result : [[ 66. 88.] [ 110. 132.]]
2 Использование оператора «+»
Этот метод обеспечивает большую эффективность кода, поскольку он уменьшает LOC (количество строк кода) и, таким образом, оптимизирует код.
Пример:
import numpy as p ar1 = p.matrix([[11, 22], [33, 44]]) ar2 = p.matrix([[55, 66], [77, 88]]) res = p.matrix(p.zeros((2,2))) print('Matrix ar1 :n', ar1) print('nMatrix ar2 :n', ar2) res = ar1 + ar2 # using '+' operator print('nResult :n', res)
Выход:
Matrix ar1 : [[11 22] [33 44]] Matrix ar2 : [[55 66] [77 88]] Result : [[ 66 88] [110 132]]
Умножение матриц
Умножение матриц в Python можно обеспечить следующими способами:
- Скалярное произведение;
- Матричный продукт.
Скалярное произведение
В скалярном произведении постоянное значение умножается на каждый элемент матрицы.
Оператор ‘*’ используется для умножения скалярного значения на элементы входной матрицы.
Пример:
import numpy as p matA = p.matrix([[11, 22], [33, 44]]) print("Matrix A:n", matA) print("Scalar Product of Matrix A:n", matA * 10)
Выход:
Matrix A: [[11 22] [33 44]] Scalar Product of Matrix A: [[110 220] [330 440]]
Функция numpy.dot()
Как упоминалось выше, мы можем использовать оператор ‘*’ только для скалярного умножения. Чтобы продолжить умножение матриц, нам нужно использовать numpy.dot()
.
Функция numpy.dot()
принимает массивы NumPy в качестве значений параметров и выполняет умножение в соответствии с основными правилами умножения матриц.
Пример:
import numpy as p matA = p.matrix([[11, 22], [33, 44]]) matB = p.matrix([[2,2], [2,2]]) print("Matrix A:n", matA) print("Matrix B:n", matB) print("Dot Product of Matrix A and Matrix B:n", p.dot(matA, matB))
Выход:
Matrix A: [[11 22] [33 44]] Matrix B: [[2 2] [2 2]] Dot Product of Matrix A and Matrix B: [[ 66 66] [154 154]]
Вычитание
Оператор ‘-‘ используется для выполнения вычитания матриц.
Пример:
import numpy as p matA = p.matrix([[11, 22], [33, 44]]) matB = p.matrix([[2,2], [2,2]]) print("Matrix A:n", matA) print("Matrix B:n", matB) print("Subtraction of Matrix A and Matrix B:n",(matA - matB))
Выход:
Matrix A: [[11 22] [33 44]] Matrix B: [[2 2] [2 2]] Subtraction of Matrix A and Matrix B: [[ 9 20] [31 42]]
Деление
Скалярное деление может выполняться на элементах матрицы в Python с помощью оператора ‘/’.
Оператор ‘/’ делит каждый элемент матрицы на скалярное / постоянное значение.
Пример:
import numpy as p matB = p.matrix([[2,2], [2,2]]) print("Matrix B:n", matB) print("Matrix B after Scalar Division operation:n",(matB/2))
Выход:
Matrix B: [[2 2] [2 2]] Matrix B after Scalar Division operation: [[ 1. 1.] [ 1. 1.]]
Транспонирование матрицы
Транспонирование матрицы в основном включает в себя переворачивание матрицы по соответствующим диагоналям, т. е. Меняет местами строки и столбцы входной матрицы. Строки становятся столбцами и наоборот.
Например: давайте рассмотрим матрицу A с размерами 3 × 2, т.е. 3 строки и 2 столбца. После выполнения операции транспонирования размеры матрицы A будут 2 × 3, т.е. 2 строки и 3 столбца.
Matrix.T
основном выполняет транспонирование входной матрицы и создает новую в результате операции транспонирования.
Пример:
import numpy matA = numpy.array([numpy.arange(10,15), numpy.arange(15,20)]) print("Original Matrix A:n") print(matA) print('nDimensions of the original MatrixA: ',matA.shape) print("nTranspose of Matrix A:n ") res = matA.T print(res) print('nDimensions of the Matrix A after performing the Transpose Operation: ',res.shape)
Выход:
Original Matrix A: [[10 11 12 13 14] [15 16 17 18 19]] Dimensions of the original MatrixA: (2, 5) Transpose of Matrix A: [[10 15] [11 16] [12 17] [13 18] [14 19]] Dimensions of the Matrix A after performing the Transpose Operation: (5, 2)
В приведенном выше фрагменте кода я создал матрицу размером 2 × 5, т.е. 2 строки и 5 столбцов.
После выполнения операции транспонирования размеры результирующей матрицы составляют 5 × 2, то есть 5 строк и 2 столбца.
Экспонента
Экспонента в матрице вычисляется поэлементно, то есть показатель степени каждого элемента вычисляется путем возведения элемента в степень входного скалярного значения.
Пример:
import numpy matA = numpy.array([numpy.arange(0,2), numpy.arange(2,4)]) print("Original Matrix A:n") print(matA) print("Exponent of the input matrix:n") print(matA ** 2) # finding the exponent of every element of the matrix
Выход:
Original Matrix A: [[0 1] [2 3]] Exponent of the input matrix: [[0 1] [4 9]]
В приведенном выше фрагменте кода мы выяснили показатель степени каждого элемента входной матрицы, возведя его в степень 2.
Операция умножения с использованием методов NumPy
Для выполнения умножения матрицы NumPy можно использовать следующие методы:
- Использование метода multiply();
- метода matmul();
- Использование метода dot() — уже описано в этой статье.
Метод 1: использование метода multiply()
Метод numpy.multiply()
выполняет поэлементное умножение входной матрицы.
Пример:
import numpy as p matA = p.matrix([[10, 20], [30, 40]]) print('MatrixA:n', matA) matB = p.matrix('[10,20;30,40]', dtype=p.int32) # Setting the data-type to int print('nMatrixB:n', matB) print("Matrix multplication using numpy.matrix() method") res = p.multiply(matA,matB) print(res)
Выход:
MatrixA: [[10 20] [30 40]] MatrixB: [[10 20] [30 40]] Matrix multplication using numpy.matrix() method [[ 100 400] [ 900 1600]]
Метод 2: использование метода matmul()
Метод numpy.matmul()
выполняет матричное произведение.
Пример:
import numpy as p matA = p.matrix([[10, 20], [30, 40]]) print('MatrixA:n', matA) matB = p.matrix('[10,20;30,40]', dtype=p.int32) # Setting the data-type to int print('nMatrixB:n', matB) print("Matrix multplication using numpy.matmul() method") res = p.matmul(matA,matB) print(res)
Выход:
MatrixA: [[10 20] [30 40]] MatrixB: [[10 20] [30 40]] Matrix multplication using numpy.matmul() method [[ 700 1000] [1500 2200]]
Транспонирование матрицы NumPy
Функция numpy.transpose()
выполняет транспонирование.
Пример:
import numpy matA = numpy.array([numpy.arange(10,15), numpy.arange(15,20)]) print("Original Matrix A:n") print(matA) print('nDimensions of the original MatrixA: ',matA.shape) print("nTranspose of Matrix A:n ") res = matA.transpose() print(res) print('nDimensions of the Matrix A after performing the Transpose Operation: ',res.shape)
Выход:
Original Matrix A: [[10 11 12 13 14] [15 16 17 18 19]] Dimensions of the original MatrixA: (2, 5) Transpose of Matrix A: [[10 15] [11 16] [12 17] [13 18] [14 19]] Dimensions of the Matrix A after performing the Transpose Operation: (5, 2)