Skip to content

DataFrame Exporter

Bases: ABC


              flowchart TD
              metaframe.src.dataframe.exporter.base.DataFrameExporter[DataFrameExporter]

              

              click metaframe.src.dataframe.exporter.base.DataFrameExporter href "" "metaframe.src.dataframe.exporter.base.DataFrameExporter"
            
Source code in metaframe/src/dataframe/exporter/base.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
class DataFrameExporter(ABC):

    Opts = ExportOptions

    def __init__(self, df: 'DataFrame', *args, **kwargs) -> None: # type: ignore  # noqa: F821
        """
        Initialize the exporter.

        Parameters
        ----------
        df : DataFrame
            The DataFrame to export.

        *args, **kwargs
            Arguments used to construct the exporter-specific
            options dataclass. Supported keys depend on the concrete exporter.

        Notes
        -----
        The provided options are validated and normalized by `prepare_opts`
        before export. The original DataFrame is never modified in place.
        """
        self.is_table = df.is_table
        self.opts = self.prepare_opts(*args, **kwargs)
        self.df = self.prepare_data(df)
        self.mfr_shape = self.set_mfr_shape()
        self.mfc_shape = self.set_mfc_shape()

    @abstractmethod
    def export(self) -> None:
        """
        Execute the export process.

        This method must be implemented by subclasses to serialize the
        prepared DataFrame into the target format.
        """
        pass

    def set_mfr_shape(self) -> None:
        """
        Sets the expected MetaFrameRow shape in the export.

        MetaFrameRow shape for non-tables, DataFrame number of rows & 
        1 (if header+index) or 0 fr tables
        """
        if not self.is_table:
            return self.df.mf(axis=0).shape
        return (self.df.shape[0], 1 if self.opts.header and self.opts.index else 0)

    def set_mfc_shape(self) -> None:
        """
        Sets the expected MetaFrameCol shape in the export.

        MetaFrameCol shape for non-tables, DataFrame number of rows & 
        1 (if header+index) or 0 fr tables
        """
        if not self.is_table:
            return self.df.mf(axis=1).shape
        return (1 if self.opts.header and self.opts.index else 0, self.df.shape[1])

    def prepare_opts(self, *args, **kwargs) -> Opts:
        """
        Normalize and infer export options.

        This method fills in missing option values using sensible defaults
        derived from the DataFrame structure (table vs non-table).

        Rules
        -----
        - ``extensive`` defaults to True for non-tables, False for tables
        - ``index`` defaults to False for tables, True for non-tables

        Notes
        -----
        This method mutates ``self.opts`` in place and is intended to be called
        internally by `export`. Users should not call it directly.
        """
        opts = self.Opts(*args, **kwargs)
        if opts.extensive is None:
            opts.extensive = not self.is_table
        elif self.is_table and opts.extensive:
            opts.extensive = False

        if opts.index is None:
            if self.is_table:
                opts.index = False
            else:
                opts.index = True

        return opts

    def prepare_data(self, df: 'DataFrame') -> 'DataFrame':  # type: ignore # noqa: F821
        """
        Prepare a copy of the DataFrame for serialization.

        The returned DataFrame is transformed to ensure it can be written and
        later reconstructed correctly by readers. Transformations may include:

        - resetting the index/columns
        - normalizing headers
        - converting indices/columns to MultiIndex form
        - adding MetaFrames rows unique identifiers

        Parameters
        ----------
        df: 'DataFrame'

        Returns
        -------
        DataFrame
            A modified copy of the original DataFrame ready for export.

        Notes
        -----
        The original DataFrame is not modified.
        """
        df = df.copy()
        if self.is_table:
            if self.opts.index:
                if df.index.name in [None, 0]:
                    # For reading to work corrrectly when index = True and header = True
                    df.index.name = DF_EMPTY_CELL
            return df
        if not self.opts.index:
            df = df.reset_index(drop=True)
            df.index.name = DF_EMPTY_CELL
        if not self.opts.header:
            df.columns = range(df.shape[1])
            # df.columns.name = DF_EMPTY_CELL
        if not is_mi(df.index):
            if df.index.name in [0, '0', '', None]:
                df.index.name = DF_EMPTY_CELL
            df.index = pd.MultiIndex.from_frame(df.mfr)
            df.index.names = [DF_EMPTY_CELL]
        if not is_mi(df.columns):
            if df.columns.name in [0, '0', '', None]:
                df.columns.name = DF_EMPTY_CELL
            df.columns = pd.MultiIndex.from_frame(df.mfc)
        if self.opts.extensive:
            # Add unique IDs to MFR and MFC
            mfr = df.mf(axis=0)
            mfr.insert(0, COLNAME_METAFRAME_UNIQUE_ID, df.mfr.index+1)
            df.mfr = mfr
            mfc = df.mf(axis=1)
            mfc.insert(0, COLNAME_METAFRAME_UNIQUE_ID, df.mfc.index+1)
            df.mfc = mfc
        return df

__init__(df, *args, **kwargs)

Initialize the exporter.

Parameters:

Name Type Description Default
df DataFrame

The DataFrame to export.

required
*args

Arguments used to construct the exporter-specific options dataclass. Supported keys depend on the concrete exporter.

()
**kwargs

Arguments used to construct the exporter-specific options dataclass. Supported keys depend on the concrete exporter.

()
Notes

The provided options are validated and normalized by prepare_opts before export. The original DataFrame is never modified in place.

Source code in metaframe/src/dataframe/exporter/base.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def __init__(self, df: 'DataFrame', *args, **kwargs) -> None: # type: ignore  # noqa: F821
    """
    Initialize the exporter.

    Parameters
    ----------
    df : DataFrame
        The DataFrame to export.

    *args, **kwargs
        Arguments used to construct the exporter-specific
        options dataclass. Supported keys depend on the concrete exporter.

    Notes
    -----
    The provided options are validated and normalized by `prepare_opts`
    before export. The original DataFrame is never modified in place.
    """
    self.is_table = df.is_table
    self.opts = self.prepare_opts(*args, **kwargs)
    self.df = self.prepare_data(df)
    self.mfr_shape = self.set_mfr_shape()
    self.mfc_shape = self.set_mfc_shape()

export() abstractmethod

Execute the export process.

This method must be implemented by subclasses to serialize the prepared DataFrame into the target format.

Source code in metaframe/src/dataframe/exporter/base.py
39
40
41
42
43
44
45
46
47
@abstractmethod
def export(self) -> None:
    """
    Execute the export process.

    This method must be implemented by subclasses to serialize the
    prepared DataFrame into the target format.
    """
    pass

set_mfr_shape()

Sets the expected MetaFrameRow shape in the export.

MetaFrameRow shape for non-tables, DataFrame number of rows & 1 (if header+index) or 0 fr tables

Source code in metaframe/src/dataframe/exporter/base.py
49
50
51
52
53
54
55
56
57
58
def set_mfr_shape(self) -> None:
    """
    Sets the expected MetaFrameRow shape in the export.

    MetaFrameRow shape for non-tables, DataFrame number of rows & 
    1 (if header+index) or 0 fr tables
    """
    if not self.is_table:
        return self.df.mf(axis=0).shape
    return (self.df.shape[0], 1 if self.opts.header and self.opts.index else 0)

set_mfc_shape()

Sets the expected MetaFrameCol shape in the export.

MetaFrameCol shape for non-tables, DataFrame number of rows & 1 (if header+index) or 0 fr tables

Source code in metaframe/src/dataframe/exporter/base.py
60
61
62
63
64
65
66
67
68
69
def set_mfc_shape(self) -> None:
    """
    Sets the expected MetaFrameCol shape in the export.

    MetaFrameCol shape for non-tables, DataFrame number of rows & 
    1 (if header+index) or 0 fr tables
    """
    if not self.is_table:
        return self.df.mf(axis=1).shape
    return (1 if self.opts.header and self.opts.index else 0, self.df.shape[1])

prepare_opts(*args, **kwargs)

Normalize and infer export options.

This method fills in missing option values using sensible defaults derived from the DataFrame structure (table vs non-table).

Rules
  • extensive defaults to True for non-tables, False for tables
  • index defaults to False for tables, True for non-tables
Notes

This method mutates self.opts in place and is intended to be called internally by export. Users should not call it directly.

Source code in metaframe/src/dataframe/exporter/base.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def prepare_opts(self, *args, **kwargs) -> Opts:
    """
    Normalize and infer export options.

    This method fills in missing option values using sensible defaults
    derived from the DataFrame structure (table vs non-table).

    Rules
    -----
    - ``extensive`` defaults to True for non-tables, False for tables
    - ``index`` defaults to False for tables, True for non-tables

    Notes
    -----
    This method mutates ``self.opts`` in place and is intended to be called
    internally by `export`. Users should not call it directly.
    """
    opts = self.Opts(*args, **kwargs)
    if opts.extensive is None:
        opts.extensive = not self.is_table
    elif self.is_table and opts.extensive:
        opts.extensive = False

    if opts.index is None:
        if self.is_table:
            opts.index = False
        else:
            opts.index = True

    return opts

prepare_data(df)

Prepare a copy of the DataFrame for serialization.

The returned DataFrame is transformed to ensure it can be written and later reconstructed correctly by readers. Transformations may include:

  • resetting the index/columns
  • normalizing headers
  • converting indices/columns to MultiIndex form
  • adding MetaFrames rows unique identifiers

Parameters:

Name Type Description Default
df DataFrame
required

Returns:

Type Description
DataFrame

A modified copy of the original DataFrame ready for export.

Notes

The original DataFrame is not modified.

Source code in metaframe/src/dataframe/exporter/base.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def prepare_data(self, df: 'DataFrame') -> 'DataFrame':  # type: ignore # noqa: F821
    """
    Prepare a copy of the DataFrame for serialization.

    The returned DataFrame is transformed to ensure it can be written and
    later reconstructed correctly by readers. Transformations may include:

    - resetting the index/columns
    - normalizing headers
    - converting indices/columns to MultiIndex form
    - adding MetaFrames rows unique identifiers

    Parameters
    ----------
    df: 'DataFrame'

    Returns
    -------
    DataFrame
        A modified copy of the original DataFrame ready for export.

    Notes
    -----
    The original DataFrame is not modified.
    """
    df = df.copy()
    if self.is_table:
        if self.opts.index:
            if df.index.name in [None, 0]:
                # For reading to work corrrectly when index = True and header = True
                df.index.name = DF_EMPTY_CELL
        return df
    if not self.opts.index:
        df = df.reset_index(drop=True)
        df.index.name = DF_EMPTY_CELL
    if not self.opts.header:
        df.columns = range(df.shape[1])
        # df.columns.name = DF_EMPTY_CELL
    if not is_mi(df.index):
        if df.index.name in [0, '0', '', None]:
            df.index.name = DF_EMPTY_CELL
        df.index = pd.MultiIndex.from_frame(df.mfr)
        df.index.names = [DF_EMPTY_CELL]
    if not is_mi(df.columns):
        if df.columns.name in [0, '0', '', None]:
            df.columns.name = DF_EMPTY_CELL
        df.columns = pd.MultiIndex.from_frame(df.mfc)
    if self.opts.extensive:
        # Add unique IDs to MFR and MFC
        mfr = df.mf(axis=0)
        mfr.insert(0, COLNAME_METAFRAME_UNIQUE_ID, df.mfr.index+1)
        df.mfr = mfr
        mfc = df.mf(axis=1)
        mfc.insert(0, COLNAME_METAFRAME_UNIQUE_ID, df.mfc.index+1)
        df.mfc = mfc
    return df