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

Метаданные

Проектирование поддержки метаданных

Пакет DataFrames.jl позволяет сохранять и извлекать метаданные на уровне таблиц и столбцов. Эта возможно благодаря функциям, определенным в интерфейсе DataAPI.jl:

Кроме того, полезным может оказаться пакет TableMetadataTools.jl. В нем определен ряд удобных функций для выполнения стандартных операций с метаданными.

Предположим, мы работаем с объектом df наподобие фрейма данных, в котором есть столбец col (обращаться к которому можно по символу Symbol, строке или целочисленному индексу).

Метаданные на уровне таблицы — это пары «ключ-значение», связанные с df. Метаданные на уровне таблицы — это пары «ключ-значение», связанные с определенным столбцом col во фрейме данных df.

Чтобы проверить, есть ли в метаданных на уровне таблицы фрейма данных df некоторый ключ key, можно использовать выражение key in metadatakeys(df). Аналогичным образом, чтобы проверить, есть ли в метаданных на уровне столбца col фрейма данных df ключ key, используйте выражение key in colmetadatakeys(df, col).

Помимо этого, с каждой парой «ключ-значение» в метаданных связана информация о стиле. В DataFrames.jl стиль метаданных влияет на то, как распространяются метаданные при преобразовании df. Поддерживаются следующие стили метаданных:

  • :default: метаданные в этом стиле считаются привязанными к конкретному состоянию df. Это означает, что любая операция с данным фреймом данных делает такие метаданные недействительными и в результате операции они удаляются. Обратите внимание, что это происходит даже в том случае, если операция в итоге не изменяет фрейм данных. То есть такие метаданные удаляются при вызове функции, которая может изменить фрейм данных. Благодаря этому можно статически определить, удаляются ли метаданные стилей, отличных от :note, после вызова функции. Лишь две функции предполагают сохранение метаданных не в стиле :note, так как эти операции специально предназначены для создания идентичной копии исходного фрейма данных:

    • конструктор DataFrame;

    • copy для фрейма данных.

  • :note: метаданные в этом стиле считаются аннотацией таблицы или столбца, которая должна распространяться при преобразованиях (правила распространения таких метаданных описываются ниже).

  • Другие стили метаданных допускаются, но в настоящее время приравниваются к стилю :default (в будущем ситуация может измениться, если будут определены другие стандартные стили метаданных).

Все функции метаданных в DataAPI.jl работают с объектами DataFrame, SubDataFrame, DataFrameRow и объектами, возвращаемыми функциями eachrow и eachcol. В этом разделе эти объекты будут в совокупности называться объектами наподобие фреймов данных, и на них распространяются следующие правила:

  • Объекты, возвращаемые функциями eachrow и eachcol, имеют те же метаданные, что и родительский объект AbstractDataFrame.

  • SubDataFrame and DataFrameRow only expose metadata from

родительского объекта DataFrame в стиле :note.

Примечательно, что метаданные не поддерживаются для GroupedDataFrame, и их нельзя добавлять, изменять или просматривать посредством самого объекта GroupedDataFrame. Это можно делать только посредством его родительского объекта (parent).

Пакет DataFrames.jl позволяет пользователям извлекать столбцы фрейма данных и выполнять с ними операции. Такие операции не влияют на метаданные. Поэтому, даже если есть метаданные в стиле :default, они могут неправильно описывать содержимое столбца, если пользователь изменил столбцы напрямую.

Принципы проектирования метаданных, относящиеся к DataFrames.jl

DataFrames.jl поддерживает хранение любого объекта в виде значений метаданных. Однако в качестве значений метаданных рекомендуется использовать строки, так как некоторые форматы хранения, например Apache Arrow, поддерживают только строки.

Для всех функций, работающих с метаданными на уровне столбцов, выдается ошибка ArgumentError, если переданный столбец отсутствует во фрейме данных.

При использовании функции metadata! или colmetadata! для добавления метаданных в SubDataFrame или DataFrameRow действуют следующие правила.

  • При использовании метаданных не в стиле :note выдается ошибка.

  • При попытке добавить пару «ключ-значение», для ключа которой в родительском фрейме данных уже имеется сопоставление со стилем, отличным от :note, выдается ошибка.

Пакет DataFrames.jl спроектирован так, что при отсутствии метаданных во фрейме данных накладные расходы в плане производительности в связи с их поддержкой отсутствуют. Поэтому, если вам нужна максимальная производительность операций, не касающихся метаданных, перед их выполнением вызовите emptymetadata! и emptycolmetadata!.

Обработка метаданных для SubDataFrame и DataFrameRow происходит с большими накладными расходами, чем для других типов с поддержкой метаданных, определенных в DataFrames.jl, потому что логика такой обработки более сложная (они поддерживают метаданные только в стиле :note, а это означает, что другие метаданные необходимо отфильтровать).

Примеры

Вот простой пример работы с метаданными в DataFrames.jl.

julia> using DataFrames

julia> df = DataFrame(name=["Jan Krzysztof Duda", "Jan Krzysztof Duda",
                            "Radosław Wojtaszek", "Radosław Wojtaszek"],
                      date=["2022-Jun", "2021-Jun", "2022-Jun", "2021-Jun"],
                      rating=[2750, 2729, 2708, 2687])
4×3 DataFrame
 Row │ name                date      rating
     │ String              String    Int64
─────┼──────────────────────────────────────
   1 │ Jan Krzysztof Duda  2022-Jun    2750
   2 │ Jan Krzysztof Duda  2021-Jun    2729
   3 │ Radosław Wojtaszek  2022-Jun    2708
   4 │ Radosław Wojtaszek  2021-Jun    2687

julia> metadatakeys(df)
()

julia> metadata!(df, "caption", "ELO ratings of chess players", style=:note);

julia> collect(metadatakeys(df))
1-element Vector{String}:
 "caption"

julia> "caption" in metadatakeys(df)
true

julia> metadata(df, "caption")
"ELO ratings of chess players"

julia> metadata(df, "caption", style=true)
("ELO ratings of chess players", :note)

julia> emptymetadata!(df);

julia> metadatakeys(df)
()

julia> colmetadatakeys(df)
()

julia> colmetadata!(df, :name, "label", "First and last name of a player", style=:note);

julia> colmetadata!(df, :date, "label", "Rating date in yyyy-u format", style=:note);

julia> colmetadata!(df, :rating, "label", "ELO rating in classical time control", style=:note);

julia> "label" in colmetadatakeys(df, :rating)
true

julia> colmetadata(df, :rating, "label")
"ELO rating in classical time control"

julia> colmetadata(df, :rating, "label", style=true)
("ELO rating in classical time control", :note)

julia> collect(colmetadatakeys(df))
3-element Vector{Pair{Symbol, Base.KeySet{String, Dict{String, Tuple{Any, Any}}}}}:
   :date => ["label"]
 :rating => ["label"]
   :name => ["label"]

julia> [only(names(df, col)) =>
        [key => colmetadata(df, col, key) for key in metakeys] for
        (col, metakeys) in colmetadatakeys(df)]
3-element Vector{Pair{String, Vector{Pair{String, String}}}}:
   "date" => ["label" => "Rating date in yyyy-u format"]
 "rating" => ["label" => "ELO rating in classical time control"]
   "name" => ["label" => "First and last name of a player"]

julia> emptycolmetadata!(df);

julia> colmetadatakeys(df)
()

Распространение метаданных в стиле :note

Важной особенностью метаданных в стиле :note является то, как они обрабатываются при преобразовании фреймов данных.

Представленные правила могут немного измениться в будущем. Изменения правил распространения метаданных в стиле :note не будут считаться критическими и могут вводиться в дополнительных версиях DataFrames.jl. Такие изменения могут вноситься на основании отзывов пользователей о том, какие правила распространения метаданных наиболее удобны на практике.

Ниже представлены наиболее общие правила распространения метаданных в стиле :note.

Для операций, принимающих на входе один фрейм данных:

  • Метаданные на уровне таблицы распространяются в возвращаемый объект фрейма данных.

  • Для метаданных на уровне столбца:

    • Во всех случаях, когда один столбец преобразуется в один столбец и его имя не изменяется (или изменяется автоматически, например вследствие устранения повторяющихся имен столбцов или переименования столбцов при объединении), метаданные на уровне столбцов сохраняются (примерами таких операций могут служить getindex, subset, объединения, mapcols).

    • Во всех случаях, когда один столбец преобразуется с помощью identity или copy в один столбец, метаданные на уровне столбцов сохраняются, даже если имя столбца изменяется (примерами таких операций могут служить rename или спецификация операции :x => :y или :x => copy => :y в select).

Для операций, принимающих на входе несколько фреймов данных, различаются два случая:

  • Если операция предполагает наличие главной таблицы (append!, prepend!, leftjoin, leftjoin!, rightjoin, semijoin, antijoin, setindex!):

    • метаданные на уровне таблицы берутся из главной таблицы;

    • метаданные на уровне столбцов для главной таблицы берутся из главной таблицы;

    • метаданные на уровне столбцов для таблицы, не являющейся главной, берутся только для столбцов, отсутствующих в главной таблице.

  • Если все таблицы равнозначны (hcat, vcat, innerjoin, outerjoin):

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

    • метаданные на уровне столбца сохраняются только для ключей, которые определены во всех переданных таблицах с этим столбцом и имеют одинаковые значения. При выполнении всех этих операций, если метаданные сохраняются, значения в парах «ключ-значение» не копируются (это важно в случае с изменяемыми значениями).

Правила распространения метаданных в стиле :note на уровне столбцов помогают принимать правильное решение в распространенных ситуациях. В частности, они предполагают, что при совпадении имен исходного и целевого столбцов метаданные для столбца не изменяются. Хотя это верно для большинства операций, в общем случае это может быть не так. Например, преобразование :x => ByRow(log) => :x может сделать недействительными метаданные, содержащие единицу измерения для переменной. В таких случаях пользователь должен использовать другое имя для выходного столбца, задать стиль метаданных :default перед операцией либо вручную удалить или изменить метаданные в столбце :x после преобразования.

Операции, при которых сохраняются метаданные в стиле :note

Большинство функций в DataFrames.jl предполагают сохранение метаданных на уровне таблицы и столбцов только в стиле :note. Некоторые функции имеют более сложную логику даже при соблюдении описанных выше общих правил (в частности, при любом преобразовании все метаданные не в стиле :note всегда удаляются). Они приведены ниже.

  • describe drops all metadata.

  • hcat: propagates table-level metadata only for keys which are defined

во всех передаваемых таблицах и имеют одинаковое значение; метаданные на уровне столбцов сохраняются.

  • vcat: propagates table-level metadata only for keys which are defined

во всех переданных таблицах и имеют одинаковые значения; метаданные на уровне столбца сохраняются только для ключей, которые определены во всех переданных таблицах с этим столбцом и имеют одинаковые значения.

  • stack: propagates table-level metadata and column-level metadata

для столбцов идентификаторов.

  • unstack: propagates table-level metadata and column-level metadata

для столбцов с ключами строк.

  • permutedims: propagates table-level metadata and drops column-level

метаданные.

  • присваивание с трансляцией не изменяет целевые метаданные. В версиях Julia до 1.7 при выполнении операций типа df.a .= s метаданные не в стиле :note не удаляются. В Julia 1.7 и более поздних версиях при выполнении таких операций сохраняются только метаданные в стиле :note.

  • При трансляции метаданные на уровне таблицы распространяются в том случае, если какой-либо ключ имеется во всех переданных фреймах данных и имеет одинаковое значение в них. Метаданные на уровне столбцов распространяются в том случае, если какой-либо ключ для определенного столбца имеется во всех переданных фреймах данных и имеет одинаковое значение в них.

  • getindex сохраняет метаданные на уровне таблицы и метаданные для выбранных столбцов.

  • setindex! не влияет на метаданные на уровне таблицы и столбцов.

  • push!, pushfirst!, insert! do not affect

метаданные на уровне таблицы и столбцов (даже если добавляются новые столбцы и отправляемая строка представляет собой объект DataFrameRow или другое значение, поддерживающее интерфейс метаданных).

метаданные целевого фрейма данных, кроме случая, когда добавляются новые столбцы, имеющие метаданные в добавляемой таблице. В этом случае метаданные сохраняются.

берутся из левой таблицы, кроме неключевых столбцов из правой таблицы, для которых метаданные берутся из правой таблицы.

  • rightjoin: table and column-level metadata is taken from the right

таблицы, кроме неключевых столбцов из левой таблицы, для которых метаданные берутся из левой таблицы.

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

берутся из левой таблицы.

  • crossjoin: propagates table-level metadata only for keys

которые определены в обоих переданных фреймах данных и имеют одинаковые значения. Метаданные на уровне столбцов распространяются из обоих переданных фреймов данных.

transform!, combine: распространяют метаданные на уровне таблицы. Метаданные на уровне столбцов распространяются в следующих случаях: а) если один столбец преобразуется в один столбец и его имя не изменяется (сюда относятся все операции выборки столбцов); б) если один столбец преобразуется с помощью функции identity или copy в один столбец независимо от того, меняется ли его имя (сюда относится переименование столбцов).