Метаданные
Проектирование поддержки метаданных
Пакет DataFrames.jl позволяет сохранять и извлекать метаданные на уровне таблиц и столбцов. Эта возможно благодаря функциям, определенным в интерфейсе DataAPI.jl:
-
для метаданных на уровне таблицы:
metadata
,metadatakeys
,metadata!
,deletemetadata!
,emptymetadata!
; -
для метаданных на уровне столбца:
colmetadata
,colmetadatakeys
,colmetadata!
,deletecolmetadata!
,emptycolmetadata!
.
Кроме того, полезным может оказаться пакет 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
, так как эти операции специально предназначены для создания идентичной копии исходного фрейма данных: -
:note
: метаданные в этом стиле считаются аннотацией таблицы или столбца, которая должна распространяться при преобразованиях (правила распространения таких метаданных описываются ниже). -
Другие стили метаданных допускаются, но в настоящее время приравниваются к стилю
:default
(в будущем ситуация может измениться, если будут определены другие стандартные стили метаданных).
Все функции метаданных в DataAPI.jl работают с объектами DataFrame
, SubDataFrame
, DataFrameRow
и объектами, возвращаемыми функциями eachrow
и eachcol
. В этом разделе эти объекты будут в совокупности называться объектами наподобие фреймов данных, и на них распространяются следующие правила:
-
Объекты, возвращаемые функциями
eachrow
иeachcol
, имеют те же метаданные, что и родительский объектAbstractDataFrame
. -
SubDataFrame
andDataFrameRow
only expose metadata from
родительского объекта DataFrame
в стиле :note
.
Примечательно, что метаданные не поддерживаются для GroupedDataFrame
, и их нельзя добавлять, изменять или просматривать посредством самого объекта GroupedDataFrame
. Это можно делать только посредством его родительского объекта (parent
).
Пакет DataFrames.jl позволяет пользователям извлекать столбцы фрейма данных и выполнять с ними операции. Такие операции не влияют на метаданные. Поэтому, даже если есть метаданные в стиле |
Принципы проектирования метаданных, относящиеся к 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
.
Для операций, принимающих на входе один фрейм данных:
-
Метаданные на уровне таблицы распространяются в возвращаемый объект фрейма данных.
-
Для метаданных на уровне столбца:
-
Во всех случаях, когда один столбец преобразуется в один столбец и его имя не изменяется (или изменяется автоматически, например вследствие устранения повторяющихся имен столбцов или переименования столбцов при объединении), метаданные на уровне столбцов сохраняются (примерами таких операций могут служить
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
Большинство функций в DataFrames.jl предполагают сохранение метаданных на уровне таблицы и столбцов только в стиле :note
. Некоторые функции имеют более сложную логику даже при соблюдении описанных выше общих правил (в частности, при любом преобразовании все метаданные не в стиле :note
всегда удаляются). Они приведены ниже.
во всех передаваемых таблицах и имеют одинаковое значение; метаданные на уровне столбцов сохраняются.
-
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
в один столбец независимо от того, меняется ли его имя (сюда относится переименование столбцов).