Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Применение математических и статистических функций Python: Numpy-массивы

Numpy

Библиотека Numpy используется в модулях для работы с различными типами данных:

  • Научными данными - SciPy
  • Табличными данными — Pandas
  • Визуализацией данных — Matplotlib, Plotly, Seaborn
  • Алгоритмами машинного обучения — Sklearn
  • Тензорами и глубокими нейронными сетями — TensorFlow
  • Изображениями — OpenCV

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

Рассмотрим недельные данные продаж в сети четырех магазинов:

# Продажи за неделю по 4 магазинам
orders =  [
    [7, 1, 7, 8],
    [4, 2, 4, 5],
    [3, 5, 2, 3],
    [8, 12, 8, 7],
    [15, 11, 13, 9],
    [21, 86, 35, 16],
    [70, 90, 124, 94]
]
orders = np.array(orders)

Одна из базовых задач аналитика — знакомство с данными. Типичный подход к этому вопросу — это оценка суммы заказов во всей сети и в каждом магазине по отдельности:

# Сложение всех элементов
print(orders.sum())
# => 670

# Сумма продаж по магазинам
print(orders.sum(axis = 0))
# => [128 207 193 142]

Кроме интегральных показателей, для оценки полезно понимать разброс значений от минимального до максимального. Мы можем обнаружить крайние значения и использовать эти данные, чтобы найти ошибки и расследовать инцидентов в работе сети:

# Минимальное значение продаж
print(orders.min())
# => 1

# Максимальное значение продаж
print(orders.max())
# => 124

# Максимальное значение продаж в первом магазине
print(orders[:, 0].max())
# => 70

# Индекс (день недели) максимального элемента в первом магазине
print(orders[:, 0].argmax())
# => 6

По абсолютным цифрам не всегда получается проверить гипотезы о состоянии сети. Для этого удобно смотреть на показатели относительно общего числа продаж:

# Деление всех продаж на суммарное значение за всю неделю по сети
print(orders / orders.sum())
# => [[0.01044776 0.00149254 0.01044776 0.0119403 ]
# [0.00597015 0.00298507 0.00597015 0.00746269]
# [0.00447761 0.00746269 0.00298507 0.00447761]
# [0.0119403  0.01791045 0.0119403  0.01044776]
# [0.02238806 0.01641791 0.01940299 0.01343284]
# [0.03134328 0.12835821 0.05223881 0.0238806 ]
# [0.10447761 0.13432836 0.18507463 0.14029851]]

Раньше в аналитике использовались статические показатели, которые не показывают динамику изменений во времени. А теперь для оценки прироста заказов по дням можно использовать кумулятивные суммы и конечные разности.

В примере ниже показана сумма для первого магазина. Каждое следующее значение равно сумме предыдущего и количеству продаж в этот день:

# Кумулятивная сумма для первого магазина
print(orders[:, 0].cumsum())
# => [  7  11  14  22  37  58 128]

# Кумулятивная сумма для последнего магазина
print(orders[:, -1].cumsum())
# => [  8  13  16  23  32  48 142]

В случае разности для всех магазинов каждая строка представляет собой поэлементную разность показателей соседних дней:

# Конечная разность между продажами по дням
print(np.diff(orders, axis=0))
# => [[-3  1 -3 -3]
# [-1  3 -2 -2]
# [ 5  7  6  4]
# [ 7 -1  5  2]
# [ 6 75 22  7]
# [49  4 89 78]]

Фиксированная точность в расчетах требует выполнить один из двух вариантов:

  • Округлить полученные показатели до целых значений сверху, снизу или к ближайшему
  • Оставить определенное количество знаков после запятой

Все эти подходы реализованы в Numpy. Ниже в примере рассмотрим приемы округления для относительных показателей продаж в магазинах. В качестве относительной величины берем среднее по всей сети:

