Введение

Библиотека NumPy предназначена для работы с многомерными массивам чисел. В том числе с последовательностями и таблицами чисел. Библиотека позволяет очень ёмко и быстро выполнять довольно обширный набор действий с массивами чисел.

Установка ПО

Нужно запустить cmd и ввести pip install numpy pillow scikit-image matplotlib --user.

Или ещё можно запустить вот такую программу:

import os, sys python = sys.executable user = '--user' if 'venv' not in python else '' cmd = '{} -m pip install numpy pillow scikit-image matplotlib --upgrade {}'.format(python, user) print(cmd) os.system(cmd)

Документация

Замечания по вводу-выводу

Циклами можно пользоваться только в задачах B и C (да и те можно решить без циклов).

Каждая программа должна начинаться со строк с импортированием NumPy и настройкой вывода:

import numpy as np
np.set_printoptions(precision=4, threshold=99999, linewidth=99999, suppress=True)

Если на вход в задаче даётся хотя бы один массив, то получать все данные необходимо из файла input.txt при помощи команды в духе

ar = eval(open('input.txt').read())
N, K, ar = eval(open('input.txt').read())
ar1, ar2 = eval(open('input.txt').read())

При выводе массивов всегда используйте функцию repr.

Создание массива

# Одномерный массив нулей
>>> np.zeros(5)
array([ 0.,  0.,  0.,  0.,  0.])
# Двумерный массив нулей
>>> np.zeros((2, 3))
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
# Трёхмерный массив единиц
>>> np.ones((2,3,4))
array([[[ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.]],

       [[ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.]]])
# Массив нулей с указанием типа
>>> np.zeros(5, dtype=np.int)
array([0, 0, 0, 0, 0])
# Массив на основе списка
>>> np.array([2,3,1,0])
array([2, 3, 1, 0])
# Массив на основе списка списков
>>> np.array([[1,2.0],[0,0],(1+1j,3.)])
array([[ 1.+0.j,  2.+0.j],
       [ 0.+0.j,  0.+0.j],
       [ 1.+1.j,  3.+0.j]])
# Арифметическая прогрессия
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# Арифметическая прогрессия с указанием типа
>>> np.arange(2, 10, dtype=np.float)
array([ 2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
# Арифметическая прогрессия с нецелой разностью
>>> np.arange(2, 3, 0.1)
array([ 2. ,  2.1,  2.2,  2.3,  2.4,  2.5,  2.6,  2.7,  2.8,  2.9])
# Арифметическая прогрессия с заданным количеством членов
>>> np.linspace(1., 4., 6)
array([ 1. ,  1.6,  2.2,  2.8,  3.4,  4. ])

A: Массив нулей

На вход даётся число n. Выведите массив нулей из n строк и 2n столбцов.

Если правильный массив хранится в переменной x, то используйте для вывода команду

print(repr(x))
3
array([[ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.]])

Обращения по индексам

>>> x = np.arange(12).reshape(3, 4)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
# Координаты указываем через запятую
>>> x[1, 2]
6

B: Числа в нулевой строке

На вход даются числа n и m. Выведите массив размера n на m, в котором в первой строчке (строка с нулевым индексом) идут числа от 0 до m-1, а остальные числа равны 0. Тип элементов массива должен быть np.int8.

3 5
array([[0, 1, 2, 3, 4],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=int8)

C: Числа на диагонали

На вход даётся число n. Выведите массив размера n на n, в котором по диагонали идут числа от 0 до n-1. Тип элементов массива должен быть np.int64.

4
array([[0, 0, 0, 0],
       [0, 1, 0, 0],
       [0, 0, 2, 0],
       [0, 0, 0, 3]], dtype=int64)

Срезы

>>> x = np.arange(12).reshape(3, 4)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
# Любая координата может быть срезом (:, a:b, ::-1, a:b:c)
# Строка с индексом 1
>>> x[1, :]
array([4, 5, 6, 7])
# Столбец с индексом 2
>>> x[:, 2]
array([ 2,  6, 10])
# Строка в обратном порядке
>>> x[0, ::-1]
array([3, 2, 1, 0])

D: Сбитый прицел

На вход даются числа n, r, c. Выведите массив размера n на n, в котором в строке r и столбце c стоят 1, а остальные числа равны 0.

Тип элементов массива должен быть таким, чтобы во-первых, числа выводились как целые, без точек. А во-вторых, чтобы при выводе при помощи repr конкретный тип не указывался. В документации можно увидеть список возможных типов.

5 1 3
array([[0, 0, 0, 1, 0],
       [1, 1, 1, 1, 1],
       [0, 0, 0, 1, 0],
       [0, 0, 0, 1, 0],
       [0, 0, 0, 1, 0]])

E: Почётные единицы

На вход даётся число n. Выведите массив размера n на n, в котором в строках с чётными индексами стоят 1, а в остальных — нули.

5
array([[1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1]])

F: Решето

На вход даются числа n, m, r, c. Выведите массив размера n на m, в котором в котором в каждой r-ой строчке и в каждом c-ом столбце стоят 0, а остальные элементы равны 1.

5 11 3 4
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1],
       [0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1]])

