I am trying to plot the data. See the following images:
And also the second image:
The following is the code that I have tried to manipulte:
private updateCircles(container, points: Example2D[]) {
let selection = container.selectAll("circle").data(points);
selection.enter().append("circle").attr("r", 3);
selection
.attr({
cx : (d: Example2D,i) => (i),
cy : (d: Example2D) => this.yScale(d.x1),
})
.style("fill", d => this.color(d.label));
}
}
The yScale is defined as:
this.yScale = d3.scale.linear()
.domain(yDomain)
.range([height - 1 * padding, 0]);
the yDomain = [-1,1]
What I am looking for is that the data must get reflected the exact way as it is in the thumbnail.
Let me know what I am missing to improve.
Try this
your tumbnail have differend height and width with new image
You get the data by doing
container.selectAll("circle").data(points);
Append it on selection by
selection.enter().append("circle").attr("r", 3);
but your svg container (width and height) on tumbnail and new image is different
Please try reformat the scale :
private updateCircles(container, points: Example2D[]) {
//get data
let selection = container.selectAll("circle").data(points);
// this is new image on the right width and hight
let rangeX = [ ] //find width [min,max] of your new image on the right
let rangeY = [ ] //find height [min,max] of your new image on the right
//try set new scale
let newxScale = d3.scale.linear()
.domain(xDomain)
.range(rangeX);
let newyScale = d3.scale.linear()
.domain(yDomain)
.range(rangeY);
//append with new scale
selection.enter().append("circle").attr("r", 3);
selection
.attr({
cx : (d: Example2D,i) => newxscale(i),
cy : (d: Example2D) => newyscale(d.x1),
})
.style("fill", d => this.color(d.label));
}
}
If you want to reflect the data same as the thumbnail. make sure your scaleX and scaleY is in the proper format.
I've build bar chart with sorting on click: https://codepen.io/wawraf/pen/gvpXWm. It's based on Mike Bostock's chart https://bl.ocks.org/mbostock/3885705.
It works fine, but when I tried to build it from scratch I realized there is something i do not fully understand: Line 72 contains following function:
var x0 = scaleX
.domain(data.sort(sort(direction))
.map(function(d) { return d[0]; }));
So it's using variable scaleX defined before (Line 16), but when instead of "scaleX" variable I want to use raw d3 reference (which is actually the same as scaleX):
var x0 = d3.scaleBand().rangeRound([0, width - margin * 2])
.domain(data.sort(sort(direction))
.map(function(d) { return d[0]; }));
axis sorting ("g" elements) doesn't work.
I would be glad if anyone could explain why it doesn't actually work.
When you do...
var x0 = scaleX.domain(data.sort(sort(direction)).map(function(d) {
return d[0];
}));
... you are not only setting a new variable x0, but changing the scaleX domain as well. As the axis is based on scaleX, not x0, it won't do the transition in your second case, which only sets x0 (without changing scaleX).
You can certainly do:
var x0 = d3.scaleBand()
.rangeRound([0, width - margin * 2])
.domain(data.sort(sort(direction))
.map(function(d) {
return d[0];
}));
As long as you change the axis' scale:
xAxis.scale(x0);
here is the updated CodePen with those changes: https://codepen.io/anon/pen/VQveBy?editors=0010
I have a 1060*629 grid in javascript. I am using svg to create a heatmap, and this piece of code helps me to assign colors to each cell of the grid(hit is an array, which stores number of hits for each cell):
var max = d3.max(hit, function(array) {
return d3.max(array);
});
inner_scale = d3.scale.linear()
.domain([0, max]).range([0, 1]);
outer_scale = d3.scale.linear()
.domain([0, 0.5, 1])
.interpolate(d3.interpolateRgb)
.range(["white", "steelblue", "black"]);
After drawing each cell using svg-rectangle, I'd like to smooth the heatmap and fade the rough edges between neighbours. Any ideas?
I want to code a reusable chart in d3 - a "normalized" stacked bar chart. The data are scaled from 0% -100% on the Y-axis - see: http://bl.ocks.org/mbostock/3886394
I have understood that I need to calculate the inverse value of the data, to scale from 0 (Y: lowest value 0%) to 1 (Y: highest value 100%).
var y = d3.scale.linear()
.range([height, 0]); // no data domain
dataSet = dataSet.map(function (d) {
return d.map(function (p, i) {
return { x: i, y: (1 / (p.Value / 100))};
});
});
However, my scaling is not working correctly
please have a look # http://jsfiddle.net/dB96T/1/
thx!!
I am using d3js to display a realtime representation of the views of a website. For this I use a stack layout and I update my dataset by JSON at the moment.
When there is only 1 or 2 views being displayed on the y axis, which is dynamic related to the amount of views in the graph, the axis labels are: 1 => 0, 0.2, 0.4, 0.6, 0.8, 1, the axis labels are: 2 => 0, 0.5, 1, 1.5, 2 This makes no sense for my dataset since it displays views of a page, and you can't have half a view.
I have a linear scale in d3js I base my y axis on
var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height]);
According to the documentation of rangeRound() I should only get whole values out of this scale. For drawing my axis I use:
var y_axis = svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(y_inverted.axis = d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(5));
Because it is a realtime application I update this every second by calling:
function update(){
y_inverted.domain([yStackMax, 0]);
y_axis.transition()
.duration(interval)
.ease("linear")
.call(y_inverted.axis);
}
yStackMax is calculated from a stacklayout, as far as I know the data used for the y values only contain integers.
var yStackMax = d3.max(layers, function(layer) {
return d3.max(layer, function(d) {
return d.y0 + d.y;
});
});
I have tried several things to get a proper value for my y axis.
d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(5).tickFormat(d3.format(",.0f"))
Got me the closest sofar, but it still displays 0, 0, 0, 1, 1, 1
Basically what I want is to only have 1 tick when yStackMax is 1, 2 ticks when it's 2, but it should also work if yStackMax is 12 or 1,000,000
Short answer: You can dynamically set the number of ticks. Set it to 1 to display only two tick labels:
var maxTicks = 5, minTicks = 1;
if (yStackMax < maxTicks) {
y_axis.ticks(minTicks)
}
else {
y_axis.ticks(maxTicks)
}
Long Answer (going a bit off topic):
While playing with your example I came up with a rather "complete solution" to all your formatting problems. Feel free to use it :)
var svg = d3.select("#svg")
var width = svg.attr("width")
var height = svg.attr("height")
var yStackMax = 100000
var interval = 500
var maxTicks = 5
var minTicks = 1
var y_inverted = d3.scale.linear().domain([0, 1]).rangeRound([0, height])
var defaultFormat = d3.format(",.0f")
var format = defaultFormat
var y_axis = d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(minTicks)
.tickFormat(doFormat)
var y_axis_root;
var decimals = 0;
function countDecimals(v){
var test = v, count = 0;
while(test > 10) {
test /= 10
count++;
}
return count;
}
function doFormat(d,i){
return format(d,i)
}
function init(){
y_axis_root = svg.append("g")
.attr("class", "y axis")
// I modified your example to move the axis to a visible part of the screen
.attr("transform", "translate(150,0)")
.call(y_axis)
}
// custom formatting functions:
function toTerra(d) { return (Math.round(d/10000000000)/100) + "T" }
function toGiga(d) { return (Math.round(d/10000000)/100) + "G" }
function toMega(d) { return (Math.round(d/10000)/100) + "M" }
function toKilo(d) { return (Math.round(d/10)/100) + "k" }
// the factor is just for testing and not needed if based on real world data
function update(factor){
factor = (factor) || 0.1;
yStackMax*=factor
decimals = countDecimals(yStackMax)
console.log("yStackMax decimals:",decimals, factor)
if (yStackMax < maxTicks) {
format = defaultFormat
y_axis.ticks(minTicks)
}
else {
y_axis.ticks(maxTicks)
if (decimals < 3 ) format = defaultFormat
else if(decimals < 6 ) format = toKilo
else if(decimals < 9 ) format = toMega
else if(decimals < 12) format = toGiga
else format = toTerra
}
y_inverted.domain([yStackMax, 0]);
y_axis_root.transition()
.duration(interval)
.ease("linear")
.call(y_axis);
}
init()
setTimeout(update, 200)
setTimeout(update, 400)
setTimeout(update, 600)
You can try it together with this html snippet:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script>
</head>
<body>
<div><svg id="svg" width="200" height="300"></svg></div>
<script src="axis.js"></script>
<button id="button1" onclick="update(10)">+</button>
<button id="button2" onclick="update(0.1)">-</button>
</body>
</html>
I know it is a bit off topic but I usually like to provide running examples/solutions. Regard the additional formatting stuff as a bonus to the actual problem.
If you ask for a certain number of ticks (via axis.ticks() ) then d3 will try to give you that many ticks - but will try to use pretty values. It has nothing to do with your data.
Your solutions are to use tickFormat, as you did, to round all the values to integer values, only ask for one tick as Juve answered, or explicitly set the tick values using axis.tickValues([...]) which would be pretty easy used in conjunction with d3.range
rangeRound will not help in this case because it relates to the output range of the scale, which in this case is the pixel offset to plot at: between 0 and height.
Going off of Superboggly's answer, this is what worked for me. First I got the max (largest) number from the y domain using y.domain().slice(-1)[0] and then I built an array of tick values from that using d3.range()...
var y_max = y.domain().slice(-1)[0]
var yAxis = d3.svg.axis()
.scale(y)
.tickValues(d3.range(y_max+1))
.tickFormat(d3.format(",.0f"))
Or just let the ticks as they are and "hide" decimal numbers
d3.svg.axis()
.scale(y_inverted)
.orient("left")
.ticks(5).tickFormat(function(d) {
if (d % 1 == 0) {
return d3.format('.f')(d)
} else {
return ""
}
});
Here is the code:
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));