Skip to content

Instantly share code, notes, and snippets.

@timelyportfolio
Last active January 6, 2018 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save timelyportfolio/f5ce5602160c1654e519e5035aef705d to your computer and use it in GitHub Desktop.
Save timelyportfolio/f5ce5602160c1654e519e5035aef705d to your computer and use it in GitHub Desktop.
alternate version of market quilt
license: mit
height: 600

Built with blockbuilder.org


This all started here ...

Omg this chart is a mess. https://t.co/pnWa2RMVcm pic.twitter.com/kFm1n1MqDl

— yoni sidi (@yoniceedee) January 4, 2018
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

These market quilts or carpets are extremely common and popular, but for almost my entire career I have thought there must be a better way to communicate this information.

This is a very ugly sketch of an alternate version with imaginary data. It is sort of like a bump chart. I don't think it is any better.

Code in R and JavaScript

library(htmltools)

# make some fake data to use with rawgraph bump chart
#  but rawgraphs doesn't work as I would like
dat <- data.frame(
  idx = rep(LETTERS[1:10], each=10),
  date = rep(
    seq.Date(from=as.Date("2000-12-31"), to=as.Date("2009-12-31"), by="years"),
    10
  ),
  perf = runif(10 * 10,-20,20),
  stringsAsFactors = FALSE
)



# so go custom
browsable(
  tagList(
    d3r::d3_dep_v4(),
    tags$script(HTML(
sprintf(
'
var data = %s
var height = 500, width = 900
var color = d3.scaleOrdinal(d3.schemeCategory10)

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr("transform", "translate(20,20)")

var sc_x = d3.scalePoint()
  .domain(data.map(d => (d.date)))
  .range([0,width - 40])

var sc_y = d3.scaleLinear()
  .domain(d3.extent(data.map(d => d.perf)))
  .range([height - 40, 0])

var line = d3.line()
  .x(d => sc_x(d.date))
  .y(d => sc_y(d.perf))
  .curve(d3.curveBasis)

var nested = d3.nest().key(d => d.idx).entries(data)

var lines = svg.selectAll(".line").data(nested)

lines.exit().remove()

lines = lines.merge(lines.enter().append("path"))

lines
  .classed("line", true)
  .attr("d", d => line(d.values))
  .style("fill", "none")
  .style("opacity", 0.5)
  .style("stroke", d => color(d.key))
  .style("stroke-width", 20)
',
  jsonlite::toJSON(dat, dataframe="rows", pretty=TRUE)
)
    ))
  )
)


# so go custom 2
browsable(
  tagList(
    d3r::d3_dep_v4(),
    tags$script(HTML(
      sprintf(
'
var data = %s

var height = 600, width = 900
var margin = {
  top: 20,
  bottom: 50,
  left: 50,
  right: 20
}

var color = d3.scaleOrdinal(d3.schemeCategory10)

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height)

var g_plot = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

var sc_x = d3.scaleBand()
  .domain(data.map(d => (d.date)))
  .range([0,width - margin.left - margin.right])

var sc_y = d3.scaleLinear()
  .domain(d3.extent(data.map(d => d.perf)))
  .range([height - margin.top - margin.bottom, 0])

var line = d3.line()
  .curve(d3.curveMonotoneX)

var horiz_smooth = 50

var nested = d3.nest().key(d => d.idx).entries(data)

var highlight = function() {
  svg.selectAll(".line").style("opacity", 0.25)
  d3.select(this).style("opacity", 1)
}

var unhighlight = function() {
  svg.selectAll(".line").style("opacity", 0.5)
}

nested.map(function(d) {
points = []

d.values.forEach(function(dd, i) {
  if(i > d.values.length - 2) {
    points.push([sc_x(dd.date) + horiz_smooth, sc_y(dd.perf)])
    return
  }

  if(i === 0) {
    points.push([sc_x(dd.date), sc_y(dd.perf)]),
    points.push([sc_x(dd.date) + horiz_smooth, sc_y(dd.perf)])
    points.push([sc_x(dd.date) + sc_x.bandwidth(), sc_y(d.values[i+1].perf)])
    return
  }

  points.push([sc_x(dd.date) + horiz_smooth, sc_y(dd.perf)])

  points.push([sc_x(dd.date) + sc_x.bandwidth(), sc_y(d.values[i+1].perf)])
})

g_plot.append("path")
  .classed("line", true)
  .attr("d", line(points))
  .style("fill", "none")
  .style("stroke", color(d.key))
  .style("stroke-width", 10)
  .style("opacity", 0.5)
  .on("mouseover", highlight)
  .on("mouseout", unhighlight)
})

g_plot.append("g")
  .call(d3.axisBottom().scale(sc_x))
  .attr("transform", "translate(" + (-sc_x.bandwidth()/2 + horiz_smooth/2) + "," + (+height - margin.top - margin.bottom) + ")")

g_plot.append("g")
  .call(d3.axisLeft().scale(sc_y))
',
  jsonlite::toJSON(dat, dataframe="rows", pretty=TRUE)
)
    ))
  )
)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="https://unpkg.com/d3"></script>
</head>
<body style="background-color:white;">
<script>
var data = [
{
"idx": "A",
"date": "2000-12-31",
"perf": -4.5761
},
{
"idx": "A",
"date": "2001-12-31",
"perf": -15.0073
},
{
"idx": "A",
"date": "2002-12-31",
"perf": -17.911
},
{
"idx": "A",
"date": "2003-12-31",
"perf": 18.5215
},
{
"idx": "A",
"date": "2004-12-31",
"perf": -15.0506
},
{
"idx": "A",
"date": "2005-12-31",
"perf": -18.6963
},
{
"idx": "A",
"date": "2006-12-31",
"perf": -8.8435
},
{
"idx": "A",
"date": "2007-12-31",
"perf": -8.6675
},
{
"idx": "A",
"date": "2008-12-31",
"perf": -6.3162
},
{
"idx": "A",
"date": "2009-12-31",
"perf": -4.4992
},
{
"idx": "B",
"date": "2000-12-31",
"perf": -15.469
},
{
"idx": "B",
"date": "2001-12-31",
"perf": 15.4334
},
{
"idx": "B",
"date": "2002-12-31",
"perf": 9.1245
},
{
"idx": "B",
"date": "2003-12-31",
"perf": 17.9742
},
{
"idx": "B",
"date": "2004-12-31",
"perf": -8.7123
},
{
"idx": "B",
"date": "2005-12-31",
"perf": 4.4175
},
{
"idx": "B",
"date": "2006-12-31",
"perf": -9.2237
},
{
"idx": "B",
"date": "2007-12-31",
"perf": -8.1461
},
{
"idx": "B",
"date": "2008-12-31",
"perf": 14.835
},
{
"idx": "B",
"date": "2009-12-31",
"perf": -14.0465
},
{
"idx": "C",
"date": "2000-12-31",
"perf": -4.4291
},
{
"idx": "C",
"date": "2001-12-31",
"perf": -3.4441
},
{
"idx": "C",
"date": "2002-12-31",
"perf": 1.7558
},
{
"idx": "C",
"date": "2003-12-31",
"perf": -5.0047
},
{
"idx": "C",
"date": "2004-12-31",
"perf": -14.5759
},
{
"idx": "C",
"date": "2005-12-31",
"perf": 6.6994
},
{
"idx": "C",
"date": "2006-12-31",
"perf": -1.5583
},
{
"idx": "C",
"date": "2007-12-31",
"perf": -13.2864
},
{
"idx": "C",
"date": "2008-12-31",
"perf": -7.2503
},
{
"idx": "C",
"date": "2009-12-31",
"perf": 15.2747
},
{
"idx": "D",
"date": "2000-12-31",
"perf": 13.9626
},
{
"idx": "D",
"date": "2001-12-31",
"perf": 3.243
},
{
"idx": "D",
"date": "2002-12-31",
"perf": -13.3865
},
{
"idx": "D",
"date": "2003-12-31",
"perf": 15.8711
},
{
"idx": "D",
"date": "2004-12-31",
"perf": -6.6842
},
{
"idx": "D",
"date": "2005-12-31",
"perf": 6.5636
},
{
"idx": "D",
"date": "2006-12-31",
"perf": 11.7851
},
{
"idx": "D",
"date": "2007-12-31",
"perf": -19.8014
},
{
"idx": "D",
"date": "2008-12-31",
"perf": 15.0866
},
{
"idx": "D",
"date": "2009-12-31",
"perf": 19.3894
},
{
"idx": "E",
"date": "2000-12-31",
"perf": -18.8695
},
{
"idx": "E",
"date": "2001-12-31",
"perf": -14.0828
},
{
"idx": "E",
"date": "2002-12-31",
"perf": 18.333
},
{
"idx": "E",
"date": "2003-12-31",
"perf": -14.6408
},
{
"idx": "E",
"date": "2004-12-31",
"perf": 19.4939
},
{
"idx": "E",
"date": "2005-12-31",
"perf": -16.546
},
{
"idx": "E",
"date": "2006-12-31",
"perf": -14.883
},
{
"idx": "E",
"date": "2007-12-31",
"perf": -5.0652
},
{
"idx": "E",
"date": "2008-12-31",
"perf": -8.3776
},
{
"idx": "E",
"date": "2009-12-31",
"perf": -15.234
},
{
"idx": "F",
"date": "2000-12-31",
"perf": -3.273
},
{
"idx": "F",
"date": "2001-12-31",
"perf": -19.6255
},
{
"idx": "F",
"date": "2002-12-31",
"perf": 7.7506
},
{
"idx": "F",
"date": "2003-12-31",
"perf": -16.6891
},
{
"idx": "F",
"date": "2004-12-31",
"perf": 2.3649
},
{
"idx": "F",
"date": "2005-12-31",
"perf": -9.3366
},
{
"idx": "F",
"date": "2006-12-31",
"perf": -2.0149
},
{
"idx": "F",
"date": "2007-12-31",
"perf": -7.6408
},
{
"idx": "F",
"date": "2008-12-31",
"perf": 1.4748
},
{
"idx": "F",
"date": "2009-12-31",
"perf": -7.5252
},
{
"idx": "G",
"date": "2000-12-31",
"perf": -9.9526
},
{
"idx": "G",
"date": "2001-12-31",
"perf": 18.2518
},
{
"idx": "G",
"date": "2002-12-31",
"perf": -17.8075
},
{
"idx": "G",
"date": "2003-12-31",
"perf": 2.4835
},
{
"idx": "G",
"date": "2004-12-31",
"perf": -10.259
},
{
"idx": "G",
"date": "2005-12-31",
"perf": 14.1983
},
{
"idx": "G",
"date": "2006-12-31",
"perf": 19.1988
},
{
"idx": "G",
"date": "2007-12-31",
"perf": 3.6613
},
{
"idx": "G",
"date": "2008-12-31",
"perf": 11.9808
},
{
"idx": "G",
"date": "2009-12-31",
"perf": -19.6471
},
{
"idx": "H",
"date": "2000-12-31",
"perf": 15.8034
},
{
"idx": "H",
"date": "2001-12-31",
"perf": -13.0795
},
{
"idx": "H",
"date": "2002-12-31",
"perf": 16.4691
},
{
"idx": "H",
"date": "2003-12-31",
"perf": -17.0362
},
{
"idx": "H",
"date": "2004-12-31",
"perf": -12.3088
},
{
"idx": "H",
"date": "2005-12-31",
"perf": 4.455
},
{
"idx": "H",
"date": "2006-12-31",
"perf": 18.8401
},
{
"idx": "H",
"date": "2007-12-31",
"perf": -15.571
},
{
"idx": "H",
"date": "2008-12-31",
"perf": 11.0688
},
{
"idx": "H",
"date": "2009-12-31",
"perf": -0.7543
},
{
"idx": "I",
"date": "2000-12-31",
"perf": 19.4466
},
{
"idx": "I",
"date": "2001-12-31",
"perf": -10.4479
},
{
"idx": "I",
"date": "2002-12-31",
"perf": -18.4353
},
{
"idx": "I",
"date": "2003-12-31",
"perf": 9.3312
},
{
"idx": "I",
"date": "2004-12-31",
"perf": 13.7115
},
{
"idx": "I",
"date": "2005-12-31",
"perf": -18.4976
},
{
"idx": "I",
"date": "2006-12-31",
"perf": -3.3736
},
{
"idx": "I",
"date": "2007-12-31",
"perf": 4.9811
},
{
"idx": "I",
"date": "2008-12-31",
"perf": -19.9589
},
{
"idx": "I",
"date": "2009-12-31",
"perf": 19.8707
},
{
"idx": "J",
"date": "2000-12-31",
"perf": 14.8278
},
{
"idx": "J",
"date": "2001-12-31",
"perf": -10.8594
},
{
"idx": "J",
"date": "2002-12-31",
"perf": -4.0786
},
{
"idx": "J",
"date": "2003-12-31",
"perf": 18.2823
},
{
"idx": "J",
"date": "2004-12-31",
"perf": 8.9381
},
{
"idx": "J",
"date": "2005-12-31",
"perf": -11.6092
},
{
"idx": "J",
"date": "2006-12-31",
"perf": 17.0184
},
{
"idx": "J",
"date": "2007-12-31",
"perf": 0.5828
},
{
"idx": "J",
"date": "2008-12-31",
"perf": -7.9249
},
{
"idx": "J",
"date": "2009-12-31",
"perf": -1.397
}
]
var height = 600, width = 900
var margin = {
top: 20,
bottom: 50,
left: 50,
right: 20
}
var color = d3.scaleOrdinal(d3.schemeCategory10)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var g_plot = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
var sc_x = d3.scaleBand()
.domain(data.map(d => (d.date)))
.range([0,width - margin.left - margin.right])
var sc_y = d3.scaleLinear()
.domain(d3.extent(data.map(d => d.perf)))
.range([height - margin.top - margin.bottom, 0])
var line = d3.line()
.curve(d3.curveMonotoneX)
var horiz_smooth = 50
var nested = d3.nest().key(d => d.idx).entries(data)
var highlight = function() {
svg.selectAll(".line").style("opacity", 0.25)
d3.select(this).style("opacity", 1)
}
var unhighlight = function() {
svg.selectAll(".line").style("opacity", 0.5)
}
nested.map(function(d) {
points = []
d.values.forEach(function(dd, i) {
if(i > d.values.length - 2) {
points.push([sc_x(dd.date) + horiz_smooth, sc_y(dd.perf)])
return
}
if(i === 0) {
points.push([sc_x(dd.date), sc_y(dd.perf)]),
points.push([sc_x(dd.date) + horiz_smooth, sc_y(dd.perf)])
points.push([sc_x(dd.date) + sc_x.bandwidth(), sc_y(d.values[i+1].perf)])
return
}
points.push([sc_x(dd.date) + horiz_smooth, sc_y(dd.perf)])
points.push([sc_x(dd.date) + sc_x.bandwidth(), sc_y(d.values[i+1].perf)])
})
g_plot.append("path")
.classed("line", true)
.attr("d", line(points))
.style("fill", "none")
.style("stroke", color(d.key))
.style("stroke-width", 10)
.style("opacity", 0.5)
.on("mouseover", highlight)
.on("mouseout", unhighlight)
})
g_plot.append("g")
.call(d3.axisBottom().scale(sc_x))
.attr("transform", "translate(" + (-sc_x.bandwidth()/2 + horiz_smooth/2) + "," + (+height - margin.top - margin.bottom) + ")")
g_plot.append("g")
.call(d3.axisLeft().scale(sc_y))
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment