absfuyu 5.6.1__py3-none-any.whl → 5.8.0__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.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (77) hide show
  1. absfuyu/__init__.py +1 -1
  2. absfuyu/__main__.py +2 -2
  3. absfuyu/cli/__init__.py +2 -2
  4. absfuyu/cli/color.py +2 -2
  5. absfuyu/cli/config_group.py +2 -2
  6. absfuyu/cli/do_group.py +2 -2
  7. absfuyu/cli/game_group.py +2 -2
  8. absfuyu/cli/tool_group.py +2 -2
  9. absfuyu/config/__init__.py +2 -2
  10. absfuyu/core/__init__.py +2 -2
  11. absfuyu/core/baseclass.py +2 -2
  12. absfuyu/core/baseclass2.py +2 -2
  13. absfuyu/core/decorator.py +2 -2
  14. absfuyu/core/docstring.py +2 -2
  15. absfuyu/core/dummy_cli.py +2 -2
  16. absfuyu/core/dummy_func.py +2 -2
  17. absfuyu/dxt/__init__.py +2 -2
  18. absfuyu/dxt/dictext.py +2 -2
  19. absfuyu/dxt/dxt_support.py +2 -2
  20. absfuyu/dxt/intext.py +2 -2
  21. absfuyu/dxt/listext.py +2 -2
  22. absfuyu/dxt/strext.py +2 -2
  23. absfuyu/extra/__init__.py +2 -2
  24. absfuyu/extra/beautiful.py +2 -2
  25. absfuyu/extra/da/__init__.py +4 -2
  26. absfuyu/extra/da/dadf.py +148 -13
  27. absfuyu/extra/da/dadf_base.py +2 -2
  28. absfuyu/extra/da/df_func.py +2 -2
  29. absfuyu/extra/da/mplt.py +2 -2
  30. absfuyu/extra/data_analysis.py +2 -2
  31. absfuyu/extra/pdf.py +2 -4
  32. absfuyu/extra/xml.py +90 -0
  33. absfuyu/fun/__init__.py +2 -2
  34. absfuyu/fun/rubik.py +2 -2
  35. absfuyu/fun/tarot.py +2 -2
  36. absfuyu/game/__init__.py +2 -2
  37. absfuyu/game/game_stat.py +2 -2
  38. absfuyu/game/sudoku.py +2 -2
  39. absfuyu/game/tictactoe.py +2 -2
  40. absfuyu/game/wordle.py +2 -2
  41. absfuyu/general/__init__.py +2 -2
  42. absfuyu/general/content.py +2 -2
  43. absfuyu/general/human.py +2 -2
  44. absfuyu/general/shape.py +2 -2
  45. absfuyu/logger.py +2 -2
  46. absfuyu/pkg_data/__init__.py +2 -2
  47. absfuyu/pkg_data/deprecated.py +2 -2
  48. absfuyu/pkg_data/logo.py +1462 -0
  49. absfuyu/sort.py +2 -2
  50. absfuyu/tools/__init__.py +2 -2
  51. absfuyu/tools/checksum.py +2 -2
  52. absfuyu/tools/converter.py +2 -2
  53. absfuyu/tools/generator.py +2 -2
  54. absfuyu/tools/inspector.py +2 -2
  55. absfuyu/tools/keygen.py +2 -2
  56. absfuyu/tools/obfuscator.py +2 -2
  57. absfuyu/tools/passwordlib.py +2 -2
  58. absfuyu/tools/shutdownizer.py +2 -2
  59. absfuyu/tools/sw.py +2 -2
  60. absfuyu/tools/web.py +2 -2
  61. absfuyu/typings.py +2 -2
  62. absfuyu/util/__init__.py +2 -2
  63. absfuyu/util/api.py +2 -2
  64. absfuyu/util/json_method.py +2 -2
  65. absfuyu/util/lunar.py +2 -2
  66. absfuyu/util/path.py +2 -2
  67. absfuyu/util/performance.py +2 -2
  68. absfuyu/util/shorten_number.py +2 -2
  69. absfuyu/util/text_table.py +2 -2
  70. absfuyu/util/zipped.py +2 -2
  71. absfuyu/version.py +2 -2
  72. {absfuyu-5.6.1.dist-info → absfuyu-5.8.0.dist-info}/METADATA +14 -4
  73. absfuyu-5.8.0.dist-info/RECORD +81 -0
  74. absfuyu-5.6.1.dist-info/RECORD +0 -79
  75. {absfuyu-5.6.1.dist-info → absfuyu-5.8.0.dist-info}/WHEEL +0 -0
  76. {absfuyu-5.6.1.dist-info → absfuyu-5.8.0.dist-info}/entry_points.txt +0 -0
  77. {absfuyu-5.6.1.dist-info → absfuyu-5.8.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/__init__.py CHANGED
@@ -22,7 +22,7 @@ Using in cmd:
22
22
  __title__ = "absfuyu"
23
23
  __author__ = "AbsoluteWinter"
24
24
  __license__ = "MIT License"
25
- __version__ = "5.6.1"
25
+ __version__ = "5.8.0"
26
26
  __all__ = [
27
27
  "core",
28
28
  "config",
absfuyu/__main__.py CHANGED
@@ -3,8 +3,8 @@ ABSFUYU
3
3
  -------
4
4
  COMMAND LINE INTERFACE
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Library
absfuyu/cli/__init__.py CHANGED
@@ -3,8 +3,8 @@ ABSFUYU
3
3
  -------
4
4
  COMMAND LINE INTERFACE
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  __all__ = ["cli"]
absfuyu/cli/color.py CHANGED
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Color
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Config
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/cli/do_group.py CHANGED
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Do
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/cli/game_group.py CHANGED
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Game
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/cli/tool_group.py CHANGED
@@ -3,8 +3,8 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Tool
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Configuration
3
3
  ----------------------
4
4
  Package configuration module
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/core/__init__.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Bases for other features
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/core/baseclass.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Bases for other features
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Bases for other features (with library)
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/core/decorator.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Decorator
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/core/docstring.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Sphinx docstring decorator
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/core/dummy_cli.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Dummy cli
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Dummy functions when other libraries are unvailable
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/dxt/__init__.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  Extension for data type such as ``list``, ``str``, ``dict``, ...
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
absfuyu/dxt/dictext.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  dict extension
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  Support classes
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/dxt/intext.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  int extension
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/dxt/listext.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  list extension
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/dxt/strext.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  str extension
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/extra/__init__.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Extra
3
3
  --------------
4
4
  Features that require additional libraries
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
 
@@ -3,8 +3,8 @@ Absfuyu: Beautiful
3
3
  ------------------
4
4
  A decorator that makes output more beautiful
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Data Analysis
3
3
  ----------------------
4
4
  Data Analyst
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -18,7 +18,9 @@ DA_MODE = False
18
18
 
19
19
  try:
20
20
  import numpy as np
21
+ import openpyxl
21
22
  import pandas as pd
23
+ import xlsxwriter
22
24
  except ImportError:
23
25
  from subprocess import run
24
26
 
absfuyu/extra/da/dadf.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Analysis
3
3
  ----------------------
4
4
  Data Analyst DataFrame
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 17/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -17,6 +17,7 @@ __all__ = [
17
17
  "DataAnalystDataFrameNAMixin",
18
18
  "DataAnalystDataFrameOtherMixin",
19
19
  "DataAnalystDataFrameDateMixin",
20
+ "DataAnalystDataFrameExportMixin",
20
21
  "DataAnalystDataFrameCityMixin",
21
22
  ]
22
23
 
@@ -31,6 +32,8 @@ from typing import Any, Literal, Self
31
32
 
32
33
  import numpy as np
33
34
  import pandas as pd
35
+ from xlsxwriter import Workbook
36
+ from xlsxwriter.worksheet import Worksheet
34
37
 
35
38
  try:
36
39
  from typing import override # type: ignore
@@ -38,7 +41,7 @@ except ImportError:
38
41
  from absfuyu.core.decorator import dummy_decorator as override
39
42
 
40
43
  from absfuyu.core.baseclass import GetClassMembersMixin
41
- from absfuyu.core.docstring import deprecated, versionadded
44
+ from absfuyu.core.docstring import deprecated, versionadded, versionchanged
42
45
  from absfuyu.extra.da.dadf_base import CityData
43
46
  from absfuyu.extra.da.dadf_base import DataAnalystDataFrameBase as DFBase
44
47
  from absfuyu.extra.da.dadf_base import SplittedDF
@@ -183,7 +186,9 @@ class DataAnalystDataFrameColumnMethodMixin(DFBase):
183
186
  @deprecated("5.1.0", reason="Use pd.DataFrame.assign(...) method instead")
184
187
  def add_blank_column(self, column_name: str, fill: Any = np.nan, /) -> Self:
185
188
  """
186
- Add a blank column
189
+ [DEPRECATED] Add a blank column.
190
+
191
+ E.g: Use `pd.DataFrame.assign(new_col=lambda x: x['old_col'])` instead
187
192
 
188
193
  Parameters
189
194
  ----------
@@ -246,9 +251,7 @@ class DataAnalystDataFrameColumnMethodMixin(DFBase):
246
251
  """
247
252
  if n is None:
248
253
  pass
249
- splited_data: pd.DataFrame = self[col].str.split(
250
- pat=pattern, n=n, expand=True, regex=regex
251
- )
254
+ splited_data: pd.DataFrame = self[col].str.split(pat=pattern, n=n, expand=True, regex=regex) # type: ignore
252
255
  num_of_splitted_cols = splited_data.shape[1]
253
256
  new_col_names = [f"{col}_{x}" for x in range(num_of_splitted_cols)]
254
257
  self[new_col_names] = splited_data
@@ -262,6 +265,7 @@ class DataAnalystDataFrameRowMethodMixin(DFBase):
262
265
  Data Analyst ``pd.DataFrame`` - Row method
263
266
 
264
267
  - Get different rows
268
+ - Add blank row
265
269
  """
266
270
 
267
271
  @versionadded("4.0.0")
@@ -297,6 +301,26 @@ class DataAnalystDataFrameRowMethodMixin(DFBase):
297
301
  )
298
302
  return self.__class__(out)
299
303
 
304
+ @versionadded("5.7.0")
305
+ def add_blank_row(self, fill: Any = np.nan, /) -> Self:
306
+ """
307
+ Add a new row to the end of a DataFrame.
308
+
309
+ Parameters
310
+ ----------
311
+ fill : Any, default np.nan
312
+ Value to fill in the new row (e.g., np.nan, None, "", 0).
313
+
314
+ Returns
315
+ -------
316
+ Self
317
+ DataFrame with the new row appended.
318
+ """
319
+ # Create a dict with all columns filled with fill
320
+ new_row = {col: fill for col in self.columns}
321
+ self.loc[len(self)] = new_row # type: ignore
322
+ return self
323
+
300
324
 
301
325
  # Info
302
326
  # ---------------------------------------------------------------------------
@@ -342,7 +366,7 @@ class DataAnalystDataFrameInfoMixin(DFBase):
342
366
  return info
343
367
 
344
368
  @override
345
- def describe(self, percentiles=None, include=None, exclude=None) -> Self:
369
+ def describe(self, percentiles=None, include=None, exclude=None) -> Self: # type: ignore
346
370
  """pd.DataFrame.describe() override"""
347
371
  return self.__class__(super().describe(percentiles, include, exclude)) # type: ignore [no-any-return]
348
372
 
@@ -486,7 +510,7 @@ class DataAnalystDataFrameInfoMixin(DFBase):
486
510
  if top is not None:
487
511
  list_of_keep: list = (
488
512
  col_df[destination_column]
489
- .head(set_min_max(top - 1, min_value=1, max_value=col_df.shape[0]))
513
+ .head(set_min_max(top - 1, min_value=1, max_value=col_df.shape[0])) # type: ignore
490
514
  .to_list()
491
515
  )
492
516
  # logger.debug(list_of_keep)
@@ -561,7 +585,7 @@ class DataAnalystDataFrameNAMixin(DFBase):
561
585
  except KeyError:
562
586
  if getattr(self, "add_blank_column", None) is not None:
563
587
  # Compatible with DataAnalystDataFrameColumnMethodMixin
564
- self.add_blank_column(column_name, fill_when_not_exist)
588
+ self.add_blank_column(column_name, fill_when_not_exist) # type: ignore
565
589
  return self
566
590
 
567
591
  def get_missing_values(
@@ -679,7 +703,7 @@ class DataAnalystDataFrameNAMixin(DFBase):
679
703
  3 -1.435079 400 400 REPLACED ywahcasi 2024-05-20
680
704
  4 0.118993 861 800 REPLACED saoupuby 2019-04-28
681
705
  """
682
- self[col] = self[col].apply(lambda x: callable(x) if pd.notnull(x) else x)
706
+ self[col] = self[col].apply(lambda x: callable(x) if pd.notnull(x) else x) # type: ignore
683
707
  return self
684
708
 
685
709
  @versionadded("5.1.0") # type: ignore
@@ -750,7 +774,7 @@ class DataAnalystDataFrameNAMixin(DFBase):
750
774
 
751
775
  # Column name
752
776
  cname = "applied_row_null" if col_name is None else col_name
753
- self[cname] = self.apply(apply_func, axis=1)
777
+ self[cname] = self.apply(apply_func, axis=1) # type: ignore
754
778
 
755
779
  return self
756
780
 
@@ -825,7 +849,7 @@ class DataAnalystDataFrameOtherMixin(DFBase):
825
849
 
826
850
  if getattr(self, "drop_columns", None) is not None:
827
851
  # Compatible with DataAnalystDataFrameColumnMethodMixin
828
- self.drop_columns(cols)
852
+ self.drop_columns(cols) # type: ignore
829
853
 
830
854
  out = self.merge(other, how="left", on=on)
831
855
  return self.__class__(out)
@@ -1018,6 +1042,105 @@ class DataAnalystDataFrameDateMixin(DFBase):
1018
1042
  return self
1019
1043
 
1020
1044
 
1045
+ # Export
1046
+ # ---------------------------------------------------------------------------
1047
+ class DataAnalystDataFrameExportMixin(DFBase):
1048
+ """
1049
+ Data Analyst ``pd.DataFrame`` - Export method
1050
+
1051
+ - da_export
1052
+ """
1053
+
1054
+ @versionchanged("5.8.0", "New parameter")
1055
+ def da_export(
1056
+ self,
1057
+ path: str,
1058
+ sheet_name: str = "Sheet1",
1059
+ *,
1060
+ auto_width: bool = True,
1061
+ cols_contain_centered_text: list[str] | None = None,
1062
+ cols_contain_number: list[str] | None = None,
1063
+ cols_contain_percentage: list[str] | None = None,
1064
+ ) -> None:
1065
+ """
1066
+ Export DataFrame with `xlsxwriter` engine
1067
+
1068
+ Parameters
1069
+ ----------
1070
+ path : Path | str
1071
+ Path to export
1072
+
1073
+ sheet_name : str, optional
1074
+ Sheet name, by default "Sheet1"
1075
+
1076
+ auto_width : bool, optional
1077
+ Auto resize column width, by default ``True``
1078
+
1079
+ cols_contain_centered_text : list[str] | None, optional
1080
+ Columns that contain centered text (Align center), by default None
1081
+
1082
+ cols_contain_number : list[str] | None, optional
1083
+ Columns that contain number value (to format as number - int), by default None
1084
+
1085
+ cols_contain_percentage : list[str] | None, optional
1086
+ Columns that contain percentage value (to format as percentage), by default None
1087
+ """
1088
+
1089
+ # Using xlsxwriter engine
1090
+ with pd.ExcelWriter(path, engine="xlsxwriter") as writer:
1091
+ self.to_excel(writer, sheet_name=sheet_name, index=False, float_format="%.2f", na_rep="")
1092
+
1093
+ # Format style
1094
+ workbook: Workbook = writer.book # type: ignore
1095
+ header_fmt = workbook.add_format(
1096
+ {
1097
+ "bold": True,
1098
+ "text_wrap": True,
1099
+ "border": 1,
1100
+ "align": "center",
1101
+ "valign": "vcenter",
1102
+ # "bg_color": "#A0BEFD",
1103
+ }
1104
+ )
1105
+ number_fmt = workbook.add_format(
1106
+ {"num_format": "#,##0", "align": "center", "valign": "vcenter"}
1107
+ ) # 1,000,000
1108
+ percent_fmt = workbook.add_format({"num_format": "0.00%", "align": "center", "valign": "vcenter"}) # 1.00%
1109
+ text_fmt = workbook.add_format({"valign": "vcenter"})
1110
+ text_center_fmt = workbook.add_format({"align": "center", "valign": "vcenter"})
1111
+
1112
+ # Format sheet
1113
+ worksheet: Worksheet = writer.sheets[sheet_name]
1114
+
1115
+ # Format header - First row
1116
+ for col_num, value in enumerate(self.columns.values):
1117
+ worksheet.write(0, col_num, value, header_fmt)
1118
+
1119
+ rules = [
1120
+ (cols_contain_number, number_fmt),
1121
+ (cols_contain_percentage, percent_fmt),
1122
+ (cols_contain_centered_text, text_center_fmt),
1123
+ ]
1124
+
1125
+ # Auto width + col format
1126
+ for i, col in enumerate(self.columns):
1127
+ # Max str len of each column
1128
+ max_len = None if auto_width is None else max(self[col].astype(str).map(len).max(), len(col)) + 2
1129
+ worksheet.set_column(i, i, max_len) # Set width
1130
+
1131
+ # Format style
1132
+ fmt = text_fmt # default
1133
+ for cols, f in rules:
1134
+ if cols is not None and col in cols:
1135
+ fmt = f
1136
+ break
1137
+ worksheet.set_column(i, i, max_len, fmt)
1138
+
1139
+ # if cols_contain_number is not None:
1140
+ # for x in cols_contain_number:
1141
+ # self[x] = pd.to_numeric(self[x], errors="coerce")
1142
+
1143
+
1021
1144
  # City
1022
1145
  # ---------------------------------------------------------------------------
1023
1146
  class DataAnalystDataFrameCityMixin(DFBase):
@@ -1091,6 +1214,7 @@ class DataAnalystDataFrameCityMixin(DFBase):
1091
1214
  class DADF(
1092
1215
  GetClassMembersMixin,
1093
1216
  DataAnalystDataFrameCityMixin,
1217
+ DataAnalystDataFrameExportMixin,
1094
1218
  DataAnalystDataFrameDateMixin,
1095
1219
  DataAnalystDataFrameOtherMixin,
1096
1220
  DataAnalystDataFrameNAMixin,
@@ -1191,3 +1315,14 @@ class DADF_WIP(DADF):
1191
1315
  """
1192
1316
 
1193
1317
  pass
1318
+
1319
+ if __name__ == "__main__":
1320
+ from pathlib import Path
1321
+
1322
+ t = DADF.sample_df().show_distribution("number_range", show_percentage=False)
1323
+ t.da_export(
1324
+ Path(__file__).parent.joinpath("a.xlsx").resolve().__str__(),
1325
+ cols_contain_number=["number_range"],
1326
+ cols_contain_percentage=["percentage"],
1327
+ )
1328
+ print(t)
@@ -3,8 +3,8 @@ Absfuyu: Data Analysis
3
3
  ----------------------
4
4
  Data Analyst DataFrame - Base/Core
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Data Analysis
3
3
  ----------------------
4
4
  DF Function
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/extra/da/mplt.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Analysis
3
3
  ----------------------
4
4
  Matplotlib Helper
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -4,8 +4,8 @@ Absfuyu: Data Analysis [W.I.P]
4
4
  Extension for ``pd.DataFrame``
5
5
  (deprecated)
6
6
 
7
- Version: 5.6.1
8
- Date updated: 12/09/2025 (dd/mm/yyyy)
7
+ Version: 5.8.0
8
+ Date updated: 16/09/2025 (dd/mm/yyyy)
9
9
  """
10
10
 
11
11
  # Library
absfuyu/extra/pdf.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: PDF
3
3
  ------------
4
4
  PDF Tool [W.I.P]
5
5
 
6
- Version: 5.6.1
7
- Date updated: 12/09/2025 (dd/mm/yyyy)
6
+ Version: 5.8.0
7
+ Date updated: 16/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -41,8 +41,6 @@ else:
41
41
 
42
42
  # Class
43
43
  # ---------------------------------------------------------------------------
44
-
45
-
46
44
  class PDFTool(BaseClass):
47
45
  def __init__(self) -> None:
48
46
  super().__init__()