Skip to content

Instantly share code, notes, and snippets.

@lsei
Last active July 9, 2017 11:31
Show Gist options
  • Save lsei/4b158d3d4c6bdeace8fbe4429ed479ff to your computer and use it in GitHub Desktop.
Save lsei/4b158d3d4c6bdeace8fbe4429ed479ff to your computer and use it in GitHub Desktop.
Monzo Zoomable Circles
height: 600
license: none

Visualising Monzo transactions in a zoomable circle chart

{
"name": "transactions",
"children": [
{
"name": "Eating Out",
"children": [
{
"name": "Allpress Espresso Bar",
"children": [
{
"amount": 600
},
{
"amount": 500
},
{
"amount": 540
},
{
"amount": 540
},
{
"amount": 400
}
]
},
{
"name": "Cafe Route",
"children": [
{
"amount": 540
},
{
"amount": 900
},
{
"amount": 430
}
]
},
{
"name": "Palm Springs",
"children": [
{
"amount": 999
}
]
},
{
"name": "Abokado",
"children": [
{
"amount": 862
}
]
},
{
"name": "pod",
"children": [
{
"amount": 824
}
]
},
{
"name": "Pret A Manger",
"children": [
{
"amount": 825
},
{
"amount": 375
},
{
"amount": 375
},
{
"amount": 400
}
]
},
{
"name": "The Diner",
"children": [
{
"amount": 1340
},
{
"amount": 2531
}
]
},
{
"name": "Javabean Cafe",
"children": [
{
"amount": 1300
}
]
},
{
"name": "FED by Water",
"children": [
{
"amount": 1245
}
]
},
{
"name": "Homeslice",
"children": [
{
"amount": 2250
},
{
"amount": 400
}
]
},
{
"name": "NEPA Coffee & Food",
"children": [
{
"amount": 475
}
]
},
{
"name": "Le Bao Ltd",
"children": [
{
"amount": 400
},
{
"amount": 1150
}
]
},
{
"name": "Brooksby's Walk",
"children": [
{
"amount": 500
},
{
"amount": 1500
}
]
},
{
"name": "Brunswick East",
"children": [
{
"amount": 590
}
]
},
{
"name": "The Bootstrap Trad",
"children": [
{
"amount": 1000
}
]
},
{
"name": "Wilton Way Café",
"children": [
{
"amount": 730
},
{
"amount": 1320
}
]
},
{
"name": "Shoreditch Grind",
"children": [
{
"amount": 900
}
]
},
{
"name": "Fernandez & Wells",
"children": [
{
"amount": 549
}
]
},
{
"name": "The Dusty Knuckle Bakery",
"children": [
{
"amount": 360
},
{
"amount": 180
},
{
"amount": 500
},
{
"amount": 180
},
{
"amount": 500
},
{
"amount": 180
},
{
"amount": 360
},
{
"amount": 500
},
{
"amount": 180
},
{
"amount": 180
},
{
"amount": 180
},
{
"amount": 180
},
{
"amount": 180
},
{
"amount": 500
},
{
"amount": 500
},
{
"amount": 180
},
{
"amount": 250
},
{
"amount": 500
},
{
"amount": 500
},
{
"amount": 500
},
{
"amount": 350
},
{
"amount": 500
}
]
},
{
"name": "YO! Sushi",
"children": [
{
"amount": 1175
}
]
},
{
"name": "Allpress Espresso",
"children": [
{
"amount": 300
},
{
"amount": 1060
},
{
"amount": 540
},
{
"amount": 540
},
{
"amount": 270
},
{
"amount": 270
},
{
"amount": 270
},
{
"amount": 1050
},
{
"amount": 870
},
{
"amount": 1050
},
{
"amount": 500
},
{
"amount": 270
},
{
"amount": 590
},
{
"amount": 500
}
]
},
{
"name": "Ye Old Cheshire Cheese",
"children": [
{
"amount": 447
},
{
"amount": 1117
}
]
},
{
"name": "Arancini Brothers ",
"children": [
{
"amount": 900
},
{
"amount": 900
},
{
"amount": 1090
},
{
"amount": 670
},
{
"amount": 550
},
{
"amount": 670
},
{
"amount": 550
},
{
"amount": 940
},
{
"amount": 940
},
{
"amount": 940
},
{
"amount": 586
},
{
"amount": 790
},
{
"amount": 940
},
{
"amount": 940
},
{
"amount": 570
}
]
},
{
"name": "Pavillion",
"children": [
{
"amount": 1170
}
]
},
{
"name": "GRAB Thai",
"children": [
{
"amount": 310
}
]
},
{
"name": "Bao",
"children": [
{
"amount": 5500
}
]
},
{
"name": "Honest Burgers",
"children": [
{
"amount": 1800
},
{
"amount": 2000
},
{
"amount": 1235
},
{
"amount": 1405
}
]
},
{
"name": "Falafelicious",
"children": [
{
"amount": 495
}
]
},
{
"name": "The Athenian",
"children": [
{
"amount": 950
}
]
},
{
"name": "Cuba Libre",
"children": [
{
"amount": 2890
}
]
},
{
"name": "Pitfield",
"children": [
{
"amount": 700
}
]
},
{
"name": "Ima Chez Ima",
"children": [
{
"amount": 1705
}
]
},
{
"name": "Voodoo Ray's",
"children": [
{
"amount": 800
},
{
"amount": 800
},
{
"amount": 800
},
{
"amount": 800
}
]
},
{
"name": "Le Pain Quotidien",
"children": [
{
"amount": 900
},
{
"amount": 596
}
]
},
{
"name": "Brillant Corners",
"children": [
{
"amount": 1600
}
]
},
{
"name": "Lacasan16",
"children": [
{
"amount": 910
}
]
},
{
"name": "Panopolis",
"children": [
{
"amount": 880
}
]
},
{
"name": "Merci Marie Limited",
"children": [
{
"amount": 550
}
]
},
{
"name": "Cafe Oto",
"children": [
{
"amount": 660
}
]
},
{
"name": "London Grind",
"children": [
{
"amount": 600
}
]
},
{
"name": "Tate Catering",
"children": [
{
"amount": 965
}
]
},
{
"name": "Itsu",
"children": [
{
"amount": 858
},
{
"amount": 499
}
]
},
{
"name": "Taylor St Baristas",
"children": [
{
"amount": 240
},
{
"amount": 780
},
{
"amount": 930
}
]
},
{
"name": "Wasabi",
"children": [
{
"amount": 599
},
{
"amount": 749
}
]
},
{
"name": "Lantana Cafe",
"children": [
{
"amount": 3758
}
]
},
{
"name": "Noir Espresso",
"children": [
{
"amount": 338
}
]
},
{
"name": "EAT",
"children": [
{
"amount": 350
}
]
},
{
"name": "Okko Restaurant",
"children": [
{
"amount": 1500
}
]
},
{
"name": "UK POS",
"children": [
{
"amount": 240
}
]
}
]
},
{
"name": "Groceries",
"children": [
{
"name": "Nisa",
"children": [
{
"amount": 771
},
{
"amount": 587
},
{
"amount": 596
},
{
"amount": 916
},
{
"amount": 1048
},
{
"amount": 665
},
{
"amount": 748
},
{
"amount": 1097
},
{
"amount": 697
},
{
"amount": 131
},
{
"amount": 765
},
{
"amount": 799
},
{
"amount": 880
},
{
"amount": 1023
},
{
"amount": 586
},
{
"amount": 1199
},
{
"amount": 1158
},
{
"amount": 777
},
{
"amount": 398
},
{
"amount": 667
},
{
"amount": 800
},
{
"amount": 1245
},
{
"amount": 498
}
]
},
{
"name": "Whole Foods Market",
"children": [
{
"amount": 429
}
]
},
{
"name": "Sainsbury's",
"children": [
{
"amount": 2753
},
{
"amount": 1175
},
{
"amount": 780
},
{
"amount": 650
},
{
"amount": 795
},
{
"amount": 635
},
{
"amount": 2540
},
{
"amount": 555
},
{
"amount": 385
},
{
"amount": 2869
},
{
"amount": 1025
},
{
"amount": 3276
},
{
"amount": 624
},
{
"amount": 730
},
{
"amount": 325
},
{
"amount": 1360
},
{
"amount": 2418
},
{
"amount": 1990
},
{
"amount": 690
},
{
"amount": 485
},
{
"amount": 959
},
{
"amount": 1355
},
{
"amount": 1355
},
{
"amount": 545
},
{
"amount": 994
},
{
"amount": 535
},
{
"amount": 505
},
{
"amount": 505
},
{
"amount": 942
},
{
"amount": 365
},
{
"amount": 515
},
{
"amount": 475
},
{
"amount": 1789
},
{
"amount": 930
},
{
"amount": 1730
},
{
"amount": 235
},
{
"amount": 225
},
{
"amount": 360
}
]
},
{
"name": "The Co-op",
"children": [
{
"amount": 593
},
{
"amount": 857
},
{
"amount": 1005
},
{
"amount": 269
},
{
"amount": 350
},
{
"amount": 693
},
{
"amount": 350
},
{
"amount": 317
},
{
"amount": 1463
},
{
"amount": 200
},
{
"amount": 619
},
{
"amount": 449
},
{
"amount": 500
},
{
"amount": 358
},
{
"amount": 298
},
{
"amount": 358
},
{
"amount": 529
}
]
},
{
"name": "Marks & Spencer",
"children": [
{
"amount": 1055
}
]
},
{
"name": "Tesco",
"children": [
{
"amount": 563
},
{
"amount": 290
},
{
"amount": 585
},
{
"amount": 326
},
{
"amount": 680
},
{
"amount": 420
},
{
"amount": 1418
}
]
},
{
"name": "The Grocery",
"children": [
{
"amount": 780
}
]
},
{
"name": "Borough Wines",
"children": [
{
"amount": 1600
}
]
},
{
"name": "Pickles Of London",
"children": [
{
"amount": 844
},
{
"amount": 635
},
{
"amount": 629
},
{
"amount": 1058
},
{
"amount": 253
},
{
"amount": 293
},
{
"amount": 648
},
{
"amount": 459
},
{
"amount": 439
},
{
"amount": 459
},
{
"amount": 655
},
{
"amount": 750
},
{
"amount": 648
},
{
"amount": 339
},
{
"amount": 462
},
{
"amount": 249
},
{
"amount": 560
},
{
"amount": 1000
},
{
"amount": 349
},
{
"amount": 86
},
{
"amount": 530
},
{
"amount": 488
},
{
"amount": 459
},
{
"amount": 186
},
{
"amount": 574
}
]
},
{
"name": "Kêu Banh Mi Deli",
"children": [
{
"amount": 620
}
]
},
{
"name": "E5 Bakehouse",
"children": [
{
"amount": 1520
},
{
"amount": 1520
},
{
"amount": 720
}
]
},
{
"name": "Dalston Local Store",
"children": [
{
"amount": 405
},
{
"amount": 646
},
{
"amount": 300
},
{
"amount": 1199
},
{
"amount": 200
},
{
"amount": 630
},
{
"amount": 300
},
{
"amount": 447
},
{
"amount": 577
},
{
"amount": 611
},
{
"amount": 1214
},
{
"amount": 142
},
{
"amount": 400
},
{
"amount": 398
},
{
"amount": 377
},
{
"amount": 676
},
{
"amount": 340
},
{
"amount": 398
},
{
"amount": 299
}
]
},
{
"name": "Poundland",
"children": [
{
"amount": 100
}
]
},
{
"name": "WH Smith",
"children": [
{
"amount": 999
}
]
},
{
"name": "Thumbs Up",
"children": [
{
"amount": 2098
}
]
},
{
"name": "Shopping Garden",
"children": [
{
"amount": 999
}
]
},
{
"name": "Flowers Supermarket",
"children": [
{
"amount": 1299
}
]
},
{
"name": "Harvest E8",
"children": [
{
"amount": 318
},
{
"amount": 785
}
]
},
{
"name": "Kumanan Services Limit",
"children": [
{
"amount": 326
}
]
}
]
},
{
"name": "Holidays",
"children": [
{
"name": "Snappy Snaps",
"children": [
{
"amount": 1596
},
{
"amount": 2396
},
{
"amount": 1000
},
{
"amount": 798
},
{
"amount": 869
}
]
},
{
"name": "Xiamen Airlines",
"children": [
{
"amount": 6319
}
]
},
{
"name": "Virgin Atlantic",
"children": [
{
"amount": 66667
}
]
}
]
},
{
"name": "Transport",
"children": [
{
"name": "Transport for London",
"children": [
{
"amount": 300
},
{
"amount": 300
},
{
"amount": 150
},
{
"amount": 240
},
{
"amount": 150
},
{
"amount": 150
},
{
"amount": 330
},
{
"amount": 170
},
{
"amount": 300
},
{
"amount": 150
},
{
"amount": 300
},
{
"amount": 300
},
{
"amount": 300
},
{
"amount": 650
},
{
"amount": 310
},
{
"amount": 310
},
{
"amount": 300
},
{
"amount": 300
},
{
"amount": 280
},
{
"amount": 330
},
{
"amount": 300
},
{
"amount": 300
},
{
"amount": 170
},
{
"amount": 170
},
{
"amount": 390
},
{
"amount": 150
},
{
"amount": 320
},
{
"amount": 150
},
{
"amount": 310
},
{
"amount": 0
},
{
"amount": 10
},
{
"amount": 510
},
{
"amount": 10
},
{
"amount": 10
},
{
"amount": 10
},
{
"amount": 300
},
{
"amount": 150
},
{
"amount": 300
}
]
},
{
"name": "Cyclelab Limited",
"children": [
{
"amount": 3000
},
{
"amount": 490
}
]
},
{
"name": "Mamachari Dalston",
"children": [
{
"amount": 12496
}
]
}
]
},
{
"name": "Entertainment",
"children": [
{
"name": "The Taproom",
"children": [
{
"amount": 795
}
]
},
{
"name": "Eighty Four",
"children": [
{
"amount": 270
}
]
},
{
"name": "Biddle Bros",
"children": [
{
"amount": 1350
},
{
"amount": 1350
}
]
},
{
"name": "Jaguar Shoes",
"children": [
{
"amount": 1060
},
{
"amount": 1060
}
]
},
{
"name": "The Spurstowe Arms",
"children": [
{
"amount": 940
},
{
"amount": 1020
},
{
"amount": 805
}
]
},
{
"name": "London Fields Lido",
"children": [
{
"amount": 50
}
]
},
{
"name": "Marvelappcom",
"children": [
{
"amount": 0
}
]
},
{
"name": "Barbican Centre",
"children": [
{
"amount": 5300
},
{
"amount": 5300
}
]
},
{
"name": "Slack",
"children": [
{
"amount": 0
}
]
},
{
"name": "Artwords Bookshop",
"children": [
{
"amount": 995
},
{
"amount": 899
},
{
"amount": 1249
}
]
},
{
"name": "Rio Cinema",
"children": [
{
"amount": 1150
}
]
},
{
"name": "Vue Cinemas",
"children": [
{
"amount": 5172
}
]
},
{
"name": "Foyles",
"children": [
{
"amount": 1599
},
{
"amount": 850
}
]
},
{
"name": "The Ready",
"children": [
{
"amount": 400
}
]
},
{
"name": "The Shacklewell Arms",
"children": [
{
"amount": 1100
}
]
},
{
"name": "Audible",
"children": [
{
"amount": 1383
},
{
"amount": 1383
}
]
},
{
"name": "Songkick",
"children": [
{
"amount": 5895
}
]
},
{
"name": "Magma",
"children": [
{
"amount": 1495
}
]
},
{
"name": "Founder's Arms",
"children": [
{
"amount": 565
},
{
"amount": 1145
}
]
},
{
"name": "Arcola Theatre",
"children": [
{
"amount": 1440
}
]
},
{
"name": "Odeon",
"children": [
{
"amount": 5550
}
]
},
{
"name": "NT's (Night Tales)",
"children": [
{
"amount": 2200
}
]
}
]
},
{
"name": "Cash",
"children": [
{
"name": "ATM",
"children": [
{
"amount": 2000
},
{
"amount": 2000
},
{
"amount": 0
},
{
"amount": 2000
},
{
"amount": 2071
},
{
"amount": 0
},
{
"amount": 11487
},
{
"amount": 2000
},
{
"amount": 3000
},
{
"amount": 1000
},
{
"amount": 8076
},
{
"amount": 2000
},
{
"amount": 0
},
{
"amount": 10486
},
{
"amount": 9320
},
{
"amount": 11711
},
{
"amount": 11711
},
{
"amount": 11699
},
{
"amount": 5847
},
{
"amount": 0
},
{
"amount": 2000
},
{
"amount": 4000
},
{
"amount": 5000
},
{
"amount": 2000
}
]
}
]
},
{
"name": "General",
"children": [
{
"name": "Indiegogo",
"children": [
{
"amount": 688
}
]
},
{
"name": "Upay",
"children": [
{
"amount": 525
},
{
"amount": 525
},
{
"amount": 365
}
]
},
{
"name": "The Doorstep",
"children": [
{
"amount": 2310
}
]
},
{
"name": "Monocle Café",
"children": [
{
"amount": 250
}
]
}
]
},
{
"name": "Expenses",
"children": [
{
"name": "Marvelappcom",
"children": [
{
"amount": 1204
},
{
"amount": 1208
},
{
"amount": 1249
},
{
"amount": 1199
},
{
"amount": 1200
},
{
"amount": 1210
},
{
"amount": 1223
}
]
},
{
"name": "The Doorstep",
"children": [
{
"amount": 2279
},
{
"amount": 2279
},
{
"amount": 2279
}
]
}
]
},
{
"name": "Shopping",
"children": [
{
"name": "Topman",
"children": [
{
"amount": 2250
}
]
},
{
"name": "Article",
"children": [
{
"amount": 11120
}
]
},
{
"name": "Cowling & Wilcox",
"children": [
{
"amount": 910
}
]
},
{
"name": "Pitfield London",
"children": [
{
"amount": 6000
}
]
},
{
"name": "Goodhood Store",
"children": [
{
"amount": 5300
}
]
},
{
"name": "IKEA",
"children": [
{
"amount": 7975
}
]
},
{
"name": "Muji",
"children": [
{
"amount": 995
}
]
},
{
"name": "Amazon",
"children": [
{
"amount": 0
},
{
"amount": 6999
},
{
"amount": 4516
}
]
},
{
"name": "The Co-op",
"children": [
{
"amount": 554
},
{
"amount": 259
},
{
"amount": 188
}
]
}
]
}
]
}
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<title>Monzo Emoji Bubble Chart</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
html,
body {
margin: 0;
padding: 0
}
</style>
</head>
<body>
<script>
var diameter = Math.min(window.innerHeight,window.innerWidth),
margin = 20,
format = d3.format(",d"),
color = color = d3.scaleLinear()
.domain([-1, 5])
.range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
.interpolate(d3.interpolateHcl);
var bubble = d3.pack()
.size([diameter, diameter])
.padding(5);
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
.attr("class", "bubble");
d3.json("data.json", function(error, data) {
if(error) throw error;
var root = d3.hierarchy(data)
.sum(function(d){ return d.amount; })
.sort(function(a, b) { return b.value - a.value; });
var focus = root,
nodes = bubble(root).descendants(),
view;
var g = svg.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node");
// .attr("transform", function(d) { return 'translate(' + d.x + "," + d.y + ")"; });
var circle = g.selectAll("circle")
.data(nodes)
.enter()
.append('circle')
.style("fill", function(d) { return d.depth > 0 ? color(d.depth) : 'white'; })
.style("opacity", function(d) { return '.5'; })
.on("click", function(d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });
var text = g.selectAll("text")
.data(nodes)
.enter()
.append("text")
.filter(function(d){ return (d.data.children || d.data.amount) })
.style("text-anchor", "middle")
.style("font-size", '.8em' )
.style("display", function(d) { return d.parent === root ? "inline" : "none"; })
.text(function(d){ return d.data.name ? d.data.name : d.data.amount/100; });
var node = d3.selectAll('circle, text');
zoomTo([root.x, root.y, root.r * 2 + margin]);
function zoom(d) {
var focus0 = focus; focus = d;
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function(d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function(t) { zoomTo(i(t)); };
});
transition.selectAll("text")
// .on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
.on("end", function(d) { this.style.display = (d.parent !== focus && !(d == focus && d.data.amount)) ? "none" : "inline"; });
}
function zoomTo(v) {
var k = diameter / v[2]; view = v;
node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
circle.attr("r", function(d) { return d.r * k; });
}
});
d3.select(self.frameElement).style("height", diameter + "px");
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment