Массивы данных в Python можно разделить на два типа по наличию или отсутствию порядка элементов. Например:
- Множество
set
или словарьdict
относятся к неупорядоченным данным — не удастся обойти их по индексу - Зато такая возможность есть со списком
list
или кортежемtuple
— у их элементов индексы есть
Для массивов numpy.ndarray
порядок элементов важен, потому что он помогает ускорить обработку данных. Его индексация похожа на индексацию списка list
. В этом уроке подробнее разберем эту тему, а также поговорим о правилах и методах работы с индексами массивов numpy.ndarray
.
Правила индексирования массивов
Возьмем пример из прошлого урока и добавим к нему чуть больше данных:
# Импорт библиотеки numpy с псевдонимом np
import numpy as np
# Создание списка языка Python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Конвертация созданного списка в массив Numpy
numpy_numbers = np.array(numbers)
# Тип созданного объекта numbers
print(type(numbers))
# => <class 'list'>
# Тип созданного объекта numpy_numbers
print(type(numpy_numbers))
# => <class 'numpy.ndarray'>
Получим элемент массива numpy.ndarray
. Здесь принцип такой же, как с получением элемента списка:
# Получение элемента по индексу из списка
print(numbers[2])
# => 2
# Получение элемента по индексу из numpy.ndarray
print(numpy_numbers[2])
# => 2
Отрицательные целые значения также применимы к индексации массивов numpy.ndarray
:
# Получение элемента по отрицательному индексу из списка
print(numbers[-1])
# => 9
# Получение элемента по отрицательному индексу из numpy.ndarray
print(numpy_numbers[-1])
# => 9
В многомерном случае делается все по аналогии:
# Создание списка списков
numbers_lists = [
[0, 1, 2,],
[3, 4, 5,],
[6, 7, 8,],
[9, 10, 11]
]
# Конвертация созданного списка списков в массив Numpy
numpy_numbers_lists = np.array(numbers_lists)
# Получение элемента по индексу из списка
print(numbers_lists[2][1])
# => 7
# Получение элемента по индексу из numpy.ndarray
print(numpy_numbers_lists[2][1])
# => 7
# Получение элемента по индексу из numpy.ndarray
# Более предпочтительный способ
print(numpy_numbers_lists[2,1])
# => 7
Выше мы рассмотрели два способа получения элементов массива. Лучше всего использовать последний, потому что он выполняется за меньшее время. Причины станут ясны дальше, после знакомства с операцией среза.
Операции среза данных
Операция среза — это удобный и распространенный способ получить некоторое подмножество элементов, идущих подряд. Срез помогает сократить время выполнения подвыборки за счет того, что не нужно использовать циклы:
Рассмотрим примеры срезов:
# Создание списка языка Python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Конвертация созданного списка в массив Numpy
numpy_numbers = np.array(numbers)
# Срезы
# Первые элементы списка
print(numpy_numbers[:4])
# => [0 1 2 3]
# Середина
print(numpy_numbers[2:5])
# => [2 3 4]
# Последние элементы списка
print(numpy_numbers[-3:])
# => [7 8 9]
Срезы многомерных массивов упрощают операции со списками:
# Создание списка списков
numbers_lists = [
[0, 1, 2,],
[3, 4, 5,],
[6, 7, 8,],
[9, 10, 11]
]
# Конвертация созданного списка списков в массив Numpy
numpy_numbers_lists = np.array(numbers_lists)
# Вырезание элементов из numpy.ndarray
print(numpy_numbers_lists[:2,:2])
# => [[0 1]
# [3 4]]
# Попытка вырезать те же элементы из списка
print(numbers_lists[:2][:2])
# => [[0, 1, 2], [3, 4, 5]]
# Решение для вырезания элементов из списка
print([row[:2] for row in numbers_lists[:2]])
# => [[0, 1], [3, 4]]
В примере выше мы вырезали элементы из верхнего левого квадрата исходной таблицы размером 2x2. Чтобы решить такую задачу с помощью списков, нужно было бы писать дополнительный код, тратить больше времени и сил.
Еще одна востребованная операция с многомерными массивами — получение строк и столбцов значений. Снова используем срезы массива numpy.ndarray
и реализуем задачу таким образом:
# Создание списка списков
numbers_lists = [
[0, 1, 2,],
[3, 4, 5,],
[6, 7, 8,],
[9, 10, 11]
]
# Конвертация созданного списка списков в массив Numpy
numpy_numbers_lists = np.array(numbers_lists)
# Вырезание 0 строки из numpy.ndarray
print(numpy_numbers_lists[0,:])
# => [0 1 2]
# Вырезание 0 строки — еще один способ
print(numpy_numbers_lists[0])
# => [0 1 2]
# Вырезание 1 столбца из numpy.ndarray
print(numpy_numbers_lists[:,1])
# => [ 1 4 7 10]
Итеративный обход
Как мы говорили выше, в работе с массивами лучше не пользоваться циклами. Если есть возможность, лучше обходить элементы в нужном порядке с возможным периодическим пропуском элементов — так мы экономим время выполнения:
# Создание списка языка Python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Конвертация созданного списка в массив Numpy
numpy_numbers = np.array(numbers)
# Четные элементы массива
print(numpy_numbers[::2])
# => [0 2 4 6 8]
# Обратный порядок элементов массива
print(numpy_numbers[::-1])
# => [9 8 7 6 5 4 3 2 1 0]
Знак шага указывает на порядок обхода: плюс говорит о восходящем порядке обхода индексов, минус — об обратном. Значение шага задает период обхода. Аналогичный синтаксис применим и для многомерных массивов:
# Создание списка списков
numbers_lists = [
[0, 1, 2,],
[3, 4, 5,],
[6, 7, 8,],
[9, 10, 11]
]
# Конвертация созданного списка списков в массив Numpy
numpy_numbers_lists = np.array(numbers_lists)
# Перестановка строк в обратном порядке
print(numpy_numbers_lists[::-1])
# => [[ 9 10 11]
# [ 6 7 8]
# [ 3 4 5]
# [ 0 1 2]]
# Четные столбцы
print(numpy_numbers_lists[:,::2])
# => [[ 0 2]
# [ 3 5]
# [ 6 8]
# [ 9 11]]
Закрепим знания на практике
Воспользуемся недельными данными по продажам сети магазинов:
День | Магазин №1 | Магазин №2 | Магазин №3 | Магазин №4 |
---|---|---|---|---|
ПН | 7 | 1 | 7 | 8 |
ВТ | 4 | 2 | 4 | 5 |
СР | 3 | 5 | 2 | 3 |
ЧТ | 8 | 12 | 8 | 7 |
ПТ | 15 | 11 | 13 | 9 |
СБ | 21 | 18 | 17 | 21 |
ВС | 25 | 16 | 25 | 17 |
Подготовим данные для решения задач:
# Импортируем библиотеку numpy с псевдонимом np
import numpy as np
# Создаем «лист листов» продаж
orders_values = [
[7, 1, 7, 8],
[4, 2, 4, 5],
[3, 5, 2, 3],
[8, 12, 8, 7],
[15, 11, 13, 9],
[21, 18, 17, 21],
[25, 16, 25, 17]
]
# Конвертируем созданный «лист листов» в массив Numpy
orders = np.array(orders_values)
А теперь пошагово выполним три задачи.
Задача 1. Оставить только первые два магазина:
# Оставляем только первые два магазина
print(orders[:,:2])
# => [[ 7, 1],
# [ 4, 2],
# [ 3, 5],
# [ 8, 12],
# [15, 11],
# [21, 18],
# [25, 16]]
Задача 2. Оставить в рассмотрении данные продаж за выходные дни:
# Оставляем в рассмотрении данные продаж за выходные дни
print(orders[-2:])
# => [[21, 18, 17, 21],
# [25, 16, 25, 17]]
Задача 3. Сделать обратный порядок дней в таблице:
# Делаем обратный порядок дней в таблице
print(orders[::-1])
# => [[25, 16, 25, 17],
# [21, 18, 17, 21],
# [15, 11, 13, 9],
# [8, 12, 8, 7],
# [3, 5, 2, 3],
# [4, 2, 4, 5],
# [7, 1, 7, 8]]
Выводы
В этом уроке мы изучили основные методы работы с индексами массивов. Индексация, срезы и итеративный обход списков list
синтаксически похожи на операции над массивами numpy.ndarray
. Однако есть и некоторые особенности, которые позволяют упростить и ускорить работу с подвыборками элементов массивов Numpy.
Самостоятельная работа
Нажмите, чтобы увидеть тестовые данные
import numpy as np
clicks_values = [
[319, 265, 319, 328],
[292, 274, 292, 301],
[283, 301, 274, 283],
[328, 364, 328, 319],
[391, 355, 373, 337],
[445, 418, 409, 445],
[481, 400, 481, 409],
[333, 267, 333, 344],
[300, 278, 300, 311],
[289, 311, 278, 289],
[344, 388, 344, 333],
[421, 377, 399, 355],
[487, 454, 443, 487],
[531, 432, 531, 443],
[312, 264, 312, 320],
[288, 272, 288, 296],
[280, 296, 272, 280],
[320, 352, 320, 312],
[376, 344, 360, 328],
[424, 400, 392, 424],
[456, 384, 456, 392],
[347, 269, 347, 360],
[308, 282, 308, 321],
[295, 321, 282, 295],
[360, 412, 360, 347],
[451, 399, 425, 373],
[529, 490, 477, 529],
[581, 464, 581, 477]
]
clicks_values = np.array(clicks_values)
Данные, которые поступают для анализа, могут иметь достаточно большой объем. Чтобы получить подвыборку, аналитик обычно пишет программы и скрипты. Попробуем упростить работу аналитика с помощью правил индексирования массивов Numpy.
Представим, что в работу поступила таблица:
-
В каждой строке — количество кликов рекламного баннера с четырех площадок
-
Каждая строка — дни месяца с понедельника по воскресенье
Всего предоставлено четыре недели статистики, то есть в таблице 28 строк. Таблица представлена в виде списка списков целочисленных значений. В руках аналитика оказался Python-интерпретатор с предустановленной библиотекой Numpy. Поможем аналитику найти нужные статистические показатели. Рассмотрим процесс пошагово:
Шаг 1. Для работы со списком входных значений необходимо сконвертировать в нужный формат данных. Напишите функцию create_click_numbers_ndarray(clicks_values: np.ndarray)
, которая возвращает массив numpy.ndarray
. Если вы успешно справились с данным заданием в предыдущем уроке, то просто повторите код вашей функции, но убедитесь в корректности ее работы на списке списков.
Нажмите, чтобы увидеть ответ
def test(create_click_numbers_ndarray, clicks_values):
# shape
assert create_click_numbers_ndarray(clicks_values).shape == (28,4)
# type
assert create_click_numbers_ndarray(clicks_values).dtype == int
def create_click_numbers_ndarray(clicks_values: np.ndarray):
return np.array(
clicks_values
)
test(create_click_numbers_ndarray, clicks_values)
Шаг 2. Напишите функцию get_max_weekly(clicks_values: np.ndarray)
, которая возвращает список значений [max_in_week_1, max_in_week_2, max_in_week_3, max_in_week_4]
, где max_in_week_*
— максимальное значение кликов в соответствующей неделе. Постарайтесь использовать срезы данных.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = [481, 531, 456, 581]
assert capsys == expected
def get_max_weekly(clicks_values: np.ndarray):
return [
clicks_values[monday * 7 : monday * 7 + 7].max()
for monday in range(4)
]
capsys = get_max_weekly(clicks_values)
test(capsys)
Шаг 3. Напишите функцию get_min_weekly(clicks_values: np.ndarray)
, которая возвращает список значений [min_in_week_1, min_in_week_2, min_in_week_3, min_in_week_4]
, где min_in_week_*
— минимальное значение кликов в соответствующей неделе. Постарайтесь использовать срезы данных.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = [265, 267, 264, 269]
assert capsys == expected
def get_min_weekly(clicks_values: np.ndarray):
return [
clicks_values[monday * 7 : monday * 7 + 7].min()
for monday in range(4)
]
capsys = get_min_weekly(clicks_values)
test(capsys)
Шаг 4. Напишите функцию get_mean_monday(clicks_values: np.ndarray)
, которая возвращает среднее значение кликов среди всех точек только в разрезе понедельников. Постарайтесь использовать срезы данных.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = 315
assert round(capsys) == expected
def get_mean_monday(clicks_values: np.ndarray):
return clicks_values[::7].mean()
capsys = get_mean_monday(clicks_values)
test(capsys)
Шаг 5. Напишите функцию get_max_click(clicks_values: np.ndarray, col_number: int)
, которая возвращает максимальное значение кликов за весь период для площадки (столбца) под номером col_number
. Постарайтесь использовать срезы данных.
Нажмите, чтобы увидеть ответ
def test(get_max_click):
assert round(get_max_click(clicks_values, 0)) == 581
assert round(get_max_click(clicks_values, 1)) == 490
assert round(get_max_click(clicks_values, 2)) == 581
assert round(get_max_click(clicks_values, 3)) == 529
def get_max_click(clicks_values: np.ndarray, col_number: int):
return clicks_values[:,col_number].max()
test(get_max_click)
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.