d3 range/domain returning negative values? - javascript

I have this array:
var bucket_contents = [8228, 21868, 12361, 15521, 3037, 2656];
I am trying to alter the width of a series of rects based on these values using range/domain. This is my code:
var bucket_width = d3.scale.linear()
.domain([d3.min(bucket_contents), d3.max(bucket_contents)])
.range([0, 1000]);
bucket.forEach(function(d, i) {
d.x = (width / bucket.length) * i;
d.y = (height / 2) - 25;
d.w = bucket_width(i);
console.log(bucket_width(i));
});
d3.select("#plot").selectAll(".bucket")
.data(bucket)
.enter()
.append("rect")
.attr("class", "bucket")
.attr("x", function(d, i) { return d.x; })
.attr("y", function(d, i) { return d.y; })
.attr('width', function(d, i) { return d.w; })
.attr('height', '50')
.style("fill", '#000');
Currently the console log bucket_width(i) inside the forEach loop outputs the following:
-138.24692900270665
-138.1948782011243
-138.14282739954194
-138.09077659795963
-138.03872579637726
-137.98667499479492
Console log on d3.min(..) is 2656 and d3.max(..) is 21868 as expected.
What am I doing wrong? I thought range 'normalizes' any values within that range, i.e. 21868 would return 1000 and 2656 would return 0.

Your problem is that you are calling bucket_width(i) instead of bucket_width(d). Hence, you are passing in values of 0, 1, etc. which are less than the min value of 2656 by quite some.
Demo: http://jsfiddle.net/2tq8S/
var contents = [8228, 21868, 12361, 15521, 3037, 2656];
var bucket_width = d3.scale.linear().domain(d3.extent(contents)).range([0, 1000]);
contents(function(d, i){
console.log( d, i, bucket_width(i), bucket_width(d) );
});
Output:
8228 0 -138.24 290.03
21868 1 -138.19 1000.00
12361 2 -138.14 505.15
15521 3 -138.09 669.63
3037 4 -138.03 19.83
2656 5 -137.98 0.00

Related

Transitioning a bar chart with negative values for the width

