Panel data frame

PortfolioOptimisers.panel_dataframeFunction
panel_dataframe(pnl::AssetPanel; nx::Option{<:VecStr} = nothing,
                ts::Option{<:AbstractVector} = nothing, fields = nothing,
                assets = nothing, layout::Symbol = :long,
                decode::Bool = true) -> DataFrames.DataFrame

Render an AssetPanel as a DataFrames.DataFrame.

This is the panel's interchange. The library persists no panel of its own, and a table is what every tool that is not this library already reads, so a caller writes the result with whatever they already use and a panel reaches disk, a plot or a spreadsheet without the library owning a format.

A panel does not name its own assets or its own observations, because the carrier that holds it names them. So nx and ts are the caller's, and each falls back to its own axis positions.

Three shapes are reachable, and fields picks between them. A single Panel Field name gives that Panel Field laid out as it stands, through panel_frame_field. Any other selection gives a layout: :long reads, :wide is lossless. A TensorPanelField has no shape of its own and the first refuses it, and it spreads into one column per trailing-axis label in both layouts, under the "<field>=<label>" name it takes in a Feature Matrix.

Algorithm

  1. Name the axes: nx or the asset positions, ts or the observation positions, each checked against the shape the panel's Panel Fields agree on.
  2. Resolve the assets with panel_frame_assets.
  3. fields is one name: render that Panel Field with panel_frame_field and return.
  4. Otherwise resolve the Panel Fields with panel_frame_fields, and render them with panel_frame_long or panel_frame_wide.

Arguments

  • pnl: The Asset Panel.
  • nx: The asset names of the panel's universe, or nothing to name each asset by its position. See VecStr.
  • ts: The observation labels, or nothing to name each observation by its position. A static panel has no observation axis and ignores it.
  • fields: One Panel Field name, a collection of them, or nothing for every Panel Field of the panel.
  • assets: One asset label, a collection of them, or nothing for every asset of the universe.
  • layout::Symbol = :long: :long for one row per (observation, asset), filtered by the active mask. :wide for one row per observation and one column per (Panel Field column, asset). Ignored when fields names a single Panel Field.
  • decode::Bool = true: Whether a CategoricalPanelField renders its levels rather than its integer codes.

Validation

  • layout is :long or :wide. Raises an ArgumentError.
  • length(nx) is the panel's asset axis. Raises a DimensionMismatch.
  • length(ts) is the panel's observation axis, when the panel is time-varying. Raises a DimensionMismatch.
  • The panel holds every named Panel Field, and the universe holds every named asset. Raises a KeyError.

Returns

  • df::DataFrames.DataFrame: The table.

Examples

julia> pnl = AssetPanel(;                        pf = [NumericPanelField(; name = "mcap", vals = [1.0, 2.0, 3.0]),                              CategoricalPanelField(; name = "sector", levels = ["Tech", "Energy"],                                                    codes = [1, 2, 1])]);julia> panel_dataframe(pnl; nx = ["A", "B", "C"])3×3 DataFrame Row │ asset   mcap     sector     │ String  Float64  String─────┼─────────────────────────   1 │ A           1.0  Tech   2 │ B           2.0  Energy   3 │ C           3.0  Tech

Related

source