Skip to content

Commit 248fa83

Browse files
authored
🐛 convert trace props to array + check for nan removal (#225)
* 🐛 convert trace props to array if not np.ndarray * 🙈 * 🧹 add tests + remove nan-values from hf props * 🙏 * 🙏
1 parent 3ad5920 commit 248fa83

File tree

3 files changed

+153
-3
lines changed

3 files changed

+153
-3
lines changed

plotly_resampler/figure_resampler/figure_resampler_interface.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -672,11 +672,15 @@ def _parse_get_trace_props(
672672
"(i.e., x and y, or hf_x and hf_y) to be <= 1 dimensional!"
673673
)
674674

675-
# Note: this also converts hf_text and hf_hovertext to a np.ndarray
676-
if isinstance(hf_text, (list, np.ndarray, pd.Series)):
675+
# Note: this converts the hf property to a np.ndarray
676+
if isinstance(hf_text, (tuple, list, np.ndarray, pd.Series)):
677677
hf_text = np.asarray(hf_text)
678-
if isinstance(hf_hovertext, (list, np.ndarray, pd.Series)):
678+
if isinstance(hf_hovertext, (tuple, list, np.ndarray, pd.Series)):
679679
hf_hovertext = np.asarray(hf_hovertext)
680+
if isinstance(hf_marker_size, (tuple, list, np.ndarray, pd.Series)):
681+
hf_marker_size = np.asarray(hf_marker_size)
682+
if isinstance(hf_marker_color, (tuple, list, np.ndarray, pd.Series)):
683+
hf_marker_color = np.asarray(hf_marker_color)
680684

681685
# Remove NaNs for efficiency (storing less meaningless data)
682686
# NaNs introduce gaps between enclosing non-NaN data points & might distort
@@ -689,6 +693,10 @@ def _parse_get_trace_props(
689693
hf_text = hf_text[not_nan_mask]
690694
if isinstance(hf_hovertext, np.ndarray):
691695
hf_hovertext = hf_hovertext[not_nan_mask]
696+
if isinstance(hf_marker_size, np.ndarray):
697+
hf_marker_size = hf_marker_size[not_nan_mask]
698+
if isinstance(hf_marker_color, np.ndarray):
699+
hf_marker_color = hf_marker_color[not_nan_mask]
692700

693701
# Try to parse the hf_x data if it is of object type or
694702
if len(hf_x) and (hf_x.dtype.type is np.str_ or hf_x.dtype == "object"):

tests/test_figure_resampler.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,77 @@ def test_hf_text_and_hf_hovertext():
583583
)
584584

585585

586+
def test_hf_text_and_hf_marker_color():
587+
# Test for https://github.com/predict-idlab/plotly-resampler/issues/224
588+
fig = FigureResampler(default_n_shown_samples=1_000)
589+
590+
x = pd.date_range("1-1-2000", "1-1-2001", periods=2_000)
591+
y = np.sin(100 * np.arange(len(x)) / len(x))
592+
text = [f'text: {yi}, color:{"black" if yi>=0.99 else "blue"}' for yi in y]
593+
marker_color = ["black" if yi >= 0.99 else "blue" for yi in y]
594+
trace = go.Scatter(
595+
x=x,
596+
y=y,
597+
marker={"color": marker_color},
598+
text=text,
599+
)
600+
fig.add_trace(trace)
601+
602+
# Check correct data types
603+
assert not isinstance(fig.hf_data[0]["text"], (tuple, list))
604+
assert fig.hf_data[0]["hovertext"] is None
605+
assert not isinstance(fig.hf_data[0]["marker_color"], (tuple, list))
606+
assert fig.hf_data[0]["marker_size"] is None
607+
608+
# Check correct hf values
609+
assert np.all(list(fig.hf_data[0]["text"]) == text)
610+
assert np.all(list(fig.hf_data[0]["marker_color"]) == marker_color)
611+
612+
# Check correct trace values
613+
assert len(fig.data[0].y) == len(fig.data[0].text)
614+
assert len(fig.data[0].y) == len(fig.data[0].marker.color)
615+
y_color = ["black" if yi >= 0.99 else "blue" for yi in fig.data[0].y]
616+
assert np.all(list(fig.data[0].marker.color) == y_color)
617+
y_text = [f"text: {yi}, color:{ci}" for yi, ci in zip(fig.data[0].y, y_color)]
618+
assert np.all(list(fig.data[0].text) == y_text)
619+
620+
621+
def test_hf_text_and_hf_hovertext_and_hf_marker_size_nans():
622+
y_orig = np.arange(10_000).astype(float)
623+
y = y_orig.copy()
624+
y[::101] = np.nan
625+
626+
y_nonan = y[~np.isnan(y)]
627+
628+
fig = FigureResampler()
629+
fig.add_trace(
630+
go.Scatter(
631+
name="blabla",
632+
text=y.astype(str),
633+
hovertext=y.astype(str)[::-1],
634+
marker={"size": y_orig},
635+
),
636+
hf_y=y,
637+
)
638+
639+
assert np.all(fig.hf_data[0]["text"] == y_nonan.astype(str))
640+
assert np.all(fig.hf_data[0]["hovertext"] == y_nonan.astype(str)[::-1])
641+
assert np.all(fig.hf_data[0]["marker_size"] == y_nonan)
642+
643+
fig = FigureResampler()
644+
fig.add_trace(
645+
go.Scatter(name="blabla"),
646+
hf_y=y,
647+
hf_text=y.astype(str),
648+
hf_hovertext=y.astype(str)[::-1],
649+
hf_marker_size=y_orig,
650+
)
651+
652+
assert np.all(fig.hf_data[0]["text"] == y_nonan.astype(str))
653+
assert np.all(fig.hf_data[0]["hovertext"] == y_nonan.astype(str)[::-1])
654+
assert np.all(fig.hf_data[0]["marker_size"] == y_nonan)
655+
656+
586657
def test_multiple_timezones():
587658
n = 5_050
588659

tests/test_figurewidget_resampler.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,77 @@ def test_hf_text_and_hf_hovertext():
420420
)
421421

