Add linear Ordinary Least Squares (OLS) regression trendlines or non-linear Locally Weighted Scatterplot Smoothing (LOWESS) trendlines to scatterplots in Python. Options for moving averages (rolling means) as well as exponentially-weighted and expanding functions.
Plotly Studio: Transform any dataset into an interactive data application in minutes with AI. Sign up for early access now.
In [1]:
import plotly.express as px df = px.data.tips() fig = px.scatter(df, x="total_bill", y="tip", trendline="ols") fig.show()Fitting multiple lines and retrieving the model parameters¶
Plotly Express will fit a trendline per trace, and allows you to access the underlying model parameters for all the models.
In [2]:
import plotly.express as px df = px.data.tips() fig = px.scatter(df, x="total_bill", y="tip", facet_col="smoker", color="sex", trendline="ols") fig.show() results = px.get_trendline_results(fig) print(results) results.query("sex == 'Male' and smoker == 'Yes'").px_fit_results.iloc[0].summary()
sex smoker px_fit_results 0 Female No <statsmodels.regression.linear_model.Regressio... 1 Female Yes <statsmodels.regression.linear_model.Regressio... 2 Male No <statsmodels.regression.linear_model.Regressio... 3 Male Yes <statsmodels.regression.linear_model.Regressio...
Out[2]:
OLS Regression Results Dep. Variable: y R-squared: 0.232 Model: OLS Adj. R-squared: 0.219 Method: Least Squares F-statistic: 17.56 Date: Tue, 12 Aug 2025 Prob (F-statistic): 9.61e-05 Time: 21:05:07 Log-Likelihood: -101.03 No. Observations: 60 AIC: 206.1 Df Residuals: 58 BIC: 210.2 Df Model: 1 Covariance Type: nonrobust coef std err t P>|t| [0.025 0.975] const 1.4253 0.424 3.361 0.001 0.576 2.274 x1 0.0730 0.017 4.190 0.000 0.038 0.108 Omnibus: 21.841 Durbin-Watson: 1.383 Prob(Omnibus): 0.000 Jarque-Bera (JB): 33.031 Skew: 1.315 Prob(JB): 6.72e-08 Kurtosis: 5.510 Cond. No. 60.4Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Displaying a single trendline with multiple traces¶new in v5.2
To display a single trendline using the entire dataset, set the trendline_scope
argument to "overall"
. The same trendline will be overlaid on all facets and animation frames. The trendline color can be overridden with trendline_color_override
.
In [3]:
import plotly.express as px df = px.data.tips() fig = px.scatter(df, x="total_bill", y="tip", symbol="smoker", color="sex", trendline="ols", trendline_scope="overall") fig.show()
In [4]:
import plotly.express as px df = px.data.tips() fig = px.scatter(df, x="total_bill", y="tip", facet_col="smoker", color="sex", trendline="ols", trendline_scope="overall", trendline_color_override="black") fig.show()OLS Parameters¶
new in v5.2
OLS trendlines can be fit with log transformations to both X or Y data using the trendline_options
argument, independently of whether or not the plot has logarithmic axes.
In [5]:
import plotly.express as px df = px.data.gapminder(year=2007) fig = px.scatter(df, x="gdpPercap", y="lifeExp", trendline="ols", trendline_options=dict(log_x=True), title="Log-transformed fit on linear axes") fig.show()
In [6]:
import plotly.express as px df = px.data.gapminder(year=2007) fig = px.scatter(df, x="gdpPercap", y="lifeExp", log_x=True, trendline="ols", trendline_options=dict(log_x=True), title="Log-scaled X axis and log-transformed fit") fig.show()
In [7]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="lowess") fig.show()
new in v5.2
The level of smoothing can be controlled via the frac
trendline option, which indicates the fraction of the data that the LOWESS smoother should include. The default is a fairly smooth line with frac=0.6666
and lowering this fraction will give a line that more closely follows the data.
In [8]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="lowess", trendline_options=dict(frac=0.1)) fig.show()Moving Averages¶
new in v5.2
Plotly Express can leverage Pandas' rolling
, ewm
and expanding
functions in trendlines as well, for example to display moving averages. Values passed to trendline_options
are passed directly to the underlying Pandas function (with the exception of the function
and function_options
keys, see below).
In [9]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="rolling", trendline_options=dict(window=5), title="5-point moving average") fig.show()
In [10]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="ewm", trendline_options=dict(halflife=2), title="Exponentially-weighted moving average (halflife of 2 points)") fig.show()
In [11]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="expanding", title="Expanding mean") fig.show()Other Functions¶
The rolling
, expanding
and ewm
trendlines support other functions than the default mean
, enabling, for example, a moving-median trendline, or an expanding-max trendline.
In [12]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="rolling", trendline_options=dict(function="median", window=5), title="Rolling Median") fig.show()
In [13]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="expanding", trendline_options=dict(function="max"), title="Expanding Maximum") fig.show()
In some cases, it is necessary to pass options into the underying Pandas function, for example the std
parameter must be provided if the win_type
argument to rolling
is "gaussian"
. This is possible with the function_args
trendline option.
In [14]:
import plotly.express as px df = px.data.stocks(datetimes=True) fig = px.scatter(df, x="date", y="GOOG", trendline="rolling", trendline_options=dict(window=5, win_type="gaussian", function_args=dict(std=2)), title="Rolling Mean with Gaussian Window") fig.show()Displaying only the trendlines¶
In some cases, it may be desirable to show only the trendlines, by removing the scatter points.
In [15]:
import plotly.express as px df = px.data.stocks(indexed=True, datetimes=True) fig = px.scatter(df, trendline="rolling", trendline_options=dict(window=5), title="5-point moving average") fig.data = [t for t in fig.data if t.mode == "lines"] fig.update_traces(showlegend=True) #trendlines have showlegend=False by default fig.show()What About Dash?¶
Dash is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.
Learn about how to install Dash at https://dash.plot.ly/installation.
Everywhere in this page that you see fig.show()
, you can display the same figure in a Dash application by passing it to the figure
argument of the Graph
component from the built-in dash_core_components
package like this:
import plotly.graph_objects as go # or plotly.express as px fig = go.Figure() # or any Plotly Express function e.g. px.bar(...) # fig.add_trace( ... ) # fig.update_layout( ... ) from dash import Dash, dcc, html app = Dash() app.layout = html.Div([ dcc.Graph(figure=fig) ]) app.run(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4