xlsxturbo 0.10.1__cp38-abi3-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xlsxturbo/__init__.py
ADDED
xlsxturbo/__init__.pyi
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Type stubs for xlsxturbo"""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, TypedDict
|
|
4
|
+
|
|
5
|
+
DateOrder = Literal["auto", "mdy", "us", "dmy", "eu", "european"]
|
|
6
|
+
ValidationType = Literal["list", "whole_number", "decimal", "text_length"]
|
|
7
|
+
|
|
8
|
+
class HeaderFormat(TypedDict, total=False):
|
|
9
|
+
"""Header cell formatting options. All fields are optional."""
|
|
10
|
+
bold: bool
|
|
11
|
+
italic: bool
|
|
12
|
+
font_color: str # '#RRGGBB' or named color (white, black, red, blue, etc.)
|
|
13
|
+
bg_color: str # '#RRGGBB' or named color
|
|
14
|
+
font_size: float
|
|
15
|
+
underline: bool
|
|
16
|
+
|
|
17
|
+
class ColumnFormat(TypedDict, total=False):
|
|
18
|
+
"""Column cell formatting options. All fields are optional."""
|
|
19
|
+
bold: bool
|
|
20
|
+
italic: bool
|
|
21
|
+
font_color: str # '#RRGGBB' or named color (white, black, red, blue, etc.)
|
|
22
|
+
bg_color: str # '#RRGGBB' or named color
|
|
23
|
+
font_size: float
|
|
24
|
+
underline: bool
|
|
25
|
+
num_format: str # Excel number format string, e.g. '0.00', '#,##0', '0.00%'
|
|
26
|
+
border: bool # Add thin border around cells
|
|
27
|
+
|
|
28
|
+
class ConditionalFormat(TypedDict, total=False):
|
|
29
|
+
"""Conditional formatting options for a column. 'type' is required.
|
|
30
|
+
|
|
31
|
+
Supported types:
|
|
32
|
+
- '2_color_scale': Gradient from min_color to max_color
|
|
33
|
+
- '3_color_scale': Gradient with min_color, mid_color, max_color
|
|
34
|
+
- 'data_bar': In-cell bar chart
|
|
35
|
+
- 'icon_set': Traffic lights, arrows, or other icons
|
|
36
|
+
"""
|
|
37
|
+
type: str # Required: '2_color_scale', '3_color_scale', 'data_bar', 'icon_set'
|
|
38
|
+
# For color scales:
|
|
39
|
+
min_color: str # '#RRGGBB' or named color for minimum value
|
|
40
|
+
mid_color: str # '#RRGGBB' or named color for midpoint (3_color_scale only)
|
|
41
|
+
max_color: str # '#RRGGBB' or named color for maximum value
|
|
42
|
+
# For data bars:
|
|
43
|
+
bar_color: str # '#RRGGBB' or named color for the bar fill
|
|
44
|
+
border_color: str # '#RRGGBB' or named color for bar border
|
|
45
|
+
solid: bool # True for solid fill, False for gradient (default)
|
|
46
|
+
direction: str # 'left_to_right', 'right_to_left', or 'context' (default)
|
|
47
|
+
# For icon sets:
|
|
48
|
+
icon_type: str # '3_arrows', '3_traffic_lights', '3_flags', '4_arrows', '5_arrows', etc. (see README for full list)
|
|
49
|
+
reverse: bool # Reverse icon order
|
|
50
|
+
icons_only: bool # Show only icons, hide values
|
|
51
|
+
|
|
52
|
+
class CommentOptions(TypedDict, total=False):
|
|
53
|
+
"""Options for cell comments/notes.
|
|
54
|
+
|
|
55
|
+
Note: 'text' is required at runtime but TypedDict doesn't enforce this.
|
|
56
|
+
"""
|
|
57
|
+
text: str # The comment text (required at runtime)
|
|
58
|
+
author: str # Author name for the comment
|
|
59
|
+
|
|
60
|
+
class ValidationOptions(TypedDict, total=False):
|
|
61
|
+
"""Data validation options for a column. 'type' is required.
|
|
62
|
+
|
|
63
|
+
Supported types:
|
|
64
|
+
- 'list': Dropdown with specified values
|
|
65
|
+
- 'whole_number': Integer between min and max
|
|
66
|
+
- 'decimal': Decimal number between min and max
|
|
67
|
+
- 'text_length': Text length between min and max
|
|
68
|
+
"""
|
|
69
|
+
type: ValidationType # Required: validation type
|
|
70
|
+
values: list[str] # For 'list' type: dropdown options
|
|
71
|
+
min: int | float # For number/text_length: minimum value
|
|
72
|
+
max: int | float # For number/text_length: maximum value
|
|
73
|
+
input_title: str # Title for input prompt
|
|
74
|
+
input_message: str # Message for input prompt
|
|
75
|
+
error_title: str # Title for error message
|
|
76
|
+
error_message: str # Message for error message
|
|
77
|
+
|
|
78
|
+
class RichTextFormat(TypedDict, total=False):
|
|
79
|
+
"""Format options for a rich text segment."""
|
|
80
|
+
bold: bool
|
|
81
|
+
italic: bool
|
|
82
|
+
font_color: str # '#RRGGBB' or named color
|
|
83
|
+
bg_color: str # '#RRGGBB' or named color
|
|
84
|
+
font_size: float
|
|
85
|
+
underline: bool
|
|
86
|
+
|
|
87
|
+
class ImageOptions(TypedDict, total=False):
|
|
88
|
+
"""Options for embedding images.
|
|
89
|
+
|
|
90
|
+
Note: 'path' is required at runtime but TypedDict doesn't enforce this.
|
|
91
|
+
"""
|
|
92
|
+
path: str # Path to image file - PNG, JPEG, GIF, BMP (required at runtime)
|
|
93
|
+
scale_width: float # Scale factor for width (1.0 = original)
|
|
94
|
+
scale_height: float # Scale factor for height (1.0 = original)
|
|
95
|
+
alt_text: str # Alternative text for accessibility
|
|
96
|
+
|
|
97
|
+
class SheetOptions(TypedDict, total=False):
|
|
98
|
+
"""Per-sheet options for dfs_to_xlsx. All fields are optional."""
|
|
99
|
+
header: bool
|
|
100
|
+
autofit: bool
|
|
101
|
+
table_style: str | None
|
|
102
|
+
freeze_panes: bool
|
|
103
|
+
column_widths: dict[int | str, float] | None # Keys: int index or '_all'
|
|
104
|
+
row_heights: dict[int, float] | None
|
|
105
|
+
table_name: str | None
|
|
106
|
+
header_format: HeaderFormat | None
|
|
107
|
+
column_formats: dict[str, ColumnFormat] | None # Pattern -> format. Patterns: 'prefix*', '*suffix', '*contains*', exact
|
|
108
|
+
conditional_formats: dict[str, ConditionalFormat] | None # Column name/pattern -> conditional format config
|
|
109
|
+
formula_columns: dict[str, str] | None # Column name -> Excel formula template with {row} placeholder
|
|
110
|
+
merged_ranges: list[tuple[str, str] | tuple[str, str, HeaderFormat]] | None # (range, text) or (range, text, format)
|
|
111
|
+
hyperlinks: list[tuple[str, str] | tuple[str, str, str]] | None # (cell, url) or (cell, url, display_text)
|
|
112
|
+
comments: dict[str, str | CommentOptions] | None # Cell ref -> comment text or options
|
|
113
|
+
validations: dict[str, ValidationOptions] | None # Column name/pattern -> validation options
|
|
114
|
+
rich_text: dict[str, list[tuple[str, RichTextFormat] | str]] | None # Cell ref -> list of (text, format) or plain text
|
|
115
|
+
images: dict[str, str | ImageOptions] | None # Cell ref -> image path or options
|
|
116
|
+
|
|
117
|
+
def csv_to_xlsx(
|
|
118
|
+
input_path: str,
|
|
119
|
+
output_path: str,
|
|
120
|
+
sheet_name: str = "Sheet1",
|
|
121
|
+
parallel: bool = False,
|
|
122
|
+
date_order: DateOrder = "auto",
|
|
123
|
+
) -> tuple[int, int]:
|
|
124
|
+
"""
|
|
125
|
+
Convert a CSV file to XLSX format with automatic type detection.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
input_path: Path to the input CSV file
|
|
129
|
+
output_path: Path for the output XLSX file
|
|
130
|
+
sheet_name: Name of the worksheet (default: "Sheet1")
|
|
131
|
+
parallel: Use multi-core parallel processing (default: False).
|
|
132
|
+
Faster for large files (100K+ rows) but uses more memory.
|
|
133
|
+
date_order: Date parsing order for ambiguous dates like "01-02-2024".
|
|
134
|
+
"auto" - ISO first, then European (DMY), then US (MDY)
|
|
135
|
+
"mdy" or "us" - US format: 01-02-2024 = January 2nd
|
|
136
|
+
"dmy" or "eu" - European format: 01-02-2024 = February 1st
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Tuple of (rows, columns) written to the Excel file
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
ValueError: If the conversion fails
|
|
143
|
+
"""
|
|
144
|
+
...
|
|
145
|
+
|
|
146
|
+
def df_to_xlsx(
|
|
147
|
+
df: object,
|
|
148
|
+
output_path: str,
|
|
149
|
+
sheet_name: str = "Sheet1",
|
|
150
|
+
header: bool = True,
|
|
151
|
+
autofit: bool = False,
|
|
152
|
+
table_style: str | None = None,
|
|
153
|
+
freeze_panes: bool = False,
|
|
154
|
+
column_widths: dict[int | str, float] | None = None,
|
|
155
|
+
table_name: str | None = None,
|
|
156
|
+
header_format: HeaderFormat | None = None,
|
|
157
|
+
row_heights: dict[int, float] | None = None,
|
|
158
|
+
constant_memory: bool = False,
|
|
159
|
+
column_formats: dict[str, ColumnFormat] | None = None,
|
|
160
|
+
conditional_formats: dict[str, ConditionalFormat] | None = None,
|
|
161
|
+
formula_columns: dict[str, str] | None = None,
|
|
162
|
+
merged_ranges: list[tuple[str, str] | tuple[str, str, HeaderFormat]] | None = None,
|
|
163
|
+
hyperlinks: list[tuple[str, str] | tuple[str, str, str]] | None = None,
|
|
164
|
+
comments: dict[str, str | CommentOptions] | None = None,
|
|
165
|
+
validations: dict[str, ValidationOptions] | None = None,
|
|
166
|
+
rich_text: dict[str, list[tuple[str, RichTextFormat] | str]] | None = None,
|
|
167
|
+
images: dict[str, str | ImageOptions] | None = None,
|
|
168
|
+
) -> tuple[int, int]:
|
|
169
|
+
"""
|
|
170
|
+
Convert a pandas or polars DataFrame to XLSX format.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
df: pandas DataFrame or polars DataFrame to export
|
|
174
|
+
output_path: Path for the output XLSX file
|
|
175
|
+
sheet_name: Name of the worksheet (default: "Sheet1")
|
|
176
|
+
header: Include column names as header row (default: True)
|
|
177
|
+
autofit: Automatically adjust column widths to fit content (default: False)
|
|
178
|
+
table_style: Apply Excel table formatting (default: None).
|
|
179
|
+
Styles: "Light1"-"Light21", "Medium1"-"Medium28", "Dark1"-"Dark11", "None".
|
|
180
|
+
freeze_panes: Freeze the header row for easier scrolling (default: False)
|
|
181
|
+
column_widths: Dict mapping column index to width. Use '_all' to cap all columns.
|
|
182
|
+
row_heights: Dict mapping row index to height in points.
|
|
183
|
+
constant_memory: Use streaming mode for minimal RAM usage (default: False).
|
|
184
|
+
table_name: Custom name for the Excel table (requires table_style).
|
|
185
|
+
header_format: Dict of header cell formatting options.
|
|
186
|
+
column_formats: Dict mapping column name patterns to format options.
|
|
187
|
+
Patterns: 'prefix*', '*suffix', '*contains*', or exact match.
|
|
188
|
+
First matching pattern wins (order preserved).
|
|
189
|
+
conditional_formats: Dict mapping column names to conditional format configs.
|
|
190
|
+
Supported types: '2_color_scale', '3_color_scale', 'data_bar', 'icon_set'.
|
|
191
|
+
Example: {'score': {'type': '2_color_scale', 'min_color': '#FF0000', 'max_color': '#00FF00'}}
|
|
192
|
+
formula_columns: Dict mapping new column names to Excel formula templates.
|
|
193
|
+
Use {row} placeholder for the current row number (1-based Excel row).
|
|
194
|
+
Example: {'Total': '=A{row}+B{row}', 'Percentage': '=C{row}/D{row}*100'}
|
|
195
|
+
merged_ranges: List of (range, text) or (range, text, format) tuples to merge cells.
|
|
196
|
+
Range uses Excel notation (e.g., 'A1:D1'). Format uses HeaderFormat options.
|
|
197
|
+
Example: [('A1:B1', 'Title'), ('C1:D1', 'Subtitle', {'bold': True})]
|
|
198
|
+
hyperlinks: List of (cell, url) or (cell, url, display_text) tuples to add clickable links.
|
|
199
|
+
Cell uses Excel notation (e.g., 'A1'). Display text is optional.
|
|
200
|
+
Example: [('A2', 'https://example.com'), ('B2', 'https://google.com', 'Google')]
|
|
201
|
+
comments: Dict mapping cell refs to comment text or CommentOptions.
|
|
202
|
+
Example: {'A1': 'Simple note'} or {'A1': {'text': 'Note', 'author': 'John'}}
|
|
203
|
+
validations: Dict mapping column name/pattern to data validation config.
|
|
204
|
+
Types: 'list' (dropdown), 'whole_number', 'decimal', 'text_length'.
|
|
205
|
+
Example: {'Status': {'type': 'list', 'values': ['Open', 'Closed']}}
|
|
206
|
+
rich_text: Dict mapping cell refs to list of (text, format) tuples or plain strings.
|
|
207
|
+
Example: {'A1': [('Bold', {'bold': True}), ' normal text']}
|
|
208
|
+
images: Dict mapping cell refs to image path or ImageOptions.
|
|
209
|
+
Example: {'B5': 'logo.png'} or {'B5': {'path': 'logo.png', 'scale_width': 0.5}}
|
|
210
|
+
"""
|
|
211
|
+
...
|
|
212
|
+
|
|
213
|
+
def dfs_to_xlsx(
|
|
214
|
+
sheets: list[tuple[object, str] | tuple[object, str, SheetOptions]],
|
|
215
|
+
output_path: str,
|
|
216
|
+
header: bool = True,
|
|
217
|
+
autofit: bool = False,
|
|
218
|
+
table_style: str | None = None,
|
|
219
|
+
freeze_panes: bool = False,
|
|
220
|
+
column_widths: dict[int | str, float] | None = None,
|
|
221
|
+
table_name: str | None = None,
|
|
222
|
+
header_format: HeaderFormat | None = None,
|
|
223
|
+
row_heights: dict[int, float] | None = None,
|
|
224
|
+
constant_memory: bool = False,
|
|
225
|
+
column_formats: dict[str, ColumnFormat] | None = None,
|
|
226
|
+
conditional_formats: dict[str, ConditionalFormat] | None = None,
|
|
227
|
+
formula_columns: dict[str, str] | None = None,
|
|
228
|
+
merged_ranges: list[tuple[str, str] | tuple[str, str, HeaderFormat]] | None = None,
|
|
229
|
+
hyperlinks: list[tuple[str, str] | tuple[str, str, str]] | None = None,
|
|
230
|
+
comments: dict[str, str | CommentOptions] | None = None,
|
|
231
|
+
validations: dict[str, ValidationOptions] | None = None,
|
|
232
|
+
rich_text: dict[str, list[tuple[str, RichTextFormat] | str]] | None = None,
|
|
233
|
+
images: dict[str, str | ImageOptions] | None = None,
|
|
234
|
+
) -> list[tuple[int, int]]:
|
|
235
|
+
"""
|
|
236
|
+
Write multiple DataFrames to separate sheets in a single workbook.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
sheets: List of (DataFrame, sheet_name) or (DataFrame, sheet_name, options) tuples.
|
|
240
|
+
output_path: Path for the output XLSX file
|
|
241
|
+
header: Include column names as header row (default: True)
|
|
242
|
+
autofit: Automatically adjust column widths (default: False)
|
|
243
|
+
table_style: Apply Excel table formatting (default: None).
|
|
244
|
+
freeze_panes: Freeze the header row (default: False)
|
|
245
|
+
column_widths: Dict mapping column index to width. Use '_all' to cap all columns.
|
|
246
|
+
row_heights: Dict mapping row index to height in points.
|
|
247
|
+
constant_memory: Use streaming mode (default: False).
|
|
248
|
+
table_name: Custom name for Excel tables (requires table_style).
|
|
249
|
+
header_format: Dict of header cell formatting options.
|
|
250
|
+
column_formats: Dict mapping column name patterns to format options.
|
|
251
|
+
Patterns: 'prefix*', '*suffix', '*contains*', or exact match.
|
|
252
|
+
conditional_formats: Dict mapping column names to conditional format configs.
|
|
253
|
+
Supported types: '2_color_scale', '3_color_scale', 'data_bar', 'icon_set'.
|
|
254
|
+
formula_columns: Dict mapping new column names to Excel formula templates.
|
|
255
|
+
Use {row} placeholder for the current row number (1-based Excel row).
|
|
256
|
+
merged_ranges: List of (range, text) or (range, text, format) tuples to merge cells.
|
|
257
|
+
Range uses Excel notation (e.g., 'A1:D1'). Format uses HeaderFormat options.
|
|
258
|
+
hyperlinks: List of (cell, url) or (cell, url, display_text) tuples to add clickable links.
|
|
259
|
+
Cell uses Excel notation (e.g., 'A1'). Display text is optional.
|
|
260
|
+
comments: Dict mapping cell refs to comment text or CommentOptions.
|
|
261
|
+
validations: Dict mapping column name/pattern to data validation config.
|
|
262
|
+
rich_text: Dict mapping cell refs to list of (text, format) tuples or plain strings.
|
|
263
|
+
images: Dict mapping cell refs to image path or ImageOptions.
|
|
264
|
+
"""
|
|
265
|
+
...
|
|
266
|
+
|
|
267
|
+
def version() -> str:
|
|
268
|
+
"""Get the version of the xlsxturbo library."""
|
|
269
|
+
...
|
|
270
|
+
|
|
271
|
+
__version__: str
|
|
Binary file
|
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xlsxturbo
|
|
3
|
+
Version: 0.10.1
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: Intended Audience :: Science/Research
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Rust
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
20
|
+
Requires-Dist: pandas ; extra == 'dev'
|
|
21
|
+
Requires-Dist: polars ; extra == 'dev'
|
|
22
|
+
Requires-Dist: openpyxl ; extra == 'dev'
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Summary: High-performance Excel writer with automatic type detection (pandas, polars, CSV)
|
|
26
|
+
Keywords: excel,xlsx,csv,converter,performance,rust
|
|
27
|
+
License: MIT
|
|
28
|
+
Requires-Python: >=3.8
|
|
29
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
30
|
+
Project-URL: Homepage, https://github.com/tstone-1/xlsxturbo
|
|
31
|
+
Project-URL: Issues, https://github.com/tstone-1/xlsxturbo/issues
|
|
32
|
+
Project-URL: Repository, https://github.com/tstone-1/xlsxturbo
|
|
33
|
+
|
|
34
|
+
# xlsxturbo
|
|
35
|
+
|
|
36
|
+
High-performance Excel writer with automatic type detection. Written in Rust, usable from Python.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **Direct DataFrame support** for pandas and polars
|
|
41
|
+
- **Excel tables** - filterable tables with 61 built-in styles (banded rows, autofilter)
|
|
42
|
+
- **Conditional formatting** - color scales, data bars, icon sets for visual data analysis
|
|
43
|
+
- **Formula columns** - add calculated columns with Excel formulas
|
|
44
|
+
- **Merged cells** - merge cell ranges for headers and titles
|
|
45
|
+
- **Hyperlinks** - add clickable links to cells
|
|
46
|
+
- **Comments/Notes** - add cell annotations with optional author
|
|
47
|
+
- **Data validation** - dropdowns, number ranges, text length constraints
|
|
48
|
+
- **Rich text** - multiple formats within a single cell
|
|
49
|
+
- **Images** - embed PNG, JPEG, GIF, BMP in cells
|
|
50
|
+
- **Auto-fit columns** - automatically adjust column widths to fit content
|
|
51
|
+
- **Custom column widths** - set specific widths per column or cap all with _all
|
|
52
|
+
- **Header styling** - bold, colors, font size for header row
|
|
53
|
+
- **Named tables** - set custom table names
|
|
54
|
+
- **Custom row heights** - set specific heights per row
|
|
55
|
+
- **Freeze panes** - freeze header row for easier scrolling
|
|
56
|
+
- **Multi-sheet workbooks** - write multiple DataFrames to one file
|
|
57
|
+
- **Per-sheet options** - override settings per sheet in multi-sheet workbooks
|
|
58
|
+
- **Constant memory mode** - minimize RAM usage for very large files
|
|
59
|
+
- **Parallel CSV processing** - optional multi-core parsing for large files
|
|
60
|
+
- **Automatic type detection** from CSV strings and Python objects:
|
|
61
|
+
- Integers and floats → Excel numbers
|
|
62
|
+
- `true`/`false` → Excel booleans
|
|
63
|
+
- Dates (`2024-01-15`, `15/01/2024`, etc.) → Excel dates with formatting
|
|
64
|
+
- Datetimes (ISO 8601) → Excel datetimes
|
|
65
|
+
- `NaN`/`Inf` → Empty cells (graceful handling)
|
|
66
|
+
- Everything else → Text
|
|
67
|
+
- **~6x faster** than pandas + openpyxl (see [benchmarks](#performance))
|
|
68
|
+
- **Memory efficient** - streams data with 1MB buffer
|
|
69
|
+
- Available as both **Python library** and **CLI tool**
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install xlsxturbo
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Or build from source:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install maturin
|
|
81
|
+
maturin develop --release
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Python Usage
|
|
85
|
+
|
|
86
|
+
### DataFrame Export (pandas/polars)
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import xlsxturbo
|
|
90
|
+
import pandas as pd
|
|
91
|
+
|
|
92
|
+
# Create a DataFrame
|
|
93
|
+
df = pd.DataFrame({
|
|
94
|
+
'name': ['Alice', 'Bob'],
|
|
95
|
+
'age': [30, 25],
|
|
96
|
+
'salary': [50000.50, 60000.75],
|
|
97
|
+
'active': [True, False]
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
# Export to XLSX (preserves types: int, float, bool, date, datetime)
|
|
101
|
+
rows, cols = xlsxturbo.df_to_xlsx(df, "output.xlsx")
|
|
102
|
+
print(f"Wrote {rows} rows and {cols} columns")
|
|
103
|
+
|
|
104
|
+
# Works with polars too!
|
|
105
|
+
import polars as pl
|
|
106
|
+
df_polars = pl.DataFrame({'x': [1, 2, 3], 'y': [4.0, 5.0, 6.0]})
|
|
107
|
+
xlsxturbo.df_to_xlsx(df_polars, "polars_output.xlsx", sheet_name="Data")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Excel Tables with Styling
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import xlsxturbo
|
|
114
|
+
import pandas as pd
|
|
115
|
+
|
|
116
|
+
df = pd.DataFrame({
|
|
117
|
+
'Product': ['Widget A', 'Widget B', 'Widget C'],
|
|
118
|
+
'Price': [19.99, 29.99, 39.99],
|
|
119
|
+
'Quantity': [100, 75, 50],
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
# Create a styled Excel table with autofilter, banded rows, and auto-fit columns
|
|
123
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
124
|
+
table_style="Medium9", # Excel's default table style
|
|
125
|
+
autofit=True, # Fit column widths to content
|
|
126
|
+
freeze_panes=True # Freeze header row for scrolling
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Available styles: Light1-Light21, Medium1-Medium28, Dark1-Dark11
|
|
130
|
+
xlsxturbo.df_to_xlsx(df, "dark_table.xlsx", table_style="Dark1", autofit=True)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Custom Column Widths and Row Heights
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
import xlsxturbo
|
|
137
|
+
import pandas as pd
|
|
138
|
+
|
|
139
|
+
df = pd.DataFrame({
|
|
140
|
+
'Name': ['Alice', 'Bob', 'Charlie'],
|
|
141
|
+
'Department': ['Engineering', 'Marketing', 'Sales'],
|
|
142
|
+
'Salary': [75000, 65000, 55000]
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
# Set specific column widths (column index -> width in characters)
|
|
146
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
147
|
+
column_widths={0: 20, 1: 25, 2: 15}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Set specific row heights (row index -> height in points)
|
|
151
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
152
|
+
row_heights={0: 25} # Make header row taller
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Combine with other options
|
|
156
|
+
xlsxturbo.df_to_xlsx(df, "styled.xlsx",
|
|
157
|
+
table_style="Medium9",
|
|
158
|
+
freeze_panes=True,
|
|
159
|
+
column_widths={0: 20, 1: 30, 2: 15},
|
|
160
|
+
row_heights={0: 22}
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Global Column Width Cap
|
|
165
|
+
|
|
166
|
+
Use `column_widths={'_all': value}` to cap all columns at a maximum width:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
import xlsxturbo
|
|
170
|
+
import pandas as pd
|
|
171
|
+
|
|
172
|
+
df = pd.DataFrame({
|
|
173
|
+
'Name': ['Alice', 'Bob'],
|
|
174
|
+
'VeryLongDescription': ['A' * 100, 'B' * 100],
|
|
175
|
+
'Score': [95, 87]
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
# Cap all columns at 30 characters
|
|
179
|
+
xlsxturbo.df_to_xlsx(df, "capped.xlsx", column_widths={'_all': 30})
|
|
180
|
+
|
|
181
|
+
# Mix specific widths with global cap (specific overrides '_all')
|
|
182
|
+
xlsxturbo.df_to_xlsx(df, "mixed.xlsx", column_widths={0: 15, '_all': 30})
|
|
183
|
+
|
|
184
|
+
# Autofit with cap: fit content, but never exceed 25 characters
|
|
185
|
+
xlsxturbo.df_to_xlsx(df, "fitted.xlsx", autofit=True, column_widths={'_all': 25})
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Named Excel Tables
|
|
189
|
+
|
|
190
|
+
Set custom names for Excel tables:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
import xlsxturbo
|
|
194
|
+
import pandas as pd
|
|
195
|
+
|
|
196
|
+
df = pd.DataFrame({'Product': ['A', 'B'], 'Price': [10, 20]})
|
|
197
|
+
|
|
198
|
+
# Name the Excel table
|
|
199
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
200
|
+
table_style="Medium2",
|
|
201
|
+
table_name="ProductPrices"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Invalid characters are auto-sanitized, digits get underscore prefix
|
|
205
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
206
|
+
table_style="Medium2",
|
|
207
|
+
table_name="2024 Sales Data!" # Becomes "_2024_Sales_Data_"
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Header Styling
|
|
212
|
+
|
|
213
|
+
Apply custom formatting to header cells:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
import xlsxturbo
|
|
217
|
+
import pandas as pd
|
|
218
|
+
|
|
219
|
+
df = pd.DataFrame({'Name': ['Alice', 'Bob'], 'Score': [95, 87]})
|
|
220
|
+
|
|
221
|
+
# Bold headers
|
|
222
|
+
xlsxturbo.df_to_xlsx(df, "bold.xlsx", header_format={'bold': True})
|
|
223
|
+
|
|
224
|
+
# Full styling with colors
|
|
225
|
+
xlsxturbo.df_to_xlsx(df, "styled.xlsx", header_format={
|
|
226
|
+
'bold': True,
|
|
227
|
+
'bg_color': '#4F81BD', # Blue background
|
|
228
|
+
'font_color': 'white' # White text
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
# Available options:
|
|
232
|
+
# - bold (bool): Bold text
|
|
233
|
+
# - italic (bool): Italic text
|
|
234
|
+
# - font_color (str): '#RRGGBB' or named color (white, black, red, blue, etc.)
|
|
235
|
+
# - bg_color (str): Background color
|
|
236
|
+
# - font_size (float): Font size in points
|
|
237
|
+
# - underline (bool): Underlined text
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Column Formatting
|
|
241
|
+
|
|
242
|
+
Apply formatting to data columns using pattern matching:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
import xlsxturbo
|
|
246
|
+
import pandas as pd
|
|
247
|
+
|
|
248
|
+
df = pd.DataFrame({
|
|
249
|
+
'product_id': [1, 2, 3],
|
|
250
|
+
'product_name': ['Widget A', 'Widget B', 'Widget C'],
|
|
251
|
+
'price_usd': [19.99, 29.99, 39.99],
|
|
252
|
+
'price_eur': [17.99, 26.99, 35.99],
|
|
253
|
+
'quantity': [100, 75, 50]
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
# Format columns by pattern
|
|
257
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx", column_formats={
|
|
258
|
+
'price_*': {'num_format': '$#,##0.00', 'bg_color': '#E8F5E9'}, # All price columns
|
|
259
|
+
'quantity': {'bold': True} # Exact match
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
# Wildcard patterns:
|
|
263
|
+
# - 'prefix*' matches columns starting with 'prefix'
|
|
264
|
+
# - '*suffix' matches columns ending with 'suffix'
|
|
265
|
+
# - '*contains*' matches columns containing 'contains'
|
|
266
|
+
# - 'exact' matches column name exactly
|
|
267
|
+
|
|
268
|
+
# Available format options:
|
|
269
|
+
# - bg_color (str): Background color ('#RRGGBB' or named)
|
|
270
|
+
# - font_color (str): Text color
|
|
271
|
+
# - num_format (str): Excel number format ('0.00', '#,##0', '0.00%', etc.)
|
|
272
|
+
# - bold (bool): Bold text
|
|
273
|
+
# - italic (bool): Italic text
|
|
274
|
+
# - underline (bool): Underlined text
|
|
275
|
+
# - border (bool): Add thin border
|
|
276
|
+
|
|
277
|
+
# First matching pattern wins (order preserved)
|
|
278
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx", column_formats={
|
|
279
|
+
'price_usd': {'bg_color': '#FFEB3B'}, # Specific: yellow for USD
|
|
280
|
+
'price_*': {'bg_color': '#E3F2FD'} # General: blue for other prices
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Multi-Sheet Workbooks
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
import xlsxturbo
|
|
288
|
+
import pandas as pd
|
|
289
|
+
|
|
290
|
+
# Write multiple DataFrames to separate sheets
|
|
291
|
+
df1 = pd.DataFrame({'product': ['A', 'B'], 'sales': [100, 200]})
|
|
292
|
+
df2 = pd.DataFrame({'region': ['East', 'West'], 'total': [500, 600]})
|
|
293
|
+
|
|
294
|
+
xlsxturbo.dfs_to_xlsx([
|
|
295
|
+
(df1, "Products"),
|
|
296
|
+
(df2, "Regions")
|
|
297
|
+
], "report.xlsx")
|
|
298
|
+
|
|
299
|
+
# With styling applied to all sheets
|
|
300
|
+
xlsxturbo.dfs_to_xlsx([
|
|
301
|
+
(df1, "Products"),
|
|
302
|
+
(df2, "Regions")
|
|
303
|
+
], "styled_report.xlsx", table_style="Medium2", autofit=True, freeze_panes=True)
|
|
304
|
+
|
|
305
|
+
# With column widths applied to all sheets
|
|
306
|
+
xlsxturbo.dfs_to_xlsx([
|
|
307
|
+
(df1, "Products"),
|
|
308
|
+
(df2, "Regions")
|
|
309
|
+
], "report.xlsx", column_widths={0: 20, 1: 15})
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Per-Sheet Options
|
|
313
|
+
|
|
314
|
+
Override global settings for individual sheets using a 3-tuple with options dict:
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
import xlsxturbo
|
|
318
|
+
import pandas as pd
|
|
319
|
+
|
|
320
|
+
df_data = pd.DataFrame({'Product': ['A', 'B'], 'Price': [10, 20]})
|
|
321
|
+
df_instructions = pd.DataFrame({'Step': [1, 2], 'Action': ['Open file', 'Review data']})
|
|
322
|
+
|
|
323
|
+
# Different settings per sheet:
|
|
324
|
+
# - "Data" sheet: has header, table style, autofit
|
|
325
|
+
# - "Instructions" sheet: no header (raw data), no table style
|
|
326
|
+
xlsxturbo.dfs_to_xlsx([
|
|
327
|
+
(df_data, "Data", {"header": True, "table_style": "Medium2"}),
|
|
328
|
+
(df_instructions, "Instructions", {"header": False, "table_style": None})
|
|
329
|
+
], "report.xlsx", autofit=True)
|
|
330
|
+
|
|
331
|
+
# Old 2-tuple API still works - uses global defaults
|
|
332
|
+
xlsxturbo.dfs_to_xlsx([
|
|
333
|
+
(df_data, "Sheet1"), # Uses global header=True, table_style=None
|
|
334
|
+
(df_instructions, "Sheet2", {"header": False}) # Override just header
|
|
335
|
+
], "mixed.xlsx", header=True, autofit=True)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Available per-sheet options:
|
|
339
|
+
- `header` (bool): Include column names as header row
|
|
340
|
+
- `autofit` (bool): Automatically adjust column widths
|
|
341
|
+
- `table_style` (str|None): Excel table style or None to disable
|
|
342
|
+
- `freeze_panes` (bool): Freeze header row
|
|
343
|
+
- `column_widths` (dict): Custom column widths
|
|
344
|
+
- `row_heights` (dict): Custom row heights
|
|
345
|
+
- `table_name` (str): Custom Excel table name
|
|
346
|
+
- `header_format` (dict): Header cell styling
|
|
347
|
+
- `column_formats` (dict): Column formatting with pattern matching
|
|
348
|
+
- `conditional_formats` (dict): Conditional formatting (color scales, data bars, icons)
|
|
349
|
+
- `formula_columns` (dict): Calculated columns with Excel formulas (column name -> formula template)
|
|
350
|
+
- `merged_ranges` (list): List of (range, text) or (range, text, format) tuples to merge cells
|
|
351
|
+
- `hyperlinks` (list): List of (cell, url) or (cell, url, display_text) tuples to add clickable links
|
|
352
|
+
- `comments` (dict): Cell comments/notes (cell_ref -> text or {text, author})
|
|
353
|
+
- `validations` (dict): Data validation rules (column name/pattern -> validation config)
|
|
354
|
+
- `rich_text` (dict): Rich text with multiple formats (cell_ref -> list of segments)
|
|
355
|
+
- `images` (dict): Embedded images (cell_ref -> path or {path, scale_width, scale_height, alt_text})
|
|
356
|
+
|
|
357
|
+
### Conditional Formatting
|
|
358
|
+
|
|
359
|
+
Apply visual formatting based on cell values:
|
|
360
|
+
|
|
361
|
+
```python
|
|
362
|
+
import xlsxturbo
|
|
363
|
+
import pandas as pd
|
|
364
|
+
|
|
365
|
+
df = pd.DataFrame({
|
|
366
|
+
'name': ['Alice', 'Bob', 'Charlie', 'Diana'],
|
|
367
|
+
'score': [95, 72, 88, 45],
|
|
368
|
+
'progress': [0.9, 0.5, 0.75, 0.3],
|
|
369
|
+
'status': [3, 2, 3, 1]
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
373
|
+
autofit=True,
|
|
374
|
+
conditional_formats={
|
|
375
|
+
# 2-color gradient: red (low) to green (high)
|
|
376
|
+
'score': {
|
|
377
|
+
'type': '2_color_scale',
|
|
378
|
+
'min_color': '#FF6B6B',
|
|
379
|
+
'max_color': '#51CF66'
|
|
380
|
+
},
|
|
381
|
+
# Data bars: in-cell bar chart
|
|
382
|
+
'progress': {
|
|
383
|
+
'type': 'data_bar',
|
|
384
|
+
'bar_color': '#339AF0',
|
|
385
|
+
'solid': True # Solid fill instead of gradient
|
|
386
|
+
},
|
|
387
|
+
# Icon set: traffic lights
|
|
388
|
+
'status': {
|
|
389
|
+
'type': 'icon_set',
|
|
390
|
+
'icon_type': '3_traffic_lights'
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Supported conditional format types:**
|
|
397
|
+
|
|
398
|
+
| Type | Options |
|
|
399
|
+
|------|---------|
|
|
400
|
+
| `2_color_scale` | `min_color`, `max_color` |
|
|
401
|
+
| `3_color_scale` | `min_color`, `mid_color`, `max_color` |
|
|
402
|
+
| `data_bar` | `bar_color`, `border_color`, `solid`, `direction` |
|
|
403
|
+
| `icon_set` | `icon_type`, `reverse`, `icons_only` |
|
|
404
|
+
|
|
405
|
+
**Available icon types:**
|
|
406
|
+
- 3 icons: `3_arrows`, `3_arrows_gray`, `3_flags`, `3_traffic_lights`, `3_traffic_lights_rimmed`, `3_signs`, `3_symbols`, `3_symbols_uncircled`
|
|
407
|
+
- 4 icons: `4_arrows`, `4_arrows_gray`, `4_traffic_lights`, `4_rating`
|
|
408
|
+
- 5 icons: `5_arrows`, `5_arrows_gray`, `5_quarters`, `5_rating`
|
|
409
|
+
|
|
410
|
+
Column patterns work with conditional formats:
|
|
411
|
+
```python
|
|
412
|
+
# Apply data bars to all columns starting with "price_"
|
|
413
|
+
conditional_formats={'price_*': {'type': 'data_bar', 'bar_color': '#9B59B6'}}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Formula Columns
|
|
417
|
+
|
|
418
|
+
Add calculated columns to your Excel output. Formulas are written after data columns and use `{row}` as a placeholder for the row number:
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
import xlsxturbo
|
|
422
|
+
import pandas as pd
|
|
423
|
+
|
|
424
|
+
df = pd.DataFrame({
|
|
425
|
+
'price': [100, 200, 150],
|
|
426
|
+
'quantity': [5, 3, 8],
|
|
427
|
+
'tax_rate': [0.1, 0.1, 0.2]
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
xlsxturbo.df_to_xlsx(df, "sales.xlsx",
|
|
431
|
+
autofit=True,
|
|
432
|
+
formula_columns={
|
|
433
|
+
'Subtotal': '=A{row}*B{row}', # price * quantity
|
|
434
|
+
'Tax': '=D{row}*C{row}', # subtotal * tax_rate
|
|
435
|
+
'Total': '=D{row}+E{row}' # subtotal + tax
|
|
436
|
+
}
|
|
437
|
+
)
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Formula columns appear after data columns (A=price, B=quantity, C=tax_rate, D=Subtotal, E=Tax, F=Total).
|
|
441
|
+
|
|
442
|
+
**Notes:**
|
|
443
|
+
- `{row}` is replaced with the Excel row number (1-based, starting at 2 for data rows when header=True)
|
|
444
|
+
- Formula columns inherit header formatting if specified
|
|
445
|
+
- Column order is preserved (first formula = first new column)
|
|
446
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
447
|
+
|
|
448
|
+
### Merged Cells
|
|
449
|
+
|
|
450
|
+
Merge cell ranges to create headers, titles, or grouped labels:
|
|
451
|
+
|
|
452
|
+
```python
|
|
453
|
+
import xlsxturbo
|
|
454
|
+
import pandas as pd
|
|
455
|
+
|
|
456
|
+
df = pd.DataFrame({
|
|
457
|
+
'product': ['Widget A', 'Widget B'],
|
|
458
|
+
'sales': [1500, 2300],
|
|
459
|
+
'revenue': [7500, 11500]
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
# Merge cells for a title above the data
|
|
463
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
464
|
+
header=True,
|
|
465
|
+
merged_ranges=[
|
|
466
|
+
# Simple merge with text (auto-centered)
|
|
467
|
+
('A1:C1', 'Q4 Sales Report'),
|
|
468
|
+
# Merge with custom formatting
|
|
469
|
+
('A2:C2', 'Regional Data', {
|
|
470
|
+
'bold': True,
|
|
471
|
+
'bg_color': '#4F81BD',
|
|
472
|
+
'font_color': 'white'
|
|
473
|
+
})
|
|
474
|
+
]
|
|
475
|
+
)
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**Merged range format:**
|
|
479
|
+
- Tuple of `(range, text)` or `(range, text, format_dict)`
|
|
480
|
+
- Range uses Excel notation: `'A1:D1'`, `'B3:B10'`, etc.
|
|
481
|
+
- Format options same as `header_format`: bold, italic, font_color, bg_color, font_size, underline
|
|
482
|
+
|
|
483
|
+
**Notes:**
|
|
484
|
+
- Merged cells are applied after data is written, so plan row positions accordingly
|
|
485
|
+
- When using with `header=True`, data starts at row 2 (Excel row 2)
|
|
486
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
487
|
+
|
|
488
|
+
### Hyperlinks
|
|
489
|
+
|
|
490
|
+
Add clickable links to cells:
|
|
491
|
+
|
|
492
|
+
```python
|
|
493
|
+
import xlsxturbo
|
|
494
|
+
import pandas as pd
|
|
495
|
+
|
|
496
|
+
df = pd.DataFrame({
|
|
497
|
+
'company': ['Anthropic', 'Google', 'Microsoft'],
|
|
498
|
+
'product': ['Claude', 'Gemini', 'Copilot'],
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
# Add hyperlinks to a new column (D) after the data columns (A, B, C with header)
|
|
502
|
+
xlsxturbo.df_to_xlsx(df, "companies.xlsx",
|
|
503
|
+
autofit=True,
|
|
504
|
+
hyperlinks=[
|
|
505
|
+
# Header for the links column
|
|
506
|
+
('C1', 'https://example.com', 'Website'),
|
|
507
|
+
# Links with company names as display text
|
|
508
|
+
('C2', 'https://anthropic.com', 'anthropic.com'),
|
|
509
|
+
('C3', 'https://google.com', 'google.com'),
|
|
510
|
+
('C4', 'https://microsoft.com', 'microsoft.com'),
|
|
511
|
+
]
|
|
512
|
+
)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Hyperlink format:**
|
|
516
|
+
- Tuple of `(cell, url)` or `(cell, url, display_text)`
|
|
517
|
+
- Cell uses Excel notation: `'A1'`, `'B5'`, etc.
|
|
518
|
+
- Display text is optional; if omitted, the URL is shown
|
|
519
|
+
|
|
520
|
+
**Notes:**
|
|
521
|
+
- Hyperlinks write to the specified cell position (overwrites existing content)
|
|
522
|
+
- To add a "links column", target cells beyond your DataFrame columns (as shown above)
|
|
523
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
524
|
+
- Not available in constant memory mode
|
|
525
|
+
|
|
526
|
+
### Comments/Notes
|
|
527
|
+
|
|
528
|
+
Add cell annotations (hover to view):
|
|
529
|
+
|
|
530
|
+
```python
|
|
531
|
+
import xlsxturbo
|
|
532
|
+
import pandas as pd
|
|
533
|
+
|
|
534
|
+
df = pd.DataFrame({
|
|
535
|
+
'product': ['Widget A', 'Widget B'],
|
|
536
|
+
'price': [19.99, 29.99]
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
xlsxturbo.df_to_xlsx(df, "report.xlsx",
|
|
540
|
+
comments={
|
|
541
|
+
# Simple text comment
|
|
542
|
+
'A1': 'This column contains product names',
|
|
543
|
+
# Comment with author
|
|
544
|
+
'B1': {'text': 'Prices in USD', 'author': 'Finance Team'}
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Comment format:**
|
|
550
|
+
- Simple: `{'A1': 'Note text'}`
|
|
551
|
+
- With author: `{'A1': {'text': 'Note text', 'author': 'Name'}}`
|
|
552
|
+
|
|
553
|
+
**Notes:**
|
|
554
|
+
- Comments appear as small red triangles in the cell corner
|
|
555
|
+
- Hover over the cell to see the comment
|
|
556
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
557
|
+
- Not available in constant memory mode
|
|
558
|
+
|
|
559
|
+
### Data Validation
|
|
560
|
+
|
|
561
|
+
Add dropdowns and input constraints:
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
import xlsxturbo
|
|
565
|
+
import pandas as pd
|
|
566
|
+
|
|
567
|
+
df = pd.DataFrame({
|
|
568
|
+
'status': ['Open', 'Closed'],
|
|
569
|
+
'score': [85, 92],
|
|
570
|
+
'price': [19.99, 29.99],
|
|
571
|
+
'code': ['ABC', 'XYZ']
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
xlsxturbo.df_to_xlsx(df, "validated.xlsx",
|
|
575
|
+
validations={
|
|
576
|
+
# Dropdown list
|
|
577
|
+
'status': {
|
|
578
|
+
'type': 'list',
|
|
579
|
+
'values': ['Open', 'Closed', 'Pending', 'Review']
|
|
580
|
+
},
|
|
581
|
+
# Whole number range (0-100)
|
|
582
|
+
'score': {
|
|
583
|
+
'type': 'whole_number',
|
|
584
|
+
'min': 0,
|
|
585
|
+
'max': 100,
|
|
586
|
+
'error_title': 'Invalid Score',
|
|
587
|
+
'error_message': 'Score must be between 0 and 100'
|
|
588
|
+
},
|
|
589
|
+
# Decimal range
|
|
590
|
+
'price': {
|
|
591
|
+
'type': 'decimal',
|
|
592
|
+
'min': 0.0,
|
|
593
|
+
'max': 999.99
|
|
594
|
+
},
|
|
595
|
+
# Text length constraint
|
|
596
|
+
'code': {
|
|
597
|
+
'type': 'text_length',
|
|
598
|
+
'min': 3,
|
|
599
|
+
'max': 10
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
)
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**Validation types:**
|
|
606
|
+
|
|
607
|
+
| Type | Aliases | Description | Options |
|
|
608
|
+
|------|---------|-------------|---------|
|
|
609
|
+
| `list` | - | Dropdown menu | `values` (list of strings, max 255 chars total) |
|
|
610
|
+
| `whole_number` | `whole`, `integer` | Integer range | `min`, `max` |
|
|
611
|
+
| `decimal` | `number` | Decimal range | `min`, `max` |
|
|
612
|
+
| `text_length` | `textlength`, `length` | Character count | `min`, `max` |
|
|
613
|
+
|
|
614
|
+
**Optional message options:**
|
|
615
|
+
- `input_title`, `input_message`: Prompt shown when cell is selected
|
|
616
|
+
- `error_title`, `error_message`: Message shown when invalid data is entered
|
|
617
|
+
|
|
618
|
+
**Notes:**
|
|
619
|
+
- Validations apply to the data rows of the specified column
|
|
620
|
+
- Column patterns work: `'score_*': {...}` matches all columns starting with `score_`
|
|
621
|
+
- If only `min` or only `max` is specified, the other defaults to the type's extreme value
|
|
622
|
+
- List validation values are limited to 255 total characters (Excel limitation)
|
|
623
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
624
|
+
- Not available in constant memory mode
|
|
625
|
+
|
|
626
|
+
### Rich Text
|
|
627
|
+
|
|
628
|
+
Multiple formats within a single cell:
|
|
629
|
+
|
|
630
|
+
```python
|
|
631
|
+
import xlsxturbo
|
|
632
|
+
import pandas as pd
|
|
633
|
+
|
|
634
|
+
df = pd.DataFrame({'A': [1, 2, 3]})
|
|
635
|
+
|
|
636
|
+
xlsxturbo.df_to_xlsx(df, "rich.xlsx",
|
|
637
|
+
rich_text={
|
|
638
|
+
'D1': [
|
|
639
|
+
('Important: ', {'bold': True, 'font_color': 'red'}),
|
|
640
|
+
'Please review ',
|
|
641
|
+
('all', {'italic': True}),
|
|
642
|
+
' values'
|
|
643
|
+
],
|
|
644
|
+
'D2': [
|
|
645
|
+
('Status: ', {'bold': True}),
|
|
646
|
+
('OK', {'font_color': 'green', 'bold': True})
|
|
647
|
+
]
|
|
648
|
+
}
|
|
649
|
+
)
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Segment format:**
|
|
653
|
+
- Formatted: `('text', {'bold': True, 'font_color': 'blue'})`
|
|
654
|
+
- Plain: `'plain text'` (no formatting)
|
|
655
|
+
|
|
656
|
+
**Available format options:**
|
|
657
|
+
- `bold` (bool)
|
|
658
|
+
- `italic` (bool)
|
|
659
|
+
- `font_color` (str): '#RRGGBB' or named color
|
|
660
|
+
- `bg_color` (str): Background color
|
|
661
|
+
- `font_size` (float)
|
|
662
|
+
- `underline` (bool)
|
|
663
|
+
|
|
664
|
+
**Notes:**
|
|
665
|
+
- Rich text writes to the specified cell position (overwrites existing content)
|
|
666
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
667
|
+
- Not available in constant memory mode
|
|
668
|
+
|
|
669
|
+
### Images
|
|
670
|
+
|
|
671
|
+
Embed images in cells:
|
|
672
|
+
|
|
673
|
+
```python
|
|
674
|
+
import xlsxturbo
|
|
675
|
+
import pandas as pd
|
|
676
|
+
|
|
677
|
+
df = pd.DataFrame({'Product': ['Widget A', 'Widget B'], 'Price': [19.99, 29.99]})
|
|
678
|
+
|
|
679
|
+
xlsxturbo.df_to_xlsx(df, "catalog.xlsx",
|
|
680
|
+
autofit=True,
|
|
681
|
+
images={
|
|
682
|
+
# Simple path
|
|
683
|
+
'C2': 'images/widget_a.png',
|
|
684
|
+
# With options
|
|
685
|
+
'C3': {
|
|
686
|
+
'path': 'images/widget_b.png',
|
|
687
|
+
'scale_width': 0.5,
|
|
688
|
+
'scale_height': 0.5,
|
|
689
|
+
'alt_text': 'Widget B photo'
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
)
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
**Image format:**
|
|
696
|
+
- Simple: `{'C2': 'path/to/image.png'}`
|
|
697
|
+
- With options: `{'C2': {'path': '...', 'scale_width': 0.5, ...}}`
|
|
698
|
+
|
|
699
|
+
**Available options:**
|
|
700
|
+
- `path` (str, required): Path to image file
|
|
701
|
+
- `scale_width` (float): Width scale factor (1.0 = original)
|
|
702
|
+
- `scale_height` (float): Height scale factor (1.0 = original)
|
|
703
|
+
- `alt_text` (str): Alternative text for accessibility
|
|
704
|
+
|
|
705
|
+
**Supported formats:** PNG, JPEG, GIF, BMP
|
|
706
|
+
|
|
707
|
+
**Notes:**
|
|
708
|
+
- Images are positioned at the specified cell (overlays any existing content)
|
|
709
|
+
- Image file must exist; non-existent files will raise an error
|
|
710
|
+
- Works with both `df_to_xlsx` and `dfs_to_xlsx` (global or per-sheet)
|
|
711
|
+
- Not available in constant memory mode
|
|
712
|
+
|
|
713
|
+
### Constant Memory Mode (Large Files)
|
|
714
|
+
|
|
715
|
+
For very large files (millions of rows), use `constant_memory=True` to minimize RAM usage:
|
|
716
|
+
|
|
717
|
+
```python
|
|
718
|
+
import xlsxturbo
|
|
719
|
+
import polars as pl
|
|
720
|
+
|
|
721
|
+
# Generate a large DataFrame
|
|
722
|
+
large_df = pl.DataFrame({
|
|
723
|
+
'id': range(1_000_000),
|
|
724
|
+
'value': [i * 1.5 for i in range(1_000_000)]
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
# Use constant_memory mode for large files
|
|
728
|
+
xlsxturbo.df_to_xlsx(large_df, "big_file.xlsx", constant_memory=True)
|
|
729
|
+
|
|
730
|
+
# Also works with dfs_to_xlsx
|
|
731
|
+
xlsxturbo.dfs_to_xlsx([
|
|
732
|
+
(large_df, "Data")
|
|
733
|
+
], "multi_sheet.xlsx", constant_memory=True)
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
**Note:** Constant memory mode disables some features that require random access:
|
|
737
|
+
- `table_style` (Excel tables)
|
|
738
|
+
- `freeze_panes`
|
|
739
|
+
- `row_heights`
|
|
740
|
+
- `conditional_formats`
|
|
741
|
+
- `merged_ranges`
|
|
742
|
+
- `hyperlinks`
|
|
743
|
+
- `comments`
|
|
744
|
+
- `validations`
|
|
745
|
+
- `rich_text`
|
|
746
|
+
- `images`
|
|
747
|
+
- `autofit`
|
|
748
|
+
- `conditional_formats`
|
|
749
|
+
- `formula_columns`
|
|
750
|
+
- `merged_ranges`
|
|
751
|
+
- `hyperlinks`
|
|
752
|
+
|
|
753
|
+
Column widths still work in constant memory mode.
|
|
754
|
+
|
|
755
|
+
### CSV Conversion
|
|
756
|
+
|
|
757
|
+
```python
|
|
758
|
+
import xlsxturbo
|
|
759
|
+
|
|
760
|
+
# Convert CSV to XLSX with automatic type detection
|
|
761
|
+
rows, cols = xlsxturbo.csv_to_xlsx("input.csv", "output.xlsx")
|
|
762
|
+
print(f"Converted {rows} rows and {cols} columns")
|
|
763
|
+
|
|
764
|
+
# Custom sheet name
|
|
765
|
+
xlsxturbo.csv_to_xlsx("data.csv", "report.xlsx", sheet_name="Sales Data")
|
|
766
|
+
|
|
767
|
+
# For large files (100K+ rows), use parallel processing
|
|
768
|
+
xlsxturbo.csv_to_xlsx("big_data.csv", "output.xlsx", parallel=True)
|
|
769
|
+
|
|
770
|
+
# Handle ambiguous dates (01-02-2024: is it Jan 2 or Feb 1?)
|
|
771
|
+
xlsxturbo.csv_to_xlsx("us_data.csv", "output.xlsx", date_order="us") # January 2
|
|
772
|
+
xlsxturbo.csv_to_xlsx("eu_data.csv", "output.xlsx", date_order="eu") # February 1
|
|
773
|
+
|
|
774
|
+
# date_order options:
|
|
775
|
+
# - "auto" (default): ISO first, then European (DMY), then US (MDY)
|
|
776
|
+
# - "mdy" or "us": US format (MM-DD-YYYY)
|
|
777
|
+
# - "dmy" or "eu": European format (DD-MM-YYYY)
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
## CLI Usage
|
|
781
|
+
|
|
782
|
+
```bash
|
|
783
|
+
xlsxturbo input.csv output.xlsx [OPTIONS]
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### Options
|
|
787
|
+
|
|
788
|
+
- `-s, --sheet-name <NAME>`: Name of the Excel sheet (default: "Sheet1")
|
|
789
|
+
- `-d, --date-order <ORDER>`: Date parsing order for ambiguous dates (default: "auto")
|
|
790
|
+
- `auto`: ISO first, then European, then US
|
|
791
|
+
- `mdy` or `us`: US format (01-02-2024 = January 2)
|
|
792
|
+
- `dmy` or `eu`: European format (01-02-2024 = February 1)
|
|
793
|
+
- `-v, --verbose`: Show progress information
|
|
794
|
+
|
|
795
|
+
### Examples
|
|
796
|
+
|
|
797
|
+
```bash
|
|
798
|
+
# Basic conversion
|
|
799
|
+
xlsxturbo sales.csv report.xlsx
|
|
800
|
+
|
|
801
|
+
# With US date format
|
|
802
|
+
xlsxturbo sales.csv report.xlsx --date-order us
|
|
803
|
+
|
|
804
|
+
# With European date format and verbose output
|
|
805
|
+
xlsxturbo sales.csv report.xlsx -d eu -v --sheet-name "Q4 Sales"
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
## Performance
|
|
809
|
+
|
|
810
|
+
*Reference benchmark on 100,000 rows x 50 columns with mixed data types. Your results will vary by system - run the benchmark yourself (see [Benchmarking](#benchmarking)).*
|
|
811
|
+
|
|
812
|
+
| Library | Time (s) | Rows/sec | vs xlsxturbo |
|
|
813
|
+
|---------|----------|----------|--------------|
|
|
814
|
+
| **xlsxturbo** | **6.65** | **15,033** | **1.0x** |
|
|
815
|
+
| polars | 25.07 | 3,988 | 3.8x |
|
|
816
|
+
| pandas + xlsxwriter | 35.60 | 2,809 | 5.4x |
|
|
817
|
+
| pandas + openpyxl | 38.85 | 2,574 | 5.8x |
|
|
818
|
+
|
|
819
|
+
*Test system: Windows 11, Python 3.14, AMD Ryzen 9 (32 threads)*
|
|
820
|
+
|
|
821
|
+
## Type Detection Examples
|
|
822
|
+
|
|
823
|
+
| CSV Value | Excel Type | Notes |
|
|
824
|
+
|-----------|------------|-------|
|
|
825
|
+
| `123` | Number | Integer |
|
|
826
|
+
| `3.14159` | Number | Float |
|
|
827
|
+
| `true` / `FALSE` | Boolean | Case insensitive |
|
|
828
|
+
| `2024-01-15` | Date | Formatted as date |
|
|
829
|
+
| `2024-01-15T10:30:00` | DateTime | ISO 8601 format |
|
|
830
|
+
| `NaN` | Empty | Graceful handling |
|
|
831
|
+
| `hello world` | Text | Default |
|
|
832
|
+
|
|
833
|
+
Supported date formats: `YYYY-MM-DD`, `YYYY/MM/DD`, `DD-MM-YYYY`, `DD/MM/YYYY`, `MM-DD-YYYY`, `MM/DD/YYYY`
|
|
834
|
+
|
|
835
|
+
## Building from Source
|
|
836
|
+
|
|
837
|
+
Requires Rust toolchain and maturin:
|
|
838
|
+
|
|
839
|
+
```bash
|
|
840
|
+
# Install maturin
|
|
841
|
+
pip install maturin
|
|
842
|
+
|
|
843
|
+
# Development build
|
|
844
|
+
maturin develop
|
|
845
|
+
|
|
846
|
+
# Release build (optimized)
|
|
847
|
+
maturin develop --release
|
|
848
|
+
|
|
849
|
+
# Build wheel for distribution
|
|
850
|
+
maturin build --release
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
## Benchmarking
|
|
854
|
+
|
|
855
|
+
Run the included benchmark scripts:
|
|
856
|
+
|
|
857
|
+
```bash
|
|
858
|
+
# Compare xlsxturbo vs other libraries (100K rows default)
|
|
859
|
+
python benchmarks/benchmark.py
|
|
860
|
+
|
|
861
|
+
# Full benchmark: small, medium, large datasets
|
|
862
|
+
python benchmarks/benchmark.py --full
|
|
863
|
+
|
|
864
|
+
# Custom size
|
|
865
|
+
python benchmarks/benchmark.py --rows 500000 --cols 100
|
|
866
|
+
|
|
867
|
+
# Output formats for CI/documentation
|
|
868
|
+
python benchmarks/benchmark.py --markdown
|
|
869
|
+
python benchmarks/benchmark.py --json
|
|
870
|
+
|
|
871
|
+
# Test parallel vs single-threaded CSV conversion
|
|
872
|
+
python benchmarks/benchmark_parallel.py
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
## License
|
|
876
|
+
|
|
877
|
+
MIT
|
|
878
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
xlsxturbo/__init__.py,sha256=jekxkYh-HO_u7AQd4aV3MW7yZJlMx5cXryS0WzwasDs,200
|
|
2
|
+
xlsxturbo/__init__.pyi,sha256=LwVxEb66h5BI81gzAYz5du0O1I2E327dNcBcc5baQUw,13662
|
|
3
|
+
xlsxturbo/xlsxturbo.abi3.so,sha256=YX_21-JERbyGql94T42anjCe6hjDZfXJVxfcBndD9_Q,1930816
|
|
4
|
+
xlsxturbo-0.10.1.dist-info/METADATA,sha256=EN7XLwDMGh3iLNuVxzcUlP0Dpi4epilYlbp2HvbsMck,26186
|
|
5
|
+
xlsxturbo-0.10.1.dist-info/WHEEL,sha256=82nIVBZDB2dcFlYZh5-bKZ63xGZXWMnioaoSK06flpU,103
|
|
6
|
+
xlsxturbo-0.10.1.dist-info/licenses/LICENSE,sha256=dUhuoK-TCRQMpuLEAdfme-qPSJI0TlcH9jlNxeg9_EQ,1056
|
|
7
|
+
xlsxturbo-0.10.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|