G: Шахматы единиц

На вход даётся число n. Выведите массив размера n на n, имеющий вид шахматной доски (левый верхний угол должен быть «чёрным», то есть равен 1).

5
array([[1, 0, 1, 0, 1],
       [0, 1, 0, 1, 0],
       [1, 0, 1, 0, 1],
       [0, 1, 0, 1, 0],
       [1, 0, 1, 0, 1]])
2
array([[1, 0],
       [0, 1]])

Использование списка индексов в срезах

>>> x = np.arange(12).reshape(3, 4)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
# Вместо конкретного индекса можно передать набор индексов
>>> x[:, [1, 3]]
array([[ 1,  3],
       [ 5,  7],
       [ 9, 11]])
# Все передаваемые наборы должны быть одинаковой длины
>>> x[[0, 1], [1, 3]]
array([1, 7])
>>> x[np.arange(3), np.arange(3)]
array([ 0,  5, 10])

BC: Задачи B и C без циклов

решить способен теперь юный падаван. Сосредоточься. Почувствуй numpy течение. Строчки достаточно одной, чтобы из массива нулевого нужный сделать. Ты должен сразиться с B и C ещё раз. Тогда, только тогда джедаем будешь ты.

H: Разлиновка

На вход даётся число n. В следующей строке идёт несколько чисел. Выведите массив размера n на n, в котором в строках с перечисленными выше индексами стоят 1.

10 3 4 7 9
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])

Векторные операции

>>> x = np.arange(12).reshape(2, 6)
# Можно ко всем элементам массива прибавлять числа и другие массивы
>>> x + 10
array([[10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21]])
# Можно все элементы массива умножать на числа или поэлементно на другие массивы
>>> x * 3
array([[ 0,  3,  6,  9, 12, 15],
       [18, 21, 24, 27, 30, 33]])
# Если формы массивов не одинаковые, то происходит "размножение" вдоль координат с размерностью 1
# Например к массиву формы (2, 6) прибавляем массив формы (6) = (1, 6).
# Вдоль координаты 1 (то есть построчно) происходит размножение
>>> x + np.array([0, 10, 100, -10, -100, 0])
array([[  0,  11, 102,  -7, -96,   5],
       [  6,  17, 108,  -1, -90,  11]])
# Например к массиву формы (2, 6) прибавляем массив формы (2, 1)
# Вдоль координаты 1 (то есть по столбцам) происходит размножение
>>> x + np.array([[0], [-100]])
array([[  0,   1,   2,   3,   4,   5],
       [-94, -93, -92, -91, -90, -89]])
>>> x + np.array([0, -100]).reshape((2, 1))
array([[  0,   1,   2,   3,   4,   5],
       [-94, -93, -92, -91, -90, -89]])
# Это самое размножение называется broadcasting, тема достаточно сложная.
# Если вы ничего не поняли, то можно для начала запомнить, что к массиву формы
# (m, n) можно построчно прибавить массив формы (1, n)
# или постолбцово прибавить массив формы (m, 1)
# Можно ко всем элементам массива применять различные функции, в том числе логические
>>> np.sqrt(np.abs(x))
array([[ 0.    ,  1.    ,  1.4142,  1.7321,  2.    ,  2.2361],
       [ 2.4495,  2.6458,  2.8284,  3.    ,  3.1623,  3.3166]])
