forked from dc-js/dc.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
complex-reduce.html
170 lines (149 loc) · 5.52 KB
/
complex-reduce.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js - Complex Reductions Example</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="../css/dc.css"/>
<style>
label {
display: inline;
padding-left: 1em;
}
label input {
vertical-align: top;
}
</style>
</head>
<body>
<div class="container">
<script type="text/javascript" src="header.js"></script>
<p>Frequently asked question: how to show the minimum/maximum of some value in the rows?</p>
<p>Some kinds of reductions require the entire set of values at every step: median, mode, minimum,
maximum. (The mean just needs a running sum.)</p>
<p>This example shows how to keep the set of values.</p>
<div id="select-operation">
<label><input type=radio name="operation" value="median" checked="true"> median</label>
<label><input type=radio name="operation" value="mode"> mode</label>
<label><input type=radio name="operation" value="min"> min</label>
<label><input type=radio name="operation" value="max"> max</label>
</div>
<div id="test-run">
<h4>Run</h4>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:runChart.filterAll().redrawGroup();">reset</a>
</div>
</div>
<div id="test-expt">
<h4>Experiment</h4>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:exptChart.filterAll().redrawGroup();">reset</a>
</div>
</div>
<script type="text/javascript" src="../js/d3.js"></script>
<script type="text/javascript" src="../js/crossfilter.js"></script>
<script type="text/javascript" src="../js/dc.js"></script>
<script type="text/javascript">
// reduction functions that keep a sorted array of rows. the rows must either have a unique key,
// or it must not matter if the same row is removed as was added previously
// instead of calculating the desired metric on every change, which is slow, we'll just maintain
// these arrays of rows and calculate the metrics when needed in the accessor
function groupArrayAdd(keyfn) {
var bisect = d3.bisector(keyfn);
return function(elements, item) {
var pos = bisect.right(elements, keyfn(item));
elements.splice(pos, 0, item);
return elements;
};
}
function groupArrayRemove(keyfn) {
var bisect = d3.bisector(keyfn);
return function(elements, item) {
var pos = bisect.left(elements, keyfn(item));
if(keyfn(elements[pos])===keyfn(item))
elements.splice(pos, 1);
return elements;
};
}
function groupArrayInit() {
return [];
}
// adapted from Code Review: Finding the mode of an array
// http://codereview.stackexchange.com/a/68431/108362
var mode = function mode(arr, acc) {
return arr.reduce(function(current, item) {
item = acc(item);
var val = current.numMapping[item] = (current.numMapping[item] || 0) + 1;
if (val > current.greatestFreq) {
current.greatestFreq = val;
current.mode = item;
}
return current;
}, {mode: null, greatestFreq: -Infinity, numMapping: {}}, arr).mode;
};
var runChart = dc.barChart("#test-run"), exptChart = dc.barChart("#test-expt");
d3.csv("morley.csv", function(error, experiments) {
experiments.forEach(function(x) {
x.Speed = +x.Speed;
});
var ndx = crossfilter(experiments),
runKey = function(d) {return +d.Run;},
exptKey = function(d) {return +d.Expt;},
speedValue = function(d) {return d.Speed;},
runDimension = ndx.dimension(runKey),
exptDimension = ndx.dimension(exptKey),
runAvgGroup = runDimension.group().reduce(groupArrayAdd(exptKey), groupArrayRemove(exptKey), groupArrayInit),
exptAvgGroup = exptDimension.group().reduce(groupArrayAdd(runKey), groupArrayRemove(runKey), groupArrayInit);
function medianSpeed(kv) {
return d3.median(kv.value, speedValue);
}
function modeSpeed(kv) {
return mode(kv.value, speedValue);
}
function minSpeed(kv) {
return d3.min(kv.value, speedValue);
}
function maxSpeed(kv) {
return d3.max(kv.value, speedValue);
}
var accessors = {
median: medianSpeed,
mode: modeSpeed,
min: minSpeed,
max: maxSpeed
};
d3.selectAll('#select-operation input')
.on('click', function() {
runChart.valueAccessor(accessors[this.value]);
exptChart.valueAccessor(accessors[this.value]);
dc.redrawAll();
});
runChart
.width(400)
.height(300)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.valueAccessor(medianSpeed)
.elasticY(true)
.brushOn(true)
.controlsUseVisibility(true)
.dimension(runDimension)
.group(runAvgGroup);
exptChart
.width(400)
.height(300)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.valueAccessor(medianSpeed)
.elasticY(true)
.brushOn(true)
.controlsUseVisibility(true)
.dimension(exptDimension)
.group(exptAvgGroup);
runChart.margins().left = exptChart.margins().left = 35;
dc.renderAll();
});
</script>
</div>
</body>
</html>