plotlibs 0.3.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.
- plotlib/__init__.py +38 -0
- plotlibs/__init__.py +110 -0
- plotlibs/backends/__init__.py +0 -0
- plotlibs/backends/renderer.py +520 -0
- plotlibs/colors.py +107 -0
- plotlibs/data.py +208 -0
- plotlibs/db.py +204 -0
- plotlibs/eda.py +157 -0
- plotlibs/fast.py +187 -0
- plotlibs/figure.py +617 -0
- plotlibs/ml.py +205 -0
- plotlibs/pyplot.py +283 -0
- plotlibs/style.py +96 -0
- plotlibs-0.3.0.dist-info/METADATA +228 -0
- plotlibs-0.3.0.dist-info/RECORD +18 -0
- plotlibs-0.3.0.dist-info/WHEEL +5 -0
- plotlibs-0.3.0.dist-info/licenses/LICENSE +21 -0
- plotlibs-0.3.0.dist-info/top_level.txt +2 -0
plotlibs/ml.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""plotlibs.ml — رسوم تعلم الآلة والتعلم العميق (تُشهر المكتبة عند ML/DL).
|
|
2
|
+
|
|
3
|
+
تعمل بـ numpy فقط — لا تحتاج sklearn/keras للرسم، لكن تقبل مخرجاتها.
|
|
4
|
+
|
|
5
|
+
import plotlibs as pl
|
|
6
|
+
pl.plot_history({"loss": [...], "val_loss": [...]})
|
|
7
|
+
pl.plot_confusion_matrix(y_true, y_pred)
|
|
8
|
+
pl.plot_roc(y_true, y_score)
|
|
9
|
+
pl.plot_feature_importance(names, values)
|
|
10
|
+
pl.plot_images(images) # للتعلم العميق / رؤية حاسوبية
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _plt():
|
|
18
|
+
from . import pyplot as plt
|
|
19
|
+
return plt
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def plot_history(history, metrics=("loss",), savefig=None, title="training history", smooth: int = 1):
|
|
23
|
+
"""ارسم history من Keras (dict) أو DataFrame أو dict of lists.
|
|
24
|
+
|
|
25
|
+
history = {"loss": [...], "val_loss": [...], "accuracy": [...]}
|
|
26
|
+
"""
|
|
27
|
+
plt = _plt()
|
|
28
|
+
if hasattr(history, "history"): # keras History object
|
|
29
|
+
history = history.history
|
|
30
|
+
if hasattr(history, "to_dict"):
|
|
31
|
+
try:
|
|
32
|
+
history = history.to_dict(orient="list")
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
keys = [k for k in history.keys() if not k.startswith("val_")] if isinstance(history, dict) else []
|
|
36
|
+
if not keys and isinstance(history, dict):
|
|
37
|
+
keys = list(history.keys())
|
|
38
|
+
if metrics == ("loss",) and keys:
|
|
39
|
+
# تلقائياً: كل المقاييس الموجودة
|
|
40
|
+
metrics = tuple(keys)
|
|
41
|
+
fig_axes = []
|
|
42
|
+
for m in metrics:
|
|
43
|
+
train = np.asarray(history[m], dtype=float).ravel() if m in history else None
|
|
44
|
+
val = np.asarray(history.get(f"val_{m}", []), dtype=float).ravel() if isinstance(history, dict) else None
|
|
45
|
+
if train is None:
|
|
46
|
+
continue
|
|
47
|
+
if smooth > 1 and train.size >= smooth:
|
|
48
|
+
train = np.convolve(train, np.ones(smooth) / smooth, mode="same")
|
|
49
|
+
plt.figure()
|
|
50
|
+
plt.plot(train, label=f"train {m}")
|
|
51
|
+
if val is not None and val.size:
|
|
52
|
+
plt.plot(val, label=f"val {m}")
|
|
53
|
+
plt.xlabel("epoch")
|
|
54
|
+
plt.ylabel(m)
|
|
55
|
+
plt.title(title if len(metrics) == 1 else f"{title} — {m}")
|
|
56
|
+
plt.legend()
|
|
57
|
+
plt.grid(True)
|
|
58
|
+
fig_axes.append(plt.gcf())
|
|
59
|
+
if savefig and len(metrics) == 1:
|
|
60
|
+
plt.savefig(savefig)
|
|
61
|
+
return fig_axes[0] if len(fig_axes) == 1 else fig_axes
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def confusion_matrix_data(y_true, y_pred, labels=None):
|
|
65
|
+
y_true = np.asarray(y_true).ravel()
|
|
66
|
+
y_pred = np.asarray(y_pred).ravel()
|
|
67
|
+
n = min(y_true.size, y_pred.size)
|
|
68
|
+
y_true, y_pred = y_true[:n], y_pred[:n]
|
|
69
|
+
if labels is None:
|
|
70
|
+
labels = sorted(map(str, np.unique(np.concatenate([y_true, y_pred]))))
|
|
71
|
+
lab_to_i = {str(l): i for i, l in enumerate(labels)}
|
|
72
|
+
cm = np.zeros((len(labels), len(labels)), dtype=float)
|
|
73
|
+
for t, p in zip(y_true, y_pred):
|
|
74
|
+
i = lab_to_i.get(str(t))
|
|
75
|
+
j = lab_to_i.get(str(p))
|
|
76
|
+
if i is not None and j is not None:
|
|
77
|
+
cm[i, j] += 1
|
|
78
|
+
return cm, labels
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def plot_confusion_matrix(y_true=None, y_pred=None, cm=None, labels=None,
|
|
82
|
+
normalize: bool = False, title="confusion matrix", **kw):
|
|
83
|
+
"""مصفوفة الالتباس — تقبل (y_true, y_pred) أو مصفوفة جاهزة cm."""
|
|
84
|
+
plt = _plt()
|
|
85
|
+
if cm is None:
|
|
86
|
+
if y_true is None or y_pred is None:
|
|
87
|
+
raise ValueError("pass (y_true, y_pred) or cm=")
|
|
88
|
+
cm, labels = confusion_matrix_data(y_true, y_pred, labels)
|
|
89
|
+
else:
|
|
90
|
+
cm = np.asarray(cm, dtype=float)
|
|
91
|
+
labels = labels or [str(i) for i in range(cm.shape[0])]
|
|
92
|
+
disp = cm / cm.sum(axis=1, keepdims=True).clip(min=1) if normalize else cm
|
|
93
|
+
plt.gca().heatmap(disp, xticks=labels, yticks=labels, annot=True, cmap="viridis")
|
|
94
|
+
plt.title(title + (" (normalized)" if normalize else ""))
|
|
95
|
+
plt.xlabel("predicted")
|
|
96
|
+
plt.ylabel("true")
|
|
97
|
+
return disp
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def plot_roc(y_true, y_score, label="", **kw):
|
|
101
|
+
"""منحنى ROC + AUC (numpy فقط)."""
|
|
102
|
+
from .fast import roc_from_scores
|
|
103
|
+
plt = _plt()
|
|
104
|
+
fpr, tpr, auc = roc_from_scores(y_true, y_score)
|
|
105
|
+
plt.plot(fpr, tpr, label=f"{label} AUC={auc:.3f}" if label else f"AUC={auc:.3f}")
|
|
106
|
+
plt.plot([0, 1], [0, 1], linestyle="--", color="gray")
|
|
107
|
+
plt.xlabel("FPR")
|
|
108
|
+
plt.ylabel("TPR")
|
|
109
|
+
plt.title("ROC curve")
|
|
110
|
+
plt.legend()
|
|
111
|
+
plt.grid(True)
|
|
112
|
+
return fpr, tpr, auc
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def plot_pr(y_true, y_score, label="", **kw):
|
|
116
|
+
"""منحنى Precision-Recall + AP."""
|
|
117
|
+
from .fast import pr_from_scores
|
|
118
|
+
plt = _plt()
|
|
119
|
+
rec, prec, ap = pr_from_scores(y_true, y_score)
|
|
120
|
+
plt.plot(rec, prec, label=f"{label} AP={ap:.3f}" if label else f"AP={ap:.3f}")
|
|
121
|
+
plt.xlabel("recall")
|
|
122
|
+
plt.ylabel("precision")
|
|
123
|
+
plt.title("precision-recall curve")
|
|
124
|
+
plt.legend()
|
|
125
|
+
plt.grid(True)
|
|
126
|
+
return rec, prec, ap
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def plot_feature_importance(names, values, top: int = 20, title="feature importance", **kw):
|
|
130
|
+
"""أهمية الميزات (XGBoost/sklearn/RF) كأشرطة أفقية مرتبة."""
|
|
131
|
+
plt = _plt()
|
|
132
|
+
names = np.asarray(names).ravel()
|
|
133
|
+
values = np.asarray(values, dtype=float).ravel()
|
|
134
|
+
n = min(names.size, values.size)
|
|
135
|
+
names, values = names[:n], values[:n]
|
|
136
|
+
order = np.argsort(values, kind="stable")[-top:]
|
|
137
|
+
plt.barh([str(names[i]) for i in order], values[order])
|
|
138
|
+
plt.title(title)
|
|
139
|
+
plt.grid(True)
|
|
140
|
+
return order
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def plot_residuals(y_true, y_pred, title="residuals", **kw):
|
|
144
|
+
"""بقايا الانحدار: predicted مقابل residual + هستوغرام."""
|
|
145
|
+
plt = _plt()
|
|
146
|
+
y_true = np.asarray(y_true, dtype=float).ravel()
|
|
147
|
+
y_pred = np.asarray(y_pred, dtype=float).ravel()
|
|
148
|
+
n = min(y_true.size, y_pred.size)
|
|
149
|
+
res = y_true[:n] - y_pred[:n]
|
|
150
|
+
fig, axs = plt.subplots(1, 2, figsize=(10, 4))
|
|
151
|
+
axs[0].scatter(y_pred[:n], res)
|
|
152
|
+
axs[0].axhline(0)
|
|
153
|
+
axs[0].set_xlabel("predicted")
|
|
154
|
+
axs[0].set_ylabel("residual")
|
|
155
|
+
axs[0].set_title(title)
|
|
156
|
+
axs[1].hist(res, bins=30)
|
|
157
|
+
axs[1].set_title("residual dist")
|
|
158
|
+
fig.suptitle(title)
|
|
159
|
+
return res
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def plot_elbow(k_list, inertia, title="elbow method", **kw):
|
|
163
|
+
plt = _plt()
|
|
164
|
+
plt.plot(np.asarray(k_list), np.asarray(inertia, dtype=float), marker="o")
|
|
165
|
+
plt.xlabel("k")
|
|
166
|
+
plt.ylabel("inertia")
|
|
167
|
+
plt.title(title)
|
|
168
|
+
plt.grid(True)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def plot_images(images, ncols: int = 8, max_images: int = 64, title="", cmap="gray", **kw):
|
|
172
|
+
"""شبكة صور للتعلم العميق (MNIST/CIFAR) — numpy فقط."""
|
|
173
|
+
plt = _plt()
|
|
174
|
+
arr = np.asarray(images)
|
|
175
|
+
if arr.ndim == 3:
|
|
176
|
+
arr = arr[:max_images]
|
|
177
|
+
elif arr.ndim == 4:
|
|
178
|
+
arr = arr[:max_images]
|
|
179
|
+
else:
|
|
180
|
+
raise ValueError("images must be (N,H,W) or (N,H,W,C)")
|
|
181
|
+
n = arr.shape[0]
|
|
182
|
+
ncols = min(ncols, n)
|
|
183
|
+
nrows = int(np.ceil(n / ncols))
|
|
184
|
+
fig, axs = plt.subplots(nrows, ncols, figsize=(ncols * 1.4, nrows * 1.4))
|
|
185
|
+
axs = np.asarray(axs, dtype=object).ravel()
|
|
186
|
+
for i in range(len(axs)):
|
|
187
|
+
if i < n:
|
|
188
|
+
axs[i].imshow(arr[i], cmap=cmap)
|
|
189
|
+
axs[i].set_title("")
|
|
190
|
+
if title:
|
|
191
|
+
fig.suptitle(title)
|
|
192
|
+
return fig
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def plot_clusters_2d(X, labels, title="clusters", **kw):
|
|
196
|
+
"""عناقيد 2D ملونة (KMeans/DBSCAN) — سريعة حتى 50k نقطة."""
|
|
197
|
+
plt = _plt()
|
|
198
|
+
X = np.asarray(X, dtype=float)
|
|
199
|
+
labels = np.asarray(labels).ravel()
|
|
200
|
+
for lab in sorted(np.unique(labels)):
|
|
201
|
+
m = labels == lab
|
|
202
|
+
plt.scatter(X[m, 0][:10000], X[m, 1][:10000], label=f"c{lab}")
|
|
203
|
+
plt.title(title)
|
|
204
|
+
plt.legend()
|
|
205
|
+
return plt.gca()
|
plotlibs/pyplot.py
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""plotlibs.pyplot — matplotlib.pyplot-compatible state machine, faster."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from . import style
|
|
5
|
+
from .figure import Figure, subplots as _subplots
|
|
6
|
+
|
|
7
|
+
_figures: dict[int, Figure] = {}
|
|
8
|
+
_current: Figure | None = None
|
|
9
|
+
_counter = [0]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _cur() -> Figure:
|
|
13
|
+
global _current
|
|
14
|
+
if _current is None:
|
|
15
|
+
_current = figure()
|
|
16
|
+
return _current
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def figure(figsize=None, dpi=None, facecolor=None, num=None):
|
|
20
|
+
global _current
|
|
21
|
+
_counter[0] += 1
|
|
22
|
+
n = num if num is not None else _counter[0]
|
|
23
|
+
fig = Figure(figsize=figsize, dpi=dpi, facecolor=facecolor, num=n)
|
|
24
|
+
_figures[n] = fig
|
|
25
|
+
_current = fig
|
|
26
|
+
return fig
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def gcf():
|
|
30
|
+
return _cur()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def gca():
|
|
34
|
+
fig = _cur()
|
|
35
|
+
if not fig.axes:
|
|
36
|
+
return fig.add_subplot(1, 1, 1)
|
|
37
|
+
return fig.axes[-1]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def subplots(nrows=1, ncols=1, figsize=None, dpi=None, **kw):
|
|
41
|
+
global _current
|
|
42
|
+
fig = Figure(figsize=figsize, dpi=dpi)
|
|
43
|
+
axs = fig.subplots(nrows, ncols)
|
|
44
|
+
_counter[0] += 1
|
|
45
|
+
_figures[_counter[0]] = fig
|
|
46
|
+
_current = fig
|
|
47
|
+
return fig, axs
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def subplot(nrows, ncols, index, **kw):
|
|
51
|
+
return _cur().add_subplot(nrows, ncols, index)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def plot(*args, **kw):
|
|
55
|
+
return gca().plot(*args, **kw)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def scatter(*args, **kw):
|
|
59
|
+
return gca().scatter(*args, **kw)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def bar(*args, **kw):
|
|
63
|
+
return gca().bar(*args, **kw)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def barh(*args, **kw):
|
|
67
|
+
return gca().barh(*args, **kw)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def hist(*args, **kw):
|
|
71
|
+
return gca().hist(*args, **kw)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def imshow(*args, **kw):
|
|
75
|
+
return gca().imshow(*args, **kw)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def pie(*args, **kw):
|
|
79
|
+
return gca().pie(*args, **kw)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def fill_between(*args, **kw):
|
|
83
|
+
return gca().fill_between(*args, **kw)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def step(*args, **kw):
|
|
87
|
+
return gca().step(*args, **kw)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def errorbar(*args, **kw):
|
|
91
|
+
return gca().errorbar(*args, **kw)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def axhline(*args, **kw):
|
|
95
|
+
return gca().axhline(*args, **kw)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def axvline(*args, **kw):
|
|
99
|
+
return gca().axvline(*args, **kw)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def text(x, y, s, **kw):
|
|
103
|
+
return gca().text(x, y, s, **kw)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def boxplot(*args, **kw):
|
|
107
|
+
return gca().boxplot(*args, **kw)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def violinplot(*args, **kw):
|
|
111
|
+
return gca().violinplot(*args, **kw)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def kde(*args, **kw):
|
|
115
|
+
return gca().kde(*args, **kw)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
density = kde
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def heatmap(*args, **kw):
|
|
122
|
+
return gca().heatmap(*args, **kw)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def corr(*args, **kw):
|
|
126
|
+
return gca().corr(*args, **kw)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def corrcoef(*args, **kw):
|
|
130
|
+
return gca().corr(*args, **kw)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def countplot(*args, **kw):
|
|
134
|
+
return gca().countplot(*args, **kw)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
count = countplot
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def area(*args, **kw):
|
|
141
|
+
return gca().area(*args, **kw)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
stackplot = area
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def hist2d(*args, **kw):
|
|
148
|
+
return gca().hist2d(*args, **kw)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def stem(*args, **kw):
|
|
152
|
+
return gca().stem(*args, **kw)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def xlim(*args, **kw):
|
|
156
|
+
ax = gca()
|
|
157
|
+
if not args and not kw:
|
|
158
|
+
return ax.get_xlim()
|
|
159
|
+
if len(args) == 2:
|
|
160
|
+
return ax.set_xlim(args[0], args[1])
|
|
161
|
+
if len(args) == 1:
|
|
162
|
+
return ax.set_xlim(args[0][0], args[0][1])
|
|
163
|
+
left = kw.pop("left", None)
|
|
164
|
+
right = kw.pop("right", None)
|
|
165
|
+
return ax.set_xlim(left, right)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def ylim(*args, **kw):
|
|
169
|
+
ax = gca()
|
|
170
|
+
if not args and not kw:
|
|
171
|
+
return ax.get_ylim()
|
|
172
|
+
if len(args) == 2:
|
|
173
|
+
return ax.set_ylim(args[0], args[1])
|
|
174
|
+
if len(args) == 1:
|
|
175
|
+
return ax.set_ylim(args[0][0], args[0][1])
|
|
176
|
+
bottom = kw.pop("bottom", None)
|
|
177
|
+
top = kw.pop("top", None)
|
|
178
|
+
return ax.set_ylim(bottom, top)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def xlabel(s, **kw):
|
|
182
|
+
gca().set_xlabel(s)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def ylabel(s, **kw):
|
|
186
|
+
gca().set_ylabel(s)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def title(s, **kw):
|
|
190
|
+
gca().set_title(s)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def suptitle(s, **kw):
|
|
194
|
+
_cur().suptitle(s)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def legend(*args, **kw):
|
|
198
|
+
return gca().legend(*args, **kw)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def grid(visible=True, **kw):
|
|
202
|
+
gca().grid(visible, **kw)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def xticks(ticks=None, labels=None, **kw):
|
|
206
|
+
ax = gca()
|
|
207
|
+
if ticks is None:
|
|
208
|
+
from .fast import nice_ticks
|
|
209
|
+
xl = ax.get_xlim()
|
|
210
|
+
return nice_ticks(xl[0], xl[1]), None
|
|
211
|
+
ax.set_xticks(ticks)
|
|
212
|
+
return ticks, labels
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def yticks(ticks=None, labels=None, **kw):
|
|
216
|
+
ax = gca()
|
|
217
|
+
if ticks is None:
|
|
218
|
+
from .fast import nice_ticks
|
|
219
|
+
yl = ax.get_ylim()
|
|
220
|
+
return nice_ticks(yl[0], yl[1]), None
|
|
221
|
+
ax.set_yticks(ticks)
|
|
222
|
+
return ticks, labels
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def xscale(s, **kw):
|
|
226
|
+
gca().set_xscale(s)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def yscale(s, **kw):
|
|
230
|
+
gca().set_yscale(s)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def semilogx(*a, **k):
|
|
234
|
+
r = plot(*a, **k)
|
|
235
|
+
gca().set_xscale("log")
|
|
236
|
+
return r
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def semilogy(*a, **k):
|
|
240
|
+
r = plot(*a, **k)
|
|
241
|
+
gca().set_yscale("log")
|
|
242
|
+
return r
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def loglog(*a, **k):
|
|
246
|
+
r = plot(*a, **k)
|
|
247
|
+
gca().set_xscale("log")
|
|
248
|
+
gca().set_yscale("log")
|
|
249
|
+
return r
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def tight_layout(**kw):
|
|
253
|
+
_cur().tight_layout(**kw)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def savefig(fname, dpi=None, **kw):
|
|
257
|
+
_cur().savefig(fname, dpi=dpi, **kw)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def show(**kw):
|
|
261
|
+
_cur().show()
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def close(*args, **kw):
|
|
265
|
+
global _current
|
|
266
|
+
_figures.clear()
|
|
267
|
+
_current = None
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def clf():
|
|
271
|
+
_cur().clf()
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def cla():
|
|
275
|
+
gca().cla()
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def rcParams_update(d):
|
|
279
|
+
style.rcParams.update(d)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
rcParams = style.rcParams
|
|
283
|
+
style_use = style.use
|
plotlibs/style.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""plotlibs.style — rcParams + styles, matplotlib-compatible subset."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
rcParams: dict = {
|
|
5
|
+
"figure.figsize": (6.4, 4.8),
|
|
6
|
+
"figure.dpi": 100,
|
|
7
|
+
"figure.facecolor": "white",
|
|
8
|
+
"axes.facecolor": "white",
|
|
9
|
+
"axes.edgecolor": "black",
|
|
10
|
+
"axes.grid": False,
|
|
11
|
+
"axes.titlesize": 13,
|
|
12
|
+
"axes.labelsize": 11,
|
|
13
|
+
"xtick.labelsize": 9,
|
|
14
|
+
"ytick.labelsize": 9,
|
|
15
|
+
"font.size": 10,
|
|
16
|
+
"lines.linewidth": 1.8,
|
|
17
|
+
"lines.markersize": 6,
|
|
18
|
+
"savefig.dpi": 100,
|
|
19
|
+
"image.cmap": "viridis",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
BRAND = {
|
|
23
|
+
"primary": "#4F46E5",
|
|
24
|
+
"accent": "#06B6D4",
|
|
25
|
+
"ink": "#0F172A",
|
|
26
|
+
"paper": "#F8FAFC",
|
|
27
|
+
"lime": "#A3E635",
|
|
28
|
+
"amber": "#FACC15",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_STYLES = {
|
|
32
|
+
"default": {},
|
|
33
|
+
"plotlibs": {"axes.facecolor": "white", "axes.grid": True,
|
|
34
|
+
"figure.facecolor": "white", "axes.edgecolor": "#0F172A",
|
|
35
|
+
"lines.linewidth": 2.2, "font.size": 11,
|
|
36
|
+
"axes.labelsize": 11, "axes.titlesize": 13,
|
|
37
|
+
"image.cmap": "viridis"},
|
|
38
|
+
"plotlibs-dark": {"figure.facecolor": "#0F172A", "axes.facecolor": "#1E293B",
|
|
39
|
+
"axes.edgecolor": "white", "axes.grid": True,
|
|
40
|
+
"lines.linewidth": 2.2, "font.size": 11},
|
|
41
|
+
"fast": {"axes.grid": True, "figure.dpi": 80},
|
|
42
|
+
"dark": {"figure.facecolor": "#1e1e1e", "axes.facecolor": "#1e1e1e",
|
|
43
|
+
"axes.edgecolor": "white", "axes.grid": True},
|
|
44
|
+
"ggplot": {"axes.facecolor": "#E5E5E5", "axes.grid": True,
|
|
45
|
+
"figure.facecolor": "#F5F5F5"},
|
|
46
|
+
"grayscale": {},
|
|
47
|
+
# extra gallery styles
|
|
48
|
+
"seaborn": {"axes.facecolor": "white", "axes.grid": True,
|
|
49
|
+
"figure.facecolor": "white", "lines.linewidth": 2.0,
|
|
50
|
+
"font.size": 11, "axes.labelsize": 11},
|
|
51
|
+
"seaborn-darkgrid": {"axes.facecolor": "#EAEAF2", "axes.grid": True,
|
|
52
|
+
"figure.facecolor": "white", "axes.edgecolor": "white",
|
|
53
|
+
"lines.linewidth": 2.0},
|
|
54
|
+
"plotly": {"axes.facecolor": "white", "axes.grid": True,
|
|
55
|
+
"figure.facecolor": "white", "lines.linewidth": 2.2,
|
|
56
|
+
"font.size": 12},
|
|
57
|
+
"publication": {"axes.facecolor": "white", "axes.grid": False,
|
|
58
|
+
"figure.dpi": 150, "lines.linewidth": 1.5,
|
|
59
|
+
"font.size": 10},
|
|
60
|
+
"darkgrid": {"figure.facecolor": "#212121", "axes.facecolor": "#2E2E2E",
|
|
61
|
+
"axes.edgecolor": "white", "axes.grid": True},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_available_ = sorted(_STYLES)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def use(name: str):
|
|
68
|
+
if name not in _STYLES:
|
|
69
|
+
raise ValueError(f"Unknown style {name!r}. Available: {_available_}")
|
|
70
|
+
base_face = rcParams.get("figure.facecolor", "white")
|
|
71
|
+
rcParams.update(_STYLES[name])
|
|
72
|
+
return base_face
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def available():
|
|
76
|
+
return list(_available_)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class _StyleCtx:
|
|
80
|
+
def __init__(self, name):
|
|
81
|
+
self.name = name
|
|
82
|
+
self._saved = None
|
|
83
|
+
|
|
84
|
+
def __enter__(self):
|
|
85
|
+
self._saved = dict(rcParams)
|
|
86
|
+
use(self.name)
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
def __exit__(self, *a):
|
|
90
|
+
rcParams.clear()
|
|
91
|
+
rcParams.update(self._saved)
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def context(name: str):
|
|
96
|
+
return _StyleCtx(name)
|