I am creating a horizontal bar chart using d3. And I am using an animation to "grow" the chart at startup. Here is the code.
// Create the svg element
d3.select("#chart-area")
.append("svg")
.attr("height", 800)
.attr("width", 800);
.data(dataValues) // This data is previously prepared
.enter().append("rect")
.style("fill", "blue")
.attr("x", function () { return xScale(0); }) // xScale is defined earlier
.attr("y", function (d) { return yScale(d); }) // yScale is defined earlier
.attr("height", yScale.bandwidth()) // yScale is defined earlier
// Initial value of "width" (before animation)
.attr("width", 0)
// Start of animation transition
.transition()
.duration(5000) // 5 seconds
.ease (d3.easeLinear);
// Final value of "width" (after animation)
.attr("width", function(d) { return Math.abs(xScale(d) - xScale(0)); })
The above code would work without any problem, and the lines would grow as intended, from 0 to whichever width, within 5 seconds.
Now, if we change the easing line to the following
// This line changed
.ease (d3.easeElasticIn);
Then, the ease would try to take the width to a negative value before going to a final positive value. As you can see here, d3.easeElasticIn returns negative values as time goes by, then back to positive, resulting in width being negative at certain points in the animation. So the bars do not render properly (because SVG specs state that if width is negative, then use 0)
I tried every solution to allow the bars to grow negatively then back out. But could not find any. How can I fix this problem?
Thanks.
As you already know, the use of d3.easeElasticIn in your specific code will create negative values for the rectangles' width, which is not allowed.
This basic demo reproduces the issue, the console (your browser's console, not the snippet's console) is populated with error messages, like this:
Error: Invalid negative value for attribute width="-85.90933910798789"
Have a look:
const svg = d3.select("svg");
const margin = 50;
const line = svg.append("line")
.attr("x1", margin)
.attr("x2", margin)
.attr("y1", 0)
.attr("y2", 150)
.style("stroke", "black")
const data = d3.range(10).map(function(d) {
return {
y: "bar" + d,
x: Math.random()
}
});
const yScale = d3.scaleBand()
.domain(data.map(function(d) {
return d.y
}))
.range([0, 150])
.padding(0.2);
const xScale = d3.scaleLinear()
.range([margin, 300]);
const bars = svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr("x", margin)
.attr("width", 0)
.style("fill", "steelblue")
.attr("y", function(d) {
return yScale(d.y)
})
.attr("height", yScale.bandwidth())
.transition()
.duration(2000)
.ease(d3.easeElasticIn)
.attr("width", function(d) {
return xScale(d.x) - margin
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
So, what's the solution?
One of them is catching those negative values as they are generated and, then, moving the rectangle to the left (using the x attribute) and converting those negative numbers to positive ones.
For that to work, we'll have to use attrTween instead of attr in the transition selection.
Like this:
.attrTween("width", function(d) {
return function(t){
return Math.abs(xScale(d.x) * t);
};
})
.attrTween("x", function(d) {
return function(t){
return xScale(d.x) * t < 0 ? margin + xScale(d.x) * t : margin;
};
})
In the snippet above, margin is just a margin that I created so you can see the bars going to the left of the axis.
And here is the demo:
const svg = d3.select("svg");
const margin = 100;
const line = svg.append("line")
.attr("x1", margin)
.attr("x2", margin)
.attr("y1", 0)
.attr("y2", 150)
.style("stroke", "black")
const data = d3.range(10).map(function(d) {
return {
y: "bar" + d,
x: Math.random()
}
});
const yScale = d3.scaleBand()
.domain(data.map(function(d) {
return d.y
}))
.range([0, 150])
.padding(0.2);
const xScale = d3.scaleLinear()
.range([0, 300 - margin]);
const bars = svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr("x", margin)
.attr("width", 0)
.style("fill", "steelblue")
.attr("y", function(d) {
return yScale(d.y)
})
.attr("height", yScale.bandwidth())
.transition()
.duration(2000)
.ease(d3.easeElasticIn)
.attrTween("width", function(d) {
return function(t) {
return Math.abs(xScale(d.x) * t);
};
})
.attrTween("x", function(d) {
return function(t) {
return xScale(d.x) * t < 0 ? margin + xScale(d.x) * t : margin;
};
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

How can I dynamically update text labels in d3?

I want to add labels to my vertical bar chart that display the current percentage value that corresponds to the current hight of the bar.
So I need to continuously update the percentage value and I also need a transition to make the text element move insync with the bar chart.
I tried this:
var percentageLabels = svg.selectAll(".percentage-label")
.data(dataset);
percentageLabels.remove();
percentageLabels
.enter()
.append("text")
.attr("class", "percentage-label")
.style("fill", "white")
.text(function(d) {
return d;
})
.attr("y", function(d) {
return y(d);
})
.attr("x", function(d, i) {
return i * (w / dataset.length) + 2.5 / 100 * w + w * 10/100;
})
.transition().duration(1750).ease("linear")
.attr("y", function(d) {
return y(d);
});
Check out the fiddle
I'd make a couple changes here. First, wrap the rect and the text in a g, so you only need to data-bind once. Then you are free to transition them together:
var uSel = svg.selectAll(".input")
.data(dataset); //<-- selection of gs
uSel.exit().remove(); //<-- anybody leaving? remove g (both rect and text)
var gs = uSel
.enter()
.append("g")
.attr("class", "input"); //<-- enter selection, append g
gs.append("rect")
.attr("fill", "rgb(250, 128, 114)"); //<-- enter selection, rect to g
gs.append("text")
.attr("class", "percentage-label")
.style("fill", "white")
.attr("x", function(d, i) {
return i * (w / dataset.length) + 2.5 / 100 * w + w * 10/100;
}); //<-- enter selection, text to g
uSel.select("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length) + 2.5 / 100 * w;
})
.attr("width", w / dataset.length - barPadding)
.attr("height", y(0))
.transition().duration(1750).ease("linear")
.attr("y", function(d) {
return y(d);
})
.attr("height", function(d) {
return h - y(d);
}); //<-- update rects with transition
uSel.select("text")
.transition().duration(1750).ease("linear")
.attr("y", function(d) {
return y(d);
})
.text(function(d) {
return d + "%";
}); //<-- update text with transition
Updated fiddle.
EDITS
To transition the text, you are probably going to have to use a custom tween function:
uSel.select("text")
.transition().duration(1750).ease("linear")
.attr("y", function(d) {
return y(d); //<-- move the text
})
.tween("", function(d) {
var self = d3.select(this),
oldValue = y.invert(self.attr("y")), //<-- get the current value
i = d3.interpolateRound(oldValue, d); //<-- interpolate to new value
return function(t) {
self.text(i(t) + '%') <-- update the text on each iteration
};
});
Updated, updated fiddle.
From the docs:
The transition.each method can be used to chain transitions and apply shared timing across a set of transitions. For example:
d3.transition()
.duration(750)
.ease("linear")
.each(function() {
d3.selectAll(".foo").transition()
.style("opacity", 0)
.remove();
})
.transition()
.each(function() {
d3.selectAll(".bar").transition()
.style("opacity", 0)
.remove();
});
You might want to check out this: https://github.com/mbostock/d3/wiki/Transitions#tween

d3 accessing nested data in grouped bar chart

I'm building a grouped bar chart by nesting a .csv file. The chart will also be viewable as a line chart, so I want a nesting structure that suits the line object. My original .csv looks like this:
Month,Actual,Forecast,Budget
Jul-14,200000,-,74073.86651
Aug-14,198426.57,-,155530.2499
Sep-14,290681.62,-,220881.4631
Oct-14,362974.9,-,314506.6437
Nov-14,397662.09,-,382407.67
Dec-14,512434.27,-,442192.1932
Jan-15,511470.25,511470.25,495847.6137
Feb-15,-,536472.5467,520849.9105
Mar-15,-,612579.9047,596957.2684
Apr-15,-,680936.5086,465313.8723
May-15,-,755526.7173,739904.081
Jun-15,-,811512.772,895890.1357
and my nesting is like this:
d3.csv("data/net.csv", function(error, data) {
if (error) throw error;
var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});
data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {
return {
name: name, // "name": the csv headers except month
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};
});
The code to build my chart is:
var bars = svg.selectAll(".barGroup")
.data(data) // Select nested data and append to new svg group elements
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });
bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name); });
The g elements are fine and the individual bars are being mapped to them, with the x value and class applied correctly.
My problem comes in accessing the data for 'rate' for the height and y value of the bars. In the form above it gives a NaN. I've also tried using the category data to append g elements and then appending the rects with:
.data(function(d) { return d.values })
This allows me to access the rate data, but maps all 36 bars to each of the rangeBands.
It also works fine in a flatter data structure, but I can't seem to use it when it's nested two levels down, despite looking through a great many examples and SO questions.
How do I access the rate data?
In response to Cyril's request, here's the full code:
var margin = {top: 20, right: 18, bottom: 80, left: 50},
w = parseInt(d3.select("#bill").style("width")) - margin.left - margin.right,
h = parseInt(d3.select("#bill").style("height")) - margin.top - margin.bottom;
var customTimeFormat = d3.time.format.multi([
[".%L", function(d) { return d.getMilliseconds(); }],
[":%S", function(d) { return d.getSeconds(); }],
["%I:%M", function(d) { return d.getMinutes(); }],
["%I %p", function(d) { return d.getHours(); }],
["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
["%b %d", function(d) { return d.getDate() != 1; }],
["%b", function(d) { return d.getMonth(); }],
["%Y", function() { return true; }]
]);
var parseDate = d3.time.format("%b-%y").parse;
var displayDate = d3.time.format("%b %Y");
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, w], .1);
var xScale1 = d3.scale.linear()
.domain([0, 2]);
var yScale = d3.scale.linear()
.range([h, 0])
.nice();
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(customTimeFormat)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-w)
.outerTickSize(0);
var svg = d3.select("#svgCont")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var thous = d3.format(",.0f")
var lineClass = d3.scale.ordinal().range(["actual", "forecast", "budget"]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<p id='date'>" + displayDate(d.date) + "</p><p id='value'>$" + thous(d.rate);
})
d3.csv("data/net.csv", function(error, data) {
if (error) throw error;
var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});
data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};
});
var min = d3.min(categories, function(d) {
return d3.min(d.values, function(d) {
return d.rate;
});
});
var max = d3.max(categories, function(d) {
return d3.max(d.values, function(d) {
return d.rate;
});
});
var minY = min < 0 ? min * 1.2 : min * 0.8;
xScale.domain(data.map(function(d) { return d.month; }));
yScale.domain([minY, (max * 1.1)]);
var barWidth = headers.length > 2 ? xScale.rangeBand() / 2 : xScale.rangeBand() ;
svg.call(tip);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var bars = svg.selectAll(".barGroup")
.data(data)
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });
bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name) + " bar"; });
var legend = svg.selectAll(".legend")
.data(headers)
.enter()
.append("g")
.attr("class", "legend");
legend.append("line")
.attr("class", function(d) { return lineClass(d); })
.attr("x1", 0)
.attr("x2", 40)
.attr("y1", function(d, i) { return (h + 30) + (i *14); })
.attr("y2", function(d, i) { return (h + 30) + (i *14); });
legend.append("text")
.attr("x", 50)
.attr("y", function(d, i) { return (h + 32) + (i *14); })
.text(function(d) { return d; });
svg.selectAll(".bar")
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
});
Update 18 Feb '16.
It seems I haven't explained what I was trying to do sufficiently well. The line and bar versions of the chart will be seen separately, i.e. users can see either one according to input to a select element. Also note that I don't have control over how the data comes in initially.
I have a version of exactly how it should work here.
This question was raised when I was still working through it, but I never solved the issue – I used a workaround of doing two separate nests of the data.
Link to jsfiddle:
https://jsfiddle.net/sladav/rLh4qwyf/1/
I think the root of the issue is that you want to use two variables that do not explicitly exist in your original data set: (1) Category and (2) Rate.
Your data is formatted in a wide format in that each category gets its own variable and the value for rate exists at the crossroads of month and one of the given categories. I think the way you're nesting ultimately is or at least should address this, but it is unclear to me if or where something gets lost in translation. Conceptually, I think it makes more sense to start with an organization that matches what you are trying to accomplish. I reformatted the original data and approached it again - on a conceptual level the nesting seems straightforward and simple...
NEW COLUMNS:
Month: Time Variable; mapped to X axis
Category: Categorical values [Actual, Forecast, Budget]; used to group/color
Rate: Numerical value; mapped to Y axis
Reorganized CSV (dropped NULLs):
Month,Category,Rate
Jul-14,Actual,200000
Aug-14,Actual,198426.57
Sep-14,Actual,290681.62
Oct-14,Actual,362974.9
Nov-14,Actual,397662.09
Dec-14,Actual,512434.27
Jan-15,Actual,511470.25
Jan-15,Forecast,511470.25
Feb-15,Forecast,536472.5467
Mar-15,Forecast,612579.9047
Apr-15,Forecast,680936.5086
May-15,Forecast,755526.7173
Jun-15,Forecast,811512.772
Jul-14,Budget,74073.86651
Aug-14,Budget,155530.2499
Sep-14,Budget,220881.4631
Oct-14,Budget,314506.6437
Nov-14,Budget,382407.67
Dec-14,Budget,442192.1932
Jan-15,Budget,495847.6137
Feb-15,Budget,520849.9105
Mar-15,Budget,596957.2684
Apr-15,Budget,465313.8723
May-15,Budget,739904.081
Jun-15,Budget,895890.1357
With your newly formatted data, you start by using d3.nest to GROUP your data explicitly with the CATEGORY variable. Now your data exists in two tiers. The first tier has three groups (one for each category). The second tier contains the RATE data for each line/set of bars. You have to nest your data selections as well - the first layer is used to draw the lines, the second layer for the bars.
Nesting your data:
var nestedData = d3.nest()
.key(function(d) { return d.Category;})
.entries(data)
Create svg groups for your grouped, 1st-tier data:
d3.select(".plot-space").selectAll(".g-category")
.data(nestedData)
.enter().append("g")
.attr("class", "g-category")
Use this data to add your lines/paths:
d3.selectAll(".g-category").append("path")
.attr("class", "line")
.attr("d", function(d){ return lineFunction(d.values);})
.style("stroke", function(d) {return color(d.key);})
Finally, "step into" 2nd-tier to add bars/rect:
d3.selectAll(".g-category").selectAll(".bars")
.data(function(d) {return d.values;})
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {return x(d.Month);})
.attr("y", function(d) {return y(d.Rate);})
.attr("width", 20)
.attr("height", function(d) {return height - y(d.Rate)})
.attr("fill", function(d) {return color(d.Category)})
This is a straightforward approach (to me at least), in that you take it one category at a time, using the grouped data to draw a line, then individual data points to draw the bars.
LAZY EDIT:
To get category bars side by side
Create ordinal scale mapping category to [1,nCategories]. Use this to dynamically offset bars with something like
translate( newScale(category)*barWidth )
To show either bars or lines (not both)
Create a function that selects bars/lines and transitions/toggles their visibility/opacity. Run when your drop-down input changes and with the drop-down input as input to the function.
The problem, I belive, is that you are binding the categories array to the bars selection, like this:
bars.selectAll("rect").data(categories)
As far as I can see (whithout a running demo) categories is an array with only four values (one for each category).
You have to go one step 'deeper' in your nested data structure.
To draw a set of bars for each category you would need to iterate over categories and bind the values array that contains the actual values to the selection.
Something like:
categories.each(function (category) {
var klass = category.name;
bars.selectAll("rect ." + klass)
.data(category.values)
.enter()
.append("rect")
.attr("class", klass)
.attr("width", barWidth)
.attr("x", function (d, i) { /* omitted */})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); });
});
---- Edit
Instead of the above code, think about drawing the bars just like you do with the lines. Like this:
var bars = svg.selectAll(".barGroup")
.data(categories)
.enter()
.append("g")
.attr("class", function (d) { return lineClass(d.name) + "Bar barGroup"; })
.attr("transform", function (d, i) {
var x = i > 1 ? xScale.rangeBand() / 2 : 0;
return "translate(" + x + ",0)";
})
.selectAll('rect')
.data(function (d) { return d.values; })
.enter()
.append("rect")
.attr("class", "bar")
.attr("width", barWidth)
.attr("x", function (d, i) { return xScale(d.date); })
.attr("y", function (d, i) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); });

