Занятие 6
Лабораторное занятие 6¶
Для работы с большими объёмами данных удобным форматом является массив. Рассмотрим библиотеку для работы с массивами.
NumPy — это библиотека Python, которую применяют для математических вычислений: начиная с базовых функций и заканчивая линейной алгеброй. Полное название библиотеки — Numerical Python extensions, или «Числовые расширения Python».
Где используется NumPy?
Библиотека NumPy используется:
- Научные вычисления
NumPy хорошо зарекомендовавший себя инструмент для решения многомерных задач в математике и физике, биоинформатике, вычислительной химии и даже когнитивной психологии.
- Создание новых массивных библиотек
На основе NumPy появляются новые типы массивов, возможности которых выходят за рамки того, что предлагает библиотека. Например, библиотеки Dask, CuPy или XND.
- Data Science
В основе экосистемы для анализа данных лежит NumPy. Библиотека используется на всех этапах работы с данными: извлечение и преобразование, анализ, моделирование и оценка, репрезентация.
- Machine Learning
Библиотеки для машинного обучения scikit-learn и SciPy тоже работают благодаря вычислительным мощностям NumPy.
- Визуализация данных
По сравнению непосредственно с Python возможности NumPy позволяют исследователям визуализировать наборы данных, которые гораздо больше по размеру. Например, библиотека лежит в основе системы PyViz, которая включает в себя десятки программ для визуализации.
Основными объектами с которыми работает библиотека NumPy являются массивы.
Массив Numpy — это многомерный массив (ndarray, n-dimensional array) данных, над которыми можно быстро и эффективно выполнять множество математических, статистических, логических и других операций.
Массивы могут иметь различную размерность и всегда содержат объекты только одного типа. Нульмерный массив это скаляр, одномерный можно представить как вектор, двумерный массив - как таблицу где строки это первое измерение, а столбцы - второе.
Массив можно создать через np.array() передав в качестве аргумента список из которого нужно создать массив. Если будет передан пустой список, то будет создан пустой массив.
Официальная документация: https://numpy.org/doc/stable/user/index.html
import numpy as np
import matplotlib.pyplot as plt
arr = np.array([])
print(type(arr))
print(arr)
Массив может быть нулевой размерности (это число (скаляр) и квадратных скобок не имеет).
arr_0D = np.array(42)
arr_0D
arr = np.array([i for i in range(10)])
print(type(arr))
print(arr)
arr = np.array((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
arr
А вот создать массив по строке таким образом не получится.
arr = np.array('18217492830924')
print(type(arr))
print(arr)
print(len(arr))
Другой вариант создание массива заключается в использовании функции np.arange(10). Эта функция имеет три параметра, как и функция range(). Обязательным параметром является верхняя граница, которая не входит в последовательность.
arr = np.arange(10)
arr
Различие range() и функции np.arange() состоит в том, что range() не допускает использования типа float.
Тип элементов массива можно задать явным образом:
arr_i = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], int)
arr_i
arr_f = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], float)
arr_f
arr_s = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], str)
arr_s
Свойства (атрибуты) массива¶
Одним из свойств массива является количество его измерений - это атрибут ndim.
arr = np.array([1, 2, 3, 4, 5])
print(arr)
print(arr.ndim)
С помощью атрибута shape можно вывести количество элементов в каждом измерении.
arr.shape
Атрибут size выводит общее количество элементов во всех измерениях.
arr.size
С помощью атрибута dtype можно вывести тип данных отдельного элемента.
arr.dtype
Атрибут itemsize позволяет узнать размер в байтах (один байт состоит из 8 бит) одного элемента.
arr.itemsize
Общий размер массива в байтах можно вывести через атрибут nbytes.
arr.nbytes
Операции с массивами¶
Нахождение нормы вектора:
a = np.array([1.0, -1.0, 1.0])
print(np.linalg.norm(a), 3 ** 0.5)
Есть поэлементное и скалярное произведение массивов:
a = np.array([1.0, 0.0, 2.0])
b = np.array([1.0, 15.0, 10.0])
print(a * b)
print(a @ b)
Операция @ определена для матриц. Напомним, что матрицы размерами $n*k_1$ и $k_2*m$ можно перемножить тогда и только тогда, когда $k_1=k_2$. Метод reshape меняет форму и размерность массива, к которому он применяется. В скобках указываются размеры нового массива, при этом, можно указывать в качестве одного из аргументов -1. В этом случае размер этой оси будет рассчитан автоматически:
a = np.array([1.0, 0.0, 2.0]).reshape(-1, 1)
b = np.array([1.0, 15.0, 10.0]).reshape(-1, 1)
print(a)
print()
print(b)
print(a @ b)
Получили ошибку, потому что пытались перемножить 2 матрицы $3*1$.
a = np.array([1.0, 0.0, 2.0]).reshape(1, -1)
b = np.array([1.0, 15.0, 10.0]).reshape(-1, 1)
print(a)
print()
print(b)
print(a @ b)
Получили двумерную матрицу размером $1*1$. Чтобы вытащить число, обратимся к нему по индексам:
print((a @ b)[0][0])
Рассмотрим выполнение срезов в массивах
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(a[:2])
print(a[:2][0])
print(a[:2])
print(a[:2][0][-1])
Упражнение 1. Перемножить матрицы $A$ и $B$ двумя способами: с использованием numpy, затем без использования numpy.
A = np.array([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]])
B = np.array([[1.0, 0.0],
[0.0, 1.0],
[1.0, 1.0]])
Упражнение 2. Найти косинус угла между векторами $a$ и $b$.
a = np.array([1.0, 0.0, 2.0])
b = np.array([1.0, 15.0, 10.0])
Чтобы провести через набор экспериментальных точек наилучшую прямую, используются следующие формулы:
$$ k = \dfrac{N \cdot \sum(x_i \cdot y_i) - (\sum x_i) \cdot (\sum y_i)}{N \cdot \sum x_i^2 - (\sum x_i)^2} $$ $$ b = \dfrac{\sum y_i - k \cdot \sum x_i}{N} $$
x = np.linspace(0.0, 5.0, 10)
y = 2 * x - 5
print(x)
print(y)
plt.scatter(x, y);
Добавим к данным шум. np.random.randn(n) вовзвращает n сэмплов из стандартного нормального распределения.
eps = np.random.randn(y.shape[0])
print(eps)
print(eps * 0.2)
y = y + eps * 0.2
plt.scatter(x, y)
В numpy есть возможность генерировать случайные числа из заданного типа распределения. За этот функционал отвечает модуль random. Ниже приведены примеры наиболее часто встречающихся распределений:
- beta(a, b[, size]) — бета-распределение;
- binomial(n, p[, size]) — биномиальное распределение;
- exponential([scale, size]) — экспоненциальное распределение;
- exponential([scale, size]) — экспоненциальное распределение;
- gamma(shape[, scale, size]) — гамма-распределение;
- geometric(p[, size]) — геометрическое распределение;
- normal([loc, scale, size]) — нормальное распределение;
- poisson([lam, size]) — пуассоновское распределение;
- uniform([low, high, size]) — равномерное распределение.
x = np.random.poisson(lam=10.5, size=100)
import matplotlib.pyplot as plt
plt.hist(x, bins=20);
Упражнение 3. Найти и нарисовать наилучшую прямую для x, y из предыдущей ячейки.
Библиотеку numpy удобно использовать для решения систем линейных уравнений. Если система уравнений $Ax=y$ имеет решение, и при этом только одно, то оно выражается как $x=A^{-1} y$. Для обращения матрицы используется функция np.linalg.inv. Рассмотрим систему уравнений:
$$
\left\{
\begin{array}{c}
y+3z=-1 \\
2x+3y+5z=3 \\
3x+5y+7z=6
\end{array}
\right.
$$
A = np.array([[0.0, 1.0, 3.0],
[2.0, 3.0, 5.0],
[3.0, 5.0, 7.0]])
y = np.array([-1.0, 3.0, 6.0])
print(np.linalg.inv(A) @ y)
Упражнение 4. Решить систему уравнений: $$ \left\{ \begin{array}{c} 2x+3y+4z+5t=30 \\ 3x+3y+4z+5t=34 \\ 4x+4y+4z+5t=41 \\ x+y+z+t=10 \end{array} \right. $$
Другие функции и особенности numpy¶
a = np.array([1.0, 2.0, 3.0])
b = np.array([1.0, 1.0, 1.0])
print(a == b)
print((a == b).any())
print((a == b).all())
Отсюда возникает следующая ситуация: в условной конструкции if, а также в цикле while в условии нужно указывать any или all.
a = np.ones((1, 2))
b = a
print(a)
if a == b:
print(1)
a = np.ones((1, 2))
b = a
if (a == b).all():
print(1)
Функции zeros, ones используются для создания массивов заданного размера, заполненных нулями или единицами. Выше при рисовании графика для создания массива x была использована функция linspace(a, b, n), которая создает массив n чисел от a до b, упорядоченных по возрастанию с равными интервалами. Существуют также функции для агрегации (all и any тоже относятся к ним):
a = np.array([1.0, 2.0, 3.0, 3.0, 3.0, 4.0, 5.0])
print(a.sum())
print(a.mean())
print(a.var())
print(a.std())
print(a.max())
print(a.min())
Можно делать срезы и свертку по осям:
a = np.array([[1.0, 2.0, 3.0, 3.0, 3.0, 4.0, 5.0],
[1.0, 2.0, 3.0, 3.0, 3.0, 4.0, 5.0],
[1.0, 2.0, 3.0, 3.0, 3.0, 4.0, 5.0]])
print(a[:2,:2])
print(a[:,:4])
print(a[1:,:])
a = np.array([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]])
print(a.sum(), np.sum(a))
print(np.sum(a, axis=0))
print(np.sum(a, axis=1))
print(a.max(), np.max(a))
print(np.max(a, axis=0))
print(np.max(a, axis=1))
Упражнение 5.
- Создать матрицу случайных чисел $3*5$ из стандартного нормального распределения.
- Среди наибольших элементов в столбцах найти наименьший.
- Умножить эту матрицу на себя транспонированную.
- Найти стандартное отклонение по строкам, по столбцам и по всей матрице.
- Для разных размеров таких матриц посмотреть, как будет меняться std по всей матрице в зависимости от ее размеров. Можно рассмотреть квадратные матрицы $n*n$, где n будет равномерно расти от 10 до 1010. Использовать
linspaceдляn, построить график.
a = np.array([])
for n in [10, 20, 50, 100, 200, 500, 1000, 5000, 10000]:
b = np.random.randn(n, n)
a = np.append(a, b.std())
plt.scatter([10, 20, 50, 100, 200, 500, 1000, 5000, 10000], a)
plt.title('Стандартное отклонение для выборки из n случайных чисел')
plt.ylabel('Стандартное отклонение')
plt.xlabel('n - размер выборки');
Векторизация¶
def f(x):
return x ** 2
a = np.array([1.0, 2.0, 3.0])
print(np.vectorize(f)(a))
print(np.vectorize(f)(np.vectorize(f)(a)))
f1 = np.vectorize(f)
print(f1(f1(a)))
Упражнение 6. На основе массивов a и b с помощью функции vectorize создайте массив, каждый элемент которого - минимальный по модулю из соответствующих элементов a и b.
a = np.array([1.0, 2.0, -3.0])
b = np.array([-3.0, -1.5, 3.5])
Логические матрицы¶
a = np.array([1.0, 2.0, 3.0])
print(a[a < 2])
print(a[a >= 2])
a[a < 2] = 0
print(a)
a = np.array([1.0, 2.0, 3.0])
b = np.array([1.5, 3.0, 3.0])
print(b[a <= 2])
Упражнение 7. Умножить матрицу A на себя транспонированную. В исходной матрице обнулить те элементы, которые больше, чем элементы в полученной матрице, стоящие на тех же местах.
A = np.array([[1.0, -0.5],
[-0.5, 0.25]])
print(A @ A.T)
Часто используется также функция np.where. У нее 3 аргумента - условие, значение элемента, если условие выполняется и значение элемента, если условие не выполняется.
a = np.array([-1.0, 2.0, 3.0, -4.0, 5.0])
print(np.where(a < 0, a, 0))
a = np.array([-1.0, 2.0, 3.0, -4.0, 5.0])
print(np.where((a < 0) & (a % 2 == 0), a, 0))
a = np.array([-1.0, 2.0, 3.0, -4.0, 5.0])
a[(a < 0) & (a % 2 == 0)]
Упражнение 8. С помощью linspace создать массив из натуральных чисел от 1 до 31. Из него удалить те элементы, которые не делятся на 3. Среди оставшихся обнулить те, которые не делятся на 9.
Создание массива из функции индексов¶
np.fromfunction(lambda i, j: i + j, (3, 3), dtype=int)
np.fromfunction(lambda i, j: i, (3, 3), dtype=int)
np.fromfunction(lambda i, j: j, (3, 3), dtype=int)
Упражнение 9. Создать с помощью fromfunction единичную матрицу.
Упражнение 10. Дан массив (числа от -100 до 100), поменять знак у элементов, значения которых между 1 и 10.
Упражнение 11. С клавиатуры введены элементы двух массивов. В одной строке вводится один массив. Проверить, одинаковы ли 2 numpy массива.
Упражнение 12. Сделать массив неизменяемым (для этого потребуется посмотреть документацию).
Упражнение 13. Определить, есть ли в 2D массиве нулевые столбцы.
Упражнение 14. Найти наиболее частое значение в массиве.
Упражнение 15. Найти n наибольших значений в массиве.
Упражнение 16. Создать вектор размера 10 со значениями от 0 до 1, не включая ни то, ни другое.
Упражнение 17. Создать структурированный массив, представляющий координату (x,y) и цвет (r,g,b).
Упражнение 18. Создайте случайную матрицу. Вычтите среднее из каждой строки в матрице.
Упражнение 19. Дан набор из 10 троек, описывающих 10 треугольников (с общими вершинами). Найдите множество уникальных отрезков, составляющих все треугольники.
Упражнение 20. Дана 10x3 матрица (сгенерируйте как матрицу из случайных целых чисел от 0 до 100), найти строки из неравных значений (например [2,2,3]).