422422

423+
def test_hf_text_and_hf_marker_color():
424+
# Test for https://github.com/predict-idlab/plotly-resampler/issues/224
425+
fig = FigureWidgetResampler(default_n_shown_samples=1_000)
426+
427+
x = pd.date_range("1-1-2000", "1-1-2001", periods=2_000)
428+
y = np.sin(100 * np.arange(len(x)) / len(x))
429+
text = [f'text: {yi}, color:{"black" if yi>=0.99 else "blue"}' for yi in y]
430+
marker_color = ["black" if yi >= 0.99 else "blue" for yi in y]
431+
trace = go.Scatter(
432+
x=x,
433+
y=y,
434+
marker={"color": marker_color},
435+
text=text,
436+
)
437+
fig.add_trace(trace)
438+
439+
# Check correct data types
440+
assert not isinstance(fig.hf_data[0]["text"], (tuple, list))
441+
assert fig.hf_data[0]["hovertext"] is None
442+
assert not isinstance(fig.hf_data[0]["marker_color"], (tuple, list))
443+
assert fig.hf_data[0]["marker_size"] is None
444+
445+
# Check correct hf values
446+
assert np.all(list(fig.hf_data[0]["text"]) == text)
447+
assert np.all(list(fig.hf_data[0]["marker_color"]) == marker_color)
448+
449+
# Check correct trace values
450+
assert len(fig.data[0].y) == len(fig.data[0].text)
451+
assert len(fig.data[0].y) == len(fig.data[0].marker.color)
452+
y_color = ["black" if yi >= 0.99 else "blue" for yi in fig.data[0].y]
453+
assert np.all(list(fig.data[0].marker.color) == y_color)
454+
y_text = [f"text: {yi}, color:{ci}" for yi, ci in zip(fig.data[0].y, y_color)]
455+
assert np.all(list(fig.data[0].text) == y_text)
456+
457+
458+
def test_hf_text_and_hf_hovertext_and_hf_marker_size_nans():
459+
y_orig = np.arange(10_000).astype(float)
460+
y = y_orig.copy()
461+
y[::101] = np.nan
462+
463+
y_nonan = y[~np.isnan(y)]
464+
465+
fig = FigureWidgetResampler()
466+
fig.add_trace(
467+
go.Scatter(
468+
name="blabla",
469+
text=y.astype(str),
470+
hovertext=y.astype(str)[::-1],
471+
marker={"size": y_orig},
472+
),
473+
hf_y=y,
474+
)
475+
476+
assert np.all(fig.hf_data[0]["text"] == y_nonan.astype(str))
477+
assert np.all(fig.hf_data[0]["hovertext"] == y_nonan.astype(str)[::-1])
478+
assert np.all(fig.hf_data[0]["marker_size"] == y_nonan)
479+
480+
fig = FigureWidgetResampler()
481+
fig.add_trace(
482+
go.Scatter(name="blabla"),
483+
hf_y=y,
484+
hf_text=y.astype(str),
485+
hf_hovertext=y.astype(str)[::-1],
486+
hf_marker_size=y_orig,
487+
)
488+
489+
assert np.all(fig.hf_data[0]["text"] == y_nonan.astype(str))
490+
assert np.all(fig.hf_data[0]["hovertext"] == y_nonan.astype(str)[::-1])
491+
assert np.all(fig.hf_data[0]["marker_size"] == y_nonan)
492+
493+
423494
def test_multiple_timezones():
424495
n = 5_050
425496

0 commit comments

Comments
 (0)