# Заказы относительно среднего значения
relative_orders = orders / orders.mean()
print(relative_orders)
# => [[0.29253731 0.04179104 0.29253731 0.33432836]
#  [0.16716418 0.08358209 0.16716418 0.20895522]
#  [0.12537313 0.20895522 0.08358209 0.12537313]
#  [0.33432836 0.50149254 0.33432836 0.29253731]
#  [0.62686567 0.45970149 0.54328358 0.3761194 ]
#  [0.87761194 3.59402985 1.46268657 0.66865672]
#  [2.92537313 3.76119403 5.18208955 3.92835821]]

# Округление до целых
print(relative_orders.round())
# => [[0. 0. 0. 0.]
#  [0. 0. 0. 0.]
#  [0. 0. 0. 0.]
#  [0. 1. 0. 0.]
#  [1. 0. 1. 0.]
#  [1. 4. 1. 1.]
#  [3. 4. 5. 4.]]

# Округление до заданного количества знаков после запятой
print(relative_orders.round(2))
# => [[0.29 0.04 0.29 0.33]
#  [0.17 0.08 0.17 0.21]
#  [0.13 0.21 0.08 0.13]
#  [0.33 0.5  0.33 0.29]
#  [0.63 0.46 0.54 0.38]
#  [0.88 3.59 1.46 0.67]
#  [2.93 3.76 5.18 3.93]]

# Округление до ближайшего сверху целого
print(np.ceil(relative_orders))
# => [[0. 0. 0. 0.]
#  [0. 0. 0. 0.]
#  [0. 0. 0. 0.]
#  [0. 1. 0. 0.]
#  [1. 0. 1. 0.]
#  [1. 4. 1. 1.]
#  [3. 4. 5. 4.]]

На продажи в магазинах могут влиять разные факторы:

  • Выходные, предпраздничные и праздничные дни
  • Акции
  • Логистические задержки
  • Погода

Все они по-разному отражаются на продажах. Если факторы плановые, то их последствия можно точно предугадать. А вот если они имеют вероятностную природу, то точность существенно падает.

В этих случаях используют статистические методы. Они позволяют оценить влияние факторов на продажи. Аналитики сперва находят статистические показатели:

  • Среднее — какое число продаж можно ожидать, если взять случайный день недели и магазин
  • Отклонение от среднего — насколько сильно реальные показатели могут отличаться от среднего
  • Медиана — значение продаж, в сравнение с которым ровно половина продаж — меньше, а другая половина — больше

Посмотрим, как находить такие показатели:

# Среднее значение продаж
print(orders.mean())
# => 23.93

# Стандартное отклонение от среднего
print(orders.std())
# => 33.64

# Медианное значение
print(np.median(orders))
# => 8.0

Более детальный анализ продаж может потребовать использования персентилей. Персентиль N% показывает значение продаж, относительно которого N% продаж меньше. Например, персентиль 50% дает значение продаж, относительно которого в данных ровно 50% значений меньше его. Оно совпадает с медианным значением. Часто используют персентили 25%, 50% и 75%:

# Персентиль 25% — ниже этого показателя лежит 25% всех продаж
print(np.percentile(orders, 25))
# => 4.75

# Персентиль 50% — медианное значение
print(np.percentile(orders, 50))
# => 8.0

# Персентиль 75% — ниже этого показателя лежит 75% всех продаж
print(np.percentile(orders, 75))
# => 17.25

На разные магазины случайные факторы могут влиять по-разному. Мы можем находить магазины с похожими закономерностями, для этого мы прибегаем к корреляционному анализу. Если корреляция близка по модулю к 1, то факторы влияют одинаково. Если она близка к 0, то факторы влияют по-разному.

Ниже рассмотрим примеры корреляции для продаж магазинов. Используемая функция возвращает корреляции в виде матрицы — тем самым она помогает найти корреляции для нескольких массивов. В случае с двумя массивами нас интересует значение в первой строке и втором столбце:

# Корелляция заказов первого и второго магазинов
print(np.corrcoef(orders[:, 0], orders[:, 1]))
# => [[1.         0.80868227]
#  [0.80868227 1.        ]]

# Корелляция заказов первого и последнего магазинов
print(np.corrcoef(orders[:, 0], orders[:, 3]))
# [[1.         0.98744122]
#  [0.98744122 1.        ]]

Выводы

