Skip to content

Core API Reference

This module contains the main classes for creating codebooks.


BookIt

bookit_df.BookIt

Create a codebook documenting a DataFrame's variables.

BookIt can be used as a context manager (auto-saves on exit if output path is provided) or with explicit .save() calls.

Attributes:

Name Type Description
title

Title of the codebook.

config

Configuration options for the codebook.

variables list[Variable]

List of Variable objects in the codebook.

Example (context manager with auto-save): >>> with BookIt("Survey Codebook", output="codebook.pdf") as book: ... book.from_dataframe(df) # Saves automatically on exit

Example (explicit save): >>> book = BookIt("Survey Codebook") >>> book.from_dataframe(df) >>> book.save("codebook.pdf")

Source code in src/bookit_df/bookit.py
 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
class BookIt:
    """Create a codebook documenting a DataFrame's variables.

    BookIt can be used as a context manager (auto-saves on exit if output path
    is provided) or with explicit .save() calls.

    Attributes:
        title: Title of the codebook.
        config: Configuration options for the codebook.
        variables: List of Variable objects in the codebook.

    Example (context manager with auto-save):
        >>> with BookIt("Survey Codebook", output="codebook.pdf") as book:
        ...     book.from_dataframe(df)
        # Saves automatically on exit

    Example (explicit save):
        >>> book = BookIt("Survey Codebook")
        >>> book.from_dataframe(df)
        >>> book.save("codebook.pdf")
    """

    def __init__(
        self,
        title: str = "Codebook",
        output: str | Path | None = None,
        *,
        author: str = "",
        date: str | None = None,
        include_toc: bool = True,
        include_title_page: bool = True,
        include_stats: bool = True,
        include_charts: bool = True,
        config: CodebookConfig | None = None,
    ) -> None:
        """Initialize a new BookIt instance.

        Args:
            title: Title of the codebook.
            output: Output file path. If provided and used as context manager,
                    the codebook will auto-save on exit.
            author: Author name(s).
            date: Date string. Defaults to today's date.
            include_toc: Whether to include table of contents.
            include_title_page: Whether to include title page.
            include_stats: Whether to include summary statistics.
            include_charts: Whether to include charts (bar/histogram).
            config: Full CodebookConfig object. If provided, overrides
                    individual settings above.
        """
        # Use provided config or build from individual args
        if config is not None:
            self.config = config
        else:
            kwargs = {
                "title": title,
                "author": author,
                "include_toc": include_toc,
                "include_title_page": include_title_page,
                "include_stats": include_stats,
                "include_charts": include_charts,
            }
            if date is not None:
                kwargs["date"] = date
            self.config = CodebookConfig(**kwargs)

        self.title = title
        self.output = Path(output) if output else None
        self.variables: list[Variable] = []
        self._saved = False

    def from_dataframe(
        self,
        df: Any,
        columns: list[str] | None = None,
        descriptions: dict[str, str] | None = None,
        value_labels: dict[str, dict[Any, str]] | None = None,
        suppress_numeric_stats: list[str] | None = None,
    ) -> "BookIt":
        """Import variables from a DataFrame.

        Args:
            df: A polars or pandas DataFrame.
            columns: List of column names to include. Defaults to all columns.
            descriptions: Dict mapping column names to descriptions.
            value_labels: Dict mapping column names to value label dicts.
            suppress_numeric_stats: List of column names for which to hide
                numeric statistics (mean, std, min, max) in output.

        Returns:
            self, for method chaining.

        Example:
            >>> book.from_dataframe(
            ...     df,
            ...     columns=["age", "income"],
            ...     descriptions={"age": "Respondent age in years"},
            ...     suppress_numeric_stats=["age"]  # Hide mean/std for age
            ... )
        """
        descriptions = descriptions or {}
        value_labels = value_labels or {}
        suppress_numeric_stats = suppress_numeric_stats or []

        # Get column list
        module = type(df).__module__
        if module.startswith("polars"):
            all_columns = df.columns
        elif module.startswith("pandas"):
            all_columns = list(df.columns)
        else:
            raise TypeError(
                f"Unsupported DataFrame type: {type(df)}. "
                "Expected polars.DataFrame or pandas.DataFrame."
            )

        columns_to_process = columns if columns else all_columns

        for col_name in columns_to_process:
            if col_name not in all_columns:
                raise ValueError(f"Column '{col_name}' not found in DataFrame.")

            self.add_variable(
                name=col_name,
                description=descriptions.get(col_name, ""),
                values=value_labels.get(col_name, {}),
                suppress_numeric_stats=(col_name in suppress_numeric_stats),
                data=df[col_name],
            )

        return self

    def add_variable(
        self,
        name: str,
        description: str = "",
        dtype: str = "",
        values: dict[Any, str] | None = None,
        context: str = "",
        suppress_numeric_stats: bool = False,
        data: Any = None,
    ) -> "BookIt":
        """Manually add a variable to the codebook.

        Args:
            name: Variable name.
            description: Human-readable description.
            dtype: Data type string. If not provided and data is given,
                   will be inferred from the data.
            values: Value labels for categorical variables.
            context: Additional contextual notes.
            suppress_numeric_stats: If True, hide mean/std/min/max in output.
            data: Optional data to compute statistics from. Accepts polars Series,
                  pandas Series, list, tuple, or numpy array.

        Returns:
            self, for method chaining.

        Example:
            >>> book.add_variable(
            ...     "score",
            ...     description="Test score",
            ...     data=[85, 90, 78, 92, None, 88]
            ... )
        """
        # Infer dtype from data if not provided
        if data is not None and not dtype:
            dtype = get_dtype_string(data)

        # Auto-suppress numeric stats for string data types
        if not suppress_numeric_stats and dtype:
            string_types = ('str', 'string', 'object', 'Utf8', 'String')
            if any(st.lower() in dtype.lower() for st in string_types):
                suppress_numeric_stats = True

        var = Variable(
            name=name,
            description=description,
            dtype=dtype,
            values=values or {},
            context=context,
            suppress_numeric_stats=suppress_numeric_stats,
        )

        # Compute statistics from data if provided
        if data is not None and self.config.include_stats:
            var.stats = compute_stats(data)

        # Store chart data if charts are enabled
        if data is not None and self.config.include_charts:
            var.chart_data = _extract_chart_data(data)

        self.variables.append(var)
        return self

    def add_context(self, variable_name: str, text: str) -> "BookIt":
        """Add contextual notes to a variable.

        Args:
            variable_name: Name of the variable to add context to.
            text: Contextual text to add.

        Returns:
            self, for method chaining.

        Raises:
            ValueError: If variable is not found.
        """
        for var in self.variables:
            if var.name == variable_name:
                var.context = text
                return self

        raise ValueError(f"Variable '{variable_name}' not found in codebook.")

    def save(self, path: str | Path | None = None) -> None:
        """Save the codebook to a file.

        Args:
            path: Output file path. Uses the path from __init__ if not provided.

        Raises:
            ValueError: If no output path is available.
        """
        output_path = Path(path) if path else self.output

        if output_path is None:
            raise ValueError(
                "No output path specified. Provide a path to save() or set "
                "output in BookIt()."
            )

        # Import renderer here to avoid circular imports
        from .renderers.pdf import PDFRenderer

        renderer = PDFRenderer(self)
        renderer.render(output_path)
        self._saved = True

    def __enter__(self) -> "BookIt":
        """Enter context manager."""
        return self

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        """Exit context manager, auto-saving if output path was set."""
        if exc_type is None and self.output is not None and not self._saved:
            self.save()

    def __repr__(self) -> str:
        """Concise representation for debugging."""
        return (
            f"BookIt(title={self.title!r}, "
            f"variables={len(self.variables)})"
        )

__init__(title='Codebook', output=None, *, author='', date=None, include_toc=True, include_title_page=True, include_stats=True, include_charts=True, config=None)

Initialize a new BookIt instance.

Parameters:

Name Type Description Default
title str

Title of the codebook.

'Codebook'
output str | Path | None

Output file path. If provided and used as context manager, the codebook will auto-save on exit.

None
author str

Author name(s).

''
date str | None

Date string. Defaults to today's date.

None
include_toc bool

Whether to include table of contents.

True
include_title_page bool

Whether to include title page.

True
include_stats bool

Whether to include summary statistics.

True
include_charts bool

Whether to include charts (bar/histogram).

True
config CodebookConfig | None

Full CodebookConfig object. If provided, overrides individual settings above.

None
Source code in src/bookit_df/bookit.py
 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
def __init__(
    self,
    title: str = "Codebook",
    output: str | Path | None = None,
    *,
    author: str = "",
    date: str | None = None,
    include_toc: bool = True,
    include_title_page: bool = True,
    include_stats: bool = True,
    include_charts: bool = True,
    config: CodebookConfig | None = None,
) -> None:
    """Initialize a new BookIt instance.

    Args:
        title: Title of the codebook.
        output: Output file path. If provided and used as context manager,
                the codebook will auto-save on exit.
        author: Author name(s).
        date: Date string. Defaults to today's date.
        include_toc: Whether to include table of contents.
        include_title_page: Whether to include title page.
        include_stats: Whether to include summary statistics.
        include_charts: Whether to include charts (bar/histogram).
        config: Full CodebookConfig object. If provided, overrides
                individual settings above.
    """
    # Use provided config or build from individual args
    if config is not None:
        self.config = config
    else:
        kwargs = {
            "title": title,
            "author": author,
            "include_toc": include_toc,
            "include_title_page": include_title_page,
            "include_stats": include_stats,
            "include_charts": include_charts,
        }
        if date is not None:
            kwargs["date"] = date
        self.config = CodebookConfig(**kwargs)

    self.title = title
    self.output = Path(output) if output else None
    self.variables: list[Variable] = []
    self._saved = False

