Panel data frame: private API

PortfolioOptimisers.panel_frame_columnsFunction
panel_frame_columns(f::NumericPanelField, decode::Bool)
panel_frame_columns(f::CategoricalPanelField, decode::Bool)
panel_frame_columns(f::TensorPanelField, decode::Bool)

Return the table columns one Panel Field contributes to a panel_dataframe, in column order.

A column is the triple (name, vals, omsk): its name, the slab of values over the panel's own axes, and the slab of its observed mask, or nothing when the Panel Field carries none. Every slab is assets on a static panel and observations × assets on a time-varying one, so panel_array_view slices any of them by asset.

The column count is where a table and a Feature Matrix part company. A Feature Matrix is numeric, so a categorical Panel Field spreads into one indicator column per level. A table column holds a string, so the same Panel Field is one column carrying the level itself. A tensor Panel Field is the same in both: one column per trailing-axis label, under the "<field>=<label>" name panel_field_labels gives it, because a label axis is the one thing a flat table cannot carry as an axis of its own.

Algorithm

The method that Julia selects is the algorithm.

  1. NumericPanelField: one column, named after the Panel Field, holding its values.
  2. CategoricalPanelField: one column, named after the Panel Field, holding the level each code names under decode, and the codes themselves otherwise.
  3. TensorPanelField: one column per trailing-axis label, named "<field>=<label>", holding that label's slice.

Arguments

  • f: The Panel Field.
  • decode::Bool: Whether a categorical Panel Field renders its levels rather than its codes. The other two kinds carry no codes and ignore it.

Returns

  • cols::Vector{Tuple{String, Any, Any}}: One (name, vals, omsk) triple per column, in column order.

Related

source
PortfolioOptimisers.panel_frame_fieldsFunction
panel_frame_fields(pnl::AssetPanel, fields::Nothing)
panel_frame_fields(pnl::AssetPanel, fields)

Resolve the Panel Fields a panel_dataframe call names, in the order it names them.

Algorithm

The method that Julia selects is the algorithm.

  1. fields is nothing: every Panel Field, in panel order.
  2. Otherwise: one Panel Field per entry, looked up by panel_field, in the order fields writes them.

Arguments

  • pnl: The Asset Panel.
  • fields: The Panel Field names to include, or nothing for every one of them.

Validation

  • The panel holds a Panel Field under each name. Raises a KeyError. See panel_field.

Returns

  • fs: The Panel Fields, in column order.

Related

source
PortfolioOptimisers.panel_frame_assetsFunction
panel_frame_assets(nx::VecStr, assets::Nothing) -> Vector{Int}
panel_frame_assets(nx::VecStr, assets::AbstractString) -> Vector{Int}
panel_frame_assets(nx::VecStr, assets) -> Vector{Int}

Resolve the asset labels a panel_dataframe call names into positions on the panel's asset axis.

The panel does not name its own assets: its Panel Fields share an asset axis, and the carrier that holds them names it. So the names are the caller's, and this is where a name becomes the position every slab is sliced by.

Algorithm

The method that Julia selects is the algorithm.

  1. assets is nothing: every position, in axis order.
  2. assets is one label: that label's position.
  3. Otherwise: one position per label, in the order assets writes them.

Arguments

  • nx: The asset names of the panel's universe. See VecStr.
  • assets: The asset labels to include, or nothing for every one of them.

Validation

  • nx names every entry of assets. Raises a KeyError.
  • No entry of assets repeats. Raises an ArgumentError.

Returns

  • j::Vector{Int}: The selected positions on the asset axis, in column order.

Related

source
PortfolioOptimisers.panel_frame_block!Function
panel_frame_block!(df::DataFrames.DataFrame, A::AbstractVector, names::VecStr) -> nothing
panel_frame_block!(df::DataFrames.DataFrame, A::AbstractMatrix, names::VecStr) -> nothing

Write one slab of a Panel Field into a wide table, as one column per asset.

Algorithm

The method that Julia selects is the algorithm, and the slab's rank is what says whether the panel is static.

  1. A vector slab is static and carries no observation axis, so each asset's value becomes a one-row column.
  2. A matrix slab is time-varying, so each asset's column of the slab becomes a column of the table.

Arguments

  • df: The table under construction. It is written to.
  • A: The slab, already sliced to the selected assets.
  • names: The column name of each selected asset, in slab column order. See VecStr.

Returns

  • nothing. df carries the result.

Related

source
PortfolioOptimisers.panel_frame_fieldFunction
panel_frame_field(pnl::AssetPanel, f::AbstractPanelField, j::VecInt, nxj::VecStr, ts, decode::Bool) -> DataFrames.DataFrame
panel_frame_field(pnl::AssetPanel, f::TensorPanelField, j::VecInt, nxj::VecStr, ts, decode::Bool)

Render one Panel Field as the table of its own shape: one row per observation, one column per asset.

This is what a panel_dataframe call that names a single Panel Field returns. A numeric or a categorical Panel Field already is observations × assets, so the table needs no second key and no layout, and it carries neither the universe masks nor the observed mask: it is the Panel Field itself, laid out as it stands.

A TensorPanelField is refused here. Its values are observations × assets × labels, so no arrangement of one row per observation and one column per asset holds them; a long or a wide layout does, by spreading the label axis into columns.

Algorithm

The method that Julia selects decides whether the Panel Field has this shape.

  1. Open the table with the observation column, unless the panel is static.
  2. Write the Panel Field's one value column, as one table column per selected asset.

Arguments

  • pnl: The Asset Panel.
  • f: The Panel Field.
  • j: The selected positions on the asset axis. See VecInt.
  • nxj: The name of each selected asset, in the order j writes them. See VecStr.
  • ts: The observation labels, ignored when the panel is static.
  • decode::Bool: Whether a categorical Panel Field renders its levels rather than its codes.

Validation

Returns

  • df::DataFrames.DataFrame: The observations × assets table, with an "observation" column first when the panel is time-varying.

Related

source
PortfolioOptimisers.panel_frame_longFunction
panel_frame_long(pnl::AssetPanel, fs, j::VecInt, nxj::VecStr, ts, decode::Bool) -> DataFrames.DataFrame

Render the selected Panel Fields in the long layout: one row per (observation, asset), one column per Panel Field column.

The rows a time-varying panel writes are filtered by the active mask, so every row of the table is an asset that was in the universe at that observation. The estimation mask stays as its own "emsk" column, because it varies within the rows that survive the filter. A static panel has neither mask and neither column, and its rows are its assets.

Algorithm

  1. Open the table with the "observation" and "asset" key columns, unless the panel is static, which carries the asset key alone.
  2. Walk the Panel Fields in order, and each one's columns from panel_frame_columns. Ravel each slab in row order, so an entry lands on the row its two keys name, and follow a Panel Field's value column with its observed-mask column when it carries one.
  3. Append the estimation mask, and drop every row the active mask does not hold.

Arguments

  • pnl: The Asset Panel.
  • fs: The Panel Fields, in column order.
  • j: The selected positions on the asset axis. See VecInt.
  • nxj: The name of each selected asset, in the order j writes them. See VecStr.
  • ts: The observation labels, ignored when the panel is static.
  • decode::Bool: Whether a categorical Panel Field renders its levels rather than its codes.

Returns

  • df::DataFrames.DataFrame: The long table.

Related

source
PortfolioOptimisers.panel_frame_wideFunction
panel_frame_wide(pnl::AssetPanel, fs, j::VecInt, nxj::VecStr, ts, decode::Bool) -> DataFrames.DataFrame

Render the selected Panel Fields in the wide layout: one row per observation, one column per (Panel Field column, asset).

A column name is the Panel Field column, an "@", and the asset: "mcap@AAPL", "beta=size@AAPL", "mcap::observed@AAPL". The layout keeps every cell of the panel, so both universe masks come with it, as the "amsk@<asset>" and "emsk@<asset>" blocks. Nothing is filtered, which is what makes the wide layout the lossless one and the long layout the one that reads.

A column name is a rendering, not a key. A caller that needs the two parts back reads the long layout, whose keys are columns of their own, rather than splitting a name on its separators.

Algorithm

  1. Open the table with the "observation" column, unless the panel is static, whose one row is every asset at once.
  2. Walk the Panel Fields in order, and each one's columns from panel_frame_columns, writing each as a block of one column per selected asset through panel_frame_block!.
  3. Append the active-mask and estimation-mask blocks, unless the panel is static.

Arguments

  • pnl: The Asset Panel.
  • fs: The Panel Fields, in column order.
  • j: The selected positions on the asset axis. See VecInt.
  • nxj: The name of each selected asset, in the order j writes them. See VecStr.
  • ts: The observation labels, ignored when the panel is static.
  • decode::Bool: Whether a categorical Panel Field renders its levels rather than its codes.

Returns

  • df::DataFrames.DataFrame: The wide table.

Related

source