I have a scale in which I don't want the negative signs to appear before the numbers - how can this be done in the d3 formatter? The scale is built as follows:
var formater = d3.format("0");
self.x = d3.scale.linear().domain([self.xmin, self.xmax]).range([0, self.settings.width])
self.axis = d3.svg.axis()
.scale(self.x)
.orient("bottom")
.tickFormat(formater);
self.axisLabels = self.svg.append("g")
.attr("class", "axis")
.attr("id", "axis")
.call(self.axis)
I see an option to add a "+" sign but not remove a "-" sign https://github.com/mbostock/d3/wiki/Formatting#wiki-d3_format
Also, is it possible to remove one label? I'm labeling from -5 to 5 on the scale, and don't want the negative signs to appear, and I don't want to label 0. Thanks.
You are using a formatter already and you do not need to rely on D3 to remove the '-' sign, you can do it yourself:
var formatter = d3.format("0");
// ...
self.axis = d3.svg.axis()
.scale(self.x)
.orient("bottom")
.tickFormat(function (d) {
if (d === 0) return ''; // No label for '0'
else if (d < 0) d = -d; // No nagative labels
return formatter(d);
});
Related
My D3 chart has a Y axis which displays some ordinal values. When the data updates, changing the list of ordinal values, I want to transition to the new axis values. This would be fine except that the text displayed for each 'tick' of the axis needs wrapping to fit the web page the chart appears within. When I use a transition in calling the axis the text wrapping stops working.
My Y axis is initially created as follows:
this.yAxisTaskTypeGroup = this.chartGroup
.append("g")
.attr("id", "yAxisTaskTypeGroup")
.attr("class", "y-axis-tasktype")
.attr("transform", "translate(0," + this.yAxisTaskTypeVerticalOffset + ")");
My scale is defined as follows:
this.yScaleTaskType = d3
.scaleOrdinal(this.yScaleTaskTypeRange)
.domain(this.yScaleTaskTypeDomain);
My Y axis is defined as follows:
this.yAxisTaskType = d3.axisLeft(this.yScaleTaskType)
.tickSize(0);
After the values for the Y axis scale's domain and range have been updated I update the axis as follows:
this.yAxisTaskTypeGroup
.transition(this.timelineTransition) // apply a transition
.call(this.yAxisTaskType)
.selectAll(".tick text")
.call(this.wrapText, this.margin.left, this.yAxisTaskTypeWrappedVerticalOffset);
The wrapText finction is as follows:
wrapText(text, width, wrappedTextOffset) {
text.each(function () {
let text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
x = text.attr("x"),
y = text.attr("y"),
dy = 0, //parseFloat(text.attr('dy')),
tspan = text.text(null)
.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
let node: SVGTSpanElement = <SVGTSpanElement>tspan.node();
let hasGreaterWidth = node.getComputedTextLength() > width;
if (hasGreaterWidth) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", lineHeight + dy + "em")
.text(word);
text.attr("y", wrappedTextOffset);
};
};
});
};
If I remove the transition the text remains wrapped correctly and updates correctly. With the transition added, the text wrapping doesn't work, though the values update still. I want to calculate the text wrap and then transition the ticks to their new locations.
Can anyone explain where I am going wrong please?
In the d3.v4 documentation the following is stated:
To generate ticks every fifteen minutes with a time scale, say:
axis.tickArguments([d3.timeMinute.every(15)]);
Is there a similar approach that can be used with values other than time? I am plotting sine and cosine curves, so I'd like the ticks to begin at -2*Math.PI, end at 2*Math.PI, and between these values I'd like a tick to occur every Math.PI/2. I could, of course, explicitly compute the tick values and supply them to the tickValue method; however, if there is a simpler way to accomplish this, as in the time-related example quoted above, I'd prefer to use that.
Setting the end ticks and specifying the precise space of the ticks in a linear scale is a pain in the neck. The reason is that D3 axis generator was created in such a way that the ticks are automatically generated and spaced. So, what is handy for someone who doesn't care too much for customisation can be a nuisance for those that want a precise customisation.
My solution here is a hack: create two scales, one linear scale that you'll use to plot your data, and a second scale, that you'll use only to make the axis and whose values you can set at your will. Here, I choose a scalePoint() for the ordinal scale.
Something like this:
var realScale = d3.scaleLinear()
.range([10,width-10])
.domain([-2*Math.PI, 2*Math.PI]);
var axisScale = d3.scalePoint()
.range([10,width-10])
.domain(["-2 \u03c0", "-1.5 \u03c0", "-\u03c0", "-0.5 \u03c0", "0",
"0.5 \u03c0", "\u03c0", "1.5 \u03c0", "2 \u03c0"]);
Don't mind the \u03c0, that's just π (pi) in Unicode.
Check this demo, hover over the circles to see their positions:
var width = 500,
height = 150;
var data = [-2, -1, 0, 0.5, 1.5];
var realScale = d3.scaleLinear()
.range([10, width - 10])
.domain([-2 * Math.PI, 2 * Math.PI]);
var axisScale = d3.scalePoint()
.range([10, width - 10])
.domain(["-2 \u03c0", "-1.5 \u03c0", "-\u03c0", "-0.5 \u03c0", "0", "0.5 \u03c0", "\u03c0", "1.5 \u03c0", "2 \u03c0"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var circles = svg.selectAll("circle").data(data)
.enter()
.append("circle")
.attr("r", 8)
.attr("fill", "teal")
.attr("cy", 50)
.attr("cx", function(d) {
return realScale(d * Math.PI)
})
.append("title")
.text(function(d) {
return "this circle is at " + d + " \u03c0"
});
var axis = d3.axisBottom(axisScale);
var gX = svg.append("g")
.attr("transform", "translate(0,100)")
.call(axis);
<script src="https://d3js.org/d3.v4.min.js"></script>
I was able to implement an x axis in units of PI/2, under program control (not manually laid out), by targetting the D3 tickValues and tickFormat methods. The call to tickValues sets the ticks at intervals of PI/2. The call to tickFormat generates appropriate tick labels. You can view the complete code on GitHub:
https://github.com/quantbo/sine_cosine
My solution is to customise tickValues and tickFormat. Only 1 scale is needed, and delegate d3.ticks function to give me the new tickValues that are proportional to Math.PI.
const piChar = String.fromCharCode(960);
const tickFormat = val => {
const piVal = val / Math.PI;
return piVal + piChar;
};
const convertSIToTrig = siDomain => {
const trigMin = siDomain[0] / Math.PI;
const trigMax = siDomain[1] / Math.PI;
return d3.ticks(trigMin, trigMax, 10).map(v => v * Math.PI);
};
const xScale = d3.scaleLinear().domain([-Math.PI * 2, Math.PI * 2]).range([0, 600]);
const xAxis = d3.axisBottom(xScale)
.tickValues(convertSIToTrig(xScale.domain()))
.tickFormat(tickFormat);
This way if your xScale's domain were changed via zoom/pan, the new tickValues are nicely generated with smaller/bigger interval
I am new to D3Js.i am following this http://codepen.io/benlister/pres/bNeLQy for stacked bar graph.I am not sure how to make the y axis absolute.Here it is shown in % .I tried adding
y.domain([0,500]);
It didnt work.Please help.
First, if you are talking about the % symbol, you have to change the tick format.
The code is this:
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".0%"));
Just remove the tickFormat, or change it to the format you want.
But your problem is bigger than that: the y axis shows percent because the data is being transformed to percentages. This is the code that does the math:
data.forEach(function (d) {
var y0 = 0;
d.rates = color.domain().map(function (name) {
console.log();;
return {
name: name,
y0: y0,
y1: y0 += +d[name],
amount: d[name]
};
});
d.rates.forEach(function (d) {
d.y0 /= y0;
d.y1 /= y0;
});
Check this example to see how to do it with absolute values:
https://bl.ocks.org/mbostock/3886208
Try changing this line
var yAxis = d3.svg.axis().scale(y).orient("left").tickFormat(d3.format(".0%"));
to
var yAxis = d3.svg.axis().scale(y).orient("left").tickFormat(d3.format("d"));
for integer values on your y axis.
See here for more details.
I have made a graph with log scale for which there are grid lines in the background. The grid lines are drawn for the sub-ticks also which i want to remove. can anyone know how to do it. I have tried it using linear scale also but it wasn't looking proper on the log graph. Please suggest some tricks. Thank you.
Here is the link to image of the graph:
http://snag.gy/24j4i.jpg
Below is my code to generate grid lines
function make_x_axis1() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(12)
}
function make_y_axis1() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(8)
}
svg1.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_axis1()
.tickSize(-height, 0, 0)
.tickFormat("")
);
svg1.append("g")
.attr("class", "grid")
.call(make_y_axis1()
.tickSize(-width,0, 0)
.tickFormat("")
);
I think what you are looking for is the .tickSubdivide setting on your axis. There are a bunch of examples at http://bl.ocks.org/GerHobbelt/3605035. This seems to be the relevant one that you are looking for:
svg.append("g")
.attr("class", "x minor endticks classical log_x")
.attr("transform", "translate(0," + axis_y + ")")
.call(d3.svg.axis().scale(log_x).tickSubdivide(example_subdiv_A(log_x))
.tickSize(12, function(d, i) {
return (2 * d.subindex == d.modulus ? 8 :
((5 * d.subindex == d.modulus) && (d.modulus >= 10) ? 6 :
((d.subindex % 2 == 0) && (d.modulus > 10) ? 5 :
(d.modulus > 10 ? 2 : 4))));
}, -10))
.call(descr("classical logarithmic, with output-dependent # of subticks, emphasis on the 2nd and other even (but only when we have 10+ subticks!) and .5 subdiv, and longer start/end ticks"));
Here he varies not only the number of subticks but also their size. If you don't want the subticks at all, you can set tickSubdivide to 0 to remove them. If you want them just as ticks on the axis, you'll need to use .tickSize and do a little math to set the tick mark size for major tick marks separately from minor tick marks.
I ran into this problem today and found a solution. I created a function to remove those gridlines manually. The gridlines which you want to remove are the one that does not have the text value.
function removeNoNameTicks(logAxisSelector) {
var targetAxis = d3.select('g.x.axis');
var gTicks = targetAxis.selectAll('.tick');
if (gTicks.length > 0 && gTicks[0]) {
gTicks[0].forEach(function (item, index) {
// find the text if the text content is ""
var textElement = d3.select(item).select('text');
if (textElement.html() == '') {
item.remove();
}
});
}
}
I'm creating a line-chart in d3js, which draws a graph of your performance over time. This means my data is a certain score at a certain point in time. Example:
2011-01-01: 75
2012-01-01: 83
2013-01-01: 50
Now I don't want to display the score as integer values on my Y-axis, but I'd like to map the integer values into useful words. Example:
a score between 50 and 70 means you've scored Excellent
a score between 25 and 50 means you've scored Very Good
etc.
What's the best way for doing this?
The implementation of my axis is as follows:
var y = d3.scale.linear().range([settings.height, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.ticks(5)
.orient("left");
y.domain(d3.extent(data, function(d) { return d.score; }));
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("x", (settings.width - 10 ))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(settings.labels.y);
You can define your own tickFormat. For example:
function scoreFormat(d) {
if (d <= 70 && d > 50) {
return "Good";
} else if (d <= 50 && d > 25) {
return "Bad";
}
return "Ugly";
}
var yAxis = d3.svg.axis()
.scale(y)
.ticks(5)
.orient("left")
.tickFormat(function(d) { return scoreFormat(d); });
Check out d3.scale.quantize, which takes a domain similar to a linear scale but breaks it to discrete range values in even chunks. If even sized chunks won't work for you, d3.scale.threshold is a similar idea except you can define your own mapping between subsets of the domain and the discrete range values.