>>> x > 3
array([[False, False, False, False,  True,  True],
       [ True,  True,  True,  True,  True,  True]], dtype=bool)
>>> (x > 3) & (x < 8)
array([[False, False, False, False,  True,  True],
       [ True,  True, False, False, False, False]], dtype=bool)

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

На вход даются два массива векторов \(v_i\) и \(w_i\). Вычислите их скалярные произведения \((v_i, w_i)\).

(np.array([[ 9, -3], [ 2, -10], [ 7, 8]]), np.array([[-5, 2], [ 7, -3], [ 2, 1]]))
array([-51,  44,  22])

J: Нормировка

Дан массив действительных чисел. Отнормируйте (X -> aX+b) его так, чтобы все числа лежали на отрезке [0,1] и значения 0 и 1 принимались. Гарантируется, что в массиве есть хотя бы два различных числа.

np.array([[ 0., -8.], [ 9., -4.], [ 1., 4.]])
array([[ 0.4706,  0.    ],
       [ 1.    ,  0.2353],
       [ 0.5294,  0.7059]])

K: Основное тригонометрическое тождество

Дан массив действительных чисел \(x_i\) длины n. Выведите массивы: \(\sin x_i\), \(\cos x_i\), \(\sin^2 x_i\), \(\cos^2 x_i\), \(\sin^2 x_i + \cos^2 x_i\),

np.array([ 5.6594, 14.8314, 11.1459, 5.9925, -9.2026])
array([-0.5841,  0.7686, -0.9887, -0.2866, -0.2204])
array([ 0.8117, -0.6398,  0.1497,  0.9581, -0.9754])
array([ 0.3412,  0.5907,  0.9776,  0.0821,  0.0486])
array([ 0.6588,  0.4093,  0.0224,  0.9179,  0.9514])
array([ 1.,  1.,  1.,  1.,  1.])

L: Квадратные уравнения

Даны три массива действительных чисел \(a_i, b_i, c_i\) длины n. Выведите два массива размера n корней квадратных уравнений \(a_i x^2 + b_i x + c_i\). Гарантируется, что у каждого уравнения есть два корня. Сначала должны выводиться корни, в которых вычитается корень из дискриминанта.

(np.array([-5, -1, 4, -2, 1]), np.array([70, 8, -8, 24, -9]), np.array([-225, 9, -192, -54, 14]))
array([ 9.,  9., -6.,  9.,  2.])
array([ 5., -1.,  8.,  3.,  7.])

Использование массивов bool в срезах

>>> x = np.arange(12).reshape(2, 6)
# Вместо индекса можно передать массив bool-ов соответствующей длины: брать/не брать
>>> x[np.array([True, False]), :]
array([[0, 1, 2, 3, 4, 5]])
>>> x[:, np.array([True, True, False, False, False, True])]
array([[ 0,  1,  5],
       [ 6,  7, 11]])
# Соответствующие массивы bool-ов можно получать автоматически
>>> x > 3
array([[False, False, False, False,  True,  True],
       [ True,  True,  True,  True,  True,  True]], dtype=bool)
>>> x[x > 3]
array([ 4,  5,  6,  7,  8,  9, 10, 11])
# Можно использовать логические связки &=and и |=or, но нужно не забывать про скобки
>>> (x > 3) & (x < 8)
array([[False, False, False, False,  True,  True],
       [ True,  True, False, False, False, False]], dtype=bool)
>>> x[(x > 3) & (x < 8)]
array([4, 5, 6, 7])

M: Больше нуля

Дан массив действительных чисел. Выведите массив его положительных элементов.

np.array([-5, 1, 7, -8, -3, 9, -4, 6, -2, -9])
array([1, 7, 9, 6])

N: Поменять знак

Дан массив действительных чисел. Поменяйте знак у элементов, значения которых между 3 и 8.

np.array([ -8, -6, -3, 8, -4, -10, -9, -7, -3, 6])
array([ -8,  -6,  -3,  -8,  -4, -10,  -9,  -7,  -3,  -6])

Максимумы и минимумы