В этом уроке мы рассмотрели математические и статистические функции библиотеки Numpy, которые используют при решении аналитических задач. На практических примерах мы увидели правила и приемы их использования.

Эти знания упрощают работу с библиотеками, использующими Numpy. На это есть несколько причин:

  • Интерфейсы многих функций используют принципы построения функций в Numpy
  • В других библиотеках структуры данных — это Numpy-масcивы
  • Методы структур данных можно дополнить методами массивов Numpy с небольшими доработками

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

Нажмите, чтобы увидеть тестовые данные
clicks_values =  [
    [319, 265, 319, 328],
    [292, 274, 292, 301],
    [283, 301, 274, 283],
    [328, 364, 328, 12],
    [391, 355, 373, 337],
    [445, 418, 409, 445],
    [481, 400, 481, 409],
    [86, 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, 14, 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, 423],
    [360, 412, 360, 347],
    [189, 399, 425, 373],
    [529, 490, 477, 529],
    [581, 464, 581, 477]
]

clicks_values = np.array(clicks_values)
clicks_values

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

Шаг 1. Напишите функцию get_stat_values(clicks_values: np.ndarray), которая возвращает кортеж статистических показателей (среднее, стандартное отклонение, медиану).

Нажмите, чтобы увидеть ответ
def test(stat_values):
    expected_value = np.array((355, 93, 344))
    # array_equal
    assert np.array_equal(expected_value, np.array(stat_values, dtype=int))

def get_stat_values(clicks_values):
	return clicks_values.mean(), clicks_values.std(), np.median(clicks_values)

stat_values = get_stat_values(clicks_values)
test(stat_values)

Шаг 2. Напишите функцию get_min_value_position(clicks_values: np.ndarray), которая возвращает позицию минимального элемента в массиве. В этом вам поможет функция unravel_index(). Также стоит помнить, что функция np.argmin() возвращает целое число. Оно обозначает, сколько элементов слева направо и сверху вниз надо пройти по массиву, чтобы найти минимальный элемент.

Нажмите, чтобы увидеть ответ
def test(min_value_position):
    expected_value = np.array((3, 3))
    # array_equal
    assert np.array_equal(expected_value, np.array(min_value_position, dtype=int))

def get_min_value_position(clicks_values):
    return np.unravel_index(np.argmin(clicks_values), clicks_values.shape)

min_value_position = get_min_value_position(clicks_values)
test(stat_values)

Шаг 3. Иногда нам нужно понять, в каком интервале сконцентрированы 50% всех центральных значений. Этот интервал называется интерквартильным размахом и строится следующим образом:

  • Левая граница — персентиль 25%

  • Правая граница — персентиль 75%

Напишите функцию get_iqr(clicks_values: np.ndarray), которая возвращает кортеж из левой и правой границ интерквартильного размаха.

Нажмите, чтобы увидеть ответ
def test(iqr):
    expected_value = np.array((299.0, 413.5))
    # array_equal
    assert np.array_equal(expected_value, np.array(iqr))

def get_iqr(clicks_values):
    return np.percentile(clicks_values, 25), np.percentile(clicks_values, 75)

iqr = get_iqr(clicks_values)
test(iqr)

Шаг 4. Напишите функцию get_corr(array1, array2), которая возвращает значение корелляции для двух заданных одномерных массивов.

Нажмите, чтобы увидеть ответ
def test(corr):
    expected_value = 0.54
    # array_equal
    assert np.isclose(expected_value, corr, atol=1e-02)

def get_corr(array1, array2):
    return np.corrcoef(array1, array2)[0,1]

corr = get_corr(clicks_values[:, 0], clicks_values[:, -1])
test(corr)

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

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

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

Об обучении на Хекслете

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

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

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

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

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

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

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

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 5 025 ₽ в месяц
новый
Сбор, анализ и интерпретация данных
9 месяцев
с нуля
Старт 25 апреля

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

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

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

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

Задавайте вопросы, если хотите обсудить теорию или упражнения. Команда поддержки Хекслета и опытные участники сообщества помогут найти ответы и решить задачу