Skip to content

Instantly share code, notes, and snippets.

@HarryStevens
Last active November 25, 2018 18:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HarryStevens/397a50a604666957e661e4c725c1022a to your computer and use it in GitHub Desktop.
Save HarryStevens/397a50a604666957e661e4c725c1022a to your computer and use it in GitHub Desktop.
LOESS Smoothing
license: gpl-3.0

LOESS (locally estimated scatterplot smoothing) is a method for producing a smooth curve through a set of scatterplot points. The implementation here is by Jason Davies from his library Science.js.

Adjust the slider to see how the curve changes depending upon its bandwidth. Higher bandwidths produce smoother lines, and vice versa. According to Wikipedia:

A user-specified input to the procedure called the "bandwidth" or "smoothing parameter" determines how much of the data is used to fit each local polynomial. The smoothing parameter, α, is the fraction of the total number n of data points that are used in each local fit.

The chart displays the average annual temperature in California from 1900 to 2017. Source: NOAA.

A prettier version of this chart was published in Axios on Nov. 15, 2018, under the headline "Humans are a wildfire threat multiplier".

[{"state_name":"California","year":1900,"value":57.5605},{"state_name":"California","year":1901,"value":57.3214},{"state_name":"California","year":1902,"value":56.2134},{"state_name":"California","year":1903,"value":56.317},{"state_name":"California","year":1904,"value":57.4467},{"state_name":"California","year":1905,"value":57.1332},{"state_name":"California","year":1906,"value":57.2392},{"state_name":"California","year":1907,"value":56.5545},{"state_name":"California","year":1908,"value":56.2721},{"state_name":"California","year":1909,"value":56.2912},{"state_name":"California","year":1910,"value":57.7532},{"state_name":"California","year":1911,"value":55.474},{"state_name":"California","year":1912,"value":55.5628},{"state_name":"California","year":1913,"value":56.5263},{"state_name":"California","year":1914,"value":57.4455},{"state_name":"California","year":1915,"value":56.8189},{"state_name":"California","year":1916,"value":55.5932},{"state_name":"California","year":1917,"value":56.6997},{"state_name":"California","year":1918,"value":56.9079},{"state_name":"California","year":1919,"value":56.3611},{"state_name":"California","year":1920,"value":56.1257},{"state_name":"California","year":1921,"value":57.3778},{"state_name":"California","year":1922,"value":56.1452},{"state_name":"California","year":1923,"value":56.4466},{"state_name":"California","year":1924,"value":57.1546},{"state_name":"California","year":1925,"value":57.206},{"state_name":"California","year":1926,"value":58.8471},{"state_name":"California","year":1927,"value":56.94},{"state_name":"California","year":1928,"value":57.7148},{"state_name":"California","year":1929,"value":57.3422},{"state_name":"California","year":1930,"value":56.9745},{"state_name":"California","year":1931,"value":58.3638},{"state_name":"California","year":1932,"value":56.9607},{"state_name":"California","year":1933,"value":56.8247},{"state_name":"California","year":1934,"value":59.7805},{"state_name":"California","year":1935,"value":56.8945},{"state_name":"California","year":1936,"value":58.6637},{"state_name":"California","year":1937,"value":57.2449},{"state_name":"California","year":1938,"value":57.3329},{"state_name":"California","year":1939,"value":58.4247},{"state_name":"California","year":1940,"value":58.8798},{"state_name":"California","year":1941,"value":57.1222},{"state_name":"California","year":1942,"value":57.1411},{"state_name":"California","year":1943,"value":57.894},{"state_name":"California","year":1944,"value":56.2842},{"state_name":"California","year":1945,"value":57.1573},{"state_name":"California","year":1946,"value":56.8022},{"state_name":"California","year":1947,"value":57.6003},{"state_name":"California","year":1948,"value":55.6891},{"state_name":"California","year":1949,"value":56.2792},{"state_name":"California","year":1950,"value":58.1899},{"state_name":"California","year":1951,"value":57.2899},{"state_name":"California","year":1952,"value":56.8036},{"state_name":"California","year":1953,"value":57.2433},{"state_name":"California","year":1954,"value":57.6142},{"state_name":"California","year":1955,"value":56.3011},{"state_name":"California","year":1956,"value":57.0172},{"state_name":"California","year":1957,"value":57.1288},{"state_name":"California","year":1958,"value":58.9603},{"state_name":"California","year":1959,"value":59.0416},{"state_name":"California","year":1960,"value":58.0866},{"state_name":"California","year":1961,"value":57.8652},{"state_name":"California","year":1962,"value":57.4384},{"state_name":"California","year":1963,"value":57.1077},{"state_name":"California","year":1964,"value":56.6202},{"state_name":"California","year":1965,"value":56.774},{"state_name":"California","year":1966,"value":58.0992},{"state_name":"California","year":1967,"value":57.5668},{"state_name":"California","year":1968,"value":57.5989},{"state_name":"California","year":1969,"value":57.5899},{"state_name":"California","year":1970,"value":57.8767},{"state_name":"California","year":1971,"value":56.2718},{"state_name":"California","year":1972,"value":57.2934},{"state_name":"California","year":1973,"value":57.3641},{"state_name":"California","year":1974,"value":57.7036},{"state_name":"California","year":1975,"value":56.3447},{"state_name":"California","year":1976,"value":57.5107},{"state_name":"California","year":1977,"value":58.0088},{"state_name":"California","year":1978,"value":57.6893},{"state_name":"California","year":1979,"value":57.7485},{"state_name":"California","year":1980,"value":58.1052},{"state_name":"California","year":1981,"value":59.3551},{"state_name":"California","year":1982,"value":56.4003},{"state_name":"California","year":1983,"value":57.6184},{"state_name":"California","year":1984,"value":58.2609},{"state_name":"California","year":1985,"value":57.3929},{"state_name":"California","year":1986,"value":58.9427},{"state_name":"California","year":1987,"value":58.3293},{"state_name":"California","year":1988,"value":58.7852},{"state_name":"California","year":1989,"value":58.1885},{"state_name":"California","year":1990,"value":58.0488},{"state_name":"California","year":1991,"value":58.16},{"state_name":"California","year":1992,"value":59.2918},{"state_name":"California","year":1993,"value":57.7408},{"state_name":"California","year":1994,"value":58.1055},{"state_name":"California","year":1995,"value":58.9677},{"state_name":"California","year":1996,"value":59.594},{"state_name":"California","year":1997,"value":59.1923},{"state_name":"California","year":1998,"value":56.7942},{"state_name":"California","year":1999,"value":58.0356},{"state_name":"California","year":2000,"value":58.8363},{"state_name":"California","year":2001,"value":59.2216},{"state_name":"California","year":2002,"value":58.8964},{"state_name":"California","year":2003,"value":59.529},{"state_name":"California","year":2004,"value":58.9128},{"state_name":"California","year":2005,"value":58.6833},{"state_name":"California","year":2006,"value":58.6975},{"state_name":"California","year":2007,"value":58.9929},{"state_name":"California","year":2008,"value":58.9459},{"state_name":"California","year":2009,"value":58.917},{"state_name":"California","year":2010,"value":57.7926},{"state_name":"California","year":2011,"value":57.5186},{"state_name":"California","year":2012,"value":59.5448},{"state_name":"California","year":2013,"value":59.3921},{"state_name":"California","year":2014,"value":61.5208},{"state_name":"California","year":2015,"value":60.7992},{"state_name":"California","year":2016,"value":60.1429},{"state_name":"California","year":2017,"value":60.4208}]
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
font-family: "Helvetica Neue", sans-serif;
}
#bandwidth {
position: absolute;
left: 100px;
}
.loess-line {
fill: none;
stroke-width: 2px;
stroke: steelblue;
}
</style>
</head>
<body>
<div id="bandwidth">
<div>Bandwidth: <span class="bandwidth"></span></div>
<input type="range" min="0" max="100" value="25" />
</div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/science@1.9.3/science.v1.min.js"></script>
<script>
var x_property = "year", y_property = "value";
var margin = {left: 30, right: 5, top: 5, bottom: 20},
aspect_ratio = .68,
width,
height;
var x_scale = d3.scaleLinear();
var y_scale = d3.scaleLinear();
var x_axis_generator = d3.axisBottom().tickFormat(d => d)
var y_axis_generator = d3.axisLeft();
var loess_generator = science.stats.loess(), loess_values, loess_data;
var line_generator = d3.line()
.x(d => x_scale(d[x_property]))
.y(d => y_scale(d[y_property]));
var input = d3.select("input");
var svg = d3.select("body").append("svg");
var g = svg.append("g");
var x_axis = g.append("g");
var y_axis = g.append("g");
d3.json("data.json").then(data => {
var x_values = data.map(d => d[x_property]);
var y_values = data.map(d => d[y_property]);
x_scale.domain(d3.extent(x_values));
y_scale.domain(d3.extent(y_values));
var points = g.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 3);
function draw(resizing, adjusting){
if (resizing){
width = window.innerWidth - margin.left - margin.right;
height = d3.min([window.innerHeight, (width * aspect_ratio)]) - margin.top - margin.bottom;
x_scale.range([0, width]);
y_scale.range([height, 0]);
x_axis_generator.scale(x_scale);
y_axis_generator.scale(y_scale);
svg
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
g.attr("transform", "translate(" + [margin.left, margin.top] + ")");
x_axis
.attr("transform", "translate(0, " + height + ")")
.call(x_axis_generator);
y_axis.call(y_axis_generator);
points.attr("transform", d => "translate(" + [x_scale(d[x_property]), y_scale(d[y_property])] + ")");
}
if (adjusting){
var new_bandwidth = input.property("value") / 100;
d3.select(".bandwidth").html(new_bandwidth.toFixed(2));
loess_generator.bandwidth(new_bandwidth);
loess_values = loess_generator(x_values, y_values);
loess_data = data.map((d, i) => ({year: d[x_property], value: loess_values[i]}));
}
var loess_line = g.selectAll(".loess-line")
.data([loess_data]);
loess_line.enter().append("path")
.attr("class", "loess-line")
.merge(loess_line)
.attr("d", line_generator);
}
draw(1, 1);
window.addEventListener("resize", _ => draw(1, 0));
input.on("input", _ => draw(0, 1));
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment