Skip to content

Instantly share code, notes, and snippets.

@tophtucker
Last active March 12, 2021 20:26
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 tophtucker/67ae8d1faa58567945cd5bbca8cc08eb to your computer and use it in GitHub Desktop.
Save tophtucker/67ae8d1faa58567945cd5bbca8cc08eb to your computer and use it in GitHub Desktop.
Swizzle

Swizzling! (Or pandas DataFrame "reshaping" / "pivoting" / "(un)stacking".)

Click and drag the i,j,k in the upper right to reorder. Refresh for random dimensions.

One can imagine more spreadsheety stuff, extending to 4D arrays and higher, and aggregating/slicing/collapsing/folding along different dimensions.

No, I'm not at all sure I'm using the term "swizzle" correctly. I don't understand matrices.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
* {
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
}
body {
font-family: sans-serif;
font-size: 20px;
}
.order {
position: fixed;
right: .5em;
top: .5em;
border: 2px solid black;
background: white;
padding: 10px;
font-weight: bold;
display: inline-block;
font-size: 2em;
width: 284px;
height: 104px;
}
.order .dim {
position: absolute;
display: inline-block;
width: 80px;
height: 80px;
padding: .25em;
text-align: center;
border: 1px solid black;
}
.order .dim:hover {
cursor: -webkit-grab;
cursor: grab;
background: rgba(0,0,0,.1);
}
.order .dim.active {
cursor: -webkit-grabbing;
cursor: grabbing;
background: black;
color: white;
z-index: 2;
}
.table {
position: absolute;
}
.table div.header {
position: absolute;
text-align: center;
padding: .5em;
font-weight: bold;
}
.table div.cell {
position: absolute;
text-align: right;
border-bottom: 1px solid black;
border-right: 1px solid white;
border-right-color: white;
}
.table div.bar {
position: absolute;
background: rgba(0,0,0,.1);
width: 100%;
}
.table div.value {
padding: .5em;
}
</style>
<body>
<div class="table"></div>
<div class="order"></div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var w = 45,
h = 45,
order = ['i', 'j', 'k'],
dim = {
i: 2 + (Math.random() * 10 | 0),
j: 2 + (Math.random() * 10 | 0),
k: 2 + (Math.random() * 10 | 0)
},
value = (i,j,k) => i + 5 + 5 * Math.sin(j) + d3.randomNormal(5)(),
rainbow = d3.scaleSequential(d3.interpolateRainbow)
.domain([0, Math.max(dim.i, dim.j, dim.k)]),
data = d3.merge(
d3.range(dim.i).map(
(i) => d3.merge(d3.range(dim.j).map(
(j) => d3.range(dim.k).map(
(k) => ({i, j, k, x: Math.round(value(i,j,k))})
)
))
)
),
headerData = [].concat(
d3.range(dim.i).map(i => ({dim: "i", val: i})),
d3.range(dim.j).map(j => ({dim: "j", val: j})),
d3.range(dim.k).map(k => ({dim: "k", val: k}))
),
barScale = d3.scaleLinear()
.domain(d3.extent(data.map(d => d.x)))
.range([0,100])
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
var label = d3.select(".order").selectAll("div.dim")
.data(order)
.enter()
.append("div")
.attr("class", "dim")
.text(d => d)
.call(renderDim)
.call(drag)
var table = d3.select(".table")
.style("left", w + "px")
.style("top", h * 2 + "px")
var header = table.selectAll("div.header")
.data(headerData)
.enter()
.append("div")
.attr("class", "header")
.html(d => d.dim + "<sub>" + d.val + "</sub>")
.call(renderHeader)
var cell = table.selectAll("div.cell")
.data(data)
.enter()
.append("div")
.attr("class", "cell")
.call(renderCell)
cell.append("div")
.attr("class", "value")
.text(d => d.x)
cell.append("div")
.attr("class", "bar")
.style("bottom", "0")
.style("height", d => barScale(d.x) + "%")
var interval = d3.interval(swizzle, 3000)
function swizzle() {
d3.shuffle(order)
d3.transition()
.duration(2000)
.call(render)
}
function render(t) {
label.transition(t)
.call(renderDim)
header.transition(t)
.call(renderHeader)
cell.transition(t)
.call(renderCell)
}
function renderDim(selection) {
selection
.filter(function() { return !d3.select(this).classed("active") })
.style("left", d => 10 + order.indexOf(d) * 90 + "px")
}
function renderHeader(selection) {
selection
.style("width", w + "px")
.style("height", h + "px")
.style("left", d => {
if(d.dim == order[0]) {
return w * dim[order[2]] * d.val + "px"
} else if(d.dim == order[1]) {
return -w + "px"
} else if(d.dim == order[2]) {
return w * d.val + "px"
}
})
.style("top", d => {
if(d.dim == order[0]) {
return -2 * h + "px"
} else if(d.dim == order[1]) {
return h * d.val + "px"
} else if(d.dim == order[2]) {
return -h + "px"
}
})
}
function renderCell(selection) {
selection
// .style("color", d => rainbow(d[order[2]]))
.style("width", w + "px")
.style("height", h + "px")
.style("border-right-color", d => d[order[2]] === dim[order[2]] - 1 ? 'rgba(0,0,0,1)' : 'rgba(0,0,0,0)')
.style("left", (d) => (w * dim[order[2]]) * d[order[0]] + w * d[order[2]] + "px")
.style("top", (d) => h * d[order[1]] + "px")
}
function dragstarted(d) {
interval.stop()
d3.select(this).classed("active", true)
}
function dragged(d) {
d3.select(this)
.style("left", d3.event.x - 40 + "px")
order = label.nodes()
.sort(
(a,b) => a.offsetLeft - b.offsetLeft)
.map(
el => d3.select(el).datum()
)
d3.transition()
.duration(100)
.call(render)
}
function dragended(d) {
d3.select(this).classed("active", false)
label.transition()
.duration(100)
.call(renderDim)
}
// do good
// ok, 12 march 2021, removing this. interesting record of my feeling of helpless desperation though!
/*
d3.select("body").on("click", () => {
if(!d3.select(d3.event.target).classed("dim")) {
window.open(Math.random() > .5 ? "https://action.aclu.org/donate-aclu" : "https://www.cair.com/donations/general-donation/campaign/#/donation")
}
})
*/
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment