craceplot 0.1.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.
Files changed (61) hide show
  1. craceplot/__init__.py +60 -0
  2. craceplot/_containers/__init__.py +0 -0
  3. craceplot/_containers/_core.py +1602 -0
  4. craceplot/_containers/_draw.py +104 -0
  5. craceplot/_inst/examples/acotsp/crace-3240078.stdout +838 -0
  6. craceplot/_inst/examples/acotsp/crace.stderr +8 -0
  7. craceplot/_inst/examples/acotsp/crace.test +216 -0
  8. craceplot/_inst/examples/acotsp/crace.train +622 -0
  9. craceplot/_inst/examples/cats200/crace-2814581.stdout +1665 -0
  10. craceplot/_inst/examples/cats200/crace.stderr +10 -0
  11. craceplot/_inst/examples/cats200/crace.test +1010 -0
  12. craceplot/_inst/examples/cats200/crace.train +655 -0
  13. craceplot/_plots/__init__.py +8 -0
  14. craceplot/_plots/_core.py +80 -0
  15. craceplot/_plots/parameters.py +1805 -0
  16. craceplot/_plots/quality.py +1406 -0
  17. craceplot/_scripts/__init__.py +4 -0
  18. craceplot/_scripts/_main.py +132 -0
  19. craceplot/_scripts/_utils.py +66 -0
  20. craceplot/_scripts/main +3 -0
  21. craceplot/_scripts/open_guide +12 -0
  22. craceplot/_settings/_description.py +168 -0
  23. craceplot/_settings/_options.json +299 -0
  24. craceplot/_utils/__init__.py +0 -0
  25. craceplot/_utils/_base.py +196 -0
  26. craceplot/_utils/_const.py +8 -0
  27. craceplot/_utils/_crace.py +42 -0
  28. craceplot/_utils/_format.py +191 -0
  29. craceplot/_vergit.py +2 -0
  30. craceplot/_version.py +24 -0
  31. craceplot/_vignettes/guide.ipynb +18943 -0
  32. craceplot-0.1.0.dist-info/METADATA +369 -0
  33. craceplot-0.1.0.dist-info/RECORD +61 -0
  34. craceplot-0.1.0.dist-info/WHEEL +5 -0
  35. craceplot-0.1.0.dist-info/entry_points.txt +2 -0
  36. craceplot-0.1.0.dist-info/licenses/LICENSE.md +21 -0
  37. craceplot-0.1.0.dist-info/top_level.txt +2 -0
  38. docs/_config.yml +51 -0
  39. docs/_static/css/custom.css +168 -0
  40. docs/_static/js/navbar.js +40 -0
  41. docs/_toc.yml +5 -0
  42. docs/index.md +4 -0
  43. docs/references/404.md +14 -0
  44. docs/references/authors.md +46 -0
  45. docs/references/citiation.bib +17 -0
  46. docs/references/functions/index.md +5 -0
  47. docs/references/functions/param_boxplot.ipynb +19 -0
  48. docs/references/index.md +44 -0
  49. docs/references/license.md +21 -0
  50. docs/references/others/favicon.ico +0 -0
  51. docs/references/others/favicon_io/about.txt +6 -0
  52. docs/references/others/favicon_io/android-chrome-192x192.png +0 -0
  53. docs/references/others/favicon_io/android-chrome-512x512.png +0 -0
  54. docs/references/others/favicon_io/apple-touch-icon.png +0 -0
  55. docs/references/others/favicon_io/favicon-16x16.png +0 -0
  56. docs/references/others/favicon_io/favicon-32x32.png +0 -0
  57. docs/references/others/favicon_io/site.webmanifest +1 -0
  58. docs/references/others/logo.png +0 -0
  59. docs/references/others/logo1.png +0 -0
  60. docs/references/plots/acotsp-parallelcoord-all.png +0 -0
  61. docs/requirements.txt +5 -0
File without changes
@@ -0,0 +1,196 @@
1
+ # craceplot._core._utils.py
2
+
3
+ import re
4
+ import copy
5
+ import inspect
6
+ import numpy as np
7
+ from functools import wraps
8
+ from typing import get_origin, get_args, Union, Literal, TypeVar, ParamSpec
9
+
10
+ P = ParamSpec("P")
11
+ R = TypeVar("R")
12
+
13
+ dispatch_table_param = {}
14
+ methods_table_param = {}
15
+ def register_param(names):
16
+ if isinstance(names, str):
17
+ names = [names]
18
+
19
+ primary_name = names[0]
20
+
21
+ def decorator(func):
22
+ for name in names:
23
+ if name not in dispatch_table_param.keys():
24
+ dispatch_table_param[name] = func
25
+ if name not in methods_table_param.keys():
26
+ methods_table_param[name] = primary_name
27
+ return func
28
+ return decorator
29
+
30
+
31
+ dispatch_table_exps = {}
32
+ methods_table_exps = {}
33
+ def register_exps(names):
34
+ if isinstance(names, str):
35
+ names = [names]
36
+
37
+ primary_name = names[0]
38
+
39
+ def decorator(func):
40
+ for name in names:
41
+ if name not in dispatch_table_exps.keys():
42
+ dispatch_table_exps[name] = func
43
+ if name not in methods_table_exps.keys():
44
+ methods_table_exps[name] = primary_name
45
+ return func
46
+ return decorator
47
+
48
+
49
+ def short_type(t):
50
+ import builtins
51
+ if t in vars(builtins).values(): # builtin types keep as-is
52
+ return t
53
+
54
+ class NewName:
55
+ __name__ = t.__name__
56
+ __qualname__ = t.__name__
57
+
58
+ try:
59
+ del NewName.__module__
60
+ except Exception:
61
+ NewName.__module__ = None
62
+
63
+ return NewName
64
+
65
+
66
+ def enforce_types(func):
67
+ sig = inspect.signature(func)
68
+
69
+ @wraps(func)
70
+ def wrapper(*args, **kwargs):
71
+ bound = sig.bind(*args, **kwargs)
72
+ bound.apply_defaults()
73
+
74
+ for name, value in bound.arguments.items():
75
+ if value is None:
76
+ continue
77
+
78
+ expected = sig.parameters[name].annotation
79
+
80
+ if expected is inspect._empty:
81
+ continue
82
+
83
+ # Forward reference
84
+ if isinstance(expected, str):
85
+ if expected == "CraceResults":
86
+ from crace.containers.crace_results import CraceResults
87
+ expected = CraceResults
88
+ else:
89
+ continue
90
+
91
+ check_type(value, expected, name)
92
+
93
+ return func(*args, **kwargs)
94
+
95
+ wrapper.__signature__ = sig
96
+ return wrapper
97
+
98
+
99
+ def check_type(value, expected, name):
100
+ origin = get_origin(expected)
101
+ args = get_args(expected)
102
+
103
+ # ===== Union: try ALL branches =====
104
+ if origin is Union:
105
+ for t in args:
106
+ try:
107
+ check_type(value, t, name)
108
+ return # any branch ok
109
+ except Exception:
110
+ pass
111
+ raise TypeError(
112
+ f"Argument/Option '{name}' does not match any allowed type in {args}, "
113
+ f"got {value!r}({type(value)})"
114
+ )
115
+
116
+ # ===== Literal =====
117
+ if origin is Literal:
118
+ if value not in args:
119
+ raise ValueError(
120
+ f"Argument/Option '{name}' must be one of {args}, got {value!r}"
121
+ )
122
+ return
123
+
124
+ # --------------------------------
125
+ # list / list[T]
126
+ # --------------------------------
127
+ if origin is list or expected is list:
128
+ if not isinstance(value, list):
129
+ raise TypeError(f"Argument/Option '{name}' must be list, got {type(value)}")
130
+
131
+ if len(args) == 0:
132
+ return
133
+
134
+ elem_type = args[0]
135
+
136
+ for v in value:
137
+ check_type(v, elem_type, name)
138
+
139
+ return
140
+
141
+ # tuple[T, ...]
142
+ if origin is tuple:
143
+ if not isinstance(value, tuple):
144
+ raise TypeError(f"Argument/Option '{name}' must be tuple")
145
+ elem_types = args
146
+ if len(elem_types) == 2 and elem_types[1] is Ellipsis:
147
+ for v in value:
148
+ check_type(v, elem_types[0], name)
149
+ else:
150
+ for v, t in zip(value, elem_types):
151
+ check_type(v, t, name)
152
+ return
153
+
154
+ # ===== NoneType =====
155
+ if expected is type(None):
156
+ if value is not None:
157
+ raise TypeError(f"Argument/Option '{name}' must be None")
158
+ return
159
+
160
+ # ===== normal class =====
161
+ if isinstance(expected, type):
162
+ if not isinstance(value, expected):
163
+ raise TypeError(
164
+ f"Argument/Option '{name}' expected {expected}, got {type(value)}"
165
+ )
166
+ return
167
+
168
+
169
+ def safe_copy(obj):
170
+ try:
171
+ return copy.deepcopy(obj)
172
+ except Exception:
173
+ return obj
174
+
175
+
176
+ def parse_range_list(s: str):
177
+ """
178
+ Parse '[start:stop]' or '[start:stop:step]' into list[int].
179
+ Python range semantics: stop is exclusive.
180
+ """
181
+ re_format = re.compile(
182
+ r'^\s*\[?\s*(-?\d+)\s*:\s*(-?\d+)(?:\s*:\s*(-?\d+(?:\.\d+)?))?\s*\]?\s*$')
183
+ m = re_format.match(s)
184
+ print(s, m)
185
+ if not m:
186
+ return None
187
+
188
+ start = int(m.group(1))
189
+ stop = int(m.group(2))
190
+ step = m.group(3)
191
+
192
+ if step is None:
193
+ return np.arange(start, stop+1).tolist()
194
+ else:
195
+ step = eval(step)
196
+ return np.arange(start, stop+step, step).tolist()
@@ -0,0 +1,8 @@
1
+ bold = '\033[1m'
2
+ underline = '\033[4m'
3
+ reset = '\033[0m'
4
+
5
+ WIDTH = 100
6
+
7
+ DELTA = 1e-4
8
+ EPSILON = 1e-10
@@ -0,0 +1,42 @@
1
+ import os
2
+ import sys
3
+
4
+ def _setup_crace(crace_main_custom_file=None):
5
+ if crace_main_custom_file is not None:
6
+ for name in list(sys.modules):
7
+ if name == "crace" or name.startswith("crace."):
8
+ del sys.modules[name]
9
+ crace_main_file = crace_main_custom_file
10
+ else:
11
+ crace_main_file = os.environ.get("CRACE_MAIN_FILE")
12
+
13
+ # case 1: if CRACE_MAIN_FILE exists
14
+ if crace_main_file and os.path.exists(crace_main_file):
15
+ # from .../crace/scripts/main.py → return .../crace
16
+ crace_home = os.path.dirname(os.path.dirname(os.path.dirname(crace_main_file)))
17
+
18
+ # insert crace home to path
19
+ if crace_home not in map(os.path.abspath, sys.path):
20
+ sys.path.insert(0, str(crace_home))
21
+
22
+ try:
23
+ import crace
24
+ return crace
25
+ except ImportError as exc:
26
+ if crace_main_file:
27
+ raise RuntimeError(
28
+ f"Failed to import crace from CRACE_MAIN_FILE directory:\n{crace_home}"
29
+ ) from exc
30
+ else:
31
+ raise RuntimeError(
32
+ "CRACE_MAIN_FILE is not set, and 'import crace' also failed.\n"
33
+ "Please set CRACE_MAIN_FILE to point to crace/scripts/main.py, \n"
34
+ "or provide the path to crace/scripts/main.py after --crace or -c in command line arguments."
35
+ ) from exc
36
+
37
+ def get_crace(crace_main_file=None):
38
+ return _setup_crace(crace_main_file)
39
+
40
+ def get_CraceResults():
41
+ from crace.containers.crace_results import CraceResults
42
+ return CraceResults
@@ -0,0 +1,191 @@
1
+ import re
2
+ import pandas as pd
3
+
4
+ from craceplot._utils._const import WIDTH
5
+
6
+ _ESCAPE_RE = re.compile(r'\\([ntrfvab\\])')
7
+ _ESCAPE_MAP = {
8
+ "n": "\n",
9
+ "t": "\t",
10
+ "r": "\r",
11
+ "f": "\f",
12
+ "v": "\v",
13
+ "a": "\a",
14
+ "b": "\b",
15
+ "\\": "\\",
16
+ }
17
+
18
+ def _decode_escape(text: str):
19
+ return _ESCAPE_RE.sub(lambda m: _ESCAPE_MAP[m.group(1)], text)
20
+
21
+ def _strip_ansi(s: str) -> str:
22
+ # CSI (colors, style, etc.)
23
+ s = re.sub(r'\x1b\[[0-9;]*m', '', s)
24
+ # OSC 8 hyperlinks: ESC ] 8 ;; ... ESC \
25
+ s = re.sub(r'\x1b]8;;.*?\x1b\\', '', s)
26
+ return s
27
+
28
+ def _ansi_wrap(text: str, width: int, hanging: int, space: bool=False):
29
+ """
30
+ textwrap based on visible width (ANSI-aware)
31
+ preserve explicit newlines
32
+ hanging applies to wrapped lines inside each paragraph
33
+ """
34
+ out = []
35
+
36
+ paras = text.split("\n")
37
+
38
+ for i, para in enumerate(paras):
39
+ if para.strip() == "":
40
+ out.append("")
41
+ continue
42
+
43
+ out.extend(_ansi_wrap_one_para(para, width, hanging, space, i==0))
44
+
45
+ return out
46
+
47
+ def _ansi_wrap_one_para(text: str, width: int, hanging: int, space: bool, init_line: bool):
48
+ base_indent = ""
49
+
50
+ if space:
51
+ text = text.replace(r"\t", "\t").expandtabs(2)
52
+ m = re.match(r"^ *", text)
53
+ base_indent = m.group(0)
54
+ text = text[len(base_indent):]
55
+
56
+ words = text.split()
57
+ lines = []
58
+ current = ""
59
+ first = True
60
+
61
+ available_width = width - len(base_indent) - hanging # available width
62
+ hanging_indent = base_indent + " " * hanging
63
+
64
+
65
+ for w in words:
66
+
67
+ test = w if not current else current + " " + w
68
+
69
+ if len(_strip_ansi(test)) > available_width:
70
+ if current:
71
+ if first and init_line:
72
+ lines.append(base_indent + current)
73
+ else:
74
+ lines.append(hanging_indent + current)
75
+ current = w
76
+ first = False
77
+ else:
78
+ current = test
79
+
80
+ if current:
81
+ if first and init_line:
82
+ lines.append(base_indent + current)
83
+ else:
84
+ lines.append(hanging_indent + current)
85
+
86
+ return lines
87
+
88
+ def _make_ascii_box(text: str, width: int, hanging: int):
89
+ """replace xwarningbox to ascii format"""
90
+ # textwrap
91
+ lines = _ansi_wrap(text, width-2, hanging)
92
+ maxw = max(len(_strip_ansi(l)) for l in lines) if lines else 0
93
+
94
+ top = " +" + "-" * (maxw + 2) + "+"
95
+ box = [top]
96
+ for l in lines:
97
+ pad = maxw - len(_strip_ansi(l))
98
+ box.append(f" | {l}{' ' * pad} |")
99
+ box.append(top)
100
+
101
+ return "\n".join(box)
102
+
103
+ def _make_ascii_list(text: str, width: int, hanging: int):
104
+ """
105
+ transform itemsize to ascii format
106
+ """
107
+ ITEM_RE = re.compile(r"\\item\[\]\s*(.*?)(?=\\item\[\]|$)", re.DOTALL)
108
+
109
+ items = ITEM_RE.findall(text)
110
+ if not items:
111
+ return ""
112
+
113
+ output = []
114
+ for item in items:
115
+ item = item.strip()
116
+
117
+ wrapped = _ansi_wrap(item, width-4, hanging) # with prefix "- "
118
+ if wrapped:
119
+ # first line with prefix "- "
120
+ output.append(f" - {wrapped[0]}")
121
+ # other lines
122
+ for w in wrapped[1:]:
123
+ output.append(f" {w}")
124
+ else:
125
+ output.append("-")
126
+
127
+ return "\n".join(output)
128
+
129
+ def format_string(text: str, width: int=WIDTH, hanging: int=0, space: bool=False):
130
+ """
131
+ - transform xwarningbox → ASCII frame
132
+ - transform itemize → ASCII list
133
+ - textwrap for other text
134
+
135
+ :param text: input string
136
+ :param width: max width of output string
137
+ :param hanging: hanging indent for wrapped lines
138
+ :param space: whether to preserve spaces and tabs at the beginning of each line
139
+ """
140
+ output = []
141
+ pos = 0
142
+
143
+ # search patterns: warningbox -> itemize
144
+ for match in re.finditer(
145
+ r"(\\begin{xwarningbox}.*?\\end{xwarningbox}|"
146
+ r"\\begin{itemize}.*?\\end{itemize})",
147
+ text,
148
+ flags=re.DOTALL,
149
+ ):
150
+ start, end = match.span()
151
+ before = text[pos:start]
152
+
153
+ # normal text in front
154
+ if before.strip():
155
+ output.append("\n".join(_ansi_wrap(before, width, hanging)))
156
+
157
+ block = match.group(1)
158
+
159
+ # xwarningbox
160
+ if block.startswith(r"\begin{xwarningbox}"):
161
+ content = re.search(r"\\begin{xwarningbox}\s*(.*?)\s*\\end{xwarningbox}", block, re.DOTALL).group(1)
162
+ output.append(_make_ascii_box(content.strip(), width, hanging))
163
+
164
+ # itemize
165
+ elif block.startswith(r"\begin{itemize}"):
166
+ content = re.search(r"\\begin{itemize}\s*(.*?)\s*\\end{itemize}", block, re.DOTALL).group(1)
167
+ output.append(_make_ascii_list(content.strip(), width, hanging))
168
+
169
+ pos = end
170
+
171
+ # normal text in the end
172
+ tail = text[pos:]
173
+ if tail.strip():
174
+ output.append("\n".join(_ansi_wrap(tail, width, hanging, space)))
175
+
176
+ return "\n".join(output)
177
+
178
+
179
+ class ConditionalReturn:
180
+ def __init__(self, **kwargs):
181
+ self.kwargs = kwargs if len(kwargs) != 0 else None
182
+
183
+ def __repr__(self):
184
+ if self.kwargs:
185
+ for k,v in self.kwargs.items():
186
+ if isinstance(v, pd.DataFrame) and 'title' in v.attrs and v.attrs['title']:
187
+ print(f"{v.attrs['title']} \n{v}\n")
188
+ elif isinstance(v, str):
189
+ print(f"{v}")
190
+ else: print(f'{k}: {v}')
191
+ return ""
craceplot/_vergit.py ADDED
@@ -0,0 +1,2 @@
1
+ __version__ = "$Format:%d$"
2
+ __full_version__ = "$Format:%H$"
craceplot/_version.py ADDED
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.0'
22
+ __version_tuple__ = version_tuple = (0, 1, 0)
23
+
24
+ __commit_id__ = commit_id = 'g120756444'