from_dataframe(df, columns=None, descriptions=None, value_labels=None, suppress_numeric_stats=None)

Import variables from a DataFrame.

Parameters:

Name Type Description Default
df Any

A polars or pandas DataFrame.

required
columns list[str] | None

List of column names to include. Defaults to all columns.

None
descriptions dict[str, str] | None

Dict mapping column names to descriptions.

None
value_labels dict[str, dict[Any, str]] | None

Dict mapping column names to value label dicts.

None
suppress_numeric_stats list[str] | None

List of column names for which to hide numeric statistics (mean, std, min, max) in output.

None

Returns:

Type Description
BookIt

self, for method chaining.

Example

book.from_dataframe( ... df, ... columns=["age", "income"], ... descriptions={"age": "Respondent age in years"}, ... suppress_numeric_stats=["age"] # Hide mean/std for age ... )

Source code in src/bookit_df/bookit.py
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
158
159
160
161
162
163
def from_dataframe(
    self,
    df: Any,
    columns: list[str] | None = None,
    descriptions: dict[str, str] | None = None,
    value_labels: dict[str, dict[Any, str]] | None = None,
    suppress_numeric_stats: list[str] | None = None,
) -> "BookIt":
    """Import variables from a DataFrame.

    Args:
        df: A polars or pandas DataFrame.
        columns: List of column names to include. Defaults to all columns.
        descriptions: Dict mapping column names to descriptions.
        value_labels: Dict mapping column names to value label dicts.
        suppress_numeric_stats: List of column names for which to hide
            numeric statistics (mean, std, min, max) in output.

    Returns:
        self, for method chaining.

    Example:
        >>> book.from_dataframe(
        ...     df,
        ...     columns=["age", "income"],
        ...     descriptions={"age": "Respondent age in years"},
        ...     suppress_numeric_stats=["age"]  # Hide mean/std for age
        ... )
    """
    descriptions = descriptions or {}
    value_labels = value_labels or {}
    suppress_numeric_stats = suppress_numeric_stats or []

    # Get column list
    module = type(df).__module__
    if module.startswith("polars"):
        all_columns = df.columns
    elif module.startswith("pandas"):
        all_columns = list(df.columns)
    else:
        raise TypeError(
            f"Unsupported DataFrame type: {type(df)}. "
            "Expected polars.DataFrame or pandas.DataFrame."
        )

    columns_to_process = columns if columns else all_columns

    for col_name in columns_to_process:
        if col_name not in all_columns:
            raise ValueError(f"Column '{col_name}' not found in DataFrame.")

        self.add_variable(
            name=col_name,
            description=descriptions.get(col_name, ""),
            values=value_labels.get(col_name, {}),
            suppress_numeric_stats=(col_name in suppress_numeric_stats),
            data=df[col_name],
        )

    return self

add_variable(name, description='', dtype='', values=None, context='', suppress_numeric_stats=False, data=None)

Manually add a variable to the codebook.

Parameters:

Name Type Description Default
name str

Variable name.

required
description str

Human-readable description.

''
dtype str

Data type string. If not provided and data is given, will be inferred from the data.

''
values dict[Any, str] | None

Value labels for categorical variables.

None
context str

Additional contextual notes.

''
suppress_numeric_stats bool

If True, hide mean/std/min/max in output.

False
data Any

Optional data to compute statistics from. Accepts polars Series, pandas Series, list, tuple, or numpy array.

None

Returns:

Type Description
BookIt

self, for method chaining.

Example

book.add_variable( ... "score", ... description="Test score", ... data=[85, 90, 78, 92, None, 88] ... )

Source code in src/bookit_df/bookit.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def add_variable(
    self,
    name: str,
    description: str = "",
    dtype: str = "",
    values: dict[Any, str] | None = None,
    context: str = "",
    suppress_numeric_stats: bool = False,
    data: Any = None,
) -> "BookIt":
    """Manually add a variable to the codebook.

    Args:
        name: Variable name.
        description: Human-readable description.
        dtype: Data type string. If not provided and data is given,
               will be inferred from the data.
        values: Value labels for categorical variables.
        context: Additional contextual notes.
        suppress_numeric_stats: If True, hide mean/std/min/max in output.
        data: Optional data to compute statistics from. Accepts polars Series,
              pandas Series, list, tuple, or numpy array.

    Returns:
        self, for method chaining.

    Example:
        >>> book.add_variable(
        ...     "score",
        ...     description="Test score",
        ...     data=[85, 90, 78, 92, None, 88]
        ... )
    """
    # Infer dtype from data if not provided
    if data is not None and not dtype:
        dtype = get_dtype_string(data)

    # Auto-suppress numeric stats for string data types
    if not suppress_numeric_stats and dtype:
        string_types = ('str', 'string', 'object', 'Utf8', 'String')
        if any(st.lower() in dtype.lower() for st in string_types):
            suppress_numeric_stats = True

    var = Variable(
        name=name,
        description=description,
        dtype=dtype,
        values=values or {},
        context=context,
        suppress_numeric_stats=suppress_numeric_stats,
    )

    # Compute statistics from data if provided
    if data is not None and self.config.include_stats:
        var.stats = compute_stats(data)

    # Store chart data if charts are enabled
    if data is not None and self.config.include_charts:
        var.chart_data = _extract_chart_data(data)

    self.variables.append(var)
    return self

add_context(variable_name, text)

Add contextual notes to a variable.

Parameters:

Name Type Description Default
variable_name str

Name of the variable to add context to.

required
text str

Contextual text to add.

required

Returns:

Type Description
BookIt

self, for method chaining.

Raises:

Type Description
ValueError

If variable is not found.

Source code in src/bookit_df/bookit.py
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def add_context(self, variable_name: str, text: str) -> "BookIt":
    """Add contextual notes to a variable.

    Args:
        variable_name: Name of the variable to add context to.
        text: Contextual text to add.

    Returns:
        self, for method chaining.

    Raises:
        ValueError: If variable is not found.
    """
    for var in self.variables:
        if var.name == variable_name:
            var.context = text
            return self

    raise ValueError(f"Variable '{variable_name}' not found in codebook.")

save(path=None)

Save the codebook to a file.

Parameters:

Name Type Description Default
path str | Path | None

Output file path. Uses the path from init if not provided.

None

Raises:

Type Description
ValueError

If no output path is available.

Source code in src/bookit_df/bookit.py
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
def save(self, path: str | Path | None = None) -> None:
    """Save the codebook to a file.

    Args:
        path: Output file path. Uses the path from __init__ if not provided.

    Raises:
        ValueError: If no output path is available.
    """
    output_path = Path(path) if path else self.output

    if output_path is None:
        raise ValueError(
            "No output path specified. Provide a path to save() or set "
            "output in BookIt()."
        )

    # Import renderer here to avoid circular imports
    from .renderers.pdf import PDFRenderer

    renderer = PDFRenderer(self)
    renderer.render(output_path)
    self._saved = True

__enter__()

Enter context manager.

Source code in src/bookit_df/bookit.py
272
273
274
def __enter__(self) -> "BookIt":
    """Enter context manager."""
    return self

__exit__(exc_type, exc_val, exc_tb)

Exit context manager, auto-saving if output path was set.

Source code in src/bookit_df/bookit.py
276
277
278
279
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
    """Exit context manager, auto-saving if output path was set."""
    if exc_type is None and self.output is not None and not self._saved:
        self.save()

__repr__()

Concise representation for debugging.

Source code in src/bookit_df/bookit.py
281
282
283
284
285
286
def __repr__(self) -> str:
    """Concise representation for debugging."""
    return (
        f"BookIt(title={self.title!r}, "
        f"variables={len(self.variables)})"
    )

Variable

bookit_df.Variable dataclass

Represents a single variable in the codebook.

Attributes:

Name Type Description
name str

The variable name (column name in DataFrame).

description str

Human-readable description of the variable.

dtype str

Data type of the variable.

values dict[Any, str]

Value labels for categorical variables (e.g., {1: "Male", 2: "Female"}).

stats VariableStats | None

Computed summary statistics.

context str

Additional contextual notes about the variable.

missing_codes list[Any]

Codes that represent missing values.

suppress_numeric_stats bool

If True, hide mean/std/min/max in output.

Example

var = Variable( ... name="age", ... description="Respondent's age in years", ... dtype="int64" ... )

Source code in src/bookit_df/variable.py
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
@dataclass
class Variable:
    """Represents a single variable in the codebook.

    Attributes:
        name: The variable name (column name in DataFrame).
        description: Human-readable description of the variable.
        dtype: Data type of the variable.
        values: Value labels for categorical variables (e.g., {1: "Male", 2: "Female"}).
        stats: Computed summary statistics.
        context: Additional contextual notes about the variable.
        missing_codes: Codes that represent missing values.
        suppress_numeric_stats: If True, hide mean/std/min/max in output.

    Example:
        >>> var = Variable(
        ...     name="age",
        ...     description="Respondent's age in years",
        ...     dtype="int64"
        ... )
    """

    name: str
    description: str = ""
    dtype: str = ""
    values: dict[Any, str] = field(default_factory=dict)
    stats: VariableStats | None = None
    context: str = ""
    missing_codes: list[Any] = field(default_factory=list)
    suppress_numeric_stats: bool = False
    chart_data: list[Any] | None = None  # Raw data for chart generation

    def __repr__(self) -> str:
        """Concise representation for debugging."""
        return f"Variable(name={self.name!r}, dtype={self.dtype!r})"

__repr__()

Concise representation for debugging.

Source code in src/bookit_df/variable.py
79
80
81
def __repr__(self) -> str:
    """Concise representation for debugging."""
    return f"Variable(name={self.name!r}, dtype={self.dtype!r})"

VariableStats

bookit_df.VariableStats dataclass

Summary statistics for a variable.

Attributes:

Name Type Description
count int

Total number of observations.

missing int

Number of missing values.

unique int

Number of unique values.

mean float | None

Mean value (numeric variables only).

std float | None

Standard deviation (numeric variables only).

min Any

Minimum value.

max Any

Maximum value.

top_values list[tuple[Any, int]]

Most frequent values with counts (categorical variables).

Example

stats = VariableStats(count=1000, missing=50, unique=10)

Source code in src/bookit_df/variable.py
 7
 8
 9
10
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
@dataclass
class VariableStats:
    """Summary statistics for a variable.

    Attributes:
        count: Total number of observations.
        missing: Number of missing values.
        unique: Number of unique values.
        mean: Mean value (numeric variables only).
        std: Standard deviation (numeric variables only).
        min: Minimum value.
        max: Maximum value.
        top_values: Most frequent values with counts (categorical variables).

    Example:
        >>> stats = VariableStats(count=1000, missing=50, unique=10)
    """

    count: int = 0
    missing: int = 0
    unique: int = 0
    mean: float | None = None
    std: float | None = None
    min: Any = None
    max: Any = None
    top_values: list[tuple[Any, int]] = field(default_factory=list)

    @property
    def valid(self) -> int:
        """Number of non-missing observations."""
        return self.count - self.missing

    @property
    def missing_pct(self) -> float:
        """Percentage of missing values."""
        if self.count == 0:
            return 0.0
        return (self.missing / self.count) * 100

missing_pct property

Percentage of missing values.

valid property

Number of non-missing observations.


CodebookConfig

bookit_df.CodebookConfig dataclass

Configuration settings for a codebook.

Attributes:

Name Type Description
title str

The title of the codebook (appears on title page and header).

author str

Author name(s) for the codebook.

date str

Date string for the codebook. Defaults to today's date.

include_toc bool

Whether to include a table of contents.

include_title_page bool

Whether to include a title page.

include_stats bool

Whether to include summary statistics for variables.

include_charts bool

Whether to include charts (bar charts for categorical, histograms for numeric variables).

Example

config = CodebookConfig( ... title="Survey Codebook", ... author="Research Team", ... include_toc=True ... )

Source code in src/bookit_df/config.py
 7
 8
 9
10
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
@dataclass
class CodebookConfig:
    """Configuration settings for a codebook.

    Attributes:
        title: The title of the codebook (appears on title page and header).
        author: Author name(s) for the codebook.
        date: Date string for the codebook. Defaults to today's date.
        include_toc: Whether to include a table of contents.
        include_title_page: Whether to include a title page.
        include_stats: Whether to include summary statistics for variables.
        include_charts: Whether to include charts (bar charts for categorical,
                        histograms for numeric variables).

    Example:
        >>> config = CodebookConfig(
        ...     title="Survey Codebook",
        ...     author="Research Team",
        ...     include_toc=True
        ... )
    """

    title: str = "Codebook"
    author: str = ""
    date: str = field(default_factory=lambda: date.today().isoformat())
    include_toc: bool = True
    include_title_page: bool = True
    include_stats: bool = True
    include_charts: bool = True