5
5
6
6
import pytest
7
7
import time
8
+ import datetime
8
9
import multiprocessing
9
10
10
11
import numpy as np
17
18
from plotly .subplots import make_subplots
18
19
from plotly_resampler import FigureResampler , LTTB , EveryNthPoint
19
20
20
- # Note: this will be used to skip / alter behavior when running browser tests on
21
+ # Note: this will be used to skip / alter behavior when running browser tests on
21
22
# non-linux platforms.
22
23
from .utils import not_on_linux
23
24
@@ -101,6 +102,7 @@ def test_add_trace_not_resampling(float_series):
101
102
hf_hovertext = "hovertext" ,
102
103
)
103
104
105
+
104
106
def test_various_dtypes (float_series ):
105
107
# List of dtypes supported by orjson >= 3.8
106
108
valid_dtype_list = [
@@ -131,11 +133,11 @@ def test_various_dtypes(float_series):
131
133
fig .full_figure_for_development ()
132
134
133
135
# List of dtypes not supported by orjson >= 3.8
134
- invalid_dtype_list = [ np .float16 ]
136
+ invalid_dtype_list = [np .float16 ]
135
137
for invalid_dtype in invalid_dtype_list :
136
138
fig = FigureResampler (go .Figure (), default_n_shown_samples = 1000 )
137
139
# nb. datapoints < default_n_shown_samples
138
- with pytest .raises (TypeError ):
140
+ with pytest .raises (TypeError ):
139
141
# if this test fails -> orjson supports f16 => remove casting frome code
140
142
fig .add_trace (
141
143
go .Scatter (name = "float_series" ),
@@ -144,6 +146,7 @@ def test_various_dtypes(float_series):
144
146
)
145
147
fig .full_figure_for_development ()
146
148
149
+
147
150
def test_max_n_samples (float_series ):
148
151
s = float_series [:5000 ]
149
152
@@ -513,6 +516,9 @@ def test_multiple_timezones():
513
516
dr .tz_convert ("Australia/Canberra" ),
514
517
]
515
518
519
+ plain_plotly_fig = make_subplots (rows = len (cs ), cols = 1 , shared_xaxes = True )
520
+ plain_plotly_fig .update_layout (height = min (300 , 250 * len (cs )))
521
+
516
522
fr_fig = FigureResampler (
517
523
make_subplots (rows = len (cs ), cols = 1 , shared_xaxes = True ),
518
524
default_n_shown_samples = 500 ,
@@ -522,13 +528,76 @@ def test_multiple_timezones():
522
528
fr_fig .update_layout (height = min (300 , 250 * len (cs )))
523
529
524
530
for i , date_range in enumerate (cs , 1 ):
531
+ name = date_range .dtype .name .split (", " )[- 1 ][:- 1 ]
532
+ plain_plotly_fig .add_trace (
533
+ go .Scattergl (x = date_range , y = dr_v , name = name ), row = i , col = 1
534
+ )
525
535
fr_fig .add_trace (
526
- go .Scattergl (name = date_range . dtype . name . split ( ", " )[ - 1 ] ),
536
+ go .Scattergl (name = name ),
527
537
hf_x = date_range ,
528
538
hf_y = dr_v ,
529
539
row = i ,
530
540
col = 1 ,
531
541
)
542
+ # Assert that the time parsing is exactly the same
543
+ assert plain_plotly_fig .data [0 ].x [0 ] == fr_fig .data [0 ].x [0 ]
544
+
545
+
546
+ def test_multiple_timezones_in_single_x_index__datetimes_and_timestamps ():
547
+ # TODO: can be improved with pytest parametrize
548
+ y = np .arange (20 )
549
+
550
+ index1 = pd .date_range ("2018-01-01" , periods = 10 , freq = "H" , tz = "US/Eastern" )
551
+ index2 = pd .date_range ("2018-01-02" , periods = 10 , freq = "H" , tz = "Asia/Dubai" )
552
+ index_timestamps = index1 .append (index2 )
553
+ assert all (isinstance (x , pd .Timestamp ) for x in index_timestamps )
554
+ index1_datetimes = pd .Index ([x .to_pydatetime () for x in index1 ])
555
+ index_datetimes = pd .Index ([x .to_pydatetime () for x in index_timestamps ])
556
+ assert not any (isinstance (x , pd .Timestamp ) for x in index_datetimes )
557
+ assert all (isinstance (x , datetime .datetime ) for x in index_datetimes )
558
+
559
+ ## Test why we throw ValueError if array is still of object type after
560
+ ## successful pd.to_datetime call
561
+ # String array of datetimes with same tz -> NOT object array
562
+ assert not pd .to_datetime (index1 .astype ("str" )).dtype == "object"
563
+ assert not pd .to_datetime (index1_datetimes .astype ("str" )).dtype == "object"
564
+ # String array of datetimes with multiple tz -> object array
565
+ assert pd .to_datetime (index_timestamps .astype ("str" )).dtype == "object"
566
+ assert pd .to_datetime (index_datetimes .astype ("str" )).dtype == "object"
567
+
568
+ for index in [index_timestamps , index_datetimes ]:
569
+ fig = go .Figure ()
570
+ fig .add_trace (go .Scattergl (x = index , y = y ))
571
+ with pytest .raises (ValueError ):
572
+ fr_fig = FigureResampler (fig , default_n_shown_samples = 10 )
573
+ # Add as hf_x as index
574
+ fr_fig = FigureResampler (default_n_shown_samples = 10 )
575
+ with pytest .raises (ValueError ):
576
+ fr_fig .add_trace (go .Scattergl (), hf_x = index , hf_y = y )
577
+ # Add as hf_x as object array of datetime values
578
+ fr_fig = FigureResampler (default_n_shown_samples = 10 )
579
+ with pytest .raises (ValueError ):
580
+ fr_fig .add_trace (go .Scattergl (), hf_x = index .values .astype ("object" ), hf_y = y )
581
+ # Add as hf_x as string array
582
+ fr_fig = FigureResampler (default_n_shown_samples = 10 )
583
+ with pytest .raises (ValueError ):
584
+ fr_fig .add_trace (go .Scattergl (), hf_x = index .astype (str ), hf_y = y )
585
+ # Add as hf_x as object array of strings
586
+ fr_fig = FigureResampler (default_n_shown_samples = 10 )
587
+ with pytest .raises (ValueError ):
588
+ fr_fig .add_trace (
589
+ go .Scattergl (), hf_x = index .astype (str ).astype ("object" ), hf_y = y
590
+ )
591
+
592
+ fig = go .Figure ()
593
+ fig .add_trace (go .Scattergl (x = index .astype ("object" ), y = y ))
594
+ with pytest .raises (ValueError ):
595
+ fr_fig = FigureResampler (fig , default_n_shown_samples = 10 )
596
+
597
+ fig = go .Figure ()
598
+ fig .add_trace (go .Scattergl (x = index .astype ("str" ), y = y ))
599
+ with pytest .raises (ValueError ):
600
+ fr_fig = FigureResampler (fig , default_n_shown_samples = 10 )
532
601
533
602
534
603
def test_proper_copy_of_wrapped_fig (float_series ):
@@ -575,6 +644,82 @@ def test_2d_input_y():
575
644
assert "1 dimensional" in e_info
576
645
577
646
647
+ def test_hf_x_object_array ():
648
+ y = np .random .randn (100 )
649
+
650
+ ## Object array of datetime
651
+ ### Should be parsed to a pd.DatetimeIndex (is more efficient than object array)
652
+ x = pd .date_range ("2020-01-01" , freq = "s" , periods = 100 ).astype ("object" )
653
+ assert x .dtype == "object"
654
+ assert isinstance (x [0 ], pd .Timestamp )
655
+ # Add in the scatter
656
+ fig = FigureResampler (default_n_shown_samples = 50 )
657
+ fig .add_trace (go .Scatter (name = "blabla" , x = x , y = y ))
658
+ assert isinstance (fig .hf_data [0 ]["x" ], pd .DatetimeIndex )
659
+ assert isinstance (fig .hf_data [0 ]["x" ][0 ], pd .Timestamp )
660
+ # Add as hf_x
661
+ fig = FigureResampler (default_n_shown_samples = 50 )
662
+ fig .add_trace (go .Scatter (name = "blabla" ), hf_x = x , hf_y = y )
663
+ assert isinstance (fig .hf_data [0 ]["x" ], pd .DatetimeIndex )
664
+ assert isinstance (fig .hf_data [0 ]["x" ][0 ], pd .Timestamp )
665
+
666
+ ## Object array of datetime strings
667
+ ### Should be parsed to a pd.DatetimeIndex (is more efficient than object array)
668
+ x = pd .date_range ("2020-01-01" , freq = "s" , periods = 100 ).astype (str ).astype ("object" )
669
+ assert x .dtype == "object"
670
+ assert isinstance (x [0 ], str )
671
+ # Add in the scatter
672
+ fig = FigureResampler (default_n_shown_samples = 50 )
673
+ fig .add_trace (go .Scatter (name = "blabla" , x = x , y = y ))
674
+ assert isinstance (fig .hf_data [0 ]["x" ], pd .DatetimeIndex )
675
+ assert isinstance (fig .hf_data [0 ]["x" ][0 ], pd .Timestamp )
676
+ # Add as hf_x
677
+ fig = FigureResampler (default_n_shown_samples = 50 )
678
+ fig .add_trace (go .Scatter (name = "blabla" ), hf_x = x , hf_y = y )
679
+ assert isinstance (fig .hf_data [0 ]["x" ], pd .DatetimeIndex )
680
+ assert isinstance (fig .hf_data [0 ]["x" ][0 ], pd .Timestamp )
681
+
682
+ ## Object array of ints
683
+ ### Should be parsed to an int array (is more efficient than object array)
684
+ x = np .arange (100 ).astype ("object" )
685
+ assert x .dtype == "object"
686
+ assert isinstance (x [0 ], int )
687
+ # Add in the scatter
688
+ fig = FigureResampler (default_n_shown_samples = 50 )
689
+ fig .add_trace (go .Scatter (name = "blabla" , x = x , y = y ))
690
+ assert np .issubdtype (fig .hf_data [0 ]["x" ].dtype , np .integer )
691
+ # Add as hf_x
692
+ fig = FigureResampler (default_n_shown_samples = 50 )
693
+ fig .add_trace (go .Scatter (name = "blabla" ), hf_x = x , hf_y = y )
694
+ assert np .issubdtype (fig .hf_data [0 ]["x" ].dtype , np .integer )
695
+
696
+ ## Object array of ints as strings
697
+ ### Should be an integer array where the values are int objects
698
+ x = np .arange (100 ).astype (str ).astype ("object" )
699
+ assert x .dtype == "object"
700
+ assert isinstance (x [0 ], str )
701
+ # Add in the scatter
702
+ fig = FigureResampler (default_n_shown_samples = 50 )
703
+ fig .add_trace (go .Scatter (name = "blabla" , x = x , y = y ))
704
+ assert np .issubdtype (fig .hf_data [0 ]["x" ].dtype , np .integer )
705
+ # Add as hf_x
706
+ fig = FigureResampler (default_n_shown_samples = 50 )
707
+ fig .add_trace (go .Scatter (name = "blabla" ), hf_x = x , hf_y = y )
708
+ assert np .issubdtype (fig .hf_data [0 ]["x" ].dtype , np .integer )
709
+
710
+ ## Object array of strings
711
+ x = np .array (["x" , "y" ] * 50 ).astype ("object" )
712
+ assert x .dtype == "object"
713
+ # Add in the scatter
714
+ with pytest .raises (ValueError ):
715
+ fig = FigureResampler (default_n_shown_samples = 50 )
716
+ fig .add_trace (go .Scatter (name = "blabla" , x = x , y = y ))
717
+ # Add as hf_x
718
+ with pytest .raises (ValueError ):
719
+ fig = FigureResampler (default_n_shown_samples = 50 )
720
+ fig .add_trace (go .Scatter (name = "blabla" ), hf_x = x , hf_y = y )
721
+
722
+
578
723
def test_time_tz_slicing ():
579
724
n = 5050
580
725
dr = pd .Series (
0 commit comments