@@ -313,7 +313,7 @@ def make_trace_kwargs(args, trace_spec, trace_data, mapping_labels, sizeref):
313
313
mapping_labels ["count" ] = "%{x}"
314
314
elif attr_name == "trendline" :
315
315
if (
316
- attr_value in ["ols" , "lowess" ]
316
+ attr_value [ 0 ] in ["ols" , "lowess" , "ma" , "ewm " ]
317
317
and args ["x" ]
318
318
and args ["y" ]
319
319
and len (trace_data [[args ["x" ], args ["y" ]]].dropna ()) > 1
@@ -345,19 +345,36 @@ def make_trace_kwargs(args, trace_spec, trace_data, mapping_labels, sizeref):
345
345
)
346
346
347
347
# preserve original values of "x" in case they're dates
348
- trace_patch ["x" ] = sorted_trace_data [args ["x" ]][
349
- np .logical_not (np .logical_or (np .isnan (y ), np .isnan (x )))
350
- ]
348
+ non_missing = np .logical_not (
349
+ np .logical_or (np .isnan (y ), np .isnan (x ))
350
+ )
351
+ trace_patch ["x" ] = sorted_trace_data [args ["x" ]][non_missing ]
351
352
352
- if attr_value == "lowess" :
353
+ if attr_value [0 ] == "lowess" :
354
+ alpha = attr_value [1 ] or 0.6666666
353
355
# missing ='drop' is the default value for lowess but not for OLS (None)
354
356
# we force it here in case statsmodels change their defaults
355
- trendline = sm .nonparametric .lowess (y , x , missing = "drop" )
357
+ trendline = sm .nonparametric .lowess (
358
+ y , x , missing = "drop" , frac = alpha
359
+ )
356
360
trace_patch ["y" ] = trendline [:, 1 ]
357
361
hover_header = "<b>LOWESS trendline</b><br><br>"
358
- elif attr_value == "ols" :
362
+ elif attr_value [0 ] == "ma" :
363
+ trace_patch ["y" ] = (
364
+ pd .Series (y [non_missing ])
365
+ .rolling (window = attr_value [1 ] or 3 )
366
+ .mean ()
367
+ )
368
+ elif attr_value [0 ] == "ewm" :
369
+ trace_patch ["y" ] = (
370
+ pd .Series (y [non_missing ])
371
+ .ewm (alpha = attr_value [1 ] or 0.5 )
372
+ .mean ()
373
+ )
374
+ elif attr_value [0 ] == "ols" :
375
+ add_constant = attr_value [1 ] is not False
359
376
fit_results = sm .OLS (
360
- y , sm .add_constant (x ), missing = "drop"
377
+ y , sm .add_constant (x ) if add_constant else x , missing = "drop"
361
378
).fit ()
362
379
trace_patch ["y" ] = fit_results .predict ()
363
380
hover_header = "<b>OLS trendline</b><br>"
@@ -368,6 +385,12 @@ def make_trace_kwargs(args, trace_spec, trace_data, mapping_labels, sizeref):
368
385
args ["x" ],
369
386
fit_results .params [0 ],
370
387
)
388
+ elif not add_constant :
389
+ hover_header += "%s = %g* %s<br>" % (
390
+ args ["y" ],
391
+ fit_results .params [0 ],
392
+ args ["x" ],
393
+ )
371
394
else :
372
395
hover_header += "%s = %g<br>" % (
373
396
args ["y" ],
@@ -1822,6 +1845,10 @@ def infer_config(args, constructor, trace_patch, layout_patch):
1822
1845
):
1823
1846
args ["facet_col_wrap" ] = 0
1824
1847
1848
+ if args .get ("trendline" , None ) is not None :
1849
+ if isinstance (args ["trendline" ], str ):
1850
+ args ["trendline" ] = (args ["trendline" ], None )
1851
+
1825
1852
# Compute applicable grouping attributes
1826
1853
for k in group_attrables :
1827
1854
if k in args :
0 commit comments