absfuyu 5.6.0__py3-none-any.whl → 5.7.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.
- absfuyu/__init__.py +1 -1
- absfuyu/__main__.py +1 -1
- absfuyu/cli/__init__.py +1 -1
- absfuyu/cli/color.py +1 -1
- absfuyu/cli/config_group.py +1 -1
- absfuyu/cli/do_group.py +1 -1
- absfuyu/cli/game_group.py +1 -1
- absfuyu/cli/tool_group.py +1 -1
- absfuyu/config/__init__.py +1 -1
- absfuyu/core/__init__.py +1 -1
- absfuyu/core/baseclass.py +1 -1
- absfuyu/core/baseclass2.py +1 -1
- absfuyu/core/decorator.py +1 -1
- absfuyu/core/docstring.py +1 -1
- absfuyu/core/dummy_cli.py +1 -1
- absfuyu/core/dummy_func.py +1 -1
- absfuyu/dxt/__init__.py +1 -1
- absfuyu/dxt/dictext.py +1 -1
- absfuyu/dxt/dxt_support.py +1 -1
- absfuyu/dxt/intext.py +1 -1
- absfuyu/dxt/listext.py +1 -1
- absfuyu/dxt/strext.py +1 -1
- absfuyu/extra/__init__.py +1 -1
- absfuyu/extra/beautiful.py +1 -1
- absfuyu/extra/da/__init__.py +4 -2
- absfuyu/extra/da/dadf.py +109 -11
- absfuyu/extra/da/dadf_base.py +1 -1
- absfuyu/extra/da/df_func.py +1 -1
- absfuyu/extra/da/mplt.py +1 -1
- absfuyu/extra/data_analysis.py +1 -1
- absfuyu/extra/pdf.py +2 -4
- absfuyu/extra/xml.py +90 -0
- absfuyu/fun/__init__.py +1 -1
- absfuyu/fun/rubik.py +1 -1
- absfuyu/fun/tarot.py +1 -1
- absfuyu/game/__init__.py +1 -1
- absfuyu/game/game_stat.py +1 -1
- absfuyu/game/sudoku.py +1 -1
- absfuyu/game/tictactoe.py +1 -1
- absfuyu/game/wordle.py +1 -1
- absfuyu/general/__init__.py +1 -1
- absfuyu/general/content.py +1 -1
- absfuyu/general/human.py +1 -1
- absfuyu/general/shape.py +1 -1
- absfuyu/logger.py +1 -1
- absfuyu/pkg_data/__init__.py +1 -1
- absfuyu/pkg_data/deprecated.py +1 -1
- absfuyu/sort.py +1 -1
- absfuyu/tools/__init__.py +1 -1
- absfuyu/tools/checksum.py +1 -1
- absfuyu/tools/converter.py +1 -1
- absfuyu/tools/generator.py +1 -1
- absfuyu/tools/inspector.py +1 -1
- absfuyu/tools/keygen.py +1 -1
- absfuyu/tools/obfuscator.py +1 -1
- absfuyu/tools/passwordlib.py +1 -1
- absfuyu/tools/shutdownizer.py +1 -1
- absfuyu/tools/sw.py +4 -3
- absfuyu/tools/web.py +1 -1
- absfuyu/typings.py +1 -1
- absfuyu/util/__init__.py +1 -1
- absfuyu/util/api.py +1 -1
- absfuyu/util/json_method.py +1 -1
- absfuyu/util/lunar.py +1 -1
- absfuyu/util/path.py +1 -1
- absfuyu/util/performance.py +1 -1
- absfuyu/util/shorten_number.py +1 -1
- absfuyu/util/text_table.py +1 -1
- absfuyu/util/zipped.py +1 -1
- absfuyu/version.py +1 -1
- {absfuyu-5.6.0.dist-info → absfuyu-5.7.0.dist-info}/METADATA +5 -4
- absfuyu-5.7.0.dist-info/RECORD +80 -0
- absfuyu-5.6.0.dist-info/RECORD +0 -79
- {absfuyu-5.6.0.dist-info → absfuyu-5.7.0.dist-info}/WHEEL +0 -0
- {absfuyu-5.6.0.dist-info → absfuyu-5.7.0.dist-info}/entry_points.txt +0 -0
- {absfuyu-5.6.0.dist-info → absfuyu-5.7.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/__init__.py
CHANGED
absfuyu/__main__.py
CHANGED
absfuyu/cli/__init__.py
CHANGED
absfuyu/cli/color.py
CHANGED
absfuyu/cli/config_group.py
CHANGED
absfuyu/cli/do_group.py
CHANGED
absfuyu/cli/game_group.py
CHANGED
absfuyu/cli/tool_group.py
CHANGED
absfuyu/config/__init__.py
CHANGED
absfuyu/core/__init__.py
CHANGED
absfuyu/core/baseclass.py
CHANGED
absfuyu/core/baseclass2.py
CHANGED
absfuyu/core/decorator.py
CHANGED
absfuyu/core/docstring.py
CHANGED
absfuyu/core/dummy_cli.py
CHANGED
absfuyu/core/dummy_func.py
CHANGED
absfuyu/dxt/__init__.py
CHANGED
absfuyu/dxt/dictext.py
CHANGED
absfuyu/dxt/dxt_support.py
CHANGED
absfuyu/dxt/intext.py
CHANGED
absfuyu/dxt/listext.py
CHANGED
absfuyu/dxt/strext.py
CHANGED
absfuyu/extra/__init__.py
CHANGED
absfuyu/extra/beautiful.py
CHANGED
absfuyu/extra/da/__init__.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Data Analysis
|
|
|
3
3
|
----------------------
|
|
4
4
|
Data Analyst
|
|
5
5
|
|
|
6
|
-
Version: 5.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 5.7.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.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 5.7.0
|
|
7
|
+
Date updated: 16/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
|
|
@@ -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
|
# ---------------------------------------------------------------------------
|
|
@@ -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,79 @@ 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
|
+
def da_export(self, path: str, sheet_name: str = "Sheet1", cols_contain_number: list[str] | None = None) -> None:
|
|
1055
|
+
"""
|
|
1056
|
+
Export DataFrame with `xlsxwriter` engine
|
|
1057
|
+
|
|
1058
|
+
Parameters
|
|
1059
|
+
----------
|
|
1060
|
+
path : Path | str
|
|
1061
|
+
Path to export
|
|
1062
|
+
|
|
1063
|
+
sheet_name : str
|
|
1064
|
+
Sheet name
|
|
1065
|
+
|
|
1066
|
+
cols_contain_number : list[str]
|
|
1067
|
+
Columns that contain number value (to format as number)
|
|
1068
|
+
"""
|
|
1069
|
+
# Using xlsxwriter engine
|
|
1070
|
+
with pd.ExcelWriter(path, engine="xlsxwriter") as writer:
|
|
1071
|
+
self.to_excel(writer, sheet_name=sheet_name, index=False, float_format="%.2f", na_rep="")
|
|
1072
|
+
|
|
1073
|
+
# Format style
|
|
1074
|
+
workbook: Workbook = writer.book # type: ignore
|
|
1075
|
+
header_fmt = workbook.add_format(
|
|
1076
|
+
{
|
|
1077
|
+
"bold": True,
|
|
1078
|
+
"text_wrap": True,
|
|
1079
|
+
"border": 1,
|
|
1080
|
+
"align": "center",
|
|
1081
|
+
"valign": "vcenter",
|
|
1082
|
+
# "bg_color": "#A0BEFD",
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
number_fmt = workbook.add_format(
|
|
1086
|
+
{
|
|
1087
|
+
"num_format": "#,##0",
|
|
1088
|
+
"align": "center",
|
|
1089
|
+
"valign": "vcenter",
|
|
1090
|
+
}
|
|
1091
|
+
) # 1,000,000
|
|
1092
|
+
text_fmt = workbook.add_format({"valign": "vcenter"})
|
|
1093
|
+
text_center_fmt = workbook.add_format({"align": "center", "valign": "vcenter"})
|
|
1094
|
+
|
|
1095
|
+
# Format sheet
|
|
1096
|
+
worksheet: Worksheet = writer.sheets[sheet_name]
|
|
1097
|
+
|
|
1098
|
+
# Format header - First row
|
|
1099
|
+
for col_num, value in enumerate(self.columns.values):
|
|
1100
|
+
worksheet.write(0, col_num, value, header_fmt)
|
|
1101
|
+
|
|
1102
|
+
# Auto width + col format
|
|
1103
|
+
for i, col in enumerate(self.columns):
|
|
1104
|
+
# Max str len of each column
|
|
1105
|
+
max_len = max(self[col].astype(str).map(len).max(), len(col)) + 2
|
|
1106
|
+
worksheet.set_column(i, i, max_len) # Set width
|
|
1107
|
+
|
|
1108
|
+
# Format style
|
|
1109
|
+
if cols_contain_number is None:
|
|
1110
|
+
continue
|
|
1111
|
+
|
|
1112
|
+
if col in cols_contain_number:
|
|
1113
|
+
worksheet.set_column(i, i, max_len, number_fmt)
|
|
1114
|
+
else:
|
|
1115
|
+
worksheet.set_column(i, i, max_len, text_fmt)
|
|
1116
|
+
|
|
1117
|
+
|
|
1021
1118
|
# City
|
|
1022
1119
|
# ---------------------------------------------------------------------------
|
|
1023
1120
|
class DataAnalystDataFrameCityMixin(DFBase):
|
|
@@ -1091,6 +1188,7 @@ class DataAnalystDataFrameCityMixin(DFBase):
|
|
|
1091
1188
|
class DADF(
|
|
1092
1189
|
GetClassMembersMixin,
|
|
1093
1190
|
DataAnalystDataFrameCityMixin,
|
|
1191
|
+
DataAnalystDataFrameExportMixin,
|
|
1094
1192
|
DataAnalystDataFrameDateMixin,
|
|
1095
1193
|
DataAnalystDataFrameOtherMixin,
|
|
1096
1194
|
DataAnalystDataFrameNAMixin,
|
absfuyu/extra/da/dadf_base.py
CHANGED
absfuyu/extra/da/df_func.py
CHANGED
absfuyu/extra/da/mplt.py
CHANGED
absfuyu/extra/data_analysis.py
CHANGED
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.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 5.7.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__()
|
absfuyu/extra/xml.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Absfuyu: XML
|
|
3
|
+
------------
|
|
4
|
+
XML Tool [W.I.P]
|
|
5
|
+
|
|
6
|
+
Version: 5.7.0
|
|
7
|
+
Date updated: 16/09/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Module level
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
__all__ = ["XML2Dict"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Library
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Self
|
|
19
|
+
|
|
20
|
+
from absfuyu.core.baseclass import GetClassMembersMixin
|
|
21
|
+
|
|
22
|
+
XML_MODE = False
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import xmltodict
|
|
26
|
+
except ImportError:
|
|
27
|
+
from subprocess import run
|
|
28
|
+
|
|
29
|
+
from absfuyu.config import ABSFUYU_CONFIG
|
|
30
|
+
|
|
31
|
+
if ABSFUYU_CONFIG._get_setting("auto-install-extra").value: # type: ignore
|
|
32
|
+
cmd = "python -m pip install -U absfuyu[full]".split()
|
|
33
|
+
run(cmd)
|
|
34
|
+
else:
|
|
35
|
+
raise SystemExit("This feature is in absfuyu[full] package") # noqa: B904
|
|
36
|
+
else:
|
|
37
|
+
XML_MODE = True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Class
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
class XML2Dict(GetClassMembersMixin):
|
|
43
|
+
"""
|
|
44
|
+
Automatically convert .xml file to dict
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Usage
|
|
48
|
+
-----
|
|
49
|
+
>>> XML2Dict(<path>)
|
|
50
|
+
>>> XML2Dict.parsed_data
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, text: str) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Automatically convert .xml file to dict
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
text : str
|
|
60
|
+
``.xml`` format text
|
|
61
|
+
"""
|
|
62
|
+
self._text = text
|
|
63
|
+
|
|
64
|
+
self.parsed_data: dict[str, Any] = self._parse_xml()
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
return f"{self.__class__.__name__}()"
|
|
68
|
+
|
|
69
|
+
def _parse_xml(self) -> dict[str, Any]:
|
|
70
|
+
"""Convert xml to dict"""
|
|
71
|
+
return xmltodict.parse(self._text, encoding="utf-8") # type: ignore
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_path(cls, xml_path: str | Path) -> Self:
|
|
75
|
+
"""
|
|
76
|
+
Convert from .xml file path
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
xml_path : str | Path
|
|
81
|
+
Path to .xml file
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
Self
|
|
86
|
+
xml to dict
|
|
87
|
+
"""
|
|
88
|
+
with Path(xml_path).open("r", encoding="utf-8") as f:
|
|
89
|
+
dat = "\n".join(f.readlines())
|
|
90
|
+
return cls(dat)
|
absfuyu/fun/__init__.py
CHANGED
absfuyu/fun/rubik.py
CHANGED
absfuyu/fun/tarot.py
CHANGED
absfuyu/game/__init__.py
CHANGED