Запуск серии мини-проектов
Я создаю серию мини-проектов. Я реализовал проекты «Личные привычки» и «Анализ погоды». Но у меня не было возможности полностью изучить возможности NumPy. Я хочу понять, почему NumPy так полезен в анализе данных. Чтобы завершить эту серию, я собираюсь продемонстрировать это в режиме реального времени.
Для наглядности я буду использовать вымышленного клиента или компанию. В этом случае нашим клиентом будет EnviroTech Dynamics, глобальный оператор промышленных сенсорных сетей.
В настоящее время EnviroTech полагается на устаревшие скрипты на Python с циклами для обработки более 1 миллиона показаний датчиков ежедневно. Этот процесс мучительно медленный, задерживает принятие важных решений по техническому обслуживанию и влияет на эффективность работы. Им нужно современное высокопроизводительное решение.
Мне поручили создать доказательство концепции на основе NumPy, чтобы продемонстрировать, как ускорить их конвейер обработки данных.
Набор данных: смоделированные показания датчиков
Чтобы доказать концепцию, я буду работать с большим смоделированным набором данных, сгенерированным с помощью модуля random в NumPy, с записями, содержащими следующие ключевые массивы:
- Температура — каждая точка данных показывает, насколько горячей работает машина или компонент системы. Эти показания могут быстро помочь нам определить, когда машина начинает перегреваться — признак возможного сбоя, неэффективности или угрозы безопасности.
- Давление — данные, показывающие, какое давление нарастает внутри системы, и находится ли оно в безопасном диапазоне.
- Коды состояния — представляют состояние работоспособности каждой машины или системы в данный момент. 0 (нормально), 1 (предупреждение), 2 (критическое), 3 (неисправно/отсутствует).
Цели проекта
Основная цель — предоставить четыре чёткие векторизованные решения проблем EnviroTech с данными, демонстрирующие скорость и мощность. Я покажу все эти решения:
- Производительность и эффективность — бенчмарк.
- Основополагающая статистическая база.
- Критическое обнаружение аномалий.
- Очистка данных и вменение.
К концу этой статьи вы должны будете полностью понять NumPy и его полезность в анализе данных.
Задача 1: производительность и эффективность
Сначала нам нужен массив данных, чтобы разница в скорости была очевидна. Я буду использовать 1 000 000 показаний температуры, которые мы планировали ранее.
import numpy as np
NUM_READINGS = 1_000_000
np.random.seed(42)
mean_temp = 45.0
std_dev_temp = 12.0
temperature_data = np.random.normal(loc=mean_temp, scale=std_dev_temp, size=NUM_READINGS)
print(f"Data array size: {temperature_data.size} elements")
print(f"First 5 temperatures: {temperature_data[:5]}")
Вывод:
Data array size: 1000000 elements
First 5 temperatures: [50.96056984 43.34082839 52.77226246 63.27635828 42.1901595 ]
Теперь, когда у нас есть записи, давайте проверим эффективность NumPy.
Предположим, мы хотим вычислить среднее значение всех этих элементов с помощью стандартного цикла Python. Это будет выглядеть примерно так:
def calculate_mean_loop(data):
total = 0
count = 0
for value in data:
total += value
count += 1
return total / count
loop_mean = calculate_mean_loop(temperature_data)
print(f"Mean (Loop method): {loop_mean:.4f}")
В этом методе нет ничего плохого. Но он довольно медленный, потому что компьютеру приходится обрабатывать каждое число по одному, постоянно перемещаясь между интерпретатором Python и процессором.
Чтобы по-настоящему продемонстрировать скорость, я буду использовать команду %timeit. Она запускает код сотни раз, чтобы получить надёжное среднее время выполнения.
print("--- Timing the Python Loop ---")
%timeit -n 10 -r 5 calculate_mean_loop(temperature_data)
Вывод:
--- Timing the Python Loop ---
244 ms ± 51.5 ms per loop (mean ± std. dev. of 5 runs, 10 loops each)
Используя -n 10, я запускаю код в цикле 10 раз (чтобы получить стабильное среднее значение), а используя -r 5, весь процесс повторяется 5 раз (для ещё большей стабильности).
Теперь давайте сравним это с векторизацией NumPy. Векторизация означает, что вся операция (в данном случае среднее значение) будет выполняться над всем массивом сразу, используя высокооптимизированный код C в фоновом режиме.
Вот как будет вычисляться среднее значение с помощью NumPy:
def calculate_mean_numpy(data):
return np.mean(data)
numpy_mean = calculate_mean_numpy(temperature_data)
print(f"Mean (NumPy method): {numpy_mean:.4f}")
Вывод:
Mean (NumPy method): 44.9808
Теперь давайте замерим время.
print("--- Timing the NumPy Vectorization ---")
%timeit -n 10 -r 5 calculate_mean_numpy(temperature_data)
Вывод:
--- Timing the NumPy Vectorization ---
1.49 ms ± 114 μs per loop (mean ± std. dev. of 5 runs, 10 loops each)
Вот огромная разница. Это как почти несуществующее. Это сила векторизации.
Давайте представим эту разницу в скорости клиенту:
«Мы сравнили два метода выполнения одних и тех же вычислений на одном миллионе показаний температуры — традиционный цикл Python и векторизованную операцию NumPy.
Разница была разительной: чистый цикл Python занимал около 244 миллисекунд на запуск, в то время как версия NumPy выполнила ту же задачу всего за 1,49 миллисекунды.
Это примерно 160-кратное улучшение скорости».
Задача 2: базовая статистическая база
Ещё одна интересная функция, которую предлагает NumPy, — это возможность выполнять базовые и продвинутые статистические операции. Это позволяет получить хороший обзор того, что происходит в вашем наборе данных. NumPy предлагает такие операции, как:
np.mean()— для вычисления среднего значения;np.median— среднее значение данных;np.std()— показывает, насколько ваши числа отличаются от среднего;np.percentile()— сообщает вам значение, ниже которого попадает определённый процент ваших данных.
Теперь, когда мы смогли предоставить альтернативное и эффективное решение для извлечения и выполнения сводок и вычислений для их огромного набора данных, мы можем начать экспериментировать с ним.
Мы уже смогли сгенерировать смоделированные данные о температуре. Давайте сделаем то же самое для давления. Расчёт давления — отличный способ продемонстрировать способность NumPy обрабатывать несколько массивов данных за считанные секунды.
Для нашего клиента это также позволяет мне продемонстрировать проверку работоспособности их промышленных систем.
np.random.seed(43)
pressure_data = np.random.uniform(low=100.0, high=500.0, size=1_000_000)
print("Data arrays ready.")
Вывод:
Data arrays ready.
Задача 3: выявление критических аномалий
Одна из моих любимых функций NumPy — это возможность быстро выявлять и фильтровать аномалии в вашем наборе данных. Чтобы продемонстрировать это, наш вымышленный клиент EnviroTech Dynamics предоставил нам ещё один полезный массив, содержащий коды состояния системы. Это показывает, как машина работает последовательно. Это просто диапазон кодов (0–3).
- 0 → нормально;
- 1 → предупреждение;
- 2 → критично;
- 3 → ошибка датчика.
Они получают миллионы показаний в день, и наша задача — найти каждую машину, которая находится в критическом состоянии и работает опасно горячей.
Ранее мы сгенерировали наши данные о температуре и давлении. Давайте сделаем то же самое для кодов состояния.
import numpy as np
np.random.seed(42)
status_codes = np.random.choice(
a=[0, 1, 2, 3],
size=len(temperature_data),
p=[0.85, 0.10, 0.03, 0.02]
)
print(status_codes[:5])
Вывод:
[0 2 0 0 0]
У каждого показания температуры теперь есть соответствующий код состояния. Это позволяет нам точно определить, какие датчики сообщают о проблемах и насколько они серьёзны.
Далее нам понадобятся какие-то пороговые значения или критерии аномалий. В большинстве сценариев всё, что выше среднего + 3 × стандартное отклонение, считается серьёзным выбросом, который вы не хотите видеть в своей системе.
temp_mean = np.mean(temperature_data)
temp_std = np.std(temperature_data)
SEVERITY_THRESHOLD = temp_mean + (3 * temp_std)
print(f"Severe Outlier Threshold: {SEVERITY_THRESHOLD:.2f}°C")
Вывод:
Severe Outlier Threshold: 80.99°C
Далее мы создадим два фильтра (маски) для выделения данных, соответствующих нашим условиям. Один для показаний, где состояние системы является критическим (код 2), а другой для показаний, где температура превышает пороговое значение.
critical_status_mask = (status_codes == 2)
high_temp_outlier_mask = (temperature_data > SEVERITY_THRESHOLD)
print(f"Critical status readings: {critical_status_mask.sum()}")
print(f"High-temp outliers: {high_temp_outlier_mask.sum()}")
Задача 4: очистка данных и вменение
Наконец, NumPy позволяет вам избавиться от несоответствий и данных, которые не имеют смысла. Вы могли столкнуться с концепцией очистки данных в анализе данных. В Python NumPy и Pandas часто используются для оптимизации этой деятельности.
Чтобы продемонстрировать это, наши status_codes содержат записи со значением 3 (неисправно/отсутствует). Если мы будем использовать эти неисправные показания температуры в нашем общем анализе, они исказят наши результаты. Решение состоит в том, чтобы заменить неисправные показания статистически обоснованным оценочным значением.
Первый шаг — выяснить, какое значение мы должны использовать для замены плохих данных. Медиана всегда является отличным выбором, потому что, в отличие от среднего, она меньше подвержена влиянию экстремальных значений.
valid_data_mask = (status_codes != 3)
valid_median_temp = np.median(temperature_data[valid_data_mask])
print(f"Median of all valid readings: {valid_median_temp:.2f}°C")
Вывод:
Median of all valid readings: 44.99°C
Теперь мы выполним некоторую условную замену с помощью мощной функции np.where(). Вот типичная структура функции.
np.where(Condition, Value_if_True, Value_if_False)
В нашем случае:
- Условие: Является ли код состояния 3 (неисправно/отсутствует)?
- Значение, если True: Используйте рассчитанное
valid_median_temp. - Значение, если False: Сохраните исходное показание температуры.
cleaned_temperature_data = np.where(
status_codes == 3,
valid_median_temp,
temperature_data
)
imputed_count = (status_codes == 3).sum()
print(f"Total Faulty readings imputed: {imputed_count}")
Вывод:
Total Faulty readings imputed: 20102
Теперь давайте проверим исправление, проверив медиану как для исходных, так и для очищенных данных.
print(f"\nOriginal Median: {np.median(temperature_data):.2f}°C")
print(f"Cleaned Median: {np.median(cleaned_temperature_data):.2f}°C")
Вывод:
Original Median: 44.99°C
Cleaned Median: 44.99°C
В этом случае даже после очистки более 20 000 неисправных записей медиана температуры осталась на уровне 44,99 °C, что указывает на то, что набор данных статистически обоснован и сбалансирован.
Заключение
И вот мы здесь! Мы начали этот проект, чтобы решить критическую проблему для EnviroTech Dynamics: необходимость более быстрого анализа данных без использования циклов. Мощность массивов NumPy и векторизации позволила нам решить проблему и подготовить аналитический конвейер к будущему.
NumPy ndarray — это тихий двигатель всей экосистемы Python для науки о данных. Каждая крупная библиотека, такая как Pandas, scikit-learn, TensorFlow и PyTorch, использует массивы NumPy в своей основе для быстрых численных вычислений.
Освоив NumPy, вы заложили мощную аналитическую основу. Следующим логическим шагом для меня будет переход от одномерных массивов к структурированному анализу с помощью библиотеки Pandas, которая организует массивы NumPy в таблицы (DataFrames) для ещё более удобной маркировки и обработки.
Спасибо за чтение! Не стесняйтесь связываться со мной:
- Medium;
- LinkedIn;
- Twitter;
- YouTube.
