Отсутствующие данные в Julia¶
Введение¶
Работа с отсутствующими данными - обычная задача при предварительной обработке данных. Хотя иногда отсутствующие значения указывают на значимое событие в данных, они часто представляют собой ненадежные или непригодные для использования точки данных. В любом случае Julia имеет множество возможностей для работы с отсутствующими данными.
Создание отсутствующих данных¶
Отсутствующие данные в Julia можно представить в нескольких формах. Например, значение NaN
(NaN64
) (не число)
x_64 = [NaN, 8, 15, 16, 23, 42]
println("Type of $(x_64[1]): ", typeof(x_64[1]))
x_64 = [NaN64, 8, 15, 16, 23, 42]
print("Type of $(x_64[1]): ", typeof(x_64[1]))
Значение NaN
при необходимости может быть задано как число с плавающей точкой меньшей разрядности:
x_32 = [NaN32, 8, 15, 16, 23, 42]
x_16 = [NaN16, 8, 15, 16, 23, 42]
Говоря о сути такой формы представления отсутствующих данных, можно заключить, что это конкретные значения типа данных с плавающей точкой, которые имеют возможность распространяться в вычислениях. Зачастую такие значения появляются в результате неопределённых операций:
x_nan = Inf*0
Как правило, в Julia значения NaN
используются для распространения в вычислениях неопределённости расчёта числовых значений. Для работы именно с отсутствующими данными корректнее будет использовать специальный объект missing
, который также распространяется в вычислениях. Такая форма представления является единственным экземпляром типа Missing
:
typeof(missing)
Это означает, в частности, что массивы, содержащие missing
среди других значений, неоднородны по типу:
x_missing = [missing, 8, 15, 16, 23, 42]
Для работы с missing
может пригодиться библиотека Missings.jl. Так, при помощи неё можно создавать массивы с такими объектами:
import Pkg; Pkg.add("Missings") # загрузка библиотеки
using Missings # подключение библиотеки
# создание массивов с отсутствующими значениями:
# массивы типа Missing
@show missings(1)
@show missings(3)
@show missings(3,1)
# массив объединения типов
@show missings(Int,3,3);
Систематизация отсутствующих данных¶
При сортировке массивов с отсутствующими данными необходимо учитывать, что объект missing
считается больше любого объекта, с которым он сравнивается:
isless(Inf, missing)
Поэтому, при сортировке пропущенные значения будут автоматически отделены, и будут находиться в конце возрастающей сортировки. В случае, если при сортировке требуется изменить порядок включения отсутствующих значений, достаточно применить атрибут lt = missingsmallest
:
sort(x_missing, rev=true, lt = missingsmallest)
Нормализация отсутствующих данных¶
Для рассмотрения примеров работы с пропусками в табличных данных подключим библиотеки DataFrames.jl и Statistics.jl.
Pkg.add(["DataFrames", "Statistics"])
using DataFrames, Statistics
Создадим таблицу тестовых данных для обработки:
df_missing = DataFrame(
имя = ["NULL", "Коля", "Юра", "Миша"],
возраст = [16, NaN, missing, 15],
рост = [171, 162, 999, 165],
)
При работе с данными разных форматов, объединенными в одну таблицу, могут возникнуть случаи, когда отсутствующие данные принимают различные значения. Для стандартизации значений отсутствующих данных удобно использовать функцию declaremissings()
из библиотеки Impute.jl:
Pkg.add("Impute")
using Impute
df_missing = Impute.declaremissings(df_missing; values=(NaN, 999, "NULL"))
Теперь, как мы видим, отсутствующие данные приведены к одному виду - объекту missing
.
Поиск отсутствующих данных¶
Для определения, является ли значение NaN
, удобно воспользоваться функцией isnan()
isnan.(x_64)
Существует и аналогичная функция для определения объектов missing
.
ismissing.(x_missing)
Определим расположение отсутствующих значений в таблице:
df_mask = ismissing.(df_missing)
Часто объекты missing
могут вызывать проблемы при обработке данных. Для этого их можно пропустить, исключить или заменить.
Пропуск отсутствующих данных¶
Для фильтрации вектора от значений NaN
достаточно воспользоваться функцией filter()
базовой библиотеки Julia.
filter(!isnan, x_64)
В случае фильтрации массива с объектом missing
тип результирующего массива не изменится. Для приведения типа массива к типу отфильтрованных значений можно воспользоваться функцией disallowmissing()
библиотеки Missings.jl.
@show x = filter(!ismissing, x_missing)
disallowmissing(x)
Следующая строка кода показывает, как отфильтровать missing
из табличных данных с помощью функции filter()
.
filter(:имя => !ismissing, df_missing)
Аналогичный результат даёт применение функции skipmissing()
базовой библиотеки:
collect(skipmissing(x_missing))
Для пропуска missing
в табличных данных в библиотеке DataFrames.jl есть более удобная функция - dropmissing()
:
dropmissing(df_missing)
Для того, чтобы при её помощи отфильтровать строки с missing
, содержащимися в конкретном столбце, вторым аргументом этой функции необходимо передать имя этого столбца:
dropmissing(df_missing, :имя)
В этом случае функция dropmissing()
возвращает новую таблицу. Если для решаемой задачи не нужно сохранять исходную таблицу, можно удалить строки с отсутствующими данными при помощи dropmissing!()
.
Замена отсутствующих данных¶
В случае необходимости замены отсутствующих данных в массиве удобно воспользоваться функцией Missings.replace()
:
рост = collect(Missings.replace(df_missing.рост, 170))
Для замены отсутствующих данных в таблицах можно воспользоваться функцией replace!()
:
replace!(df_missing.возраст, missing => 15)
Нужно заметить, что формат данных результирующего столбца не изменяется. Другой способ - применение функции coalesce()
:
df_missing.имя = coalesce.(df_missing.имя, "Ваня")
df_missing.рост = coalesce.(df_missing.рост, mean(skipmissing(df_missing.рост)))
df_missing
Заключение¶
В этом примере были рассмотрены способы создания, систематизации, нормализации, поиска, пропуска и замены отсутствующих данных в Julia.