mergeron_extra 2024.739099.2__py3-none-any.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.
@@ -0,0 +1,631 @@
1
+ """
2
+ Methods for writing data from Python to fresh Excel workbooks using
3
+ the third-party package, `xlsxwriter`.
4
+
5
+ Includes a flexible system of defining cell formats.
6
+
7
+ NOTES
8
+ -----
9
+
10
+ This module is designed for producing formatted summary output. For
11
+ writing bulk data to Excel, facilities provided in third-party packages
12
+ such as `polars <https://pola.rs/>`_ likely provide better performance.
13
+
14
+ License
15
+ ========
16
+
17
+ Copyright 2017-2023 S. Murthy Kambhampaty
18
+ Licese: MIT
19
+ https://mit-license.org/
20
+
21
+
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from collections.abc import Sequence
27
+ from typing import Any, ClassVar, Literal, TypeAlias, TypedDict
28
+
29
+ import numpy as np
30
+ import xlsxwriter # type: ignore
31
+ from aenum import Enum, extend_enum, unique # type: ignore
32
+ from numpy.typing import NDArray
33
+
34
+ from . import VERSION
35
+
36
+ __version__ = VERSION
37
+
38
+ Workbook = xlsxwriter.Workbook
39
+
40
+ XLBorderType: TypeAlias = Literal[
41
+ "none",
42
+ "thin",
43
+ "medium",
44
+ "dashed",
45
+ "dotted",
46
+ "thick",
47
+ "double",
48
+ "hair",
49
+ "medium_dashed",
50
+ "dash_dot",
51
+ "medium_dash_dot",
52
+ "dash_dot_dot",
53
+ "medium_dash_dot_dot",
54
+ "slant_dash_dot",
55
+ True,
56
+ False,
57
+ 0,
58
+ 1,
59
+ 2,
60
+ 3,
61
+ 4,
62
+ 5,
63
+ 6,
64
+ 7,
65
+ 8,
66
+ 9,
67
+ 10,
68
+ 11,
69
+ 12,
70
+ 13,
71
+ ]
72
+
73
+
74
+ class CFmtVal(TypedDict, total=False):
75
+ """Keys for xlsxwriter Format objects.
76
+
77
+ This is a partial list based on formats of interest.
78
+ """
79
+
80
+ font_name: str
81
+ font_size: int
82
+ font_color: str
83
+ align: Literal[
84
+ "left", "center", "right", "center_across", "top", "bottom", "vcenter"
85
+ ]
86
+ text_wrap: bool
87
+ rotation: int # integer, 0-360
88
+ indent: int
89
+ shrink: bool
90
+ bold: bool
91
+ italic: bool
92
+ underline: Literal[
93
+ True,
94
+ False,
95
+ 1,
96
+ 2,
97
+ 33,
98
+ 34,
99
+ "single",
100
+ "double",
101
+ "accountingSingle",
102
+ "accountingDouble",
103
+ ]
104
+ font_strikeout: bool
105
+ font_script: Literal[1, 2]
106
+
107
+ num_format: str
108
+
109
+ pattern: int
110
+ fg_color: str # html color string, no #
111
+ bg_color: str # html color string, no #
112
+
113
+ hidden: bool
114
+ locked: bool
115
+
116
+ border: XLBorderType
117
+ bottom: XLBorderType
118
+ left: XLBorderType
119
+ right: XLBorderType
120
+ top: XLBorderType
121
+ border_color: str # html color string, no #
122
+ bottom_color: str # html color string, no #
123
+ left_color: str # html color string, no #
124
+ right_color: str # html color string, no #
125
+ top_color: str # html color string, no #
126
+
127
+ diag_border: XLBorderType
128
+ diag_border_color: str # html color string, no #
129
+ diag_type: Literal[
130
+ 1, 2, 3, "up", "down", "left", "right", "cross", "diagonalUp", "diagonalDown"
131
+ ]
132
+
133
+
134
+ @unique
135
+ class CFmt(dict, Enum): # type: ignore
136
+ """
137
+ Cell format enums for xlsxwriter Format objects.
138
+
139
+ The enums defined here, or sequences of (any of) them
140
+ and any added with :meth:`CFmt.add_new`, are
141
+ rendered as :code:`xlsxWriter.Workbook.Format` objects
142
+ with :meth:`CFmt.xl_fmt`.
143
+
144
+ NOTES
145
+ -----
146
+
147
+ For more information about xlsxwriter cell formats,
148
+ see, https://xlsxwriter.readthedocs.io/format.html
149
+
150
+ """
151
+
152
+ XL_DEFAULT: ClassVar = {"font_name": "Calibri", "font_size": 11}
153
+ XL_DEFAULT_2003: ClassVar = {"font_name": "Arial", "font_size": 10}
154
+
155
+ A_CTR: ClassVar = {"align": "center"}
156
+ A_CTR_ACROSS: ClassVar = {"align": "center_across"}
157
+ A_LEFT: ClassVar = {"align": "left"}
158
+ A_RIGHT: ClassVar = {"align": "right"}
159
+ V_TOP: ClassVar = {"align": "top"}
160
+ V_BOTTOM: ClassVar = {"align": "bottom"}
161
+ V_CTR: ClassVar = {"align": "vcenter"}
162
+
163
+ TEXT_WRAP: ClassVar = {"text_wrap": True}
164
+ TEXT_ROTATE: ClassVar = {"rotation": 90}
165
+ IND_1: ClassVar = {"indent": 1}
166
+
167
+ BOLD: ClassVar = {"bold": True}
168
+ BOLD_ITALIC: ClassVar = {"bold": True, "italic": True}
169
+ ITALIC: ClassVar = {"italic": True}
170
+ ULINE: ClassVar = {"underline": "single"}
171
+ SOUT: ClassVar = {"font_strikeout": True}
172
+ # Useful with write_rich_text()
173
+ SUPERSCRIPT: ClassVar = {"font_script": 1}
174
+ SUBSCRIPT: ClassVar = {"font_script": 2}
175
+
176
+ AREA_NUM: ClassVar = {"num_format": "0.00000000"}
177
+ DOLLAR_NUM: ClassVar = {"num_format": "[$$-409]#,##0.00"}
178
+ DT_NUM: ClassVar = {"num_format": "mm/dd/yyyy"}
179
+ PCT_NUM: ClassVar = {"num_format": "##0%"}
180
+ PCT2_NUM: ClassVar = {"num_format": "##0.00%"}
181
+ PCT4_NUM: ClassVar = {"num_format": "##0.0000%"}
182
+ PCT6_NUM: ClassVar = {"num_format": "##0.000000%"}
183
+ PCT8_NUM: ClassVar = {"num_format": "##0.00000000%"}
184
+ QTY_NUM: ClassVar = {"num_format": "#,##0.0"}
185
+
186
+ BAR_FILL: ClassVar = {"pattern": 1, "bg_color": "dfeadf"}
187
+ HDR_FILL: ClassVar = {"pattern": 1, "bg_color": "bfbfbf"}
188
+
189
+ FULL_BORDER: ClassVar = {"border": 1, "border_color": "000000"}
190
+ BOTTOM_BORDER: ClassVar = {"bottom": 1, "bottom_color": "000000"}
191
+ LEFT_BORDER: ClassVar = {"left": 1, "left_color": "000000"}
192
+ RIGHT_BORDER: ClassVar = {"right": 1, "right_color": "000000"}
193
+ TOP_BORDER: ClassVar = {"top": 1, "top_color": "000000"}
194
+ HDR_BORDER: ClassVar = TOP_BORDER | BOTTOM_BORDER
195
+
196
+ @classmethod
197
+ def add_new(self, _fmt_name: str, _xlsx_fmt_dict: CFmtVal, /) -> CFmt:
198
+ """
199
+ Add new :class:`CFmt` object to instance.
200
+
201
+ Parameters
202
+ ----------
203
+ _fmt_name
204
+ Name of new member to be added to :class:`CFmt`
205
+ _xlsx_fmt_dict
206
+ Any valid argument to :code:`xlsxwriter.Workbook.add_format()`, or union of
207
+ same with one or more :class:`CFmt` objects, e.g.,
208
+ :code:`CFmt.HDR_BORDER | CFmt.HDR_FILL` or
209
+ :code:`CFmt.HDR_BORDER | {"pattern": 1, "bg_color": "f2f2f2"}`
210
+
211
+ Returns
212
+ -------
213
+ None
214
+
215
+ """
216
+
217
+ return extend_enum(self, _fmt_name, _xlsx_fmt_dict) # type: ignore
218
+
219
+ @classmethod
220
+ def ensure_cell_format_spec_tuple(
221
+ self, _cell_format: Sequence[CFmt | Sequence[CFmt]], /
222
+ ) -> bool:
223
+ """
224
+ Test that a given format specification is a tuple of :class:`CFmt` enums
225
+
226
+ Parameters
227
+ ----------
228
+ _cell_format
229
+ Format specification
230
+
231
+ Raises
232
+ ------
233
+ ValueError
234
+ If format specification is not a sequence of (sequences of)
235
+ :class:`CFmt` enums
236
+
237
+ Returns
238
+ -------
239
+ True if format specification passes, else False
240
+
241
+ """
242
+
243
+ for _cf in _cell_format:
244
+ if isinstance(_cf, tuple):
245
+ self.ensure_cell_format_spec_tuple(_cf)
246
+
247
+ if not (isinstance(_cf, CFmt),):
248
+ raise ValueError(
249
+ "Improperly specified format tuple for writing array."
250
+ " Must be tuple of :class:`CFmt` enums."
251
+ )
252
+
253
+ return True
254
+
255
+ @classmethod
256
+ def xl_fmt(
257
+ self,
258
+ _xl_book: xlsxwriter.Workbook,
259
+ _cell_fmt: Sequence[CFmt | Sequence[CFmt]] | CFmt | None,
260
+ /,
261
+ ) -> xlsxwriter.format.Format:
262
+ """
263
+ Return :code:`xlsxwriter` :code:`Format` object given a :class:`CFmt` enum, or tuple thereof.
264
+
265
+ Parameters
266
+ ----------
267
+ _xl_book
268
+ :code:`xlsxwriter.Workbook` object
269
+
270
+ _cell_fmt
271
+ :class:`CFmt` enum object, or tuple thereof
272
+
273
+ Raises
274
+ ------
275
+ ValueError
276
+ If format specification is not one of None, a :class:`CFmt` enum, or
277
+ a :code:`xlsxwriter.format.Format` object
278
+
279
+ Returns
280
+ -------
281
+ :code:`xlsxwriter` :code:`Format` object
282
+
283
+ """
284
+
285
+ if isinstance(_cell_fmt, xlsxwriter.format.Format):
286
+ return _cell_fmt
287
+ elif _cell_fmt is None:
288
+ return _xl_book.add_format(CFmt.XL_DEFAULT.value)
289
+
290
+ _cell_fmt_dict: CFmtVal = {}
291
+ if isinstance(_cell_fmt, Sequence):
292
+ self.ensure_cell_format_spec_tuple(_cell_fmt)
293
+ for _cf in _cell_fmt:
294
+ _cell_fmt_dict = (
295
+ (_cell_fmt_dict | _cfi.value for _cfi in _cf)
296
+ if isinstance(_cf, Sequence)
297
+ else _cell_fmt_dict | _cf.value
298
+ )
299
+ elif isinstance(_cell_fmt, CFmt):
300
+ _cell_fmt_dict = _cell_fmt.value
301
+ else:
302
+ raise ValueError("Improperly specified format specification.")
303
+
304
+ return _xl_book.add_format(_cell_fmt_dict)
305
+
306
+
307
+ def write_header(
308
+ _xl_sheet: xlsxwriter.worksheet.Worksheet,
309
+ /,
310
+ *,
311
+ center_header: str | None = None,
312
+ left_header: str | None = None,
313
+ right_header: str | None = None,
314
+ ) -> None:
315
+ """Write header text to given worksheet.
316
+
317
+ Parameters
318
+ ----------
319
+ _xl_sheet
320
+ Worksheet object
321
+ center_header
322
+ Text for center header
323
+ left_header
324
+ Text for left header
325
+ right_header
326
+ Text for right header
327
+
328
+ Raises
329
+ ------
330
+ ValueError
331
+ Must specify at least one header
332
+
333
+ Returns
334
+ -------
335
+ None
336
+ """
337
+ if any((center_header, left_header, right_header)):
338
+ _xl_sheet.set_header(
339
+ "".join([
340
+ f"&L{left_header}" if left_header else "",
341
+ f"&C{center_header}" if center_header else "",
342
+ f"&R{right_header}" if right_header else "",
343
+ ])
344
+ )
345
+
346
+ else:
347
+ raise ValueError("must specify at least one header")
348
+
349
+
350
+ def write_footer(
351
+ _xl_sheet: xlsxwriter.worksheet.Worksheet,
352
+ /,
353
+ *,
354
+ center_footer: str | None = None,
355
+ left_footer: str | None = None,
356
+ right_footer: str | None = None,
357
+ ) -> None:
358
+ """Write footer text to given worksheet.
359
+
360
+ Parameters
361
+ ----------
362
+ _xl_sheet
363
+ Worksheet object
364
+ center_footer
365
+ Text for center footer
366
+ left_footer
367
+ Text for left footer
368
+ right_footer
369
+ Text for right footer
370
+
371
+ Raises
372
+ ------
373
+ ValueError
374
+ Must specify at least one footer
375
+
376
+ Returns
377
+ -------
378
+ None
379
+ """
380
+
381
+ if any((center_footer, left_footer, right_footer)):
382
+ _xl_sheet.set_footer(
383
+ "".join([
384
+ f"&L{left_footer}" if left_footer else "",
385
+ f"&C{center_footer}" if center_footer else "",
386
+ f"&R{right_footer}" if right_footer else "",
387
+ ])
388
+ )
389
+
390
+ else:
391
+ raise ValueError("must specify at least one footer")
392
+
393
+
394
+ def array_to_sheet(
395
+ _xl_book: xlsxwriter.workbook.Workbook,
396
+ _xl_sheet: xlsxwriter.worksheet.Worksheet,
397
+ _data_table: Sequence[Any] | NDArray[Any],
398
+ _row_id: int,
399
+ _col_id: int = 0,
400
+ /,
401
+ *,
402
+ cell_format: Sequence[CFmt | Sequence[CFmt]] | CFmt | None = None,
403
+ empty_as_blank: bool = True,
404
+ green_bar_flag: bool = True,
405
+ ragged_flag: bool = True,
406
+ ) -> tuple[int, int]:
407
+ """
408
+ Write a 2-D array to a worksheet.
409
+
410
+ The given array is required be a two-dimensional array, whether
411
+ a nested list, nested tuple, or a 2-D numpy ndarray. The array is assumed
412
+ to be ragged by default, i.e. not all rows are the same length, and some
413
+ cells may contain lists, etc. For rectangular arrays, set `ragged_flag` to
414
+ false if you wish to provide a format tuple with distinct formats for each
415
+ column in the rectangular array.
416
+
417
+
418
+ Parameters
419
+ ----------
420
+ _xl_book
421
+ Workbook object
422
+
423
+ _xl_sheet
424
+ Worksheet object to which to write the give array
425
+
426
+ _data_table
427
+ Array to be written
428
+
429
+ _row_id
430
+ Row number of top left corner of range to write to
431
+
432
+ _col_id
433
+ Column number of top left corner of range to write to
434
+
435
+ cell_format
436
+ Format specification for range to be written
437
+
438
+ green_bar_flag
439
+ Whether to highlight alternating rows as in green bar paper
440
+
441
+ ragged_flag
442
+ Whether to write ragged array, i.e. rows not all the same length
443
+ or not all cells are scalar-valued
444
+
445
+
446
+ Raises
447
+ ------
448
+ ValueError
449
+ If array is not two-dimensional
450
+
451
+ ValueError
452
+ If ragged_flag is False and array is not rectangular
453
+
454
+ ValueError
455
+ If array is not rectangular and cell_format is a Sequence
456
+
457
+ ValueError
458
+ If array is rectangular but length of format tuple does not
459
+ match row-length
460
+
461
+
462
+ Returns
463
+ -------
464
+ Tuple giving address of cell at right below and after range written
465
+
466
+
467
+ Notes
468
+ -----
469
+
470
+ The keyword argument cell_format may be passed a tuple of :class:`CFmt` enums,
471
+ if, and only if, ragged_flag is False. If cell_format is a tuple, it must
472
+ have length equal to the number of cells in each row of the passed array.
473
+ Further, members of cell_format must each be a :class:`CFmt` enum or a
474
+ tuple of :class:`CFmt` enums; in other words, :meth:`CFmt.ensure_cell_format_spec_tuple`
475
+ must return True for any tuple `_c` passed as `cell_format`.
476
+
477
+ """
478
+
479
+ if not ragged_flag:
480
+ try:
481
+ if np.ndim(_data_table) != 2:
482
+ raise ValueError("Given array must be two-dimensional.")
483
+ except ValueError as _err:
484
+ raise ValueError(
485
+ "Given array must be rectangular and homogenous, with scalar members."
486
+ " Alternatively, try with ragged_flag=True."
487
+ )
488
+ raise _err
489
+ elif not (
490
+ isinstance(_data_table, Sequence | np.ndarray)
491
+ and hasattr(_data_table[0], "__len__")
492
+ ):
493
+ raise ValueError("Given array must be two-dimensional array.")
494
+
495
+ # Get the array dimensions and row and column numbers for Excel
496
+ _num_rows = len(_data_table)
497
+ _bottom_row_id = _row_id + _num_rows
498
+ _num_cols = len(_data_table[0])
499
+ _right_column_id = _col_id + _num_cols
500
+
501
+ if isinstance(cell_format, Sequence):
502
+ if _num_rows > 1 and ragged_flag:
503
+ raise ValueError(
504
+ "It is not clear whether the sequence of formats applies to all cells,"
505
+ " or to each cell respectively. Please provide a single-valued cell_format."
506
+ " Alternatively, you can iterate over the array using scalar_to_sheet()."
507
+ )
508
+ elif not len(cell_format) == len(_data_table[0]):
509
+ raise ValueError("Format tuple does not match data in length.")
510
+ CFmt.ensure_cell_format_spec_tuple(cell_format)
511
+ _cell_format: Sequence[CFmt | Sequence[CFmt]] = cell_format
512
+ elif isinstance(cell_format, CFmt):
513
+ _cell_format = (cell_format,) * len(_data_table[0])
514
+ else:
515
+ _cell_format = (CFmt.XL_DEFAULT,) * len(_data_table[0])
516
+
517
+ # construct vector of xlslwrter.format.Format objects
518
+ _wbk_formats = tuple(CFmt.xl_fmt(_xl_book, _cf) for _cf in _cell_format)
519
+ if _num_rows > 1:
520
+ _wbk_formats_greened = (
521
+ tuple(
522
+ CFmt.xl_fmt(
523
+ _xl_book,
524
+ (*_cf, CFmt.BAR_FILL)
525
+ if isinstance(_cf, Sequence)
526
+ else (_cf, CFmt.BAR_FILL),
527
+ )
528
+ for _cf in _cell_format
529
+ )
530
+ if green_bar_flag
531
+ else _wbk_formats
532
+ )
533
+
534
+ for _ri, _rv in enumerate(_data_table):
535
+ _wbk_fmt_tuple = _wbk_formats_greened if _ri % 2 else _wbk_formats
536
+ for _ci, _cv in enumerate(_rv):
537
+ _cf = _wbk_fmt_tuple[_ci]
538
+ scalar_to_sheet(
539
+ _xl_sheet,
540
+ _row_id + _ri,
541
+ _col_id + _ci,
542
+ _cv,
543
+ _cf,
544
+ empty_as_blank=empty_as_blank,
545
+ )
546
+
547
+ _right_column_id = _col_id + _ci + 1 if _ci > _num_cols else _right_column_id
548
+
549
+ return _bottom_row_id, _right_column_id
550
+
551
+
552
+ def scalar_to_sheet(
553
+ _xl_sheet: xlsxwriter.worksheet.Worksheet,
554
+ _cell_addr_0: str | int = "A1",
555
+ /,
556
+ *_s2s_args: Any,
557
+ empty_as_blank: bool = True,
558
+ ) -> None:
559
+ """
560
+ Write to a single cell in a worksheet.
561
+
562
+ Parameters
563
+ ----------
564
+ _xl_sheet
565
+ Worksheet object to which to write the give array
566
+
567
+ _cell_addr_0
568
+ First element of a cell address, which may be the entire address
569
+ in 'A1' format or the row-part in 'Row-column' format
570
+
571
+ _s2s_args
572
+ Other arguments, which may be just the cell value to be written and the
573
+ cell format, or the column-part of the 'Row-column' address along with
574
+ cell value and cell format.
575
+
576
+ Raises
577
+ ------
578
+ ValueError
579
+ If too many or too few arguments
580
+ ValueError
581
+ If incorrect/incomplete specification for Excel cell data
582
+
583
+ Returns
584
+ -------
585
+ None
586
+
587
+ Notes
588
+ -----
589
+ For more information on xlsxwriter cell-address notation, see:
590
+ https://xlsxwriter.readthedocs.io/working_with_cell_notation.html
591
+
592
+ """
593
+
594
+ _cell_addr: tuple[int | str, ...] = ()
595
+ _cell_val: Any = None
596
+ _cell_fmt: xlsxwriter.format.Format = CFmt.XL_DEFAULT
597
+
598
+ if isinstance(_cell_addr_0, str):
599
+ if len(_s2s_args) not in (1, 2):
600
+ raise ValueError("Incorrect number of arguments.")
601
+ _cell_addr = (_cell_addr_0,)
602
+ _cell_val = _s2s_args[0]
603
+ _cell_fmt = _s2s_args[1] if len(_s2s_args) == 2 else None
604
+ elif isinstance(_cell_addr_0, int):
605
+ if len(_s2s_args) not in (2, 3) or not isinstance(_s2s_args[0], int):
606
+ raise ValueError("Incorrect/incomplete specification for Excel cell data.")
607
+ _cell_addr = (_cell_addr_0, _s2s_args[0])
608
+ _cell_val = _s2s_args[1]
609
+ _cell_fmt = _s2s_args[2] if len(_s2s_args) == 3 else None
610
+ else:
611
+ raise ValueError("Incorrect/incomplete specification for Excel cell data.")
612
+
613
+ _write_args = (
614
+ (*_cell_addr, repr(_cell_val))
615
+ if np.ndim(_cell_val) or _cell_val in (np.inf, -np.inf, np.nan)
616
+ else (*_cell_addr, _cell_val)
617
+ )
618
+ _write_args = (*_write_args, _cell_fmt) if _cell_fmt else _write_args
619
+
620
+ if empty_as_blank and (_cell_val is None or _cell_val == ""):
621
+ _xl_sheet.write_blank(*_write_args)
622
+ elif (
623
+ _cell_val is None
624
+ or _cell_val == ""
625
+ or isinstance(_cell_val, str)
626
+ or np.ndim(_cell_val)
627
+ or _cell_val in (np.inf, -np.inf, np.nan)
628
+ ):
629
+ _xl_sheet.write_string(*_write_args)
630
+ else:
631
+ _xl_sheet.write(*_write_args)
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.1
2
+ Name: mergeron_extra
3
+ Version: 2024.739099.2
4
+ Summary: Tools for users of the mergeron package.
5
+ License: MIT
6
+ Author: Murthy Kambhampaty
7
+ Author-email: smk@capeconomics.com
8
+ Requires-Python: >=3.12,<4.0
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: End Users/Desktop
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: Implementation :: CPython
20
+ Requires-Dist: aenum (>=3.1.15,<4.0.0)
21
+ Requires-Dist: certifi (>=2023.11.17)
22
+ Requires-Dist: matplotlib (>=3.8)
23
+ Requires-Dist: numpy (>=1.26,<2)
24
+ Requires-Dist: scipy (>=1.12)
25
+ Requires-Dist: xlsxwriter (>=3.1)
26
+ Description-Content-Type: text/x-rst
27
+
28
+ mergeron_extra: Support package for users of `mergeron`
29
+ =======================================================
30
+
31
+ Modules potentially useful to users of the :code:`mergeron` package, but which do not implement the principal functions of the package, and are hence considered "extras" or "external" modules. One of these modules is, in fact, repackaged here although published independently.
32
+
33
+ The module :code:`mergeron_extra.proportions_tests` provides methods for estimating confidence intervals for proportions and for contrasts (differences) in proportions. This module is coded for conformance to the literature and to results from the corresponding modules in :code:`R`. Although written from scratch, the APIs implemented in the module included here are designed for consistency with the APIs in, :code:`statsmodels.stats.proportion` from the package, :code:`statsmodels` (https://pypi.org/project/statsmodels/). To access these directly:
34
+
35
+ .. code-block:: python
36
+
37
+ import mergeron_extra.proportions_tests as prci
38
+
39
+ Module :code:`mergeron_extra.xlsxw_helper` is useful for writing highly formatted output to spreadsheets with xlsx format. The class, :code:`mergeron_extra.xlsxw_helper.CFmt` and function, :code:`mergeron_extra.xlsxw_helper.array_to_sheet` are of particular interest, and can be accessed as :code:`xlh.CFmt` and :code:`xlh.array_to_sheet` with the following import:
40
+
41
+ .. code-block:: python
42
+
43
+ import mergeron_extra.xlsxw_helper as xlh
44
+
45
+ A recent version of Paul Tol's python module, :code:`tol_colors.py`, which provides high-contrast color schemes for making displays with improved visibility for individuals with color-blindness, is redistributed within this package. Other than re-formatting and type annotation, the :code:`mergeron_extra.tol_colors` module is re-distributed as downloaded from, https://personal.sron.nl/~pault/data/tol_colors.py. The :code:`tol_colors.py` module is distributed under the Standard 3-clause BSD license. To access the :code:`mergeron_extra.tol_colors` module directly:
46
+
47
+ .. code-block:: python
48
+
49
+ import mergeron_extra.tol_colors as ptc
50
+
51
+ .. image:: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json
52
+ :alt: Poetry
53
+ :target: https://python-poetry.org/
54
+
55
+ .. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
56
+ :alt: Ruff
57
+ :target: https://github.com/astral-sh/ruff
58
+
59
+ .. image:: https://www.mypy-lang.org/static/mypy_badge.svg
60
+ :alt: Checked with mypy
61
+ :target: https://mypy-lang.org/
62
+
63
+ .. image:: https://img.shields.io/badge/License-MIT-yellow.svg
64
+ :alt: License: MIT
65
+ :target: https://opensource.org/licenses/MIT
66
+
67
+
@@ -0,0 +1,7 @@
1
+ mergeron_extra/__init__.py,sha256=0fQDmet2P2Y-ZUfcfzkjwsmoaUiIYAqIslK1BSRfWvk,466
2
+ mergeron_extra/proportions_tests.py,sha256=b_o_Ps3L55gh0yCZXlP7eMgbMuB0jVNQvLJKGwgybnA,15164
3
+ mergeron_extra/tol_colors.py,sha256=QBw8s-ZGpUpIIYOplHbLFZSXVoa6eDDJDtzSScP954E,22303
4
+ mergeron_extra/xlsxw_helper.py,sha256=gkavKiKrnZ3O2I-tKqoe9UFFm5przHzu7RD_QB2KrG4,18003
5
+ mergeron_extra-2024.739099.2.dist-info/METADATA,sha256=zy-oAu0-0tZ3qgDnJsEriCOTXfIcme01ksJAVwLOnxU,3695
6
+ mergeron_extra-2024.739099.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
7
+ mergeron_extra-2024.739099.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any