Chart not updating properly

I am trying to follow Mike Bostock's tutorial on d3js (http://mbostock.github.io/d3/tutorial/bar-2.html) to understand how to update charts dynamically but I am facing some hurdles.
In my chart, my bars on the left, rather than being simply removed, are sent behind my chart and I can't figure out why:
JS:
var t = 1297110663, // start time (seconds since epoch)
v = 70, // start value (subscribers)
data = d3.range(33).map(next); // starting dataset
function next() {
return {
time: ++t,
value: v = ~~Math.max(10, Math.min(90, v + 10 * (Math.random() - .5)))
};
}
setInterval(function(){
data.shift();
data.push(next());
console.log(data);
redraw();
}, 1000);
var w = 20,
h =80;
var x = d3.scale.linear()
.domain([0, 1])
.range([0, w]);
var y = d3.scale.linear()
.domain([0, 100])
.rangeRound([0, h]);
var chart = d3.select(".container").append("svg")
.attr("class", "chart")
.attr("width", w * data.length - 1)
.attr("height", h);
chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i){ return x(i) - 0.5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); });
function redraw(){
console.log(data);
var rect = chart.selectAll('rect')
.data(data, function(d){ return d.time; });
rect.enter().insert("rect")
.attr("x", function(d, i) { return x(i + 1) - .5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); })
rect.transition() // Shouldn't I use .update() here?
.duration(1000)
.attr("x", function(d, i) { return x(i) - .5; });
rect.exit().transition()
.duration(1000)
.attr('x', function(d, i) { return x(i - 1) - .5})
.remove();
}
Here is the jsfiddle: http://jsfiddle.net/kkMR4/
Another thing I don't understand is why we dont use .update()? If I understand correctly .enter() is used to create the DOM element where data didnt find any match in the DOM and .exit() is used to find the DOM elements which are not in data, so shouldn't I use update() to move all the other column to the left?
Many thanks
Best
The problem is in this block:
rect.exit().transition()
.duration(1000)
.attr('x', function(d, i) { return x(i - 1) - .5})
.remove();
The third line (.attr), reassigns the coordinates. If you want them to truly exit, you can remove this line.
rect.exit().transition()
.duration(1000)
.remove();

