Исследовательский анализ и объяснение данных
Инженерный анализ телеметрических данных сталелитейного предприятия
Введение
Любое промышленное предприятие ежедневно генерирует огромные объёмы данных: показания счётчиков, параметры процессов, метеосводки, данные о выбросах. Инженерная ценность этих данных появляется в тот момент, когда мы ищем в них закономерности: как связаны параметры, какие режимы работы существуют, есть ли скрытые циклы для оптимизации.
В этой работе мы последовательно проанализируем телеметрические данные сталелитейного предприятия за 2018 год. Набор содержит 35 040 записей с 15-минутным интервалом: потребление активной и реактивной энергии, выбросы CO₂, коэффициент мощности, температуру и влажность. Мы применим весь инструменты инженерного аналитика: описательные статистики, корреляции, визуализацию, разбиение на сезоны и уровни загрузки, метод главных компонент, кластеризацию и спектральный анализ. Цель не просто построить графики, а понять, как работает предприятие и где скрыты резервы эффективности.
Каждый шаг сопровождается инженерной интерпретацией. Мы увидим, что высокая корреляция между потреблением и выбросами CO₂ позволяет косвенно контролировать экологические показатели. Обнаружим три устойчивых режима работы с разной эффективностью. Найдём суточные и недельные циклы, важные при планировании нагрузки. Анализ коэффициента мощности покажет, где энергия теряется впустую. В итоге получим целостную картину производственной системы, пригодную для принятия инженерных решений.
Подключение библиотек
Подключим библиотеки, необходимые для исследовательского анализа данных. Каждая из них решает свою задачу:
-
Statistics и StatsBase – базовые статистические функции: среднее, стандартное отклонение, квантили, корреляции;
-
StatsPlots и Plots – построение графиков: гистограмм, диаграмм рассеяния, тепловых карт, коробчатых диаграмм;
-
CSV и DataFrames – чтение и обработка табличных данных;
-
Dates – работа с датами и временем, что важно при анализе временных рядов;
-
Clustering – алгоритмы кластеризации, в частности k-средних;
-
MultivariateStats – метод главных компонент для снижения размерности;
-
HypothesisTests – статистические критерии для проверки гипотез;
-
FFTW – быстрое преобразование Фурье для спектрального анализа;
-
Random – воспроизводимость результатов при случайных операциях.
for pkg in (["Statistics", "StatsBase", "StatsPlots", "CSV", "DataFrames", "Dates", "Clustering", "MultivariateStats", "HypothesisTests", "FFTW", "Random"])
try
eval(Meta.parse("using $pkg"))
catch
Pkg.add(pkg)
eval(Meta.parse("using $pkg"))
end
end
Загрузка набора данных
Загрузим CSV-файл, с телеметрическими данными сталелитейного предприятия. Он содержит 35040 записей с данными энергопотребления, температуры и влажности за 2018 год.
df = CSV.read("Сталелитейные_данные.csv", DataFrame)
println("Размерность: ", size(df))
println("Столбцы: ", names(df))
Преобразование даты
Создадим столбец DateTime для временного анализа.
df.DateTime = DateTime.(df.date, dateformat"dd/mm/yyyy HH:MM")
println("Диапазон: ", minimum(df.DateTime), " - ", maximum(df.DateTime))
Описательная статистика
Первый шаг любого анализа – получить сводку по всем числовым переменным. Функция describe покажет среднее, минимум, максимум, стандартное отклонение и квартили. Это позволяет быстро оценить разброс параметров и обнаружить аномалии.
числовые = [:Usage_kWh, :Lagging_Current_Reactive_Power_kVarh,
:Leading_Current_Reactive_Power_kVarh, :CO2_tCO2_,
:Lagging_Current_Power_Factor, :Leading_Current_Power_Factor,
:Temperature_C, :Humidity_Pct]
describe(df[:, числовые], :all)
Корреляционная матрица
Построим тепловую карту корреляций между всеми числовыми параметрами. Это ключевой инструмент исследовательского анализа: мы сразу видим, какие параметры связаны друг с другом. Например, ожидаем высокую корреляцию между потреблением энергии и выбросами CO₂.
corr_matrix = cor(Matrix(df[:, числовые]))
heatmap(corr_matrix,
xticks=(1:length(числовые), string.(числовые)),
yticks=(1:length(числовые), string.(числовые)),
xrotation=45, size=(700, 600),
title="Корреляционная матрица параметров")
Числовые значения корреляций
Выведем числовые значения корреляций для более точной оценки. Отсортируем пары параметров по убыванию абсолютной величины корреляции.
println("Топ-10 корреляций:\n")
корреляции = Float64[]
пары = String[]
for i in 1:length(числовые)
for j in (i+1):length(числовые)
push!(корреляции, corr_matrix[i, j])
push!(пары, "$(числовые[i]) - $(числовые[j])")
end
end
idx = sortperm(abs.(корреляции), rev=true)
for k in 1:10
println(" $(пары[idx[k]]): $(round(корреляции[idx[k]], digits=3))")
end
График рассеяния с цветовым кодированием
Построим диаграмму рассеяния для двух наиболее связанных параметров. Добавим цветовое кодирование по температуре. Это позволяет увидеть, как третья переменная влияет на взаимосвязь двух других.
scatter(df.Usage_kWh[1:1000], df.CO2_tCO2_[1:1000],
marker_z=df.Temperature_C[1:1000],
xlabel="Энергопотребление, кВт·ч", ylabel="Выбросы CO₂",
title="Энергопотребление и выбросы СO₂",
color=:thermal, legend=:bottomright, size=(700, 500))
Анализ по сезонам
Добавим столбец с сезоном года на основе месяца. Затем сравним среднее потребление по сезонам. Это типичный инженерный анализ: разбить данные на группы и сравнить статистики.
df.Season = [["Зима", "Зима", "Весна", "Весна", "Весна", "Лето",
"Лето", "Лето", "Осень", "Осень", "Осень", "Зима"][month(dt)]
for dt in df.DateTime]
season_stats = combine(groupby(df, :Season),
:Usage_kWh => mean => :avg_usage,
:Usage_kWh => std => :std_usage,
:Temperature_C => mean => :avg_temp)
println("Статистика по сезонам:")
season_stats
Визуализация по сезонам
Построим диаграмму (boxplot) энергопотребления по сезонам. Диаграмма показывает медиану, квартили и выбросы - это даёт более полную картину распределения, чем просто среднее.
boxplot(df.Season, df.Usage_kWh,
xlabel="Сезон", ylabel="Энергопотребление, кВт·ч",
title="Распределение энергопотребления по сезонам",
legend=false, size=(700, 450))
Анализ по времени суток
Выделим час суток из DateTime и проанализируем суточный профиль потребления. Это важно для понимания режимов работы предприятия.
df.Hour = hour.(df.DateTime)
hourly_stats = combine(groupby(df, :Hour),
:Usage_kWh => mean => :avg_usage,
:Usage_kWh => std => :std_usage)
plot(hourly_stats.Hour, hourly_stats.avg_usage,
ribbon=hourly_stats.std_usage,
xlabel="Час суток", ylabel="Среднее потребление, кВт·ч",
title="Суточный профиль энергопотребления",
legend=false, linewidth=2, size=(700, 400))
Добавление признаков загрузки
Используем данные об энергопотреблении для классификации уровня загрузки. Разделим на три категории по квартилям: низкая, средняя и высокая загрузка.
q33, q67 = quantile(df.Usage_kWh, [0.33, 0.67])
df.LoadLevel = [x <= q33 ? "Низкая" : x <= q67 ? "Средняя" : "Высокая"
for x in df.Usage_kWh]
println("Границы: низкая < $(round(q33, digits=1)), средняя < $(round(q67, digits=1)), высокая")
println("Распределение по уровням:")
combine(groupby(df, :LoadLevel), nrow => :count)
Многомерный анализ: PCA
Применим метод главных компонент (Principal Component Analysis) для снижения размерности. Это позволит увидеть структуру данных в двумерном пространстве и обнаружить скрытые закономерности. PCA находит направления максимального разброса данных.
pca_data = Matrix(df[:, числовые])
pca_data = (pca_data .- mean(pca_data, dims=1)) ./ std(pca_data, dims=1)
pca_model = fit(PCA, pca_data', maxoutdim=2)
проекция = MultivariateStats.transform(pca_model, pca_data')
общая_var = tvar(pca_model)
объясн_var = principalvar(pca_model, 2)
println("Объяснённая дисперсия: ", round.(объясн_var / общая_var, digits=3))
scatter(проекция[1, 1:2000], проекция[2, 1:2000],
xlabel="PC1", ylabel="PC2",
title="PCA энергопотребления (первые 2000 точек)",
alpha=0.5, legend=false, size=(600, 450))
Кластеризация режимов работы
Применим алгоритм k-средних для выделения типичных режимов работы предприятия. Это метод автоматической группировки данных без учителя. Попробуем выделить 3 кластера и посмотрим, совпадают ли они с уровнями загрузки.
k = 3
cluster_result = kmeans(pca_data', k)
df.Cluster = cluster_result.assignments
println("Размеры кластеров:\n")
for i in 1:k
println(" Кластер $i: $(sum(df.Cluster .== i)) точек")
end
Визуализация кластеров
Отобразим кластеры на PCA-проекции разными цветами. Это покажет, насколько хорошо алгоритм разделил данные.
scatter(проекция[1, 1:2000], проекция[2, 1:2000],
group=df.Cluster[1:2000],
xlabel="PC1", ylabel="PC2",
title="Кластеры режимов работы",
legend=false,
alpha=0.5, size=(600, 450))
Характеристики кластеров
Для каждого кластера вычислим средние значения ключевых параметров. Это позволит интерпретировать кластеры: например, кластер с высоким энергопотреблением и высокой температурой может соответствовать дневной смене летом.
cluster_stats = combine(groupby(df, :Cluster),
:Usage_kWh => mean => :avg_usage,
:Temperature_C => mean => :avg_temp,
:Humidity_Pct => mean => :avg_humidity,
:Lagging_Current_Power_Factor => mean => :avg_pf)
println("Характеристики кластеров:")
cluster_stats
Проверка гипотезы: будни и выходные
Добавим признак дня недели и проверим статистическую гипотезу: отличается ли энергопотребление в будние и выходные дни. Используем t-критерий Стьюдента для независимых выборок.
df.DayOfWeek = dayofweek.(df.DateTime)
df.IsWeekend = df.DayOfWeek .>= 6
будни = df.Usage_kWh[.!df.IsWeekend]
выходные = df.Usage_kWh[df.IsWeekend]
println("Среднее в будни: ", round(mean(будни), digits=2))
println("Среднее в выходные: ", round(mean(выходные), digits=2))
println("t-тест: ", UnequalVarianceTTest(будни, выходные))
Реактивная мощность и коэффициент мощности
Проанализируем коэффициент мощности - важный показатель эффективности энергопотребления. Низкий коэффициент мощности означает, что предприятие потребляет много реактивной энергии, за которую могут начисляться штрафы.
histogram(df.Lagging_Current_Power_Factor, bins=40,
xlabel="Коэффициент мощности, %", ylabel="Частота",
title="Распределение коэффициента мощности",
legend=false, size=(600, 400))
vline!([mean(df.Lagging_Current_Power_Factor)],
linewidth=2, linestyle=:dash, label="Среднее")
Спектральный анализ временного ряда
Применим FFT к временному ряду потребления, чтобы найти периодические составляющие. Это поможет выявить суточные, недельные и сезонные циклы.
signal = df.Usage_kWh[1:4096] .- mean(df.Usage_kWh[1:4096])
spectrum = abs.(fft(signal))
freqs = (0:2047) * (1/0.25) / 4096 * 24 # циклов в сутки
plot(freqs[2:100], spectrum[2:100],
xlabel="Частота, циклов/сутки", ylabel="Амплитуда",
title="Спектр энергопотребления",
linewidth=1.5, legend=false, size=(700, 400))
vline!([1.0], linestyle=:dash, label="Суточный цикл")
vline!([1/7], linestyle=:dot, label="Недельный цикл")
Сохранение результатов анализа
Сохраним обработанный DataFrame со всеми добавленными признаками (сезон, час, уровень загрузки, кластер) в новый CSV-файл.
CSV.write("Новые_данные.csv", df)
println("Столбцы: ", names(df))
Заключение
Мы провели сквозной исследовательский анализ телеметрических данных сталелитейного предприятия, пройдя путь от исходной таблицы до содержательных инженерных выводов. Данные содержат богатую внутреннюю структуру, и мы научились извлекать её последовательно.
Корреляционный анализ выявил сильнейшую связь между энергопотреблением и выбросами CO₂, что позволяет косвенно контролировать экологию по данным счётчиков. Анализ по сезонам показал рост потребления зимой из-за отопительной нагрузки. Суточный профиль выявил двухпиковый режим с утренним подъёмом и вечерним спадом. Разбиение на уровни загрузки дало три равномерные группы наблюдений.
Метод главных компонент показал структуру данных в двумерном пространстве, а кластеризация выделила три устойчивых режима: высокой загрузки, низкой загрузки с плохой эффективностью и низкой загрузки с отличной эффективностью. Спектральный анализ подтвердил суточный и недельный циклы, а также двухсменный график. Гистограмма коэффициента мощности с двумя пиками дополнила картину.
Все выводы получены без сложных моделей машинного обучения, только статистикой и визуализацией. Полученные закономерности могут стать основой для выбора признаков, постановки задач прогнозирования и разработки систем мониторинга.