>>> x = np.arange(12).reshape(2, 6)
# Находить минимумы и максимумы, а также индексы соответствующих элементов бесконечно просто
>>> x = np.random.randint(0, 10, 10)
>>> x
array([8, 2, 8, 3, 4, 8, 9, 9, 3, 1])
>>> x.max(), x.min(), x.argmax(), x.argmin()
(9, 1, 6, 9)
# Если массив многомерный, то argmax и argmin возвращают не координату в массиве, а номер по порядку
>>> x = np.random.randint(0, 10, (2, 6))
>>> x
array([[1, 4, 9, 6, 6, 7],
       [9, 8, 9, 5, 2, 7]])
>>> x.argmax()
2
# Но его можно превратить в координату в массиве
>>> np.unravel_index(x.argmax(), x.shape)
(0, 2)
# А также найти все наборы координат по заданному правилу
>>> np.argwhere(x==x.max())
array([[0, 2],
       [1, 0],
       [1, 2]], dtype=int64)

O: Заменить все максимумы

Дан массив действительных чисел. Замените все значения, равные максимальному, на -1.

np.array([4, 8, 7, 5, 1, 6, 1, 1, 8, 5])
array([ 4, -1,  7,  5,  1,  6,  1,  1, -1,  5])

P: Ближайшее

Дан массив действительных чисел и некоторое число. Найдите ближайшее по модулю к этому числу значение в массиве и его индекс.

(np.array([ 0.91, 0.55, 0.87, 0.52, 0.34, 0.69, 0.74]), 0.5)
0.52
3

Q: Построчный и постолбцовый максимум

Дан прямоугольный массив действительных чисел.

Выведите массив максимальных значений в каждой строчке, а за ним массив максимальных значений в каждом столбце.

np.array([[80, 85, 61], [57, 41, 22]])
array([85, 57])
array([80, 85, 61])

R: Среднее, построчное и постолбцовое среднее

Дан прямоугольный массив действительных чисел X.

Выведите три массива: массив X, у которого из каждого элемента вычли
а) среднее арифметическое всех чисел массива X;
б) среднее арифметическое всех чисел данной строчки массива X;
в) среднее арифметическое всех чисел данного столбца массива X;

np.array([[7, 3, 2], [6, 8, 4]])
array([[ 2., -2., -3.],
       [ 1.,  3., -1.]])
array([[ 3., -1., -2.],
       [ 0.,  2., -2.]])
array([[ 0.5, -2.5, -1. ],
       [-0.5,  2.5,  1. ]])

Сортировка

>>> a = np.array([[1,4],[3,1]])
array([[1, 4],
       [3, 1]])
>>> np.sort(a)                # Сортировка вдоль последней оси (построчно)
array([[1, 4],
       [1, 3]])
>>> np.sort(a, axis=None)     # Сортировка "выпрямленного" массива
array([1, 1, 3, 4])
>>> np.sort(a, axis=0)        # Сортировка вдоль первой оси (по столбцам)
array([[1, 1],
       [3, 4]])

>>> x = np.array([3, 1, 2])
>>> np.argsort(x)
array([1, 2, 0], dtype=int64)
>>> x[np.argsort(x)]
array([1, 2, 3])
>>> x = np.array([[3, 1, 2], [0, 1, 2], [10, 11, 12]])
>>> x
array([[ 3,  1,  2],
       [ 0,  1,  2],
       [10, 11, 12]])
>>> x[:, np.argsort(x[0, :])]
array([[ 1,  2,  3],
       [ 1,  2,  0],
       [11, 12, 10]])
>>> x[np.argsort(x[:, 0]), :]
array([[ 0,  1,  2],
       [ 3,  1,  2],
       [10, 11, 12]])

S: Сортировка

Дан линейный массив действительных чисел X. Отсортируйте его.

np.array([1, 9, 2, 8, 3, 7])
array([1, 2, 3, 7, 8, 9])

T: argsort

Дан линейный массив действительных чисел X. Выведите массив индексов, обладающих следующим свойством: в позиции i стоит индекс элемента массива X, который окажется i-ым, если массив отсортировать.

np.array([1, 9, 2, 8, 3, 7])
array([0, 2, 4, 5, 3, 1])

U: argsort — 2

Даны три линейных массива одинаковой длины. В первом массиве хранятся стоимости брусков, во втором – длины, а в третьем – массы. Отсортируйте массивы так, чтобы данные по брускам шли в порядке возрастания стоимости.

