Skip to content

Commit

Permalink
Implement color by group in dotchart
Browse files Browse the repository at this point in the history
  • Loading branch information
kbroman committed May 18, 2016
1 parent b591209 commit 844759a
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 41 deletions.
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
## d3panels 1.1.3 (2016-05-18)
## d3panels 1.1.4 (2016-05-18)

- Add functions `trichart()`, for plotting trinomial probabilities in an
equilateral triangle, and `histchart()`, for plotting histograms (as
curves).

- `dotchart` can take `group` vector in data, to color points by category.

- Handle missing values in `group` in `add_curves`, `add_points`, and
`curvechart`.

Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "d3panels",
"version": "1.1.3",
"version": "1.1.4",
"description": "D3-based graphics panels",
"homepage": "https://github.com/kbroman/d3panels",
"authors": [
Expand Down
60 changes: 51 additions & 9 deletions d3panels.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
!function() { // encapsulate d3panels functions
var d3panels = {
version: "1.1.3"
version: "1.1.4"
};
// Generated by CoffeeScript 1.10.0
d3panels.formatAxis = function(d, extra_digits) {
Expand Down Expand Up @@ -2356,7 +2356,7 @@ d3panels.dotchart = function(chartOpts) {
color: "#cdcdcd",
width: 5
};
pointcolor = (ref10 = chartOpts != null ? chartOpts.pointcolor : void 0) != null ? ref10 : "slateblue";
pointcolor = (ref10 = chartOpts != null ? chartOpts.pointcolor : void 0) != null ? ref10 : null;
pointstroke = (ref11 = chartOpts != null ? chartOpts.pointstroke : void 0) != null ? ref11 : "black";
pointsize = (ref12 = chartOpts != null ? chartOpts.pointsize : void 0) != null ? ref12 : 3;
jitter = (ref13 = chartOpts != null ? chartOpts.jitter : void 0) != null ? ref13 : "beeswarm";
Expand All @@ -2371,7 +2371,7 @@ d3panels.dotchart = function(chartOpts) {
indtip = null;
svg = null;
chart = function(selection, data) {
var collision, force, gravity, i, indID, j, jitter_width, k, l, len, myframe, nearbyPoints, p, pointGroup, q, ref17, ref18, ref19, results, scaledPoints, tick, u, x, xlim, xv, y;
var collision, force, g, gravity, group, i, indID, j, jitter_width, k, l, len, myframe, nearbyPoints, ngroup, p, pointGroup, q, ref17, ref18, ref19, ref20, results, scaledPoints, tick, u, x, xlim, xv, y;
if (data.x == null) {
d3panels.displayError("dotchart: data.x is missing");
}
Expand All @@ -2391,6 +2391,46 @@ d3panels.dotchart = function(chartOpts) {
if (indID.length !== x.length) {
d3panels.displayError("dotchart: length(indID) [" + indID.length + "] != length(x) [" + x.length + "]");
}
group = (ref19 = data != null ? data.group : void 0) != null ? ref19 : (function() {
var l, len, results1;
results1 = [];
for (l = 0, len = x.length; l < len; l++) {
i = x[l];
results1.push(1);
}
return results1;
})();
ngroup = d3.max(group);
group = (function() {
var l, len, results1;
results1 = [];
for (l = 0, len = group.length; l < len; l++) {
g = group[l];
results1.push(g != null ? g - 1 : g);
}
return results1;
})();
if (d3panels.sumArray((function() {
var l, len, results1;
results1 = [];
for (l = 0, len = group.length; l < len; l++) {
g = group[l];
results1.push(g < 0 || g > ngroup - 1);
}
return results1;
})()) > 0) {
d3panels.displayError("dotchart: group values out of range");
console.log("ngroup: " + ngroup);
console.log("distinct groups: " + (d3panels.unique(group)));
}
if (group.length !== x.length) {
d3panels.displayError("dotchart: group.length (" + group.length + ") != x.length (" + x.length + ")");
}
pointcolor = pointcolor != null ? pointcolor : d3panels.selectGroupColors(ngroup, "dark");
pointcolor = d3panels.expand2vector(pointcolor, ngroup);
if (pointcolor.length < ngroup) {
d3panels.displayError("add_points: pointcolor.length (" + pointcolor.length + ") < ngroup (" + ngroup + ")");
}
xcategories = xcategories != null ? xcategories : d3panels.unique(x);
xcatlabels = xcatlabels != null ? xcatlabels : xcategories;
if (xcatlabels.length !== xcategories.length) {
Expand All @@ -2411,7 +2451,7 @@ d3panels.dotchart = function(chartOpts) {
console.log("x:");
console.log(x);
for (i in x) {
if ((x[i] != null) && !(ref19 = x[i], indexOf.call(xcategories, ref19) >= 0)) {
if ((x[i] != null) && !(ref20 = x[i], indexOf.call(xcategories, ref20) >= 0)) {
x[i] = null;
}
}
Expand Down Expand Up @@ -2500,7 +2540,9 @@ d3panels.dotchart = function(chartOpts) {
pointGroup = svg.append("g").attr("id", "points");
points = pointGroup.selectAll("empty").data(scaledPoints).enter().append("circle").attr("class", function(d, i) {
return "pt" + i;
}).attr("r", pointsize).attr("fill", pointcolor).attr("stroke", pointstroke).attr("stroke-width", "1").attr("cx", function(d) {
}).attr("r", pointsize).attr("fill", function(d, i) {
return pointcolor[group[i]];
}).attr("stroke", pointstroke).attr("stroke-width", "1").attr("cx", function(d) {
return d.x;
}).attr("cy", function(d) {
return d.y;
Expand Down Expand Up @@ -2564,11 +2606,11 @@ d3panels.dotchart = function(chartOpts) {
}
};
collision = function(p, alpha) {
var d, dx, dy, len1, m, ref20, results1;
ref20 = nearbyPoints[p.index];
var d, dx, dy, len1, m, ref21, results1;
ref21 = nearbyPoints[p.index];
results1 = [];
for (m = 0, len1 = ref20.length; m < len1; m++) {
i = ref20[m];
for (m = 0, len1 = ref21.length; m < len1; m++) {
i = ref21[m];
q = scaledPoints[i];
dx = p.x - q.x;
dy = p.y - q.y;
Expand Down
8 changes: 4 additions & 4 deletions d3panels.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions doc/Source/dotchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The data is an associative array with a set of vectors, all of the same length:
- `x` &mdash; categories (as values 1, 2, 3, ...)
- `y` &mdash; responses
- `indID` &mdash; individual IDs (optional)
- `group` &mdash; category in 1,2,3,... (for determining point color, optional)

### Example

Expand Down
3 changes: 2 additions & 1 deletion doc/dotchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The data is an associative array with a set of vectors, all of the same length:
- `x` &mdash; categories (as values 1, 2, 3, ...)
- `y` &mdash; responses
- `indID` &mdash; individual IDs (optional)
- `group` &mdash; category in 1,2,3,... (for determining point color, optional)

### Example

Expand All @@ -31,7 +32,7 @@ mychart(d3.select('body'), data)
- `xlab` &mdash; x-axis title \[default `"Group"`\]
- `ylab` &mdash; y-axis title \[default `"Response"`\]
- `xlineOpts` &mdash; color and width of vertical lines \[default `{color:"#cdcdcd", width:5}`\]
- `pointcolor` &mdash; fill color of points \[default `"slateblue"`\]
- `pointcolor` &mdash; fill color of points \[default `null`\]
- `pointstroke` &mdash; color of points' outer circle \[default `"black"`\]
- `pointsize` &mdash; color of points \[default `3`\]
- `jitter` &mdash; method for jittering points (beeswarm|random|none) \[default `"beeswarm"`\]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "d3panels",
"version": "1.1.3",
"version": "1.1.4",
"description": "D3-based graphics panels",
"authors": [
"Karl Broman <[email protected]>"
Expand Down
2 changes: 1 addition & 1 deletion src/d3panels_top.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
!function() { // encapsulate d3panels functions
var d3panels = {
version: "1.1.3"
version: "1.1.4"
};
23 changes: 20 additions & 3 deletions src/dotchart.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ d3panels.dotchart = (chartOpts) ->
xlab = chartOpts?.xlab ? "Group" # x-axis title
ylab = chartOpts?.ylab ? "Response" # y-axis title
xlineOpts = chartOpts?.xlineOpts ? {color:"#cdcdcd", width:5} # color and width of vertical lines
pointcolor = chartOpts?.pointcolor ? "slateblue" # fill color of points
pointcolor = chartOpts?.pointcolor ? null # fill color of points
pointstroke = chartOpts?.pointstroke ? "black" # color of points' outer circle
pointsize = chartOpts?.pointsize ? 3 # color of points
jitter = chartOpts?.jitter ? "beeswarm" # method for jittering points (beeswarm|random|none)
Expand All @@ -34,7 +34,7 @@ d3panels.dotchart = (chartOpts) ->
# accessors end

## the main function
chart = (selection, data) -> # data = {x, y, indID} # x should be a set of positive integers; xcategories has the possible values
chart = (selection, data) -> # data = {x, y, indID, group} # x should be a set of positive integers; xcategories has the possible values

d3panels.displayError("dotchart: data.x is missing") unless data.x?
d3panels.displayError("dotchart: data.y is missing") unless data.y?
Expand All @@ -52,6 +52,23 @@ d3panels.dotchart = (chartOpts) ->
if indID.length != x.length
d3panels.displayError("dotchart: length(indID) [#{indID.length}] != length(x) [#{x.length}]")

# groups of colors
group = data?.group ? (1 for i in x)
ngroup = d3.max(group)
group = ((if g? then g-1 else g) for g in group) # changed from (1,2,3,...) to (0,1,2,...)
if d3panels.sumArray(g < 0 or g > ngroup-1 for g in group) > 0
d3panels.displayError("dotchart: group values out of range")
console.log("ngroup: #{ngroup}")
console.log("distinct groups: #{d3panels.unique(group)}")
if group.length != x.length
d3panels.displayError("dotchart: group.length (#{group.length}) != x.length (#{x.length})")

# colors of the points in the different groups
pointcolor = pointcolor ? d3panels.selectGroupColors(ngroup, "dark")
pointcolor = d3panels.expand2vector(pointcolor, ngroup)
if pointcolor.length < ngroup
d3panels.displayError("add_points: pointcolor.length (#{pointcolor.length}) < ngroup (#{ngroup})")

xcategories = xcategories ? d3panels.unique(x)
xcatlabels = xcatlabels ? xcategories
if xcatlabels.length != xcategories.length
Expand Down Expand Up @@ -140,7 +157,7 @@ d3panels.dotchart = (chartOpts) ->
.append("circle")
.attr("class", (d,i) -> "pt#{i}")
.attr("r", pointsize)
.attr("fill", pointcolor)
.attr("fill", (d,i) -> pointcolor[group[i]])
.attr("stroke", pointstroke)
.attr("stroke-width", "1")
.attr("cx", (d) -> d.x)
Expand Down
25 changes: 5 additions & 20 deletions test/dotchart/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<title>Test dotchart</title>
<script charset="utf-8" type="text/javascript" src="../../bower_components/d3/d3.min.js"></script>
<script type="text/javascript" src="../../bower_components/d3-tip/index.js"></script>
<script type="text/javascript" src="../../bower_components/colorbrewer/colorbrewer.js"></script>

<script type="text/javascript" src="../../d3panels.js"></script>
<link rel=stylesheet type="text/css" href="../../d3panels.css">
Expand Down Expand Up @@ -42,62 +43,46 @@ <h4><a name="ex4">Example 4</a> (More data, horizontal)</h4>

<h4><a name="ex5">Example 5</a> (Random jitter)</h4>
<div class="qtlcharts" id="chart5"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex6">Example 6</a> (Random jitter, horizontal)</h4>
<div class="qtlcharts" id="chart6"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex7">Example 7</a> (No jitter)</h4>
<div class="qtlcharts" id="chart7"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex8">Example 8</a> (No jitter, horizontal)</h4>
<div class="qtlcharts" id="chart8"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex9">Example 9</a> (Missing y values)</h4>
<div class="qtlcharts" id="chart9"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex10">Example 10</a> (Missing y values, horizontal)</h4>
<div class="qtlcharts" id="chart10"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex11">Example 11</a> (Missing x values)</h4>
<div class="qtlcharts" id="chart11"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex12">Example 12</a> (Missing x values, horizontal)</h4>
<div class="qtlcharts" id="chart12"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex13">Example 13</a> (Missing x and y values)</h4>
<div class="qtlcharts" id="chart13"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex14">Example 14</a> (Missing x and y values, horizontal)</h4>
<div class="qtlcharts" id="chart14"></div>
<p class="caption">Hover over points to have them change
size; click to change color</p>
<hr/>

<h4><a name="ex15">Example 15</a> (Color by group)</h4>
<div class="qtlcharts" id="chart15"></div>
<hr/>

<p class="caption">Source at GitHub:
Expand Down
15 changes: 15 additions & 0 deletions test/dotchart/test_dotchart.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,18 @@ d3.json "data.json", (data) ->
these_data.x[0] = null
these_data.y[0] = null
mychart(d3.select("div#chart14"), these_data)

# Example 15: More data
mychart15 = d3panels.dotchart({
title:"Color by group"
height:h
width:w
margin:margin})

ng = 4
n = 75*ng
x = (Math.ceil(Math.random()*ng) for i in [1..n])
y = (Math.random()*4+20+xv for xv in x)
group = ((if Math.random() < 0.5 then 1 else 2) for xv in x)
these_data = {x:x, y:y, group:group}
mychart15(d3.select("div#chart15"), these_data)

0 comments on commit 844759a

Please sign in to comment.