Библиотека Numpy дает мощный и удобный высокоуровневый аппарат для работы с многомерными данными. Для работы с ними в Numpy разработана своя собственная структура данных — массив numpy.ndarray
. Именно под эту структуру оптимизирована работа всего функционала библиотеки.
В этом уроке познакомимся с тем, как создавать массив ndarray из стандартных типов данных языка Python и попробуем на практике решить ряд простых аналитических задач.
Структура данных библиотеки Numpy
Чтобы создать структуру numpy.ndarray
, нужно конвертировать список list
. Для конвертации из множества set
требуется дополнительное приведение типа данных.
Рассмотрим на таком примере:
# Импортируем библиотеку numpy с псевдонимом np
import numpy as np
# Создаем простой пример списка языка Python
simple_list = [1, 2, 3, 4, 5]
# Конвертируем созданный список в массив Numpy
my_first_ndarray = np.array(simple_list, dtype=int)
# Тип созданного объекта
print(type(my_first_ndarray))
# => <class 'numpy.ndarray'>
# Результат стандартного вывода
print(my_first_ndarray)
# => [1 2 3 4 5]
А теперь разберем этот код подробнее. Сам пример показывает встроенную функциональность для создания структуры numpy.ndarray
. Мы импортируем библиотеку Numpy, создаем короткий список значений simple_list
, а затем конвертируем в массив my_first_ndarray
. Для этого вызываем конструктор np.array()
с объектами для конвертации.
С учетом примера выше, обратная конвертация в список происходит так:
print(ndarray_from_list.tolist())
Конвертация из списка Python — это самая популярная операция, с помощью которой создается структура numpy.ndarray
.
Так происходит потому, что обмен данными между функциями и сервисами удобно производить в стандартных структурах данных языка. Другими словами, можно не вводить структуры данных сторонних библиотек и не усложнять программу.
Но при разработке сложных программ модуль numpy.ndarray
может быть только частью общей структуры. В таких случаях используют стандартные типы данных языка для обмена данными между функциональными частями программ.
В итоге порядок работы с данными при работе с Numpy выглядит следующим образом:
Как правило, вычислительные и аналитические модули в виде входных данных ожидают списки значений. Всю оптимизацию они делают уже внутри себя в собственных абстракциях, невидимых извне. Это сделано для простоты интеграции.
Допустимые типы данных
Поговорим подробнее о типах элементов массива, которые можно использовать для numpy.ndarray
. Продолжим работать с тем же примером и воспользуемся следующим методом:
# Проверяем тип полученного массива
print(my_first_ndarray.dtype.type)
# => <class 'numpy.int64'>
Как и ожидалось, тип данных — int64. Необязательно ограничиваться только им:
# Целочисленный массив
print(np.array([1, 2, 3], dtype=int).dtype.type)
# => <class 'numpy.int64'>
# Массив строк
print(np.array([1, 2, 3], dtype=str).dtype.type)
# => <class 'numpy.str_'>
# Массив чисел с плавающей запятой
print(np.array([1, 2, 3], dtype=float).dtype.type)
# => <class 'numpy.float64'>
Обратите внимание, что для экземпляра структуры numpy.ndarray
нельзя использовать сразу несколько типов данных. Проще говоря, все элементы в массиве должны быть однотипные. Посмотрим, как конструктор сам определит тип данных при конвертации:
# Все элементы списка целочисленные
print(np.array([1, 2, 3]).dtype.type)
# => <class 'numpy.int64'>
# Все элементы списка — это строки
print(np.array(['1', '2', '3']).dtype.type)
# => <class 'numpy.str_'>
# Элементы списка как текстовые, так и целочисленные
print(np.array(['1', 2, 3]).dtype.type)
# => <class 'numpy.str_'>
Заметим, что ошибки при конвертации смешанного типа элементов массива не произошло. Конвертор просто привел все данные к строковому типу.
Как Numpy работает на практике
Функциональность библиотеки Numpy настолько интуитивна, что уже сейчас можно решить простую аналитическую задачку.
Представим продажи ноутбуков в магазине за одну неделю:
День | Магазин №1 |
---|---|
0 | 7 |
1 | 4 |
2 | 3 |
3 | 8 |
4 | 15 |
5 | 21 |
6 | 25 |
На практике такие данные обычно хранятся в табличном виде в базе данных. Чтобы упростить пример, мы пропустили этап выгрузки — подразумевается, что данные приходят в вычислительный модуль уже в виде списка значений.
Поработаем с данными с помощью библиотеки Numpy:
# Импортируем библиотеку numpy с псевдонимом np
import numpy as np
# Создаем список продаж — представим, что считали его из базы данных
orders_list = [7, 4, 3, 8, 15, 21, 25]
# Конвертируем созданный список в массив Numpy
orders_ndarray = np.array(orders_list, dtype=int)
# Тип созданного объекта
print(type(orders_ndarray))
# => <class 'numpy.ndarray'>
# Результат стандартного вывода
print(orders_ndarray)
# => [ 7 4 3 8 15 21 25]
Попробуем найти день недели с самыми низкими продажами. Опыт работы с Python подсказывает, что метод будет называться min()
или minimum()
. Найдем минимальное количество продаж и заодно день недели, в который оно совершено:
# Находим минимальный элемент массива
print(orders_ndarray.min())
# => 3
# Находим порядковый номер минимального элемента массива
print(orders_ndarray.argmin())
# => 2
Чтобы найти наибольшее количество продаж, достаточно поменять одну функцию:
# Находим максимальный элемент массива
print(orders_ndarray.max())
# => 25
На практике часто анализ не ограничивается только одной неделей продаж и одним магазином. В этом случае набор данных представлен в виде списка списков элементов — это уже двумерная структура, которая в математике называется матрицей.
В Numpy реализация инициализации массивов и функций работы с ними не зависит от размерности данных, что существенно упрощает разработку.
В современных библиотеках можно применять одну и ту же функцию к различным типам данных. Рассмотрим это на примере, похожем на предыдущий. Найдем день с самыми низкими доходами во всей сети магазинов. Рассмотрим недельные продажи в четырех магазинах:
День | Магазин №1 | Магазин №2 | Магазин №3 | Магазин №4 |
---|---|---|---|---|
0 | 7 | 1 | 7 | 8 |
1 | 4 | 2 | 4 | 5 |
2 | 3 | 5 | 2 | 3 |
3 | 8 | 12 | 8 | 7 |
4 | 15 | 11 | 13 | 9 |
5 | 21 | 18 | 17 | 21 |
6 | 25 | 16 | 25 | 17 |
Мы ожидаем, что функционально все должно быть реализовано похожим образом. Давайте в этом убедимся, взглянув на код:
# Импортируем библиотеку numpy с псевдонимом np
import numpy as np
# Создаем «список списков продаж»
orders_list = [
[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_ndarray = np.array(orders_list, dtype=int)
# Описываем тип созданного объекта
print(type(orders_ndarray))
# => <class 'numpy.ndarray'>
# Находим минимальный элемент массива
print(orders_ndarray.min())
# => 1
В приведенном примере метод min()
находит минимальный элемент среди всех значений массива.
Большинство функций в Numpy реализованы так, что методы и функции выполняют одинаковые операции, вне зависимости от типа данных на входе.
В программировании такой подход называется полиморфизмом. Он упрощает разработку и делает код более простым для анализа и поддержки.
Выводы
Сегодня мы познакомились с основной структурой данных библиотеки Numpy — массивом numpy.ndarray
.
В работе с ним мы используем указанный тип данных — это обусловлено оптимизацией работы функций внутри библиотеки. У Numpy понятный интерфейс для конвертации типа данных list
из стандартной библиотеки Python.
Новые знания мы сразу закрепили на практической задаче — вычислили день с самыми низкими доходами в сети магазинов. Эта задача показывает, насколько Numpy упрощает вычисления работу с входными данными разной размерности.
Самостоятельная работа
Нажмите, чтобы увидеть тестовые данные
import random
random.seed(42)
min_num = 1200
max_num = 2500
input_click_numbers = [random.randrange(min_num, max_num) for _ in range(365)]
Первичный статистический анализ данных — одна из самых частых задач для аналитика. Представим, что в работу поступил список ежедневных кликов сайта компании, который был собран за 365 дней. В руках аналитика оказался Python-интерпретатор с предустановленной библиотекой быстрых вычислений Numpy. Поможем аналитику найти нужные статистические показатели и ключевые значения. Пройдем этот процесс по шагам:
Шаг 1. Для любого инструмента важна его версия, чтобы ориентироваться на актуальную документацию и не вызывать конфликтов с другими библиотеками. Импортируйте модуль numpy с псевдонимом np и выведите его версию.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = '1.0.0'
assert np.__version__ >= expected
import numpy as np
capsys = np.__version__
test(capsys)
Шаг 2. Чтобы работать со списком входных значений в нашем вычислительном модуле, нужно сконвертировать список в нужный формат данных. Напишите функцию create_click_numbers_ndarray()
, которая возвращает массив numpy.ndarray
, созданный из списка целочисленных значений.
Нажмите, чтобы увидеть ответ
def test(create_click_numbers_ndarray, input_click_numbers):
# shape
assert create_click_numbers_ndarray(input_click_numbers).shape[0] == 365
# type
assert create_click_numbers_ndarray(input_click_numbers).dtype == int
def create_click_numbers_ndarray(input_click_numbers):
return np.array(
input_click_numbers
)
test(create_click_numbers_ndarray, input_click_numbers)
Шаг 3. Найдите размах выборки — разницу между максимальным и минимальным элементами. Эта простая характеристика позволяет оценить величину разброса значений кликов на сайт.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = 1297
assert round(capsys) == expected
capsys = create_click_numbers_ndarray(input_click_numbers).max() - create_click_numbers_ndarray(input_click_numbers).min()
test(capsys)
Шаг 4. Размах выборки станет более информативным, если мы сравним его со средним значением элементов. Он покажет, насколько сильно могут отклоняться клики на сайт от своего среднего значения.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = 1829
assert round(capsys) == expected
capsys = create_click_numbers_ndarray(input_click_numbers).mean()
test(capsys)
Шаг 5. Если общая статистическая картина понятна аналитику, то для других сотрудников компании будет интереснее другое. Они захотят разобраться с конкретными крайними случаями в значениях данных, дать им бизнес оценку и обоснование. Найдите день в году, в который было совершено наибольшее количество кликов.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = 362
assert capsys == expected
capsys = create_click_numbers_ndarray(input_click_numbers).argmax()
test(capsys)
Шаг 6. Найдите день в году, в который было совершено наименьшее количество кликов.
Нажмите, чтобы увидеть ответ
def test(capsys):
expected = 156
assert capsys == expected
capsys = create_click_numbers_ndarray(input_click_numbers).argmin()
test(capsys)
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.