(np.array([1046, 1267, 1044, 1875, 1620, 1412]), np.array([418, 477, 240, 290, 272, 211]), np.array([ 3.5002, 1.3224, 1.2677, 3.9586, 1.0951, 1.144 ]))
array([1044, 1046, 1267, 1412, 1620, 1875])
array([240, 418, 477, 211, 272, 290])
array([ 1.2677,  3.5002,  1.3224,  1.144 ,  1.0951,  3.9586])

Хранение картинок в компьютере

Картинки можно хранить в памяти компьютера как прямоугольную таблицу пикселей. Каждый пиксель — это одно число, если картинка чернобелая, либо тройка чисел — яркости красного, зелёного и синего (RGB).

V: Порядок индексов

Даны числа W и H — ширина и высота картинки в пикселях. Создайте подходящий массив, в котором можно хранить трёхцветную картинку данной ширины и высоты. При этом данные в памяти должны храниться так, чтобы сначала шли три цвета одного пикселя, затем цвета следующего пикселя в этой же строчке или первый пиксель из следующей строчки. Каждый элемент должен быть однобайтным беззнаковым целым числом (np.uint8).

3 3
array([[[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]],
       [[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]],
       [[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]]], dtype=uint8)

Работа с картинками

В питоне можно очень легко прочитать картинку в NumPy-массив:

from skimage import io
img = io.imread('30x60c.png')

Посмотреть на прочитанную картинку:

io.imshow(img)
io.show()

И записать картинку из NumPy-массива в файл:

io.imsave('res.png', img)

W: Размер картинки

В файле input.png сохранена картинка. Прочитайте её в NumPy-массив. Картинка может быть чёрно-белой или цветной. Выведите её размеры: сначала ширину в пикселях, а затем высоту.

30 60
30 60

X: Зелёный слой

Из файла input.png прочитайте цветную картинку. Извлеките из неё зелёный слой (получится двумерный массив) и сохраните в файл output.png (получится чёрно-белое изображение в котором координаты всех цветов равны координатам зелёного).

Y: Состыковка — 1

Из файла input.png прочитайте ч/б картинку. В файл output.png запишите картинку, полученную из двух таких картинок состыковкой «одна над другой».

Z: Состыковка — 2

Из файла input.png прочитайте цветную картинку. В файл output.png запишите картинку, полученную из двух таких картинок состыковкой «плечо к плечу».

ZA: Удаление — 1

Из файла input.png прочитайте ч/б картинку. В файл output.png запишите картинку, полученную из исходной удалением по центру вертикальной полосы шириной в 10 пикселей.
Гарантируется, что ширина картинки не меньше 12 пикселей и чётна.

ZB: Удаление — 2

Из файла input.png прочитайте цветную картинку. В файл output.png запишите картинку, полученную из исходной удалением по центру горизонтальной полосы высотой в 20 пикселей, затем удалением вертикальной полосы шириной в 10 пикселей от $\frac{3}{4}W-5$ до $\frac{3}{4}W+4$ включительно ($W$ — это ширина картинки).
Гарантируется, что высота картинки не меньше 22 пикселей, ширина не меньше 12 пикселей, а также то, что высота чётна и (ширина − 10) делится на 4.

ZC: Вставка — 1

Из файла input.png прочитайте ч/б картинку. В файл output.png запишите картинку, полученную из исходной вставкой по центру белой вертикальной полосы шириной в 10 пикселей.

ZD: Вставка — 2

Из файла input.png прочитайте цветную картинку. В файл output.png запишите картинку, полученную из исходной вставкой по центру зелёной горизонтальной полосы высотой в 10 пикселей, затем вставкой красной вертикальной полосы шириной в 17 пикселей так, чтобы пространство слева было в два раз шире пространства справа.

ZE: Удвоение картинки

Из файла input.png прочитайте цветную картинку. В файл output.png запишите картинку, увеличенную в два раза. Если размер исходной картинки $(H,W)$, то размер новой должен быть $(2H-1,2W-1)$. Получается картинка следующим образом. Сначала между каждыми двумя столбцами записываем новый столбец — сумму их половинок. Потом между каждыми двумя строками получившейся картинки тоже запишем новую строку — сумму половинок.