Skip to content

Instantly share code, notes, and snippets.

@badosa
Last active November 21, 2019 05:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save badosa/6184099 to your computer and use it in GitHub Desktop.
Save badosa/6184099 to your computer and use it in GitHub Desktop.
CPI, a Hierarchical Dimension

The JSON-stat format allows providers to declare hierarchical relationships between categories in a dimension using the child property.

This visualization uses hierarchy.json, a sample JSON-stat file that contains the Australian Bureau of Statistics’ Consumer Price Index (CPI) Commodity Classification.

It shows how to build a tree structure with the JSON-stat Javascript Toolkit using a recursive function (the tree function). The new structure is then displayed as a collapsible tree in D3 using the same code in Mike Bostock’s block #4339083.

Click on the nodes to expand or collapse.

{
"hierarchy" : {
"label" : "Demo of hierarchical dimension",
"source" : "16th Series CPI Commodity Classification (http://www.abs.gov.au/AUSSTATS/abs@.nsf/DetailsPage/6401.0.55.0042011)",
"updated" : "2011-07-01",
"value" : { "0" : null },
"dimension" : {
"id" : ["commodity"],
"size" : [132],
"commodity" : {
"label" : "CPI Commodity",
"category" : {
"index" : [ "T", "1", "1.1", "1.1.1", "1.1.2", "1.1.3", "1.1.4", "1.2", "1.2.1", "1.2.2", "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.3", "1.3.1", "1.3.2", "1.3.3", "1.4", "1.4.1", "1.4.2", "1.5", "1.5.1", "1.5.2", "1.5.3", "1.5.4", "1.5.5", "1.5.6", "1.6", "1.6.1", "1.6.2", "1.7", "1.7.1", "1.7.2", "2", "2.1", "2.1.1", "2.1.2", "2.1.3", "2.2", "2.2.1", "3", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.2", "3.2.1", "3.2.2", "3.2.3", "3.3", "3.3.1", "3.3.2", "4", "4.1", "4.1.1", "4.2", "4.2.1", "4.3", "4.3.1", "4.3.2", "4.4", "4.4.1", "4.4.2", "4.4.3", "5", "5.1", "5.1.1", "5.1.2", "5.2", "5.2.1", "5.3", "5.3.1", "5.3.2", "5.3.3", "5.3.4", "5.4", "5.4.1", "5.4.2", "5.4.3", "5.5", "5.5.1", "5.5.2", "5.5.3", "6", "6.1", "6.1.1", "6.1.2", "6.2", "6.2.1", "6.2.2", "7", "7.1", "7.1.1", "7.1.2", "7.1.3", "7.1.4", "7.1.5", "7.2", "7.2.1", "8", "8.1", "8.1.1", "8.1.2", "9", "9.1", "9.1.1", "9.1.2", "9.2", "9.2.1", "9.2.2", "9.3", "9.3.1", "9.3.2", "9.4", "9.4.1", "9.4.2", "9.4.3", "9.4.4", "9.4.5", "9.4.6", "10", "10.1", "10.1.1", "10.1.2", "10.1.3", "11", "11.1", "11.1.1", "11.2", "11.2.1", "11.2.2" ],
"label" : {
"T" : "Total",
"1" : "Food and non-alcoholic beverages",
"1.1" : "Bread and cereal products",
"1.1.1" : "Bread",
"1.1.2" : "Cakes and biscuits",
"1.1.3" : "Breakfast cereals",
"1.1.4" : "Other cereal products",
"1.2" : "Meat and seafoods",
"1.2.1" : "Beef and veal",
"1.2.2" : "Pork",
"1.2.3" : "Lamb and goat",
"1.2.4" : "Poultry",
"1.2.5" : "Other meats",
"1.2.6" : "Fish and other seafood",
"1.3" : "Dairy and related products",
"1.3.1" : "Milk",
"1.3.2" : "Cheese",
"1.3.3" : "Ice cream and other dairy products",
"1.4" : "Fruit and vegetables",
"1.4.1" : "Fruit",
"1.4.2" : "Vegetables",
"1.5" : "Food products n.e.c.",
"1.5.1" : "Eggs",
"1.5.2" : "Jams, honey and spreads",
"1.5.3" : "Food additives and condiments",
"1.5.4" : "Oils and fats",
"1.5.5" : "Snacks and confectionery",
"1.5.6" : "Other food products n.e.c.",
"1.6" : "Non-alcoholic beverages",
"1.6.1" : "Coffee, tea and cocoa",
"1.6.2" : "Waters, soft drinks and juices",
"1.7" : "Meals out and take away foods",
"1.7.1" : "Restaurant meals",
"1.7.2" : "Take away and fast foods",
"2" : "Alcohol and tobacco",
"2.1" : "Alcoholic beverages",
"2.1.1" : "Spirits",
"2.1.2" : "Wine",
"2.1.3" : "Beer",
"2.2" : "Tobacco",
"2.2.1" : "Tobacco",
"3" : "Clothing and footwear",
"3.1" : "Garments",
"3.1.1" : "Garments for men",
"3.1.2" : "Garments for women",
"3.1.3" : "Garments for infants and children",
"3.2" : "Footwear",
"3.2.1" : "Footwear for men",
"3.2.2" : "Footwear for women",
"3.2.3" : "Footwear for infants and children",
"3.3" : "Accessories and clothing services",
"3.3.1" : "Accessories",
"3.3.2" : "Cleaning, repair and hire of clothing and footwear",
"4" : "Housing",
"4.1" : "Rents",
"4.1.1" : "Rents",
"4.2" : "New dwelling purchase by owner-occupiers",
"4.2.1" : "New dwelling purchase by owner-occupiers",
"4.3" : "Other housing",
"4.3.1" : "Maintenance and repair of the dwelling",
"4.3.2" : "Property rates and charges",
"4.4" : "Utilities",
"4.4.1" : "Water and sewerage",
"4.4.2" : "Electricity",
"4.4.3" : "Gas and other household fuels",
"5" : "Furnishings, household equipment and services",
"5.1" : "Furniture and furnishings",
"5.1.1" : "Furniture",
"5.1.2" : "Carpets and other floor coverings",
"5.2" : "Household textiles",
"5.2.1" : "Household textiles",
"5.3" : "Household appliances, utensils and tools",
"5.3.1" : "Major household appliances",
"5.3.2" : "Small electric household appliances ",
"5.3.3" : "Glassware, tableware and household utensils",
"5.3.4" : "Tools and equipment for house and garden",
"5.4" : "Non-durable household products",
"5.4.1" : "Cleaning and maintenance products",
"5.4.2" : "Personal care products",
"5.4.3" : "Other non-durable household products",
"5.5" : "Domestic and household services",
"5.5.1" : "Child care",
"5.5.2" : "Hairdressing and personal grooming services",
"5.5.3" : "Other household services",
"6" : "Health",
"6.1" : "Medical products, appliances and equipment",
"6.1.1" : "Pharmaceutical products",
"6.1.2" : "Therapeutic appliances and equipment",
"6.2" : "Medical, dental and hospital services",
"6.2.1" : "Medical and hospital services",
"6.2.2" : "Dental services",
"7" : "Transport",
"7.1" : "Private motoring",
"7.1.1" : "Motor vehicles",
"7.1.2" : "Spare parts and accessories for motor vehicles",
"7.1.3" : "Automotive fuel",
"7.1.4" : "Maintenance and repair of motor vehicles",
"7.1.5" : "Other services in respect of motor vehicles",
"7.2" : "Urban transport fares",
"7.2.1" : "Urban transport fares",
"8" : "Communication",
"8.1" : "Communication",
"8.1.1" : "Postal services",
"8.1.2" : "Telecommunication equipment and services",
"9" : "Recreation and culture",
"9.1" : "Audio, visual and computing equipment and services",
"9.1.1" : "Audio, visual and computing equipment",
"9.1.2" : "Audio, visual and computing media and services",
"9.2" : "Newspapers, books and stationery",
"9.2.1" : "Books",
"9.2.2" : "Newspapers, magazines and stationery",
"9.3" : "Holiday travel and accommodation",
"9.3.1" : "Domestic holiday travel and accommodation",
"9.3.2" : "International holiday travel and accommodation",
"9.4" : "Other recreation, sport and culture",
"9.4.1" : "Equipment for sports, camping and open-air recreation",
"9.4.2" : "Games, toys and hobbies",
"9.4.3" : "Pets and related products",
"9.4.4" : "Veterinary and other services for pets",
"9.4.5" : "Sports participation",
"9.4.6" : "Other recreational, sporting and cultural services",
"10" : "Education",
"10.1" : "Education",
"10.1.1" : "Preschool and primary education",
"10.1.2" : "Secondary education",
"10.1.3" : "Tertiary education",
"11" : "Insurance and financial services",
"11.1" : "Insurance",
"11.1.1" : "Insurance",
"11.2" : "Financial services",
"11.2.1" : "Deposit and loan facilities (direct charges)",
"11.2.2" : "Other financial services"
},
"child" : {
"T" : [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" ],
"1" : [ "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7" ],
"2" : [ "2.1", "2.2" ],
"3" : [ "3.1", "3.2", "3.3" ],
"4" : [ "4.1", "4.2", "4.3", "4.4" ],
"5" : [ "5.1", "5.2", "5.3", "5.4", "5.5" ],
"6" : [ "6.1", "6.2" ],
"7" : [ "7.1", "7.2" ],
"8" : [ "8.1" ],
"9" : [ "9.1", "9.2", "9.3", "9.4" ],
"10" : [ "10.1" ],
"11" : [ "11.1", "11.2" ],
"1.1" : [ "1.1.1", "1.1.2", "1.1.3", "1.1.4" ],
"1.2" : [ "1.2.1", "1.2.2", "1.2.3", "1.2.4", "1.2.5", "1.2.6" ],
"1.3" : [ "1.3.1", "1.3.2", "1.3.3" ],
"1.4" : [ "1.4.1", "1.4.2" ],
"1.5" : [ "1.5.1", "1.5.2", "1.5.3", "1.5.4", "1.5.5", "1.5.6"],
"1.6" : [ "1.6.1", "1.6.2" ],
"1.7" : [ "1.7.1", "1.7.2" ],
"2.1" : [ "2.1.1", "2.1.2", "2.1.3" ],
"2.2" : [ "2.2.1" ],
"3.1" : [ "3.1.1", "3.1.2", "3.1.3" ],
"3.2" : [ "3.2.1", "3.2.2", "3.2.3" ],
"3.3" : [ "3.3.1", "3.3.2" ],
"4.1" : [ "4.1.1" ],
"4.2": [ "4.2.1" ],
"4.3" : [ "4.3.1", "4.3.2" ],
"4.4" : [ "4.4.1", "4.4.2", "4.4.3" ],
"5.1" : [ "5.1.1", "5.1.2" ],
"5.2" : [ "5.2.1" ],
"5.3" : [ "5.3.1", "5.3.2", "5.3.3", "5.3.4" ],
"5.4" : [ "5.4.1", "5.4.2", "5.4.3" ],
"5.5" : [ "5.5.1", "5.5.2", "5.5.3" ],
"6.1" : [ "6.1.1", "6.1.2" ],
"6.2" : [ "6.2.1", "6.2.2" ],
"7.1" : [ "7.1.1", "7.1.2", "7.1.3", "7.1.4", "7.1.5" ],
"7.2" : [ "7.2.1" ],
"8.1" : [ "8.1.1", "8.1.2" ],
"9.1" : [ "9.1.1", "9.1.2" ],
"9.2" : [ "9.2.1", "9.2.2" ],
"9.3" : [ "9.3.1", "9.3.2" ],
"9.4" : [ "9.4.1", "9.4.2", "9.4.3", "9.4.4", "9.4.5", "9.4.6" ],
"10.1" : [ "10.1.1", "10.1.2", "10.1.3" ],
"11.1" : [ "11.1.1" ],
"11.2" : [ "11.2.1", "11.2.2" ]
}
}
}
}
}
}
<!DOCTYPE html>
<html>
<head>
<title>CPI, A Hierarchical Dimension</title>
<link href="/d/6184099/style.css" type="text/css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/jsonstat@0.13.13"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
////////////////////////////////////////////////////////////
// JSON-stat
// The treef function builds a tree from hierchical dimension
////////////////////////////////////////////////////////////
function treef( dim ) {
var
haveparent=[],
root=[],
getChild=function( dim, array ){
if( array!==null ){
array.forEach(function( e, i ){
var
children=dim.Category( e ).id,
label=dim.Category( e ).label
;
array[i]=( children===null ) ? { name : label } : { name : label, children : getChild( dim, children ) };
});
return array;
}else{
return null;
}
}
;
//haveparent
dim.Category().forEach(function(e,i){
if( e.length ){
e.id.forEach(function( c ){
haveparent.push( c );
})
}
});
//first level (root)
dim.Category().forEach(function( e, i ){
if( haveparent.indexOf( dim.id[i] )==-1 ){
root.push( dim.id[i] );
}
});
return getChild( dim, root );
}
JSONstat("/d/6184099/hierarchy.json", function(){
var
hie=treef( this.Dataset( 0 ).Dimension( 0 ) ), // the tree, built from Dimension( 0 )
////////////////////////////////////////////////////////////
// D3
// Use a tree layout to visualize the tree
// Code by Mike Bostock: http://bl.ocks.org/4339083
////////////////////////////////////////////////////////////
margin={top: 20, right: 120, bottom: 20, left: 120},
width=960 - margin.right - margin.left,
height=800 - margin.top - margin.bottom,
i=0,
duration=750,
root,
tree=d3.layout.tree()
.size( [height, width] ),
diagonal=d3.svg.diagonal()
.projection(function( d ){ return [d.y, d.x]; }),
svg=d3.select("body").append("svg")
.attr( "width", width + margin.right + margin.left )
.attr( "height", height + margin.top + margin.bottom )
.append( "g" )
.attr( "transform", "translate(" + margin.left + "," + margin.top + ")" )
;
hie.forEach(function( flare ){ // In the original Bostock code the data came from /d/4063550/flare.json
root=flare;
root.x0=height / 2;
root.y0=0;
function collapse( d ){
if( d.children ){
d._children=d.children;
d._children.forEach( collapse );
d.children=null;
}
}
root.children.forEach( collapse );
update( root );
});
d3.select( self.frameElement ).style( "height", "800px" );
function update( source ){
// Compute the new tree layout.
var
nodes=tree.nodes(root).reverse(),
links=tree.links(nodes)
;
// Normalize for fixed-depth.
nodes.forEach(function( d ) { d.y=d.depth * 180; });
// Update the nodes…
var node=svg.selectAll( "g.node" )
.data( nodes, function( d ){ return d.id || (d.id=++i); })
;
// Enter any new nodes at the parent's previous position.
var nodeEnter=node.enter().append( "g" )
.attr( "class", "node" )
.attr( "transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; } )
.on( "click", click )
;
nodeEnter.append( "circle" )
.attr( "r", 1e-6 )
.style( "fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; } )
;
nodeEnter.append( "text" )
.attr( "x", function(d) { return d.children || d._children ? -10 : 10; } )
.attr( "dy", ".35em" )
.attr( "text-anchor", function(d) { return d.children || d._children ? "end" : "start"; } )
.text( function( d ){ return d.name; })
.style( "fill-opacity", 1e-6 )
;
// Transition nodes to their new position.
var nodeUpdate=node.transition()
.duration( duration )
.attr( "transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; } )
;
nodeUpdate.select( "circle" )
.attr( "r", 4.5 )
.style( "fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; } )
;
nodeUpdate.select( "text" )
.style( "fill-opacity", 1 )
;
// Transition exiting nodes to the parent's new position.
var nodeExit=node.exit().transition()
.duration( duration )
.attr( "transfo rm", function(d) { return "translate(" + source.y + "," + source.x + ")"; } )
.remove()
;
nodeExit.select( "circle" ).attr( "r", 1e-6 );
nodeExit.select( "text" ).style( "fill-opacity", 1e-6 );
// Update the links…
var link=svg.selectAll( "path.link" )
.data( links, function( d ){ return d.target.id; } )
;
// Enter any new links at the parent's previous position.
link.enter().insert( "path", "g" )
.attr( "class", "link" )
.attr( "d", function( d ){
var o={x: source.x0, y: source.y0};
return diagonal( {source: o, target: o} );
})
;
// Transition links to their new position.
link.transition()
.duration( duration )
.attr( "d", diagonal )
;
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration( duration )
.attr( "d", function( d ){
var o={x: source.x, y: source.y};
return diagonal( {source: o, target: o} );
})
.remove()
;
// Stash the old positions for transition.
nodes.forEach(function( d ){
d.x0=d.x;
d.y0=d.y;
});
}
// Toggle children on click.
function click( d ){
if ( d.children ){
d._children=d.children;
d.children=null;
}else{
d.children=d._children;
d._children=null;
}
update(d);
}
});
</script>
</body>
</html>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment