Документация Engee
Notebook

Первичная обработка инженерных данных

Введение

Современная инженерия немыслима без обработки данных: показания датчиков, акустические сигналы диагностики, результаты измерений с испытательных стендов. Все эти сведения поступают в разнородных форматах — от простых текстовых файлов и таблиц Excel до специализированных бинарных форматов вроде MAT и WAV. Умение корректно прочитать, осмотреть, преобразовать и визуализировать такие данные составляет базовый навык инженера-исследователя и предшествует любому содержательному анализу.

Настоящая работа представляет собой практическое введение в обработку инженерных данных и сигналов в Engee. На сквозных примерах демонстрируется полный цикл работы с информацией: загрузка из распространённых форматов (CSV, XLSX, TXT, MAT, WAV, PNG), первичный осмотр и контроль типов, преобразование временных меток, визуализация временных рядов, спектральный анализ методом быстрого преобразования Фурье, сжатие сигнала до компактного набора признаков, а также переупаковка набора данных в требуемую структуру.

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

Присоединение библиотек

Подключим библиотеки для работы с данными, сигналами и визуализацией. StatsBase и Statistics обеспечат статистические функции. CSV, XLSX и MAT позволят читать данные из соответствующих форматов. WAV - для аудиофайлов. Images - для загрузки изображений. DSP и FFTW - для спектрального анализа.

In [ ]:
for pkg in (["Statistics", "StatsBase", "StatsPlots", "CSV", "DataFrames", "Dates", "XLSX", "MAT", "WAV", "Images", "FileIO", "DSP", "FFTW", "DelimitedFiles"])
    try
        eval(Meta.parse("using $pkg"))
    catch
        Pkg.add(pkg)
        eval(Meta.parse("using $pkg"))
    end
end
include("media_player.jl");

Чтение CSV-файла с данными энергопотребления

Начнём с самого распространённого формата инженерных данных - CSV. Прочитаем файл с показателями энергопотребления сталелитейного предприятия. Функция CSV.read возвращает DataFrame - табличную структуру, где столбцы доступны по именам. Сразу выведем размерность, чтобы понять масштаб данных.

In [ ]:
df_energy = CSV.read("Steel_Industry.csv", DataFrame)
println("Размерность данных: ", size(df_energy))
println("Имена столбцов: ", names(df_energy))
Размерность данных: (35040, 11)
Имена столбцов: ["date", "Usage_kWh", "Lagging_Current_Reactive_Power_kVarh", "Leading_Current_Reactive_Power_kVarh", "CO2_tCO2_", "Lagging_Current_Power_Factor", "Leading_Current_Power_Factor", "NSM", "WeekStatus", "Day_of_week", "Load_Type"]

Первичный осмотр данных

Первое правило инженера при получении данных - посмотреть на них глазами. Выведем первые и последние строки, типы столбцов. Это поможет обнаружить проблемы: пропуски, некорректные типы, неожиданные значения.

In [ ]:
println("Первые 10 строк:")
first(df_energy, 10)
Первые 10 строк:
Out[0]:
10×11 DataFrame
RowdateUsage_kWhLagging_Current_Reactive_Power_kVarhLeading_Current_Reactive_Power_kVarhCO2_tCO2_Lagging_Current_Power_FactorLeading_Current_Power_FactorNSMWeekStatusDay_of_weekLoad_Type
String31Float64Float64Float64Float64Float64Float64Int64String7String15String15
101/01/2018 00:153.172.950.00.073.21100.0900WeekdayMondayLight_Load
201/01/2018 00:304.04.460.00.066.77100.01800WeekdayMondayLight_Load
301/01/2018 00:453.243.280.00.070.28100.02700WeekdayMondayLight_Load
401/01/2018 01:003.313.560.00.068.09100.03600WeekdayMondayLight_Load
501/01/2018 01:153.824.50.00.064.72100.04500WeekdayMondayLight_Load
601/01/2018 01:303.283.560.00.067.76100.05400WeekdayMondayLight_Load
701/01/2018 01:453.64.140.00.065.62100.06300WeekdayMondayLight_Load
801/01/2018 02:003.64.280.00.064.37100.07200WeekdayMondayLight_Load
901/01/2018 02:153.283.640.00.066.94100.08100WeekdayMondayLight_Load
1001/01/2018 02:303.784.720.00.062.51100.09000WeekdayMondayLight_Load
In [ ]:
println("\nТипы данных столбцов:")
[names(df_energy) eltype.(eachcol(df_energy))]
Типы данных столбцов:
Out[0]:
11×2 Matrix{Any}:
 "date"                                  String31
 "Usage_kWh"                             Float64
 "Lagging_Current_Reactive_Power_kVarh"  Float64
 "Leading_Current_Reactive_Power_kVarh"  Float64
 "CO2_tCO2_"                             Float64
 "Lagging_Current_Power_Factor"          Float64
 "Leading_Current_Power_Factor"          Float64
 "NSM"                                   Int64
 "WeekStatus"                            String7
 "Day_of_week"                           String15
 "Load_Type"                             String15

Преобразование даты и времени

В инженерных данных временные метки часто хранятся как строки. Преобразуем столбец date в формат DateTime. После преобразования можем вычислить диапазон дат и средний шаг измерений. Добавим новый столбец DateTime в DataFrame.

In [ ]:
df_energy.DateTime = DateTime.(df_energy.date, dateformat"dd/mm/yyyy HH:MM")
println("Диапазон: ", minimum(df_energy.DateTime), " — ", maximum(df_energy.DateTime))

step_sec = mean([Dates.value(dt)/1000 for dt in diff(df_energy.DateTime)])
println("Средний шаг: ", round(step_sec, digits=2), " сек")
Диапазон: 2018-01-01T00:00:00 — 2018-12-31T23:45:00
Средний шаг: 897.53 сек

Визуализация временного ряда

График - лучший способ понять поведение системы. Построим график энергопотребления за весь период. По оси X - время, по Y - киловатт-часы. Видим цикличность и выбросы.

In [ ]:
plot(df_energy.DateTime, df_energy.Usage_kWh,
     xlabel="Время", ylabel="кВт·ч",
     title="Энергопотребление",
     linewidth=0.5, legend=false, size=(900, 350))
Out[0]:

Чтение данных из MAT-файла

Формат MATLAB (.mat) - стандарт в инженерной практике. В нём могут храниться сигналы, матрицы, структуры. Прочитаем файл с акустическими сигналами подшипника. Функция matread возвращает словарь, где ключи - имена переменных, значения - матрицы.

In [ ]:
подшипник = matread("Bearing.mat")
println("Ключи в файле: ", keys(подшипник))
println("Размер сигнала normal: ", size(подшипник["normal"]))
Ключи в файле: ["normal", "roller", "inner", "outer"]
Размер сигнала normal: (1, 120000)

Преобразование MAT-данных в вектор

Данные из MAT-файла хранятся как матрицы размером 1×N. Преобразуем их в одномерные векторы с помощью функции vec. Теперь это обычный массив чисел Julia, с которым можно выполнять математические операции.

In [ ]:
норма = vec(подшипник["normal"])
ролик = vec(подшипник["roller"])
println("Тип данных: ", typeof(норма))
println("Длина сигнала: ", length(норма))
println("\nПервые 5 отсчётов нормального сигнала: \n", норма[1:5])
Тип данных: Vector{Float64}
Длина сигнала: 120000

Первые 5 отсчётов нормального сигнала: 
[0.08866153846153846, 0.05862092307692307, -0.056951999999999996, -0.05862092307692307, -0.04985907692307692]

Чтение аудиофайла WAV

Акустические данные часто хранятся в аудиоформатах. Функция wavread возвращает массив отсчётов и частоту дискретизации. В отличие от MAT, здесь данные сразу представлены как вектор.

In [ ]:
сигнал_wav, fs_wav = WAV.wavread("Подшипник_1.wav")
println("Частота дискретизации: ", fs_wav, " Гц")
println("Длительность: ", round(length(сигнал_wav)/fs_wav, digits=2), " сек")
Частота дискретизации: 10000.0 Гц
Длительность: 12.0 сек

Воспроизведение аудиофайла в Engee

Воспроизведём акустический сигнал подшипника с помощью встроенного аудиоплеера. Для воспроизведения аудиофайла запустите данную кодовую ячейку, воспроизведение начнётся автоматически.

In [ ]:
media_player("$(@__DIR__)/Подшипник_1.wav", mode="audio")

Спектральный анализ через FFT

Быстрое преобразование Фурье переводит сигнал из временной области в частотную. Это ключевой инструмент анализа вибраций и акустики. Центрируем сигнал, применяем FFT, берём модуль для амплитудного спектра. Построим только первую половину (частоты от 0 до Найквиста).

In [ ]:
сигнал = норма[1:4000] .- mean(норма[1:4000])
спектр = abs.(fft(сигнал))
N = length(сигнал)
частоты = (0:N÷2-1) * 10000 / N

plot(частоты, спектр[1:N÷2],
     xlabel="Частота, Гц", ylabel="Амплитуда",
     title="Акустический спектр подшипника",
     linewidth=1, legend=false, size=(800, 350))
Out[0]:

Сжатие данных через FFT

Одна из практических задач - сжатие данных. Посмотрим на спектр и определим, в каком диапазоне сосредоточена основная энергия сигнала. Затем оставим только несколько ключевых частотных компонент - это и будет сжатое представление. Вместо 4000 отсчётов сигнала можно хранить 20 чисел.

In [ ]:
амплитуды = спектр[1:N÷2]
энергия_общая = sum(амплитуды.^2)
кум_энергия = cumsum(амплитуды.^2) / энергия_общая

plot(частоты, кум_энергия,
     xlabel="Частота, Гц", ylabel="Накопленная энергия",
     title="Кумулятивная энергия спектра",
     linewidth=2, legend=false, size=(800, 350))
hline!([0.95], linestyle=:dash, label="95% энергии")
Out[0]:

Выбор информативных частотных полос

Разобьём частотный диапазон на 20 равных полос и вычислим среднюю амплитуду в каждой. Это даст компактное представление сигнала - 20 чисел, которые можно хранить в таблице.

In [ ]:
кол_полос = 20
полосы = range(0, 5000, length=кол_полос+1)
признаки_fft = zeros(кол_полос)

for i in 1:кол_полос
    маска = (частоты .>= полосы[i]) .& (частоты .< полосы[i+1])
    if sum(маска) > 0
        признаки_fft[i] = mean(амплитуды[маска])
    end
end

bar(1:кол_полос, признаки_fft,
    xlabel="Номер полосы", ylabel="Средняя амплитуда",
    title="Сжатое представление сигнала (20 полос)",
    legend=false, size=(700, 300))
Out[0]:

Чтение изображения

Прочитаем файл с изображением. Отобразим тип изображения и его размер.

In [ ]:
изобр = Images.load("image.png")
println("Тип изображения: ", typeof(изобр))
println("Размер: ", size(изобр))
Тип изображения: Matrix{RGBA{N0f8}}
Размер: (954, 923)

Отобразим изображение.

In [ ]:
изобр
Out[0]:
No description has been provided for this image

Преобразование изображения в числовую матрицу

Преобразуем изображение в матрицу чисел.

In [ ]:
каналы = Float64.(Images.channelview(изобр))
каналы = permutedims(каналы, (2, 3, 1))
Out[0]:
954×923×4 Array{Float64, 3}:
[:, :, 1] =
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 ⋮                                       ⋱  ⋮                   
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078

[:, :, 2] =
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 ⋮                                       ⋱  ⋮                   
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078

[:, :, 3] =
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 ⋮                                       ⋱  ⋮                   
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078  …  0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078
 0.996078  0.996078  0.996078  0.996078     0.996078  0.996078  0.996078

[:, :, 4] =
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  …  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  …  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  …  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 ⋮                        ⋮              ⋱                      ⋮         
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  …  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0  …  1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0  1.0  1.0  1.0     1.0  1.0  1.0  1.0  1.0  1.0  1.0

Изображение представлено трёхмерным массивом 954×923×4. Первые два измерения это 954 строки и 923 столбца пикселей. Третье измерение — четыре цветовых канала: красный, зелёный, синий и альфа-канал прозрачности. Каждое значение находится в диапазоне от 0 до 1, где для цветовых каналов 0 означает отсутствие цвета, 1 — максимальную интенсивность, а для альфа-канала 1 означает полную непрозрачность.

Чтение текстового файла с данными

Текстовые файлы - простейший формат хранения. Прочитаем txt-файл с числовыми данными о температуре окружающей среды, определим количество значений, диапазон, и выведем первые 5 значений.

In [ ]:
температура = vec(readdlm("температура.txt"))
println("Количество значений: ", length(температура))
println("Диапазон: ", minimum(температура), " ... ", maximum(температура), " °C")
println("Первые 5 значений: ", температура[1:5])
Количество значений: 35040
Диапазон: -28.9 ... 37.4 °C
Первые 5 значений: [-12.3, -9.8, -9.7, -10.9, -13.4]

Чтение данных из Excel

Excel-файлы широко распространены в промышленности. Аналогично прочитаем xlsx-файл c данными о влажности окружающей среды.

In [ ]:
X = XLSX.readxlsx("влажность.xlsx")
лист = X[1]
влажность = Float64.(лист["A"][2:end])
println("Количество значений: ", length(влажность),)
println("Диапазон: ", minimum(влажность), " ... ", maximum(влажность), " %")
println("Первые 5 значений: ", влажность[1:5])
Количество значений: 35040
Диапазон: 32.8 ... 98.0 %
Первые 5 значений: [91.2, 82.8, 86.0, 88.8, 86.5]

Преобразование набора данных

Преобразуем набор данных, когда часть исходных столбцов не нужна, но требуется добавить новые данные из внешних источников. Удалим столбцы NSM, WeekStatus, Day_of_week и Load_Type, а затем добавим столбцы температуры и влажности, загруженные ранее.

In [ ]:
select!(df_energy, Not([:NSM, :WeekStatus, :Day_of_week, :Load_Type]))
println("Столбцы после удаления: \n\n", names(df_energy))
Столбцы после удаления: 

["date", "Usage_kWh", "Lagging_Current_Reactive_Power_kVarh", "Leading_Current_Reactive_Power_kVarh", "CO2_tCO2_", "Lagging_Current_Power_Factor", "Leading_Current_Power_Factor", "DateTime"]

Добавим ранее загруженные переменные температуры и влажности как новые столбцы. Количество строк должно совпадать.

In [ ]:
df_energy.Temperature_C = температура
df_energy.Humidity_Pct = влажность
Out[0]:
35040-element Vector{Float64}:
 91.2
 82.8
 86.0
 88.8
 86.5
 89.4
 85.1
 82.5
 89.5
 92.6
 91.1
 93.7
 84.4
  ⋮
 90.2
 85.4
 83.3
 94.1
 85.3
 88.5
 90.4
 88.5
 86.8
 91.0
 91.4
 95.4

Отобразим первые 10 строк обновлённого набора данных.

In [ ]:
println("Первые 10 строк:")
first(df_energy, 10)
Первые 10 строк:
Out[0]:
10×10 DataFrame
RowdateUsage_kWhLagging_Current_Reactive_Power_kVarhLeading_Current_Reactive_Power_kVarhCO2_tCO2_Lagging_Current_Power_FactorLeading_Current_Power_FactorDateTimeTemperature_CHumidity_Pct
String31Float64Float64Float64Float64Float64Float64DateTimeFloat64Float64
101/01/2018 00:153.172.950.00.073.21100.02018-01-01T00:15:00-12.391.2
201/01/2018 00:304.04.460.00.066.77100.02018-01-01T00:30:00-9.882.8
301/01/2018 00:453.243.280.00.070.28100.02018-01-01T00:45:00-9.786.0
401/01/2018 01:003.313.560.00.068.09100.02018-01-01T01:00:00-10.988.8
501/01/2018 01:153.824.50.00.064.72100.02018-01-01T01:15:00-13.486.5
601/01/2018 01:303.283.560.00.067.76100.02018-01-01T01:30:00-12.589.4
701/01/2018 01:453.64.140.00.065.62100.02018-01-01T01:45:00-14.085.1
801/01/2018 02:003.64.280.00.064.37100.02018-01-01T02:00:00-10.082.5
901/01/2018 02:153.283.640.00.066.94100.02018-01-01T02:15:00-11.489.5
1001/01/2018 02:303.784.720.00.062.51100.02018-01-01T02:30:00-10.392.6

Сохраним преобразованный набор данных в новый CSV-файл. Исходный файл Steel_Industry.csv остаётся без изменений - все преобразования сохранены в новом файле Сталелитейные_данные.csv.

In [ ]:
CSV.write("Сталелитейные_данные.csv", df_energy)
Out[0]:
"Сталелитейные_данные.csv"

Преобразование MAT-файла

Структура данных в MAT-файле также может быть представлена по-разному. Предположим, нам поступило 4 файла с данными подшипников с испытательного стенда:

  • H_1_0.mat – полностью исправный подшипник;
  • B_11_1.mat – неисправность роликового элемента;
  • I_1_1.mat – неисправность внутреннего кольца;
  • O_6_1.mat – неисправность внешнего кольца.

Но для модели анализа данных требуется иная структура – один MAT-файл с четырьмя ключами "normal", "roller", "inner", "outer", где:

normal – полностью исправный подшипник;

roller – неисправность роликового элемента;

inner – неисправность внутреннего кольца;

outer – неисправность внешнего кольца.

Загрузим первый файл.

In [ ]:
норм_тест = matread("H_1_0.mat")
Out[0]:
Dict{String, Any} with 1 entry:
  "H_1_0" => [-0.186744 0.078973 1819.0 400.0; 0.844698 0.078973 0.0 0.0; … ; -…

Данный файл имеет иную структуру данных, отличающуюся от требуемой. Отобразим данные.

In [ ]:
норм_тест = норм_тест["H_1_0"]
Out[0]:
420000×4 Matrix{Float64}:
 -0.186744   0.078973  1819.0  400.0
  0.844698   0.078973     0.0    0.0
  2.06954    0.078973     0.0    0.0
  3.35884    0.079631     0.0    0.0
  4.45475    0.078973     0.0    0.0
  5.77628    0.07996      0.0    0.0
  4.51921    0.080288     0.0    0.0
  1.84391    0.081275     0.0    0.0
 -1.63721    0.081275     0.0    0.0
 -4.53814    0.080617     0.0    0.0
 -5.66628    0.07996      0.0    0.0
 -7.8581     0.078316     0.0    0.0
 -8.18042    0.077658     0.0    0.0
  ⋮                            
  4.42251   -0.02985      0.0    0.0
  6.16307   -0.030508     0.0    0.0
  7.00112   -0.030837     0.0    0.0
  6.93666   -0.030837     0.0    0.0
  6.83996   -0.030508     0.0    0.0
  6.67879   -0.02985      0.0    0.0
  3.6167    -0.031166     0.0    0.0
  0.135582  -0.030837     0.0    0.0
 -1.25042   -0.028864     0.0    0.0
 -2.79758   -0.029522     0.0    0.0
 -4.05465   -0.028207     0.0    0.0
 -3.73233   -0.028864     0.0    0.0

Файл имеет один ключ "H_1_0, данные в котором является матрицей. Нам известно, что акустические данные расположены во втором столбце. Другие данные нам не нужны. Выполним извлечение этих данных.

In [ ]:
норм_тест = норм_тест[:,2]
Out[0]:
420000-element Vector{Float64}:
  0.078973
  0.078973
  0.078973
  0.079631
  0.078973
  0.07996
  0.080288
  0.081275
  0.081275
  0.080617
  0.07996
  0.078316
  0.077658
  ⋮
 -0.02985
 -0.030508
 -0.030837
 -0.030837
 -0.030508
 -0.02985
 -0.031166
 -0.030837
 -0.028864
 -0.029522
 -0.028207
 -0.028864

Откроем остальные MAT-файлы.

In [ ]:
ролик_тест = matread("B_11_1.mat")
внут_тест = matread("I_1_1.mat")
внеш_тест = matread("O_6_1.mat")
Out[0]:
Dict{String, Any} with 1 entry:
  "O_6_1" => [5.55065 0.047082 1818.0 400.0; 33.3351 0.038534 0.0 0.0; … ; 12.4…

Выполним извлечение матриц.

In [ ]:
ролик_тест = ролик_тест["B_11_1"]
внут_тест = внут_тест["I_1_1"]
внеш_тест = внеш_тест["O_6_1"]
Out[0]:
420000×4 Matrix{Float64}:
   5.55065    0.047082  1818.0  400.0
  33.3351     0.038534     0.0    0.0
  39.5883     0.033274     0.0    0.0
  43.843      0.038205     0.0    0.0
  52.578      0.033274     0.0    0.0
  31.6913     0.043466     0.0    0.0
   4.48698    0.030972     0.0    0.0
   6.03414    0.026698     0.0    0.0
 -22.6206     0.030315     0.0    0.0
 -23.781      0.042808     0.0    0.0
 -26.8431     0.023739     0.0    0.0
 -41.1866     0.03426      0.0    0.0
 -24.2322     0.018479     0.0    0.0
   ⋮                            
   8.90284   -0.018672     0.0    0.0
  -0.057814  -0.018343     0.0    0.0
 -13.3699    -0.017686     0.0    0.0
 -17.0121    -0.015713     0.0    0.0
 -17.6568    -0.021631     0.0    0.0
 -10.888     -0.018672     0.0    0.0
 -15.7551    -0.019987     0.0    0.0
  -2.44302   -0.013412     0.0    0.0
   8.93507   -0.009467     0.0    0.0
  24.7613    -0.00848      0.0    0.0
  12.4807    -0.016042     0.0    0.0
  15.5428    -0.012426     0.0    0.0

Далее аналогично выполним извлечение второго столбца из каждой матрицы.

In [ ]:
ролик_тест = ролик_тест[:,2]
внут_тест = внут_тест[:,2]
внеш_тест = внеш_тест[:,2]
Out[0]:
420000-element Vector{Float64}:
  0.047082
  0.038534
  0.033274
  0.038205
  0.033274
  0.043466
  0.030972
  0.026698
  0.030315
  0.042808
  0.023739
  0.03426
  0.018479
  ⋮
 -0.018672
 -0.018343
 -0.017686
 -0.015713
 -0.021631
 -0.018672
 -0.019987
 -0.013412
 -0.009467
 -0.00848
 -0.016042
 -0.012426

Теперь у нас есть четыре вектора с акустическими данными: норм_тест ролик_тест, внут_тест, внеш_тест. Их необходимо собрать в один MAT-файл обозначенной ранее структуры. Создадим словарь с нужными ключами.

In [ ]:
данные = Dict(
    "normal" => норм_тест,
    "roller" => ролик_тест,
    "inner"  => внут_тест,
    "outer"  => внеш_тест)
Out[0]:
Dict{String, Vector{Float64}} with 4 entries:
  "normal" => [0.078973, 0.078973, 0.078973, 0.079631, 0.078973, 0.07996, 0.080…
  "roller" => [0.029329, 0.025055, 0.023739, 0.025383, 0.027356, 0.029657, 0.03…
  "inner"  => [0.07437, 0.083576, 0.088179, 0.088508, 0.092453, 0.092453, 0.088…
  "outer"  => [0.047082, 0.038534, 0.033274, 0.038205, 0.033274, 0.043466, 0.03…

Сохраним эти данные в новый MAT-файл.

In [ ]:
matwrite("Подшипник_тест.mat", данные)

Прочитаем созданный файл.

In [ ]:
matread("Подшипник_тест.mat")
Out[0]:
Dict{String, Any} with 4 entries:
  "normal" => [0.078973, 0.078973, 0.078973, 0.079631, 0.078973, 0.07996, 0.080…
  "roller" => [0.029329, 0.025055, 0.023739, 0.025383, 0.027356, 0.029657, 0.03…
  "inner"  => [0.07437, 0.083576, 0.088179, 0.088508, 0.092453, 0.092453, 0.088…
  "outer"  => [0.047082, 0.038534, 0.033274, 0.038205, 0.033274, 0.043466, 0.03…

Структура созданного файла соответствуем требуемой. Данные готовы для дальнейшего использования.

Заключение

В ходе данной работы рассмотрен весь путь подготовки инженерных данных и сигналов к анализу. Показано, как единообразно загружать сведения из разнородных источников – табличных (CSV, XLSX), текстовых, бинарных (MAT), аудио (WAV) и графических форматов, и как приводить их к виду, пригодному для вычислений: векторам, матрицам и таблицам DataFrame. Продемонстрированы операции контроля структуры данных, преобразования временных меток, визуализации временных рядов, спектрального анализа через FFT и сжатия сигнала до компактного набора информативных признаков.

Отдельное внимание уделено преобразованию наборов данных: объединению таблиц из внешних источников, удалению избыточных столбцов и переупаковке разрозненных MAT-файлов в единую структуру с заданными ключами. Эти операции отражают типичные задачи инженерной практики, где формат хранения данных не совпадает с форматом, требуемым моделью или методикой анализа. Представленные приёмы образуют основу для последующих этапов – статистической обработки, построения диагностических моделей и машинного обучения.