jarvisplot 1.0.1__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.
- jarvisplot/Figure/adapters.py +773 -0
- jarvisplot/Figure/cards/std_axes_adapter_config.json +23 -0
- jarvisplot/Figure/data_pipelines.py +87 -0
- jarvisplot/Figure/figure.py +1573 -0
- jarvisplot/Figure/helper.py +217 -0
- jarvisplot/Figure/load_data.py +252 -0
- jarvisplot/__init__.py +0 -0
- jarvisplot/cards/a4paper/1x1/ternary.json +6 -0
- jarvisplot/cards/a4paper/2x1/rect.json +106 -0
- jarvisplot/cards/a4paper/2x1/rect5x1.json +344 -0
- jarvisplot/cards/a4paper/2x1/rect_cmap.json +181 -0
- jarvisplot/cards/a4paper/2x1/ternary.json +139 -0
- jarvisplot/cards/a4paper/2x1/ternary_cmap.json +189 -0
- jarvisplot/cards/a4paper/4x1/rect.json +106 -0
- jarvisplot/cards/a4paper/4x1/rect_cmap.json +174 -0
- jarvisplot/cards/a4paper/4x1/ternary.json +139 -0
- jarvisplot/cards/a4paper/4x1/ternary_cmap.json +189 -0
- jarvisplot/cards/args.json +50 -0
- jarvisplot/cards/colors/colormaps.json +140 -0
- jarvisplot/cards/default/output.json +11 -0
- jarvisplot/cards/gambit/1x1/ternary.json +6 -0
- jarvisplot/cards/gambit/2x1/rect_cmap.json +200 -0
- jarvisplot/cards/gambit/2x1/ternary.json +139 -0
- jarvisplot/cards/gambit/2x1/ternary_cmap.json +205 -0
- jarvisplot/cards/icons/JarvisHEP.png +0 -0
- jarvisplot/cards/icons/gambit.png +0 -0
- jarvisplot/cards/icons/gambit_small.png +0 -0
- jarvisplot/cards/style_preference.json +23 -0
- jarvisplot/cli.py +64 -0
- jarvisplot/client.py +6 -0
- jarvisplot/config.py +69 -0
- jarvisplot/core.py +237 -0
- jarvisplot/data_loader.py +441 -0
- jarvisplot/inner_func.py +162 -0
- jarvisplot/utils/__init__.py +0 -0
- jarvisplot/utils/cmaps.py +258 -0
- jarvisplot/utils/interpolator.py +377 -0
- jarvisplot-1.0.1.dist-info/METADATA +80 -0
- jarvisplot-1.0.1.dist-info/RECORD +42 -0
- jarvisplot-1.0.1.dist-info/WHEEL +5 -0
- jarvisplot-1.0.1.dist-info/entry_points.txt +2 -0
- jarvisplot-1.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# jarvisplot/Figure/helper.py
|
|
2
|
+
#!/usr/bin/env python3
|
|
3
|
+
|
|
4
|
+
from matplotlib.axes import Axes
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
def split_fill_kwargs(kw_all):
|
|
8
|
+
edge_kws = {
|
|
9
|
+
"edgecolor", "ec",
|
|
10
|
+
"linewidth", "lw",
|
|
11
|
+
"linestyle", "ls",
|
|
12
|
+
"joinstyle", "capstyle",
|
|
13
|
+
"alpha"
|
|
14
|
+
}
|
|
15
|
+
face_kws = {
|
|
16
|
+
"facecolor", "fc",
|
|
17
|
+
"hatch",
|
|
18
|
+
"hatch_linewidth",
|
|
19
|
+
"alpha", "edgecolor"
|
|
20
|
+
}
|
|
21
|
+
kw_edge = {}
|
|
22
|
+
kw_face = {}
|
|
23
|
+
kw_rest = {}
|
|
24
|
+
for k, v in kw_all.items():
|
|
25
|
+
if k in edge_kws:
|
|
26
|
+
kw_edge[k] = v
|
|
27
|
+
if k in face_kws:
|
|
28
|
+
kw_face[k] = v
|
|
29
|
+
else:
|
|
30
|
+
kw_rest[k] = v
|
|
31
|
+
return kw_edge, kw_face, kw_rest
|
|
32
|
+
|
|
33
|
+
def plot_shapely_boundary(ax, geom, *, transform=None, **plot_kw):
|
|
34
|
+
if geom is None or geom.is_empty:
|
|
35
|
+
return []
|
|
36
|
+
|
|
37
|
+
lines = []
|
|
38
|
+
gt = geom.geom_type
|
|
39
|
+
if gt == "LineString":
|
|
40
|
+
lines = [geom]
|
|
41
|
+
elif gt == "MultiLineString":
|
|
42
|
+
lines = list(geom.geoms)
|
|
43
|
+
elif gt == "GeometryCollection":
|
|
44
|
+
for g in geom.geoms:
|
|
45
|
+
if g.geom_type == "LineString":
|
|
46
|
+
lines.append(g)
|
|
47
|
+
elif g.geom_type == "MultiLineString":
|
|
48
|
+
lines.extend(list(g.geoms))
|
|
49
|
+
else:
|
|
50
|
+
# 兜底:有时 boundary 可能给出别的类型
|
|
51
|
+
try:
|
|
52
|
+
b = geom.boundary
|
|
53
|
+
return _plot_shapely_boundary(ax, b, transform=transform, **plot_kw)
|
|
54
|
+
except Exception:
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
artists = []
|
|
58
|
+
for ln in lines:
|
|
59
|
+
xs, ys = ln.coords.xy
|
|
60
|
+
artists += ax.plot(list(xs), list(ys), transform=transform, **plot_kw)
|
|
61
|
+
return artists
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# helper: convert infinite regions to finite polygons (public-domain recipe)
|
|
65
|
+
# Clip polygon to rectangle extent (Sutherland-Hodgman for convex quad)
|
|
66
|
+
def voronoi_finite_polygons_2d(vor, radius=None):
|
|
67
|
+
if vor.points.shape[1] != 2:
|
|
68
|
+
raise ValueError("Requires 2D input")
|
|
69
|
+
new_regions = []
|
|
70
|
+
new_vertices = vor.vertices.tolist()
|
|
71
|
+
center = vor.points.mean(axis=0)
|
|
72
|
+
if radius is None:
|
|
73
|
+
radius = np.ptp(vor.points).max() * 2
|
|
74
|
+
# Construct a map ridge_vertices -> points (pairs)
|
|
75
|
+
all_ridges = {}
|
|
76
|
+
for (p1, p2), (v1, v2) in zip(vor.ridge_points, vor.ridge_vertices):
|
|
77
|
+
all_ridges.setdefault(p1, []).append((p2, v1, v2))
|
|
78
|
+
all_ridges.setdefault(p2, []).append((p1, v1, v2))
|
|
79
|
+
# Reconstruct infinite regions
|
|
80
|
+
for p1, region_index in enumerate(vor.point_region):
|
|
81
|
+
vertices = vor.regions[region_index]
|
|
82
|
+
if -1 not in vertices:
|
|
83
|
+
new_regions.append(vertices)
|
|
84
|
+
continue
|
|
85
|
+
ridges = all_ridges[p1]
|
|
86
|
+
new_region = [v for v in vertices if v != -1]
|
|
87
|
+
for p2, v1, v2 in ridges:
|
|
88
|
+
if v2 < 0: v1, v2 = v2, v1
|
|
89
|
+
if v1 >= 0 and v2 >= 0:
|
|
90
|
+
continue
|
|
91
|
+
# Compute the missing endpoint at infinity
|
|
92
|
+
t = vor.points[p2] - vor.points[p1]
|
|
93
|
+
t /= np.linalg.norm(t)
|
|
94
|
+
n = np.array([-t[1], t[0]]) # normal
|
|
95
|
+
midpoint = vor.points[[p1, p2]].mean(axis=0)
|
|
96
|
+
direction = np.sign(np.dot(midpoint - center, n)) * n
|
|
97
|
+
far_point = vor.vertices[v2] + direction * radius
|
|
98
|
+
new_region.append(len(new_vertices))
|
|
99
|
+
new_vertices.append(far_point.tolist())
|
|
100
|
+
# Sort region vertices counterclockwise
|
|
101
|
+
vs = np.asarray([new_vertices[v] for v in new_region])
|
|
102
|
+
c = vs.mean(axis=0)
|
|
103
|
+
angles = np.arctan2(vs[:, 1] - c[1], vs[:, 0] - c[0])
|
|
104
|
+
new_region = np.array(new_region)[np.argsort(angles)].tolist()
|
|
105
|
+
new_regions.append(new_region)
|
|
106
|
+
return new_regions, np.asarray(new_vertices)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _clip_poly_to_rect(poly, rect):
|
|
110
|
+
# rect: (xmin, xmax, ymin, ymax)
|
|
111
|
+
xmin, xmax, ymin, ymax = rect
|
|
112
|
+
def _clip(edges, inside, intersect):
|
|
113
|
+
out = []
|
|
114
|
+
if not edges:
|
|
115
|
+
return out
|
|
116
|
+
S = edges[-1]
|
|
117
|
+
for E in edges:
|
|
118
|
+
if inside(E):
|
|
119
|
+
if inside(S):
|
|
120
|
+
out.append(E)
|
|
121
|
+
else:
|
|
122
|
+
out.append(intersect(S, E))
|
|
123
|
+
out.append(E)
|
|
124
|
+
elif inside(S):
|
|
125
|
+
out.append(intersect(S, E))
|
|
126
|
+
S = E
|
|
127
|
+
return out
|
|
128
|
+
def clip_left(P):
|
|
129
|
+
return _clip(P, lambda p: p[0] >= xmin, lambda s ,e: (xmin, s[1] + (e[1 ] -s[1] ) *(xmin - s[0] ) /(e[0 ] -s[0]) ))
|
|
130
|
+
def clip_right(P):
|
|
131
|
+
return _clip(P, lambda p: p[0] <= xmax, lambda s ,e: (xmax, s[1] + (e[1 ] -s[1] ) *(xmax - s[0] ) /(e[0 ] -s[0]) ))
|
|
132
|
+
def clip_bottom(P):
|
|
133
|
+
return _clip(P, lambda p: p[1] >= ymin, lambda s ,e: (s[0] + (e[0 ] -s[0] ) *(ymin - s[1] ) /(e[1 ] -s[1]), ymin ))
|
|
134
|
+
def clip_top(P):
|
|
135
|
+
return _clip(P, lambda p: p[1] <= ymax, lambda s ,e: (s[0] + (e[0 ] -s[0] ) *(ymax - s[1] ) /(e[1 ] -s[1]), ymax ))
|
|
136
|
+
P = poly
|
|
137
|
+
for fn in (clip_left, clip_right, clip_bottom, clip_top):
|
|
138
|
+
P = fn(P)
|
|
139
|
+
if not P:
|
|
140
|
+
break
|
|
141
|
+
return P
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ---- helpers for masking by extend on filled contours ----
|
|
146
|
+
|
|
147
|
+
def _resolve_vlim(z, vmin=None, vmax=None, levels=None, norm=None):
|
|
148
|
+
"""Derive effective vmin/vmax from norm/levels/fallback to data."""
|
|
149
|
+
import numpy as np
|
|
150
|
+
if norm is not None:
|
|
151
|
+
vmin = getattr(norm, "vmin", vmin)
|
|
152
|
+
vmax = getattr(norm, "vmax", vmax)
|
|
153
|
+
if levels is not None and np.ndim(levels) > 0:
|
|
154
|
+
vmin = levels[0] if vmin is None else vmin
|
|
155
|
+
vmax = levels[-1] if vmax is None else vmax
|
|
156
|
+
if vmin is None:
|
|
157
|
+
vmin = float(np.nanmin(z))
|
|
158
|
+
if vmax is None:
|
|
159
|
+
vmax = float(np.nanmax(z))
|
|
160
|
+
return float(vmin), float(vmax)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _mask_by_extend(z, *, extend="neither", vmin=None, vmax=None, levels=None, norm=None):
|
|
164
|
+
"""
|
|
165
|
+
Return masked z according to extend semantics:
|
|
166
|
+
- 'min' : mask z < vmin
|
|
167
|
+
- 'max' : mask z > vmax
|
|
168
|
+
- 'both' : mask outside [vmin, vmax]
|
|
169
|
+
- 'neither': no masking
|
|
170
|
+
Also returns effective (vmin, vmax).
|
|
171
|
+
"""
|
|
172
|
+
import numpy as np
|
|
173
|
+
z = np.asarray(z)
|
|
174
|
+
e = (extend or "neither").lower()
|
|
175
|
+
if e not in ("neither", "min", "max", "both"):
|
|
176
|
+
e = "neither"
|
|
177
|
+
vmin_eff, vmax_eff = _resolve_vlim(z, vmin=vmin, vmax=vmax, levels=levels, norm=norm)
|
|
178
|
+
mask = np.zeros_like(z, dtype=bool)
|
|
179
|
+
if e in ("min", "both"):
|
|
180
|
+
mask |= (z < vmin_eff)
|
|
181
|
+
if e in ("max", "both"):
|
|
182
|
+
mask |= (z > vmax_eff)
|
|
183
|
+
return np.ma.masked_array(z, mask=mask), vmin_eff, vmax_eff
|
|
184
|
+
|
|
185
|
+
# —— 小工具:对 artist 或容器做统一 clip_path 应用 ——
|
|
186
|
+
def _auto_clip(artists, ax: Axes, clip_path):
|
|
187
|
+
if clip_path is None:
|
|
188
|
+
return artists
|
|
189
|
+
def _apply_one(a):
|
|
190
|
+
try:
|
|
191
|
+
a.set_clip_path(clip_path, transform=ax.transData)
|
|
192
|
+
except Exception:
|
|
193
|
+
pass
|
|
194
|
+
# always apply to the container itself first
|
|
195
|
+
_apply_one(artists)
|
|
196
|
+
try:
|
|
197
|
+
iter(artists)
|
|
198
|
+
except TypeError:
|
|
199
|
+
return artists
|
|
200
|
+
else:
|
|
201
|
+
for item in artists:
|
|
202
|
+
# 先试 artist 本体
|
|
203
|
+
_apply_one(item)
|
|
204
|
+
# matplotlib 常见容器:collections / patches / lines
|
|
205
|
+
coll = getattr(item, "collections", None)
|
|
206
|
+
if coll:
|
|
207
|
+
for c in coll:
|
|
208
|
+
_apply_one(c)
|
|
209
|
+
patches = getattr(item, "patches", None)
|
|
210
|
+
if patches:
|
|
211
|
+
for p in patches:
|
|
212
|
+
_apply_one(p)
|
|
213
|
+
lines = getattr(item, "lines", None)
|
|
214
|
+
if lines:
|
|
215
|
+
for ln in lines:
|
|
216
|
+
_apply_one(ln)
|
|
217
|
+
return artists
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import json
|
|
6
|
+
from copy import deepcopy
|
|
7
|
+
|
|
8
|
+
def eval_series(df: pd.DataFrame, set: dict, logger):
|
|
9
|
+
"""
|
|
10
|
+
Evaluate an expression/column name against df safely.
|
|
11
|
+
- If expr is a direct column name, returns that series.
|
|
12
|
+
- If expr is a python expression, eval with df columns in scope.
|
|
13
|
+
"""
|
|
14
|
+
try:
|
|
15
|
+
logger.debug("Loading variable expression -> {}".format(set['expr']))
|
|
16
|
+
except:
|
|
17
|
+
pass
|
|
18
|
+
if not "expr" in set.keys():
|
|
19
|
+
raise ValueError(f"expr need for axes {set}.")
|
|
20
|
+
if set["expr"] in df.columns:
|
|
21
|
+
arr = df[set["expr"]].values
|
|
22
|
+
if np.isnan(arr).sum() and "fillna" in set.keys():
|
|
23
|
+
arr = np.where(np.isnan(arr), float(set['fillna']), arr)
|
|
24
|
+
else:
|
|
25
|
+
# safe-ish eval with only df columns in locals
|
|
26
|
+
local_vars = df.to_dict("series")
|
|
27
|
+
import math
|
|
28
|
+
from ..inner_func import update_funcs
|
|
29
|
+
allowed_globals = update_funcs({"np": np, "math": math})
|
|
30
|
+
arr = eval(set["expr"], allowed_globals, local_vars)
|
|
31
|
+
if np.isnan(arr).sum() and "fillna" in set.keys():
|
|
32
|
+
arr = np.where(np.isnan(arr), float(set['fillna']), arr)
|
|
33
|
+
return np.asarray(arr)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def profiling(df, prof, logger):
|
|
37
|
+
def profile_bridson_sorted(idx, xx, yy, zz, radius, msk):
|
|
38
|
+
for i in range(len(idx)):
|
|
39
|
+
if not msk[i]:
|
|
40
|
+
continue
|
|
41
|
+
dx = xx[idx > idx[i]] - xx[i]
|
|
42
|
+
dy = yy[idx > idx[i]] - yy[i]
|
|
43
|
+
dz = zz[idx > idx[i]] - zz[i]
|
|
44
|
+
dist0 = (dx**2 + dy**2)**0.5
|
|
45
|
+
dist1 = (dx**2 + dy**2 + dz**2)**0.5
|
|
46
|
+
near0 = (dist0 < 0.707 * radius) | (dist0 < radius) & (dist1 > radius)
|
|
47
|
+
sel = (idx > idx[i])
|
|
48
|
+
msk[sel] &= ~near0
|
|
49
|
+
return msk
|
|
50
|
+
|
|
51
|
+
bin = prof.get("bin", 100)
|
|
52
|
+
coors = prof.get("coordinates", {})
|
|
53
|
+
obj = prof.get("objective", "max")
|
|
54
|
+
grid = prof.get("grid_points", "rect")
|
|
55
|
+
gdata = None
|
|
56
|
+
|
|
57
|
+
radius = 1 / bin
|
|
58
|
+
if "expr" in coors['x'].keys():
|
|
59
|
+
x = eval_series(df, coors['x'], logger)
|
|
60
|
+
else:
|
|
61
|
+
x = df['x']
|
|
62
|
+
|
|
63
|
+
if "expr" in coors['y'].keys():
|
|
64
|
+
y = eval_series(df, coors['y'], logger)
|
|
65
|
+
else:
|
|
66
|
+
y = df['y']
|
|
67
|
+
|
|
68
|
+
if "expr" in coors['z'].keys():
|
|
69
|
+
z = eval_series(df, coors['z'], logger)
|
|
70
|
+
else:
|
|
71
|
+
z = df['z']
|
|
72
|
+
|
|
73
|
+
logger.debug("After loading profiling x, y, z. ")
|
|
74
|
+
|
|
75
|
+
if grid == "ternary":
|
|
76
|
+
xlim = coors['x'].get("lim", [0, 1])
|
|
77
|
+
ylim = coors['y'].get("lim", [0, 1])
|
|
78
|
+
zlim = coors['z'].get("lim", [np.min(z), np.max(z)])
|
|
79
|
+
xscale = coors['x'].get("scale", "linear")
|
|
80
|
+
yscale = coors['y'].get("scale", "linear")
|
|
81
|
+
zscale = coors['z'].get("scale", "linear")
|
|
82
|
+
zind = coors['z'].get("name", "z")
|
|
83
|
+
xind = coors['x'].get("name", "x")
|
|
84
|
+
yind = coors['y'].get("name", "y")
|
|
85
|
+
elif grid == "rect":
|
|
86
|
+
xlim = coors['x'].get("lim", [np.min(x), np.max(x)])
|
|
87
|
+
ylim = coors['y'].get("lim", [np.min(y), np.max(y)])
|
|
88
|
+
zlim = coors['z'].get("lim", [np.min(z), np.max(z)])
|
|
89
|
+
|
|
90
|
+
xscale = coors['x'].get("scale", "linear")
|
|
91
|
+
yscale = coors['y'].get("scale", "linear")
|
|
92
|
+
zscale = coors['z'].get("scale", "linear")
|
|
93
|
+
|
|
94
|
+
zind = coors['z'].get("name", "z")
|
|
95
|
+
xind = coors['x'].get("name", "x")
|
|
96
|
+
yind = coors['y'].get("name", "y")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# profiling will add new columns into dataframe, so that can be used in the next step
|
|
100
|
+
df[xind] = x
|
|
101
|
+
df[yind] = y
|
|
102
|
+
df[zind] = z
|
|
103
|
+
# print(x.min(), x.max(), y.min(), y.max(), z.min(), z.max())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if grid == "ternary":
|
|
107
|
+
bb = np.linspace(0, 1, bin + 1)
|
|
108
|
+
rr = np.linspace(0, 1, bin + 1)
|
|
109
|
+
Bg, Rg = np.meshgrid(bb, rr)
|
|
110
|
+
r = Rg.ravel()
|
|
111
|
+
b = Bg.ravel()
|
|
112
|
+
l = 1.0 - b - r
|
|
113
|
+
mask = (l >= 0) & (b >= 0) & (r >= 0)
|
|
114
|
+
x = b + 0.5 * r
|
|
115
|
+
y = r
|
|
116
|
+
xxg, yyg = x[mask], y[mask]
|
|
117
|
+
llg, bbg, rrg, = l[mask], b[mask], r[mask]
|
|
118
|
+
gdata = pd.DataFrame({
|
|
119
|
+
xind: xxg,
|
|
120
|
+
yind: yyg,
|
|
121
|
+
zind: np.ones(xxg.shape) * (np.min(z) - 0.1)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
elif grid == "rect":
|
|
125
|
+
xx = np.linspace(xlim[0], xlim[1], bin+1)
|
|
126
|
+
yy = np.linspace(ylim[0], ylim[1], bin+1)
|
|
127
|
+
xg, yg = np.meshgrid(xx, yy)
|
|
128
|
+
|
|
129
|
+
gdata = pd.DataFrame({
|
|
130
|
+
xind: xg.ravel(),
|
|
131
|
+
yind: yg.ravel(),
|
|
132
|
+
zind: np.ones(xg.ravel().shape) * (np.min(z) - 0.1)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
if obj == "max":
|
|
136
|
+
df = df.sort_values(zind, ascending=False).reset_index(drop=True)
|
|
137
|
+
elif obj == "min":
|
|
138
|
+
df = df.sort_values(zind, ascending=True).reset_index(drop=True)
|
|
139
|
+
else:
|
|
140
|
+
df = df.sort_values(zind, ascending=False).reset_index(drop=True)
|
|
141
|
+
logger.error("Sort dataset method: objective: {} not support, using default value -> 'max'".format(obj))
|
|
142
|
+
df = pd.concat([df, gdata], ignore_index=True)
|
|
143
|
+
|
|
144
|
+
idx = deepcopy(np.array(df.index))
|
|
145
|
+
xx = deepcopy(np.array(df[xind]))
|
|
146
|
+
yy = deepcopy(np.array(df[yind]))
|
|
147
|
+
zz = deepcopy(np.array(df[zind]))
|
|
148
|
+
# mapping xx, yy, zz to range [0, 1]
|
|
149
|
+
if xscale == "log":
|
|
150
|
+
xx = (np.log(xx) - np.log(xlim[0])) / (np.log(xlim[1]) - np.log(xlim[0]))
|
|
151
|
+
else: # linear scale
|
|
152
|
+
xx = (xx - xlim[0]) / (xlim[1] - xlim[0])
|
|
153
|
+
|
|
154
|
+
if yscale == "log":
|
|
155
|
+
yy = (np.log(yy) - np.log(ylim[0])) / (np.log(ylim[1]) - np.log(ylim[0]))
|
|
156
|
+
else: # linear scale
|
|
157
|
+
yy = (yy - ylim[0]) / (ylim[1] - ylim[0])
|
|
158
|
+
|
|
159
|
+
if zscale == "log":
|
|
160
|
+
zz = (np.log(zz) - np.log(zlim[0])) / (np.log(zlim[1]) - np.log(zlim[0]))
|
|
161
|
+
else: # linear scale
|
|
162
|
+
zz = (zz - zlim[0]) / (zlim[1] - zlim[0])
|
|
163
|
+
|
|
164
|
+
# (removed print(radius))
|
|
165
|
+
msk = np.full(idx.shape, True)
|
|
166
|
+
msk = profile_bridson_sorted(idx, xx, yy, zz, radius, msk)
|
|
167
|
+
df = df.iloc[idx[msk]]
|
|
168
|
+
|
|
169
|
+
return df
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def filter(df, condition, logger):
|
|
177
|
+
try:
|
|
178
|
+
if isinstance(condition, bool):
|
|
179
|
+
return df.copy() if condition else df.iloc[0:0].copy()
|
|
180
|
+
if isinstance(condition, (int, float)) and condition in (0, 1):
|
|
181
|
+
return df.copy() if int(condition) == 1 else df.iloc[0:0].copy()
|
|
182
|
+
|
|
183
|
+
if isinstance(condition, str):
|
|
184
|
+
s = condition.strip()
|
|
185
|
+
low = s.lower()
|
|
186
|
+
if low in {"true", "t", "yes", "y"}:
|
|
187
|
+
return df.copy()
|
|
188
|
+
if low in {"false", "f", "no", "n"}:
|
|
189
|
+
return df.iloc[0:0].copy()
|
|
190
|
+
s = s.replace("&&", " & ").replace("||", " | ")
|
|
191
|
+
condition = s
|
|
192
|
+
else:
|
|
193
|
+
raise TypeError(f"Unsupported condition type: {type(condition)}")
|
|
194
|
+
|
|
195
|
+
from ..inner_func import update_funcs
|
|
196
|
+
import math
|
|
197
|
+
allowed_globals = update_funcs({"np": np, "math": math})
|
|
198
|
+
local_vars = df.to_dict("series")
|
|
199
|
+
mask = eval(condition, allowed_globals, local_vars)
|
|
200
|
+
|
|
201
|
+
if isinstance(mask, (bool, np.bool_, int, float)):
|
|
202
|
+
return df.copy() if bool(mask) else df.iloc[0:0].copy()
|
|
203
|
+
if not isinstance(mask, pd.Series):
|
|
204
|
+
mask = pd.Series(mask, index=df.index)
|
|
205
|
+
mask = mask.astype(bool)
|
|
206
|
+
return df[mask].copy()
|
|
207
|
+
except Exception as e:
|
|
208
|
+
logger.error(f"Errors when evaluating condition -> {condition}:\n\t{e}")
|
|
209
|
+
return pd.DataFrame(index=df.index).iloc[0:0].copy()
|
|
210
|
+
|
|
211
|
+
def addcolumn(df, adds, logger):
|
|
212
|
+
try:
|
|
213
|
+
name = adds.get("name", False)
|
|
214
|
+
expr = adds.get("expr", False)
|
|
215
|
+
if not (name and expr):
|
|
216
|
+
logger.error("Error in loading add_column -> {}".format(adds))
|
|
217
|
+
from ..inner_func import update_funcs
|
|
218
|
+
import math
|
|
219
|
+
allowed_globals = update_funcs({"np": np, "math": math})
|
|
220
|
+
local_vars = df.to_dict("series")
|
|
221
|
+
value = eval(str(expr), allowed_globals, local_vars)
|
|
222
|
+
df[name] = value
|
|
223
|
+
return df
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.error("Errors when add new column -> {}:\n\t{}".format(adds, json.dumps(e)))
|
|
226
|
+
return df
|
|
227
|
+
|
|
228
|
+
def sortby(df, expr, logger):
|
|
229
|
+
try:
|
|
230
|
+
return sort_df_by_expr(df, expr)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.warning(f"sortby failed for expr={expr}: {e}")
|
|
233
|
+
return df
|
|
234
|
+
|
|
235
|
+
def sort_df_by_expr(self, df: pd.DataFrame, expr: str, logger) -> pd.DataFrame:
|
|
236
|
+
"""
|
|
237
|
+
Sort the dataframe by evaluating the given expression.
|
|
238
|
+
The expression can be a column name or a valid expression understood by _eval_series.
|
|
239
|
+
Returns a new DataFrame sorted ascending by the evaluated values.
|
|
240
|
+
"""
|
|
241
|
+
if df is None or expr is None:
|
|
242
|
+
return df
|
|
243
|
+
try:
|
|
244
|
+
# Try evaluate as expression (could be column or expression)
|
|
245
|
+
values = eval_series(df, {"expr": expr}, logger)
|
|
246
|
+
df = df.assign(__sortkey__=values)
|
|
247
|
+
df = df.sort_values(by="__sortkey__", ascending=True)
|
|
248
|
+
df = df.drop(columns=["__sortkey__"])
|
|
249
|
+
return df
|
|
250
|
+
except Exception as e:
|
|
251
|
+
logger.warning(f"LB: sortby failed for expr={expr}: {e}")
|
|
252
|
+
return df
|
jarvisplot/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Frame": {
|
|
3
|
+
"figure": {
|
|
4
|
+
"figsize": [
|
|
5
|
+
3.3,
|
|
6
|
+
2.75
|
|
7
|
+
]
|
|
8
|
+
},
|
|
9
|
+
"axes": {
|
|
10
|
+
"axlogo": {
|
|
11
|
+
"rect": [
|
|
12
|
+
0.01,
|
|
13
|
+
0.01,
|
|
14
|
+
0.06,
|
|
15
|
+
0.072
|
|
16
|
+
],
|
|
17
|
+
"frameon": false,
|
|
18
|
+
"yticks": [],
|
|
19
|
+
"xticks": [],
|
|
20
|
+
"xlim": [
|
|
21
|
+
0,
|
|
22
|
+
1024
|
|
23
|
+
],
|
|
24
|
+
"ylim": [
|
|
25
|
+
1024,
|
|
26
|
+
0
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"ax": {
|
|
30
|
+
"rect": [
|
|
31
|
+
0.140,
|
|
32
|
+
0.168,
|
|
33
|
+
0.840,
|
|
34
|
+
0.775
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"ax": {
|
|
39
|
+
"frame": {
|
|
40
|
+
"linewidth": 0.8,
|
|
41
|
+
"solid_capstyle": "projecting",
|
|
42
|
+
"solid_joinstyle": "miter",
|
|
43
|
+
"clip_on": false,
|
|
44
|
+
"c": "#21171A",
|
|
45
|
+
"zorder": 100
|
|
46
|
+
},
|
|
47
|
+
"grid": {
|
|
48
|
+
"sep": 0.1,
|
|
49
|
+
"style": {
|
|
50
|
+
"color": "#C2C2C269",
|
|
51
|
+
"linewidth": 0.3,
|
|
52
|
+
"linestyle": "--"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"ticks": {
|
|
56
|
+
"both": {
|
|
57
|
+
"labelsize": 6,
|
|
58
|
+
"direction": "in",
|
|
59
|
+
"labelfontfamily": "sans",
|
|
60
|
+
"top": true,
|
|
61
|
+
"left": true,
|
|
62
|
+
"right": true,
|
|
63
|
+
"bottom": true,
|
|
64
|
+
"which": "both"
|
|
65
|
+
},
|
|
66
|
+
"major": {
|
|
67
|
+
"which": "major",
|
|
68
|
+
"length": 4,
|
|
69
|
+
"width": 0.4,
|
|
70
|
+
"color": "black"
|
|
71
|
+
},
|
|
72
|
+
"minor": {
|
|
73
|
+
"which": "minor",
|
|
74
|
+
"length": 2,
|
|
75
|
+
"width": 0.4,
|
|
76
|
+
"color": "black"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"labels": {
|
|
80
|
+
"xlabel": {
|
|
81
|
+
"loc": "right",
|
|
82
|
+
"fontsize": 12,
|
|
83
|
+
"fontfamily": "STIXGeneral"
|
|
84
|
+
},
|
|
85
|
+
"ylabel": {
|
|
86
|
+
"loc": "top",
|
|
87
|
+
"fontsize": 12,
|
|
88
|
+
"fontfamily": "STIXGeneral"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"axlogo": {
|
|
93
|
+
"file": "&JP/jarvisplot/cards/icons/JarvisHEP.png"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"Style": {
|
|
97
|
+
"scatter": {
|
|
98
|
+
"s": 1.0,
|
|
99
|
+
"marker": "^",
|
|
100
|
+
"linewidths": 0.02,
|
|
101
|
+
"alpha": 1.0,
|
|
102
|
+
"zorder": 30,
|
|
103
|
+
"edgecolor": "white"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|