D3 barplot only using half my data

I have a d3.js barplot using some json data containing 12 elements. The data value I'm using for bar height is fpkm. I'm able to return that value as a callback to d3's data function- but only for half the elements.
My problem is that only the first half of the values are appearing in my barplot. I only get 6 rows corresponding to my first 6 values.
I made a fiddle here: http://jsfiddle.net/z9Mvt/
I can't seem to figure out why it's only using half the elements in my json.
Any help = appreciated.
html:
<div align='center' id="GECGplot" style='width:98%;text-align:center;'></plot>
and the js:
var gecgData= {"nodeName":"GECG","children":[{"nodeName":0,"nodeData":{"id":"643139","library_id":"SI_5589","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.1241","fpkm_conf_lo":"0.898502","fpkm_conf_hi":"1.34969","fpkm_status":"OK","fpkm_percentile_compendium":"8.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":1,"nodeData":{"id":"872561","library_id":"SI_5596","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.12666","fpkm_conf_lo":"0.871059","fpkm_conf_hi":"1.38226","fpkm_status":"OK","fpkm_percentile_compendium":"16.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":2,"nodeData":{"id":"1031623","library_id":"SI_5553","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.21305","fpkm_conf_lo":"0.949369","fpkm_conf_hi":"1.47674","fpkm_status":"OK","fpkm_percentile_compendium":"25.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":3,"nodeData":{"id":"248423","library_id":"SI_5486","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.98203","fpkm_conf_lo":"1.64888","fpkm_conf_hi":"2.31519","fpkm_status":"OK","fpkm_percentile_compendium":"33.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":4,"nodeData":{"id":"1039674","library_id":"SI_5554","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.24514","fpkm_conf_lo":"1.83333","fpkm_conf_hi":"2.65696","fpkm_status":"OK","fpkm_percentile_compendium":"41.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":5,"nodeData":{"id":"304849","library_id":"SI_5485","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.29868","fpkm_conf_lo":"2.02514","fpkm_conf_hi":"2.57221","fpkm_status":"OK","fpkm_percentile_compendium":"50.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":6,"nodeData":{"id":"417495","library_id":"SI_5484","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.61196","fpkm_conf_lo":"2.28949","fpkm_conf_hi":"2.93442","fpkm_status":"OK","fpkm_percentile_compendium":"58.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":7,"nodeData":{"id":"928522","library_id":"SI_5595","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.94397","fpkm_conf_lo":"2.61962","fpkm_conf_hi":"3.26832","fpkm_status":"OK","fpkm_percentile_compendium":"66.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":8,"nodeData":{"id":"622876","library_id":"SI_5552","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"3.27303","fpkm_conf_lo":"2.79509","fpkm_conf_hi":"3.75097","fpkm_status":"OK","fpkm_percentile_compendium":"75.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":9,"nodeData":{"id":"50230","library_id":"SI_5487","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"9.88611","fpkm_conf_lo":"8.6495","fpkm_conf_hi":"11.1227","fpkm_status":"OK","fpkm_percentile_compendium":"83.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":10,"nodeData":{"id":"816444","library_id":"SI_5594","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"15.1868","fpkm_conf_lo":"13.8218","fpkm_conf_hi":"16.5519","fpkm_status":"OK","fpkm_percentile_compendium":"91.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":11,"nodeData":{"id":"496931","library_id":"SI_5551","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"52.249","fpkm_conf_lo":"50.8217","fpkm_conf_hi":"53.6763","fpkm_status":"OK","fpkm_percentile_compendium":"100.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}}]}
;
//Width and height
// var w = $('#GECGplot').width();
var w = 700;
var h = 300;
var barPadding = 1;
var margin = {top: 40, right: 10, bottom: 20, left: 10};
var xScale = d3.scale.linear().
domain([0, 20]). // your data minimum and maximum
range([0, h]); // the pixels to map to, e.g., the width of the diagram.
//Create SVG element
var svg = d3.select("#GECGplot")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
// .data(dataset)
.data(function(d, i) {
return plotData[i].nodeData.fpkm;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
// alert(plotData.length);
return i * (w / plotData.length);
})
.attr("y", function(d, i) {
alert(plotData[i].nodeData.fpkm);
return h - (plotData[i].nodeData.fpkm * 50); //Height minus data value
})
.attr("width", w / plotData.length - barPadding)
.attr("height", function(d, i) {
return plotData[i].nodeData.fpkm * 50; //Just the data value
})
.attr("fill", function(d, i) {
return "rgb(0, 0, " + (plotData[i].nodeData.fpkm * 50) + ")";
})
svg.selectAll("text")
.data(function(d, i) {
return plotData[i].nodeData.fpkm;
})
.enter()
.append("text")
.text(function(d, i) {
return plotData[i].nodeData.fpkm;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return i * (w / plotData.length) + (w / plotData.length - barPadding) / 2;
})
.attr("y", function(d, i) {
return h - (plotData[i].nodeData.fpkm * 50) + 14;
})
// alert(tableSchema);
Here you go. You bind the array "children" to the rectangle elements so you dont need the argument 'i' to access the value you need.
Also, I would recommend using the d3.scale.ordinal() for your x axis as opposed to calculating it explicitly from the data. Litte more flexible.
http://jsfiddle.net/Cef4D/
svg.selectAll("rect")
.data(plotData)
.enter().append("rect")
.attr("x", function(d, i) {return i * (w / plotData.length);})
.attr("y", function(d) {
return h - (d.nodeData.fpkm * 50); //Height minus data value
})
.attr("width", w / plotData.length - barPadding)
.attr("height", function(d, i) {
return d.nodeData.fpkm * 50; //Just the data value
})
.attr("fill", function(d, i) {
return "rgb(0, 0, " + (d.nodeData.fpkm * 50) + ")";
})

Categories