I was curious about which d3 apis people use in their blocks.
(I would love to set the table headers not to scroll but have forgotten how to do that)
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0; }
table { table-layout: fixed; margin-top: 5em; font-family: sans-serif; font-size: 8px; border-collapse: separate; border-spacing: 0px; }
thead th { width: 1ex; text-align: left; transform: rotate(-90deg) translateX(10px); }
thead th:first-child { width: 8em; transform: none; }
tr:hover { outline: 1px solid white; }
tbody th { background-color: #eee; }
td { text-align: right; margin: 0; padding: 0; }
</style>
</head>
<body>
<script>
var dom = d3.select("body").append("table")
.attr("width", "100%");
d3.json('blocks-api.json', (error, blocks) => {
if (error) throw error;
var records = flattenApi(blocks, 'userId');
var aggregateData = aggregate(records);
var rowNamesAndCounts = uniqueColumn(records, 0);
var columnNamesAndCounts = uniqueColumn(records, 1);
var columnNames = columnNamesAndCounts.names.filter((column) => columnNamesAndCounts.counts[column] > 20);
console.log(columnNamesAndCounts.counts['descending']);
// Output table headers
var header = dom.append('thead')
.append('tr');
header.append('th');
columnNames.forEach(function(columnName) {
header.append('th').text(columnName);
});
// Output table rows
var body = dom.append('tbody');
rowNamesAndCounts.names.forEach(function(rowName) {
var row = body.append('tr');
row.append('th').text(rowName);
var columnValues = columnNames.map((columnName) => aggregateData[[rowName, columnName]] || 0);
var scale = 240 / Math.sqrt(d3.max(columnValues)+1);
columnValues.forEach((count) => {
row.append('td')
.text(count || "")
.style('background-color', d3.hsl((Math.sqrt(count)*scale)|0, 0.5, 0.8));
});
})
});
// Given records {key: value, {'api': {api: count, ...}}}
// output a flat array [key, api] (*ignoring the count*)
const renameApi = {'hsl': 'color*', 'rgb': 'color*', 'lab': 'color*', 'hcl': 'color*',
'xhr': 'xhr*', 'json': 'xhr*', 'tsv': 'xhr*', 'xml': 'xhr*', 'csv': 'xhr*', 'loadData': 'xhr*', 'dsv': 'xhr*',
'min': 'extent*', 'max': 'extent*', 'extent': 'extent*',
'select': 'select*', 'selectAll': 'select*',
'ascending': '{a,de}cending*', 'descending': '{a,de}cending*'
};
function flattenApi(blocks, key) {
var results = [];
blocks.forEach(function(block) {
Object.keys(block.api).forEach(function(api) {
api = api.split('.')[1];
api = api.replace(/interpolate.*/, 'interpolate*');
api = api.replace(/scale.*/, 'scale*');
api = api.replace(/axis.*/, 'axis*');
api = renameApi[api] || api;
results.push([block[key], api]);
})
});
return results;
}
// Given a table [[v0, v1, v2, ...], ...] and an index i,
// return the set of unique v_i values, most frequent first
function uniqueColumn(table, column) {
var seen = {};
var results = [];
table.forEach(function(row) {
var key = row[column];
if (!seen[key]) {
results.push(key);
seen[key] = 0;
}
seen[key]++;
});
results.sort((a, b) => seen[b] - seen[a]);
return {names: results, counts: seen};
}
// Given a table [[a, b], ...] return a map
// {[a, b].toString(): count}
function aggregate(table) {
var results = {};
table.forEach(function(row) {
results[row] = (results[row] || 0) + 1;
});
return results;
}
</script>
</body>