Support "pin-to-target-curve" overlay method again
Yah yah, i know it's the same as before (the N > 2 curves case with out-of-range-minor rescaling the previously scaled curves isn't fixed yet...) but, this is a much better and optional implementation in less code. Further we're now better leveraging various new cached properties and methods on `Viz`. We now handle different `overlay_technique: str` options using `match:` syntax in the 2ndary scaling loop, stash the returns scalars per curve in `overlay_table`, and store and iterate the curves by dispersion measure sort order. Further wrt "pin-to-target-curve" mode, which currently still pins to the largest measured dispersion curve in the overlay set: - pop major Ci overlay table entries at start for sub-calcs usage when handling the "minor requires major rescale after pin" case. - (finally) correctly rescale the major curve y-mxmn to whatever the latest minor overlay curve by calcing the inverse transform from the minor *at that point*: - the intersect point being that which the minor has starts support on the major's x-domain* using the new `Viz.scalars_from_index()` and, - checking that the minor is not out of range (versus what the major's transform calcs it to be, in which case, - calc the inverse transform from the current out-of-range minor and use it to project the new y-mxmn for the major/target based on the same intersect-reference point in the x-domain used by the minor. - always handle the target-major Ci specially by only setting the `mx_ymn` / `mx_ymn` value when iterating that entry in the overlay table. - add todos around also doing the last sub-sub bullet for all previously major-transform scaled minor overlays (this is coming next..i hope). - add a final 3rd overlay loop which goes through a final `scaled: dict` to apply all range values to each view; this is where we will eventually solve that last edge case of an out-of-range minor's scaling needing to be used to rescale already scaled minors XDlog_linearized_curve_overlays
parent
4d11c5c89c
commit
6601dea8cc
|
@ -62,8 +62,8 @@ class OverlayT(Struct):
|
||||||
in the target co-domain.
|
in the target co-domain.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
start_t: float | None = None
|
|
||||||
viz: Viz | None = None
|
viz: Viz | None = None
|
||||||
|
start_t: float | None = None
|
||||||
|
|
||||||
# % "range" computed from some ref value to the mn/mx
|
# % "range" computed from some ref value to the mn/mx
|
||||||
rng: float | None = None
|
rng: float | None = None
|
||||||
|
@ -201,13 +201,13 @@ def overlay_viewlists(
|
||||||
] | None = None,
|
] | None = None,
|
||||||
|
|
||||||
overlay_technique: Literal[
|
overlay_technique: Literal[
|
||||||
'loglin_to_first',
|
'loglin_ref_to_curve',
|
||||||
'loglin_to_sigma',
|
'loglin_ref_to_first',
|
||||||
'mnmx',
|
'mxmn',
|
||||||
'solo',
|
'solo',
|
||||||
] = 'loglin_to_first',
|
] = 'loglin_ref_to_curve',
|
||||||
|
|
||||||
# internal instrumentation
|
# internal debug
|
||||||
debug_print: bool = False,
|
debug_print: bool = False,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -236,8 +236,6 @@ def overlay_viewlists(
|
||||||
# -> for any "group" overlay we want to dispersion normalize
|
# -> for any "group" overlay we want to dispersion normalize
|
||||||
# and scale minor charts onto the major chart: the chart
|
# and scale minor charts onto the major chart: the chart
|
||||||
# with the most dispersion in the set.
|
# with the most dispersion in the set.
|
||||||
major_sigma_viz: Viz = None
|
|
||||||
mx_disp: float = 0
|
|
||||||
|
|
||||||
# collect certain flows have grapics objects **in seperate
|
# collect certain flows have grapics objects **in seperate
|
||||||
# plots/viewboxes** into groups and do a common calc to
|
# plots/viewboxes** into groups and do a common calc to
|
||||||
|
@ -245,8 +243,9 @@ def overlay_viewlists(
|
||||||
# this is primarly used for our so called "log-linearized
|
# this is primarly used for our so called "log-linearized
|
||||||
# multi-plot" overlay technique.
|
# multi-plot" overlay technique.
|
||||||
overlay_table: dict[
|
overlay_table: dict[
|
||||||
ChartView,
|
float,
|
||||||
tuple[
|
tuple[
|
||||||
|
ChartView,
|
||||||
Viz,
|
Viz,
|
||||||
float, # y start
|
float, # y start
|
||||||
float, # y min
|
float, # y min
|
||||||
|
@ -254,6 +253,8 @@ def overlay_viewlists(
|
||||||
float, # y median
|
float, # y median
|
||||||
slice, # in-view array slice
|
slice, # in-view array slice
|
||||||
np.ndarray, # in-view array
|
np.ndarray, # in-view array
|
||||||
|
float, # returns up scalar
|
||||||
|
float, # return down scalar
|
||||||
],
|
],
|
||||||
] = {}
|
] = {}
|
||||||
|
|
||||||
|
@ -323,11 +324,15 @@ def overlay_viewlists(
|
||||||
profiler(f'{viz.name}@{chart_name} common pi sort')
|
profiler(f'{viz.name}@{chart_name} common pi sort')
|
||||||
|
|
||||||
# non-overlay group case
|
# non-overlay group case
|
||||||
if not viz.is_ohlc:
|
if (
|
||||||
|
not viz.is_ohlc
|
||||||
|
or overlay_technique == 'solo'
|
||||||
|
):
|
||||||
pi.vb._set_yrange(yrange=yrange)
|
pi.vb._set_yrange(yrange=yrange)
|
||||||
profiler(
|
profiler(
|
||||||
f'{viz.name}@{chart_name} simple std `._set_yrange()`'
|
f'{viz.name}@{chart_name} simple std `._set_yrange()`'
|
||||||
)
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
# handle overlay log-linearized group scaling cases
|
# handle overlay log-linearized group scaling cases
|
||||||
# TODO: a better predicate here, likely something
|
# TODO: a better predicate here, likely something
|
||||||
|
@ -338,15 +343,12 @@ def overlay_viewlists(
|
||||||
ymn, ymx = yrange
|
ymn, ymx = yrange
|
||||||
|
|
||||||
# determine start datum in view
|
# determine start datum in view
|
||||||
arr = viz.shm.array
|
in_view = viz.vs.in_view
|
||||||
in_view = arr[read_slc]
|
|
||||||
if not in_view.size:
|
if not in_view.size:
|
||||||
log.warning(f'{viz.name} not in view?')
|
log.warning(f'{viz.name} not in view?')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# row_start = arr[read_slc.start - 1]
|
row_start = in_view[0]
|
||||||
row_start = arr[read_slc.start]
|
|
||||||
|
|
||||||
if viz.is_ohlc:
|
if viz.is_ohlc:
|
||||||
y_ref = row_start['open']
|
y_ref = row_start['open']
|
||||||
else:
|
else:
|
||||||
|
@ -355,9 +357,11 @@ def overlay_viewlists(
|
||||||
profiler(f'{viz.name}@{chart_name} MINOR curve median')
|
profiler(f'{viz.name}@{chart_name} MINOR curve median')
|
||||||
|
|
||||||
key = 'open' if viz.is_ohlc else viz.name
|
key = 'open' if viz.is_ohlc else viz.name
|
||||||
start_t = in_view[0]['time']
|
start_t = row_start['time']
|
||||||
r_down = (ymn - y_ref) / y_ref
|
|
||||||
|
# returns scalars
|
||||||
r_up = (ymx - y_ref) / y_ref
|
r_up = (ymx - y_ref) / y_ref
|
||||||
|
r_down = (ymn - y_ref) / y_ref
|
||||||
|
|
||||||
msg = (
|
msg = (
|
||||||
f'### {viz.name}@{chart_name} ###\n'
|
f'### {viz.name}@{chart_name} ###\n'
|
||||||
|
@ -431,6 +435,8 @@ def overlay_viewlists(
|
||||||
if debug_print:
|
if debug_print:
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
|
# is the current up `OverlayT` not yet defined or
|
||||||
|
# the current `r_up` greater then the previous max.
|
||||||
if (
|
if (
|
||||||
upt.rng is None
|
upt.rng is None
|
||||||
or (
|
or (
|
||||||
|
@ -483,27 +489,37 @@ def overlay_viewlists(
|
||||||
profiler(msg)
|
profiler(msg)
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
# find curve with max dispersion
|
# disp = viz.disp_from_range(yref=y_ref)
|
||||||
disp = abs(ymx - ymn) / y_ref
|
# if disp is None:
|
||||||
if disp > mx_disp:
|
# print(f'{viz.name}: WTF NO DISP')
|
||||||
major_sigma_viz = viz
|
# continue
|
||||||
mx_disp = disp
|
|
||||||
|
|
||||||
overlay_table[viz.plot.vb] = (
|
# r_up, r_dn = disp
|
||||||
|
disp = r_up - r_down
|
||||||
|
|
||||||
|
# register curves by a "full" dispersion metric for
|
||||||
|
# later sort order in the overlay (technique
|
||||||
|
# ) application loop below.
|
||||||
|
overlay_table[disp] = (
|
||||||
|
viz.plot.vb,
|
||||||
viz,
|
viz,
|
||||||
|
|
||||||
y_ref,
|
y_ref,
|
||||||
ymn,
|
ymn,
|
||||||
ymx,
|
ymx,
|
||||||
|
|
||||||
read_slc,
|
read_slc,
|
||||||
in_view,
|
in_view,
|
||||||
)
|
|
||||||
|
|
||||||
|
r_up,
|
||||||
|
r_down,
|
||||||
|
)
|
||||||
profiler(f'{viz.name}@{chart_name} yrange scan complete')
|
profiler(f'{viz.name}@{chart_name} yrange scan complete')
|
||||||
|
|
||||||
# NOTE: if no there were no overlay charts
|
# NOTE: if no there were no overlay charts
|
||||||
# detected/collected (could be either no group detected or
|
# detected/collected (could be either no group detected or
|
||||||
# chart with a single symbol, thus a single viz/overlay)
|
# chart with a single symbol, thus a single viz/overlay)
|
||||||
# then we ONLY set the lone chart's (viz) yrange and short
|
# then we ONLY set the mone chart's (viz) yrange and short
|
||||||
# circuit to the next chart in the linked charts loop. IOW
|
# circuit to the next chart in the linked charts loop. IOW
|
||||||
# there's no reason to go through the overlay dispersion
|
# there's no reason to go through the overlay dispersion
|
||||||
# scaling in the next loop below when only one curve is
|
# scaling in the next loop below when only one curve is
|
||||||
|
@ -534,13 +550,20 @@ def overlay_viewlists(
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
mxmns_by_common_pi
|
mxmns_by_common_pi
|
||||||
and not major_sigma_viz
|
# and not major_sigma_viz
|
||||||
|
and not overlay_table
|
||||||
):
|
):
|
||||||
# move to next chart in linked set since
|
# move to next chart in linked set since
|
||||||
# no overlay transforming is needed.
|
# no overlay transforming is needed.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
profiler(f'<{chart_name}>.interact_graphics_cycle({name})')
|
msg = (
|
||||||
|
f'`Viz` curve first pass complete\n'
|
||||||
|
f'overlay_table: {overlay_table.keys()}\n'
|
||||||
|
)
|
||||||
|
profiler(msg)
|
||||||
|
if debug_print:
|
||||||
|
print(msg)
|
||||||
|
|
||||||
# if a minor curves scaling brings it "outside" the range of
|
# if a minor curves scaling brings it "outside" the range of
|
||||||
# the major curve (in major curve co-domain terms) then we
|
# the major curve (in major curve co-domain terms) then we
|
||||||
|
@ -548,21 +571,39 @@ def overlay_viewlists(
|
||||||
# below placeholder denotes when this occurs.
|
# below placeholder denotes when this occurs.
|
||||||
# group_mxmn: None | tuple[float, float] = None
|
# group_mxmn: None | tuple[float, float] = None
|
||||||
|
|
||||||
# TODO: probably re-write this loop as a compiled cpython or
|
r_up_mx: float
|
||||||
# numba func.
|
r_dn_mn: float
|
||||||
|
mx_disp = max(overlay_table)
|
||||||
|
mx_entry = overlay_table[mx_disp]
|
||||||
|
(
|
||||||
|
_, # viewbox
|
||||||
|
mx_viz, # viz
|
||||||
|
_, # y_ref
|
||||||
|
mx_ymn,
|
||||||
|
mx_ymx,
|
||||||
|
_, # read_slc
|
||||||
|
_, # in_view array
|
||||||
|
r_up_mx,
|
||||||
|
r_dn_mn,
|
||||||
|
) = mx_entry
|
||||||
|
|
||||||
|
scaled: dict[float, tuple[float, float, float]] = {}
|
||||||
|
|
||||||
# conduct "log-linearized multi-plot" scalings for all groups
|
# conduct "log-linearized multi-plot" scalings for all groups
|
||||||
for (
|
# -> iterate all curves Ci in dispersion-measure sorted order
|
||||||
view,
|
# going from smallest swing to largest.
|
||||||
|
for full_disp in sorted(overlay_table):
|
||||||
(
|
(
|
||||||
|
view,
|
||||||
viz,
|
viz,
|
||||||
y_start,
|
y_start,
|
||||||
y_min,
|
y_min,
|
||||||
y_max,
|
y_max,
|
||||||
read_slc,
|
read_slc,
|
||||||
minor_in_view,
|
minor_in_view,
|
||||||
)
|
r_up,
|
||||||
) in overlay_table.items():
|
r_dn,
|
||||||
|
) = overlay_table[full_disp]
|
||||||
|
|
||||||
key = 'open' if viz.is_ohlc else viz.name
|
key = 'open' if viz.is_ohlc else viz.name
|
||||||
|
|
||||||
|
@ -575,8 +616,99 @@ def overlay_viewlists(
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ymn = dnt.apply_rng(y_start)
|
xref = minor_in_view[0]['time']
|
||||||
ymx = upt.apply_rng(y_start)
|
match overlay_technique:
|
||||||
|
|
||||||
|
# Pin this curve to the "major dispersion" (or other
|
||||||
|
# target) curve by finding the intersect datum and
|
||||||
|
# then scaling according to the returns log-lin transort
|
||||||
|
# 'at that intersect reference data'. If the pinning
|
||||||
|
# results in this (minor/pinned) curve being out of view
|
||||||
|
# adjust the returns scalars to match this curves min
|
||||||
|
# y-range to stay in view.
|
||||||
|
case 'loglin_ref_to_curve':
|
||||||
|
|
||||||
|
# TODO: technically we only need to do this here if
|
||||||
|
#
|
||||||
|
if viz is not mx_viz:
|
||||||
|
(
|
||||||
|
i_start,
|
||||||
|
y_ref_major,
|
||||||
|
r_major_up_here,
|
||||||
|
r_major_down_here,
|
||||||
|
) = mx_viz.scalars_from_index(xref)
|
||||||
|
|
||||||
|
# transform y-range scaling to be the same as the
|
||||||
|
# equivalent "intersect" datum on the major
|
||||||
|
# dispersion curve (or other target "pin to"
|
||||||
|
# equivalent).
|
||||||
|
ymn = y_start * (1 + r_major_down_here)
|
||||||
|
if ymn > y_min:
|
||||||
|
ymn = y_min
|
||||||
|
r_dn_minor = (ymn - y_start) / y_start
|
||||||
|
|
||||||
|
mx_ymn = y_ref_major * (1 + r_dn_minor)
|
||||||
|
|
||||||
|
# TODO: rescale all already scaled curves to
|
||||||
|
# new increased range for this side.
|
||||||
|
# for (
|
||||||
|
# view,
|
||||||
|
# (yref, ymn, ymx)
|
||||||
|
# ) in scaled.items():
|
||||||
|
# pass
|
||||||
|
|
||||||
|
ymx = y_start * (1 + r_major_up_here)
|
||||||
|
if ymx < y_max:
|
||||||
|
ymx = y_max
|
||||||
|
r_up_minor = (ymx - y_start) / y_start
|
||||||
|
mx_ymx = y_ref_major * (1 + r_up_minor)
|
||||||
|
|
||||||
|
if debug_print:
|
||||||
|
print(
|
||||||
|
f'Minor SCALARS {viz.name}:\n'
|
||||||
|
f'xref: {xref}\n'
|
||||||
|
f'dn: {r_major_down_here}\n'
|
||||||
|
f'up: {r_major_up_here}\n'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if debug_print:
|
||||||
|
print(
|
||||||
|
f'MAJOR SCALARS {viz.name}:\n'
|
||||||
|
f'dn: {r_dn_mn}\n'
|
||||||
|
f'up: {r_up_mx}\n'
|
||||||
|
)
|
||||||
|
# target/major curve's mxmn may have been
|
||||||
|
# reset by minor overlay steps above.
|
||||||
|
ymn = mx_ymn
|
||||||
|
ymx = mx_ymx
|
||||||
|
|
||||||
|
# Pin all curves by their first datum in view to all
|
||||||
|
# others such that each curve's earliest datum provides the
|
||||||
|
# reference point for returns vs. every other curve in
|
||||||
|
# view.
|
||||||
|
case 'loglin_ref_to_first':
|
||||||
|
ymn = dnt.apply_rng(y_start)
|
||||||
|
ymx = upt.apply_rng(y_start)
|
||||||
|
|
||||||
|
# Do not pin curves by log-linearizing their y-ranges,
|
||||||
|
# instead allow each curve to fully scale to the
|
||||||
|
# time-series in view's min and max y-values.
|
||||||
|
case 'mxmn':
|
||||||
|
ymn = y_min
|
||||||
|
ymx = y_max
|
||||||
|
|
||||||
|
case _:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'overlay_technique is invalid `{overlay_technique}'
|
||||||
|
)
|
||||||
|
|
||||||
|
scaled[view] = (y_start, ymn, ymx)
|
||||||
|
|
||||||
|
for (
|
||||||
|
view,
|
||||||
|
(yref, ymn, ymx)
|
||||||
|
|
||||||
|
) in scaled.items():
|
||||||
|
|
||||||
# NOTE XXX: we have to set each curve's range once (and
|
# NOTE XXX: we have to set each curve's range once (and
|
||||||
# ONLY ONCE) here since we're doing this entire routine
|
# ONLY ONCE) here since we're doing this entire routine
|
||||||
|
@ -594,6 +726,8 @@ def overlay_viewlists(
|
||||||
f'LOGLIN SCALE CYCLE: {viz.name}@{chart_name}\n'
|
f'LOGLIN SCALE CYCLE: {viz.name}@{chart_name}\n'
|
||||||
f'UP MAJOR C: {upt.viz.name} with disp: {upt.rng}\n'
|
f'UP MAJOR C: {upt.viz.name} with disp: {upt.rng}\n'
|
||||||
f'DOWN MAJOR C: {dnt.viz.name} with disp: {dnt.rng}\n'
|
f'DOWN MAJOR C: {dnt.viz.name} with disp: {dnt.rng}\n'
|
||||||
|
f'disp: {disp}\n'
|
||||||
|
f'xref for MINOR: {xref}\n'
|
||||||
f'y_start: {y_start}\n'
|
f'y_start: {y_start}\n'
|
||||||
f'y min: {y_min}\n'
|
f'y min: {y_min}\n'
|
||||||
f'y max: {y_max}\n'
|
f'y max: {y_max}\n'
|
||||||
|
@ -614,7 +748,10 @@ def overlay_viewlists(
|
||||||
+
|
+
|
||||||
'\n'
|
'\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
profiler(f'<{chart_name}>.interact_graphics_cycle()')
|
||||||
|
|
||||||
if not do_linked_charts:
|
if not do_linked_charts:
|
||||||
return
|
break
|
||||||
|
|
||||||
profiler.finish()
|
profiler.finish()
|
||||||
|
|
Loading…
Reference in New Issue