Зарегистрируйтесь, чтобы продолжить обучение

Индексы, срезы и итеративный обход Python: Numpy-массивы

Numpy

Массивы данных в 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.


Самостоятельная работа

Данные, которые поступают для анализа, могут иметь достаточно большой объем. Чтобы получить подвыборку, аналитик обычно пишет программы и скрипты. Попробуем упростить работу аналитика с помощью правил индексирования массивов Numpy.

Представим, что в работу поступила таблица click_values:

  • В каждой строке — количество кликов рекламного баннера с четырех площадок

  • Каждая строка — дни месяца с понедельника по воскресенье

clicks_values = [
    [319, 265, 319, 328],
    [292, 274, 292, 301],
    [283, 301, 274, 283],
    ...
]

Всего предоставлено четыре недели статистики, то есть в таблице 28 строк. Таблица представлена в виде списка списков целочисленных значений. В руках аналитика оказался Python-интерпретатор с предустановленной библиотекой Numpy. Поможем аналитику найти нужные статистические показатели. Рассмотрим процесс пошагово:

Шаг 1. Для работы со списком входных значений необходимо сконвертировать в нужный формат данных. Напишите функцию create_click_numbers_ndarray(), которая принимает таблицу click_values и возвращает массив numpy.ndarray.

Нажмите, чтобы увидеть ответ
    def create_click_numbers_ndarray(clicks_values):
        return np.array(clicks_values)

Шаг 2. Напишите функцию get_max_weekly(clicks_values), которая возвращает список значений [max_in_week_1, max_in_week_2, max_in_week_3, max_in_week_4], где max_in_week_* — максимальное значение кликов в соответствующей неделе. Постарайтесь использовать срезы данных.

Нажмите, чтобы увидеть ответ
    def get_max_weekly(clicks_values):
        return [
            clicks_values[monday * 7 : monday * 7 + 7].max()
            for monday in range(4)
        ]

Шаг 3. Напишите функцию get_min_weekly(clicks_values), которая возвращает список значений [min_in_week_1, min_in_week_2, min_in_week_3, min_in_week_4], где min_in_week_* — минимальное значение кликов в соответствующей неделе. Постарайтесь использовать срезы данных.

Нажмите, чтобы увидеть ответ
   def get_min_weekly(clicks_values):
        return [
            clicks_values[monday * 7 : monday * 7 + 7].min()
            for monday in range(4)
        ]

Шаг 4. Напишите функцию get_mean_monday(clicks_values), которая возвращает среднее значение кликов среди всех точек только в разрезе понедельников. Постарайтесь использовать срезы данных.

Нажмите, чтобы увидеть ответ
    def get_mean_monday(clicks_values):
        return clicks_values[::7].mean()

Дополнительные материалы

  1. Учебное пособие по NumPy

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
новый
Google таблицы, SQL, Python, Superset, Tableau, Pandas, визуализация данных, Anaconda, Jupyter Notebook, A/B-тесты, ROI
9 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»