summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2014-06-12 17:07:34 +0200
committerPeter Wu <peter@lekensteyn.nl>2014-06-12 17:07:34 +0200
commitb1262d77a3dfd57701b45a0f215314b5b3ebdd5a (patch)
tree59db9f64db0cc75d82fb4e8c33f7f65128486208
parent0180fc32695249d471259ed67a6b0245b082b324 (diff)
downloadd3viz-b1262d77a3dfd57701b45a0f215314b5b3ebdd5a.tar.gz
Add visualizer for hashtags vs users brands prefs
./run-psql csv "WITH q AS ($(cat queries/userbrand.sql)) SELECT brand, lhashtag as hashtag, sum, total, percentage FROM q" > drugs.csv
-rw-r--r--addicted-brands.html77
-rw-r--r--js/addicted-brands.js130
2 files changed, 207 insertions, 0 deletions
diff --git a/addicted-brands.html b/addicted-brands.html
new file mode 100644
index 0000000..d8f9d9c
--- /dev/null
+++ b/addicted-brands.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Addicted Brands</title>
+</head>
+<style>
+html, body {
+ padding: 0;
+ margin: 0;
+}
+#legenda { display: none; }
+/*
+#legenda {
+ position: fixed;
+}
+#legenda span {
+ color: white;
+ width: 100px;
+ display: block;
+}
+#charts {
+ margin-left: 100px;
+}
+*/
+/* graphs and bars */
+.graph {
+ position: relative;
+ height: 200px;
+ background: #eee;
+ padding: 5px;
+ margin: 3px;
+ width: 300px;
+ display: inline-block;
+ border-radius: 16px;
+}
+.graph .hashtag:before {
+ content: '#';
+}
+.graph .hashtag {
+ font-weight: bold;
+ text-shadow: #ccc -1px -1px;
+}
+.graph .bar {
+ text-align: center;
+ background: red;
+ position: absolute;
+ width: 40px;
+ bottom: 1em;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
+}
+.graph .perc {
+ position: absolute;
+ top: -1em;
+}
+.graph .details {
+ color: white;
+ text-shadow: #999 0 0 9px;
+}
+.graph .brand {
+ position: absolute;
+ bottom: -1em;
+ font-size: smaller;
+ left: 0;
+}
+</style>
+<link rel="stylesheet" href="svg.css">
+<body>
+
+<div id="legenda"></div>
+<div id="charts"></div>
+
+<script src="lib/d3.js"></script>
+<script src="js/addicted-brands.js"></script>
+</body>
+</html>
diff --git a/js/addicted-brands.js b/js/addicted-brands.js
new file mode 100644
index 0000000..69f2412
--- /dev/null
+++ b/js/addicted-brands.js
@@ -0,0 +1,130 @@
+/* jshint devel:true, browser:true */
+/* global d3 */
+'use strict';
+
+// scaling factor to enlarge bars to fit the canvas.
+var FACTOR = 9;
+
+function main() {
+ d3.csv('drugs.csv')
+ .row(function (d) {
+ return {
+ hashtag: d.hashtag,
+ brand: d.brand,
+ sum: +d.sum,
+ total: +d.total,
+ percentage: FACTOR * d.sum / d.total
+ //percentage: d.percentage / 100
+ };
+ })
+ .get(function (errors, rows) {
+ if (errors) {
+ console.log('Failed to load CSV:', errors);
+ return;
+ }
+ processData(rows);
+ });
+}
+
+function processData(rows) {
+ // map from hashtags to a map from brands to percentages
+ // { hashtag: String, brands: [ { brand: String, percent: int }... ] }
+ var data = {};
+ // values of the data map
+ var dataArr = [];
+ var brands = ['samsung', 'apple', 'htc', 'sony', 'lg', 'huawei'];
+ var colors = ["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c"];
+
+ rows.forEach(function (d) {
+ if (!(d.hashtag in data)) {
+ data[d.hashtag] = {
+ hashtag: d.hashtag,
+ brands: []
+ };
+ dataArr.push(data[d.hashtag]);
+ }
+ data[d.hashtag].brands.push({
+ brand: d.brand,
+ percentage: d.percentage,
+ // extra details
+ sum: d.sum
+ });
+
+ // Find all available brands, add brand as key with dummy value
+ if (brands.indexOf(d.brand) < 0) {
+ console.log('AAAAAH! Unknown brand, adding:', d.brand);
+ brands.push(d.brand);
+ }
+ });
+ console.log(data);
+
+ d3.select('#legenda')
+ .selectAll('span')
+ .data(brands)
+ .enter()
+ .append('span')
+ .style('background', function (d, i) {
+ return colors[i];
+ })
+ .text(function (brand) {
+ return brand;
+ });
+
+ var graph = d3.select('#charts').selectAll('.graph')
+ .data(dataArr, keyFnHashtag)
+ .enter()
+ .append('div')
+ .attr('class', 'graph');
+
+ graph.append('span')
+ .attr('class', 'hashtag')
+ .text(function (d) {
+ return d.hashtag;
+ });
+
+ // bars!
+ var bar = graph.selectAll('.bar')
+ .data(function (d) {
+ return d.brands;
+ })
+ .enter()
+ // bar itself
+ .append('div')
+ .attr('class', 'bar')
+ .text(function (d) {
+ return d.percent;
+ })
+ .style('background', function (d) {
+ return colors[brands.indexOf(d.brand)];
+ })
+ .style('left', function (d) {
+ return (10 + 50 * brands.indexOf(d.brand)) + 'px';
+ })
+ .style('height', function (d) {
+ return (150 * d.percentage) + 'px';
+ });
+ // text with percentage
+ bar.append('span')
+ .attr('class', 'perc')
+ .text(function (d) {
+ return (100 * d.percentage / FACTOR).toFixed(2);
+ });
+ // number of users mentioning this tag with this brand preference
+ bar.append('span')
+ .attr('class', 'details')
+ .text(function (d) {
+ return d.sum;
+ });
+ // brand name
+ bar.append('span')
+ .attr('class', 'brand')
+ .text(function (d) {
+ return d.brand;
+ });
+}
+
+function keyFnHashtag(d) {
+ return d.hashtag;
+}
+
+main();