I Need your help... why is this Chart not running.. Error in Console (d3.v2.js Zeile 2396):
TypeError: string.substring is not a function
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
Could anybody help me please?
<script type="text/javascript">
var w = 960,
h = 500,
p = [20, 50, 30, 20],
x = d3.scale.ordinal().rangeRoundBands([0, w - p[1] - p[3]]),
y = d3.scale.linear().range([0, h - p[0] - p[2]]),
z = d3.scale.ordinal().range(["lightpink", "darkgray", "lightblue"]),
parse = d3.time.format("%m/%Y").parse,
format = d3.time.format("%b");
var data = [
[
new Date('1991-01-18T00:00:00'),
52.380001068115234,
28.56999969482422,
10.0
],
[
new Date('1994-11-17T00:00:00'),
57.88999938964844,
21.049999237060547,
10.0
]
];
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + p[3] + "," + (h - p[2]) + ")");
// Transpose the data into layers by cause.
var causes = d3.layout.stack()([data[1], data[2], data[3]].map(function(cause) {
return data.map(function(d) {
return {x: parse(d[0]), y: +d[cause]};
});
}));
// Compute the x-domain (by date) and y-domain (by top).
x.domain(causes[0].map(function(d) { return d.x; }));
y.domain([0, d3.max(causes[causes.length - 1], function(d) { return d.y0 + d.y; })]);
// Add a group for each cause.
var cause = svg.selectAll("g.cause")
.data(causes)
.enter().append("svg:g")
.attr("class", "cause")
.style("fill", function(d, i) { return z(i); })
.style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });
// Add a rect for each date.
var rect = cause.selectAll("rect")
.data(Object)
.enter().append("svg:rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand());
// Add a label per date.
var label = svg.selectAll("text")
.data(x.domain())
.enter().append("svg:text")
.attr("x", function(d) { return x(d) + x.rangeBand() / 2; })
.attr("y", 6)
.attr("text-anchor", "middle")
.attr("dy", ".71em")
.text(format);
// Add y-axis rules.
var rule = svg.selectAll("g.rule")
.data(y.ticks(5))
.enter().append("svg:g")
.attr("class", "rule")
.attr("transform", function(d) { return "translate(0," + -y(d) + ")"; });
rule.append("svg:line")
.attr("x2", w - p[1] - p[3])
.style("stroke", function(d) { return d ? "#fff" : "#000"; })
.style("stroke-opacity", function(d) { return d ? .7 : null; });
rule.append("svg:text")
.attr("x", w - p[1] - p[3] + 6)
.attr("dy", ".35em")
.text(d3.format(",d"));
</script>
The problem is here:
var causes = d3.layout.stack()([data[1], data[2], data[3]].map(function(cause) {
return data.map(function(d) {
return {x: parse(d[0]), y: +d[cause]};
});
}));
d[0] is already a date object - new Date('1991-01-18T00:00:00') - and parse is expecting a string.
Instead, pass d[0] directly:
return {x: d[0], y: +d[cause]};
Sidenote: check out the debugging tools for chrome. Most of the error messages you get working with d3 will not give very useful messages (like "TypeError: string.substring is not a function") and being able to look through the stack is extremely helpful.
Related
I am creating a horizontal animated d3 chart. How do you reverse the x axis and position the bars in a more dynamic way.
Are the bars the correct width or is the xaxis scale correct? Using d3 version 4
//horizontal work in progress
http://jsfiddle.net/ueg3bjf7/
//vertical chart code this is based from
http://jsfiddle.net/myf1zhar/
$(document).ready(function() {
var $this = $(".barchart");
var w = $this.data("width");
var h = $this.data("height");
var data = $this.data("data");
var data = [{
"label": "Apples",
"value": 100
},
{
"label": "Pears",
"value": 120
},
{
"label": "Bananas",
"value": 20
}
];
var configurations = $this.data("configurations");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#2b2d39", "#c12f39", "#f8dd2f", "#1b91dc"];
return colores_g[n % colores_g.length];
}
//asess the margin bottom for the chart based on the max char label
var charLabelCount = [];
data.map(function(d) {
var labelStr = d.label.toString();
charLabelCount.push(labelStr.length);
})
var maxChars = charLabelCount.reduce(function(a, b) {
return Math.max(a, b);
});
var bottomMarg = 60;
if (maxChars > 15) {
bottomMarg = 170;
}
//bottom margin calculation
var margin = {
top: 15,
right: 20,
bottom: bottomMarg,
left: 40
},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var yAxis = d3.axisBottom(y);
var xAxis = d3.axisLeft(x);
var svg = d3.select($this[0])
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("viewBox", "0 0 " + w + " " + h)
.attr("preserveAspectRatio", "xMidYMid meet")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "barchartg");
function sortBy(array, key) {
var sorted = array.sort(function(a, b) {
return parseFloat(b[key]) - parseFloat(a[key]);
});
return sorted;
}
var sortedMax = 45;
if (configurations) {
if (configurations[0]["maxValue"]) {
sortedMax = configurations[0]["maxValue"] + 5;
}
} else {
sortedMax = sortBy(data, "value")[0]["value"] + 5;
}
x.domain(data.map(function(d) {
return d.label;
}));
y.domain([0, sortedMax]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0,25)")
.call(xAxis);
svg.selectAll(".x.axis text")
.attr("transform", "rotate(-60) translate(-5,-5)")
.style("text-anchor", "end");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("fill", function(d, i) {
return colores_google(i);
})
.attr("x", function(d) {
return 0;
})
.attr("width", function(d) {
return d.value;
})
.attr("y", function(d, i) {
return 45 + (i * 90);
})
.attr("height", function(d) {
return 50;
});
d3.selectAll("rect").transition()
.duration(500)
.delay(function(d, i) {
return 500 * i;
})
.attr("width", function(d) {
return 0;
})
setTimeout(function() {
d3.selectAll("rect").transition()
.duration(500)
.delay(function(d, i) {
return 600 * (3 - i);
})
.attr("width", function(d) {
return d.value;
})
}, 2000);
});
I will try to answer your questions.
How do you reverse the x axis
You have to change the domain of the axis
y.domain([sortedMax, 0]);
position the bars
You have to translate the axis to the width of your graph
svg.append("g").attr("transform", "translate(0, 300)").attr("class", "y axis")
Are the bars the correct width or is the xaxis scale correct?
You have to use a multiplier to calculate the width of each bar, using the max width of your graph and your max value. I have added the 25 pixels of the translate of the x axis
var mult = (w + 25) / sortedMax;
...
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("fill", function(d, i) {
return colores_google(i);
})
.attr("x", function(d) {
return 0;
})
.attr("width", function(d) {
return d.value * mult;
})
.attr("y", function(d, i) {
return 45 + (i * 90);
})
.attr("height", function(d) {
return 50;
});
...
setTimeout(function() {
d3.selectAll("rect").transition()
.duration(500)
.delay(function(d, i) {
return 600 * (3 - i);
})
.attr("width", function(d) {
return d.value * mult;
})
}, 2000);
You can see the result in this fiddle http://jsfiddle.net/jfLgawue/65/
I have csv data with a column of years and then columns of age groups with rates:
year,15-19,20-24,25-29,...
1999,0,1.3,8.1,...
2000,0,1.3,8.3,...
.
.
I've mapped the data as shown below. Once mapped the data is in the form of:
>Array[Object, Object, ...]
Each Object is of the form:
>id:"age group"
values:Array[]
>Object
>rate: ""
>year: ""
I'm having trouble with the max value for the y.domain. It seems to be pulling the max value from my 4th object and stopping there. Any help figuring out what's wrong with the max call would be appreciated.
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// define scales
var x = d3.scaleLinear().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
//define color scale
z = d3.scaleOrdinal(d3.schemeCategory10);
//define line generator
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.rate); });
//load data
d3.csv("breaseByageSpread.csv", function(error, data) {
if(error) throw error;
var ages = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {year: d.year, rate: d[id]};
})
};
});
//define x axis
x.domain(d3.extent(data, function(d) { return d.year; }));
//define y axis
y.domain([
d3.min(ages, function(c) { return d3.min(c.values, function(d) { return d.rate; }); }),
d3.max(ages, function(c) { return d3.max(c.values, function(d) { return d.rate; }); })
]);
// define color scale
z.domain(ages.map(function(c) { return c.id; }));
//append x axis
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.ticks(15)
.tickFormat(d3.format('d')));
//append y axis
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -50)
.attr('x', -125)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Rate per 100,000 ppl");
//append rate data to svg
var rate = g.selectAll(".rate")
.data(ages)
.enter().append("g")
.attr("class", "age");
//append rate path to svg
rate.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.id); });
rate.append("text")
.datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.year) + "," + y(d.value.rate) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
function type(d, _, columns) {
//d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
};
});
I found the problem. The rates data was coming in as strings. To convert it to a numeric I changed the following code when loading the data:
Old:
values: data.map(function(d) {
return {year: d.year, rate: d[id]};
New:
values: data.map(function(d) {
return {year: d.year, rate: +d[id]};
I have prototype code I am working with here:
jsfiddle
The example shows how to add a segment when new data is added but not how to remove it again when the data changes [back]. I am fairly new to d3 and don't quite get the exit() function yet...
if you reverse the initial and second dataset you will see that the grapes segment is never removed. Thanks in advance!
any help would be great!
The update code: (my final chart needs to update on a timer when data changes)
var arcs = arc_grp.selectAll("path")
.data(donut(data));
arcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc)
.each(function(d) { return this.current = d; });
var sliceLabel = label_group.selectAll("text")
.data(donut(data));
sliceLabel.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) { return "translate(" + (arc.centroid(d)) +
")"; })
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) { return 1e-6; }
else { return 1; }
})
.text(function(d) { return d.data.label; });
};
To remove elements without data, you have to use exit(), which...
Returns the exit selection: existing DOM elements in the selection for which no new datum was found.
So, inside your updateChart function, you need an exit selection for both paths and texts:
var newArcs = arcs.data(donut(dataset));
newArcs.exit().remove();
var newSlices = sliceLabel.data(donut(dataset));
newSlices.exit().remove();
Here is your updated code:
// Setup all the constants
var duration = 500;
var width = 500
var height = 300
var radius = Math.floor(Math.min(width / 2, height / 2) * 0.9);
var colors = ["#d62728", "#ff9900", "#004963", "#3497D3"];
// Test Data
var d2 = [{
label: 'apples',
value: 20
}, {
label: 'oranges',
value: 50
}, {
label: 'pears',
value: 100
}];
var d1 = [{
label: 'apples',
value: 100
}, {
label: 'oranges',
value: 20
}, {
label: 'pears',
value: 20
}, {
label: 'grapes',
value: 20
}];
// Set the initial data
var data = d1
var updateChart = function(dataset) {
var newArcs = arcs.data(donut(dataset));
newArcs.exit().remove();
newArcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
newArcs.transition()
.duration(duration)
.attrTween("d", arcTween);
var newSlices = sliceLabel.data(donut(dataset));
newSlices.exit().remove();
newSlices.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + (arc.centroid(d)) + ")";
})
.style("fill-opacity", function(d) {
if (d.value === 0) {
return 1e-6;
} else {
return 1;
}
});
sliceLabel.data(donut(dataset)).enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) {
return "translate(" + (arc.centroid(d)) + ")";
})
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) {
return 1e-6;
} else {
return 1;
}
})
.text(function(d) {
return d.data.label;
});
};
var color = d3.scale.category20();
var donut = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.innerRadius(radius * .4)
.outerRadius(radius);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var arc_grp = svg.append("g")
.attr("class", "arcGrp")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var label_group = svg.append("g")
.attr("class", "lblGroup")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var arcs = arc_grp.selectAll("path")
.data(donut(data));
arcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.each(function(d) {
return this.current = d;
});
var sliceLabel = label_group.selectAll("text")
.data(donut(data));
sliceLabel.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) {
return "translate(" + (arc.centroid(d)) + ")";
})
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) {
return 1e-6;
} else {
return 1;
}
})
.text(function(d) {
return d.data.label;
});
// Update the data
setInterval(function(model) {
return updateChart(d2);
}, 2000);
// Tween Function
var arcTween = function(a) {
var i = d3.interpolate(this.current, a);
this.current = i(0);
return function(t) {
return arc(i(t));
};
};
.arcLabel {
font: 10px sans-serif;
fill: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
In Normalized stacked bar I am trying to bind data in all rect in a bar but wrong value is passed. I adopted my code from this example and made it horizontal. Below is my code and I have created a plunker as well. In .text function entire object is passed. Can someone help me where I am going wrong
var svg = d3.select("svg"),
margin = {
top: 20,
right: 60,
bottom: 30,
left: 40
},
/*width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,*/
width = 120,
height = 120,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var y = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var x = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(['#02CA22', '#FB5652', '#FFB005']);
var stack = d3.stack()
.offset(d3.stackOffsetExpand);
d3.csv("data.csv", type, function (error, data) {
if (error) throw error;
/*data.sort(function(a, b) {
return b[data.columns[1]] / b.total - a[data.columns[1]] / a.total;
});*/
y.domain(data.map(function (d) {
return d.State;
}));
z.domain(data.columns.slice(1));
var serie = g.selectAll(".serie")
.data(stack.keys(data.columns.slice(1))(data))
.enter().append("g")
.attr("class", "serie")
.attr("fill", function (d) {
return z(d.key);
});
var rect = serie.selectAll("rect")
.data(function (d) {
return d;
}).enter();
rect.append("rect")
.attr("y", function (d) {
return y(d.data.State);
})
.attr("x", function (d) {
return x(d[1]);
})
.attr("width", function (d) {
return x(d[0]) - x(d[1]);
})
.attr("height", y.bandwidth());
rect.append("text")
.text(function (d) {
console.log('d');
console.log(d);
console.log(d.data.key);
return 'val';
})
.attr("y", function (d) { return y(d.data.State) + y.bandwidth() / 2; })
.attr("x", function (d) {
return x(d[1]);
});
/* g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(2, "%"));*/
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y));
var legend = serie.append("g")
.attr("class", "legend")
.attr("transform", function (d) {
var d = d[0];
return "translate(" + ((x(d[0]) + x(d[1])) / 2) + ", " + (y(d.data.State) - y.bandwidth()) + ")";
});
/*legend.append("line")
.attr("y1", 5)
.attr("x1", 15)
.attr("x2", 15)
.attr("y2", 12)
.attr("stroke", "#000");
legend.append("text")
.attr("x", 9)
.attr("dy", "0.35em")
.attr("fill", "#000")
.style("font", "10px sans-serif")
.text(function (d) {
return d.key;
}); */
});
function type(d, i, columns) {
var t;
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}
I think the best way to do this is to modify your subselection data-binding to include that information:
var rect = serie.selectAll("rect")
.data(function (d) {
// return all the data you need as flat as possible
var rv = d.map(function(da){
return {p: da, key: d.key, state: da.data.State}
});
return rv;
}).enter();
The text is then available as:
rect.append("text")
.text(function (d) {
return d.key;
})
.attr("y", function (d) { return y(d.state) + y.bandwidth() / 2; })
.attr("x", function (d) {
return x(d.p[1]);
});
Updated Plunker.
I'm trying to generate a parallel coordinate using d3.js
My problem is that the first scale should display different strings.
with the original code it looks like this:
and with my test it looks like this (no lines):
the error code is:
Error: Invalid value for attribute d="M33,NaNL99,161.37817638266068L165,6.543121881682145L231,16.962488563586458L297,180"
here is my code:
function parallelChart (id, size) {
if(size == 'small') {
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 460 - margin.left - margin.right,
height = 230 - margin.top - margin.bottom;
} else {
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
}
var x = d3.scale.ordinal().rangePoints([0, width], 1),
y = {},
dragging = {};
var line = d3.svg.line(),
axis = d3.svg.axis().orient("left"),
background,
foreground;
var svg = d3.select(id).append("svg")
.attr("class", 'center-block')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Original
d3.csv("dataNew.csv", function(error, healthdata) {
x.domain(dimensions = d3.keys(healthdata[0]).filter(function(d) {
return d != "Datum" && (y[d] = d3.scale.linear()
.domain(d3.extent(healthdata, function(p) { return +p[d]; }))
.range([height, 0]));
}));
// this did not work
// d3.csv("dataNew.csv", function(error, healthdata) {
// x.domain(dimensions = d3.keys(healthdata[0]).filter(function(d) {
// if(d == "Datum") {
// return d == "Datum" && ( (y[d] = d3.time.scale()
// .domain(d3.extent(healthdata, function(p) { return +p[d]; }))
// .range([height, 0])));
// }
// return d != "Datum" && ( (y[d] = d3.scale.linear()
// .domain(d3.extent(healthdata, function(p) { return +p[d]; }))
// .range([height, 0])));
// }));
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(healthdata)
.enter().append("path")
.attr("d", path);
// Add blue foreground lines for focus.
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(healthdata)
.enter().append("path")
.attr("d", path);
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; })
.call(d3.behavior.drag()
.origin(function(d) { return {x: x(d)}; })
.on("dragstart", function(d) {
dragging[d] = x(d);
background.attr("visibility", "hidden");
})
.on("drag", function(d) {
dragging[d] = Math.min(width, Math.max(0, d3.event.x));
foreground.attr("d", path);
dimensions.sort(function(a, b) { return position(a) - position(b); });
x.domain(dimensions);
g.attr("transform", function(d) { return "translate(" + position(d) + ")"; })
})
.on("dragend", function(d) {
delete dragging[d];
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
transition(foreground).attr("d", path);
background
.attr("d", path)
.transition()
.delay(500)
.duration(0)
.attr("visibility", null);
}));
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d; });
// Add and store a brush for each axis.
g.append("g")
.attr("class", "brush")
.each(function(d) {
d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brushstart", brushstart).on("brush", brush));
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
});
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function path(d) {
return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; }));
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }),
extents = actives.map(function(p) { return y[p].brush.extent(); });
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? null : "none";
});
}
}
Here goes one example where the author manage string and numbers in the same parallel coord:
http://bl.ocks.org/syntagmatic/4020926
Create an array of dimensions that will be further used...
var dimensions = [
{
name: "name",
scale: d3.scale.ordinal().rangePoints([0, height]),
type: "string"
},
{
name: "economy (mpg)",
scale: d3.scale.linear().range([0, height]),
type: "number"
},
...
]
...before load the data, define the domains by mapping your previous dimensions definition...
var x = d3.scale.ordinal()
.domain(dimensions.map(function(d) { return d.name; }))
.rangePoints([0, width]);
...define a variable dimension (pay attention, dimensions != dimension) with the locations of each axis...
var dimension = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d.name) + ")"; });
...once the data is loaded, execute a for each to define the domain of each dimension...
d3.csv("cars.small.csv", function(data) {
dimensions.forEach(function(dimension) {
dimension.scale.domain(dimension.type === "number"
? d3.extent(data, function(d) { return +d[dimension.name]; })
: data.map(function(d) { return d[dimension.name]; }).sort());
});
...
}
... axis lines and foreground are still loaded in the same way...
svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("d", draw);
svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("d", draw);
... this code will load the text of each axis, observe that it is now using properties from the dimensions that we defined in the beggining.
dimension.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(yAxis.scale(d.scale)); })
.append("text")
.attr("class", "title")
.attr("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d.name; });
that`s all =).