call_report.fca.convert_wide_format_to_long_format#

call_report.fca.convert_wide_format_to_long_format(*, wide: NativeDataFrame, dataframe_type: None = None) NativeDataFrame[source]#
call_report.fca.convert_wide_format_to_long_format(*, wide: NativeDataFrame, dataframe_type: Literal['pandas']) pandas.DataFrame
call_report.fca.convert_wide_format_to_long_format(*, wide: NativeDataFrame, dataframe_type: Literal['pyarrow_table']) pyarrow.Table
call_report.fca.convert_wide_format_to_long_format(*, wide: NativeDataFrame, dataframe_type: Literal['polars_dataframe']) polars.DataFrame
call_report.fca.convert_wide_format_to_long_format(*, wide: NativeDataFrame, dataframe_type: Literal['polars_lazyframe']) polars.LazyFrame

Convert an already-built wide-format frame to long format.

This function is self-contained. wide’s own column names fully describe the long-format row each one unpivots to (see _parse_wide_column_key), so it needs no FCACallReport instance, layout lookups, or other external metadata.

Column names are schema rather than data. A wide frame has at most a few thousand columns, known statically via collect_schema, so they are parsed in plain Python rather than with narwhals string expressions. That is necessary, not merely stylistic: narwhals.Expr.str.split requires a pyarrow-backed pandas series and raises TypeError on plain numpy-backed pandas, this package’s default backend, and Expr.str has no regex-capture-group equivalent to work around it. The reshape itself, one unpivot plus a join against a small lookup frame built from the parsed column names, uses only lazy-safe narwhals operations, so it stays a deferred, uncollected query when wide is a polars.LazyFrame. No duplicate-grain check is needed either, since each wide column maps to exactly one (schedule, code_column, code_value, variable_name) tuple by construction, making the long-format grain unique automatically.

Columns are always returned in the order UNINUM, period, schedule, code_column, code_value, variable_name, value, is_multiple. That order is part of the contract, so a positional read of this frame matches one built by the other route.

Every row with a non-null value matches what FCACallReport.to_long_format would build directly from the same source data, but row counts can still differ. Pivoting fills in every (UNINUM, period) by wide-column combination as an explicit null row, including combinations no institution actually reported, such as an investment code one institution used and another never did. to_long_format only ever has a row for a combination that genuinely appeared in the source. Filter to non-null value before comparing row-for-row against a directly-built long-format frame.

Parameters:
wideNativeDataFrame

A wide-format frame, e.g. from FCACallReport.to_wide_format.

dataframe_type{“pandas”, “pyarrow_table”, “polars_lazyframe”, “polars_dataframe”}, optional

The dataframe type to convert the result to as a final step. Leave this None (the default) to get back whatever backend call_report.config.get_config currently has configured.

Returns:
NativeDataFrame

The long-format frame (see FCACallReport.to_long_format for the schema), of the configured backend, or of dataframe_type if it was supplied.

Raises:
ReshapeError

If a column name in wide (other than RESHAPE_INDEX) matches neither the plain nor the coded wide-format naming convention.

Examples

>>> from call_report.fca.transport import PackagedArchiveTransport
>>> from call_report.fca.report import FCACallReport
>>> report = FCACallReport(
...     start="2026-03-31",
...     end="2026-03-31",
...     transport=PackagedArchiveTransport(),
... )
>>> wide = report.to_wide_format(schedules=["RC", "RCB"])
>>> long = convert_wide_format_to_long_format(wide=wide)
>>> list(long.columns)
['UNINUM', 'period', 'schedule', 'code_column', 'code_value', 'variable_name', 'value', 'is_multiple']