trying to learn D3 and on the label axis seem to be stacked upon one another.
I have tried to turn the scale calls into reusable functions however this may be exacerbating the issue.
The data:
data=[];
data.push({'label':'#1','cost':15000,'saving':18000});
data.push({'label':'#2','cost':40000,'saving':65000});
data.push({'label':'#3','cost':55000,'saving':80000});
data.push({'label':'#4','cost':15000,'saving':30000});
The scale:
function getY(d){
return yScale = d3.scaleBand()
.domain(d3.range(0, d.length))
.range([0, (height-20)])
.paddingInner(0.1)
.paddingOuter(0.1)
}
Trying to build the axis:
labels=[]
$.each(data,function(){
labels.push(this.label);
})
var hAxis = d3.axisRight()
.scale( getY(data) )
.tickValues( labels )
Everything seems to be working but the labels seem to be receiving something incorrect on the translate:
<g class="tick" opacity="1" transform="translate(0,NaN)"><line stroke="#000" x2="6">
Where is the NaN being received from, I assume an incorrectly coded scale.
Fiddle:
https://jsfiddle.net/L9mmy5d6/
tickValues specifies which values should have ticks, not what those ticks should look like. You can only specify values in the domain of the scale here.
If you have a standardized change you want (say, adding # to each number (and incrementing by one so that the numbers start at 1)), then you could use:
var hAxis = d3.axisRight()
.scale( getY(data) )
.tickFormat(function(d,i) { return "#"+(d+1) } )
(fiddle)
Alternatively, if you want to pursue the same approach that you have, you could use something like:
var hAxis = d3.axisRight()
.scale( getY(data) )
.tickFormat(function(d,i) { return labels[i] })
(fiddle)
Keep in mind that your number of ticks should correspond to the the number of labels if using the index. You could also use d to set the label in this case, as the tick marks correspond to the index.
Related
I am working on continuous color legend using d3.interpolateViridis. I have problem in displaying the legend tick values. I want to display my min(at one end) and max(at another end) (domain values) in legend. I tried changing ticks value but no help.
Here is my code snippet:
//scale
var colorScale2 = d3.scaleSequential(d3.interpolateViridis).domain([0, 0.38]);
//other code
var legendscale = d3.scaleLinear()
.range([0, legendheight - margin.top - margin.bottom])
.domain(colorscale.domain());
// scale tick
var legendaxis = d3.axisRight()
.scale(legendscale)
.tickSize(16)
.ticks(2);
Also, I have shared JS fiddle link where it takes tick as 0.0 and 0.2(this is supposed to be max value: 0.38).
https://jsfiddle.net/shru90/e42vcLy0/30/
Note: My min value is 0 and max is 0.38(which can vary based on data)
By default, the axis puts ticks at nice, round values. If there are specific values that you want to have tick marks for, then you can set the tickValues:
d3.axisRight()
.scale(legendscale)
.tickSize(16)
.tickValues(legendscale.domain());
Summary:
My doubts are regarding the dc.js box plot chart. I am not able to successfully set domain for the y axis of the chart. Also can box plot handle negative values?
In Detail:
I am getting an error on the console:
Error: Invalid value for <circle> attribute cy="NaN"
Closer examination revealed that the problem is with the box plot indeed. The relevant code is given below:
alltwtssearchtrackdim = alltwtsndx.dimension(function (d) {return d.searchtrack;});
alltwtssearchtrackboxplotgroup = alltwtssearchtrackdim.group().reduce(
function(p,v) {
p.splice(d3.bisectLeft(p, v.sentiment), 0,v.sentiment);
return p;
},
function(p,v) {
p.splice(_.indexOf(p, v.sentiment, true), 1);
return p;
},
function() {
return [];
}
);
boxplotsentiment
.width(500)
.height(250)
.margins({top: 10, right: 50, bottom: 30, left: 30})
.y(d3.scale.linear().domain([-1.000, 1.000]))
.dimension(alltwtssearchtrackdim)
.group(alltwtssearchtrackboxplotgroup)
.elasticY(true)
.elasticX(true)
boxplotsentiment.tickFormat(d3.format('.3f'));
Now sentiment can vary from -1 to +1. Its the sentiment associated with tweets while search track is an ordinal variable. I checked the group. The value array and quartiles are calculated.
Problems that I am having:
Not able to set y axis domain between -1 and +1
The error I am getting on the console: Is it due to negative values?
Kindly help
Not exactly an answer to your question, but I had problems in fitting the y axis to the value extent when working with small numbers. Turns out the y axis extent is too large on small numbers. In these cases, try setting
.elasticY(true)
.yAxisPadding('10%')
or some other sane value to yAxisPadding.
I have a parallel coordinates plot that is based off this code: http://bl.ocks.org/syntagmatic/2409451
I am trying to get the tick marks and the numbers on the y axes to scale from the min to the max of the data rather than autoscaling to the conveniently linear numbers like it currently done.
I have not been able to find any example of using d3 or js where a plot of any sort does this unless the data happens to land on those values.
I have been able to just show the min and max value, but cannot get ticks between these by replacing the 3rd line of //Add an axis and title with:
.each(function(d) {d3.select(this).call(d3.svg.axis().scale(y[d]).tickValues(y[d].domain()).orient("left")); })
For reference, the data file is read in as a .csv and ends up looking like this with alphabet representing the headings in the .csv file:
var example_data = [
{"a":5,"b":480,"c":250,"d":100,"e":220},
{"a":1,"b":90,"c":50,"d":33,"e":88}
];
EDIT:
The main issue is iterating over the array that has the domains for each column to create a new array with the tick values. Tick values can be set using:
d3.svg.axis().scale(y[d]).tickValues(value 1[d],value 2[d], etc)
y[d] is set by:
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) {
return d != "name" && (y[d] = d3.scale.linear()
.domain(d3.extent(cars, function(p) { return +p[d]; }))
.range([h, 0]));
}));
Since you have the min and the max you can map them in any way you want to any scale you want [y0,yn]. For example with y0 = 100, yn = 500 (because HTML counts from top and down).
Here I use a linear scale
d3.scale.linear()
.domain([yourMin,yourMax])
.range([y0,yn]);
Does this help?
I have a d3 barchart for which I pull data from firebase. I want to add the labels to the x-axis. Here is the code of my barchart:
new Firebase('https://exampl.firebaseIO.com/example').on('value', function (snapshot) {
var lst = [];
snapshot.forEach(function(childSnapshot) {lst.push(childSnapshot.val());});
var magValue = new crossfilter(lst).dimension(function (d) {return d.count;});
var magLabel = new crossfilter(lst).dimension(function (d) {return d.Owner;});
dc.barChart("#dc-magnitude-chart")
.width(480)
.height(150)
.margins({top: 10, right: 10, bottom: 20, left: 40})
.dimension(magValue) // the values across the x axis
.group(magValue.group().reduceSum(function(d) {return d.count;})) // the values on the y axis
.transitionDuration(500)
.centerBar(true)
.gap(56) // bar width Keep increasing to get right then back off.
.x(d3.scale.linear().domain([0.5, 7.5]))
.elasticY(true)
.xAxis().tickFormat(function(v) {return v;});
dc.renderAll();
});
magValue is the simple count of occurences and it is diplayd on the x-axis. I want the names that are stored in magLabel variable to be displayed below the counts. Thanks.
For reference: in the comments on #bencripps answer, the OP talks about using xAxis.tickValues(['One','two','three','four','five','six','seven']).
tickValues is actually if you want to specify custom ticks within the scale you're using. Right now, you're using a linear scale:
.x(d3.scale.linear().domain([0.5, 7.5]))
so it expects your tick values to be points on that scale where it can draw ticks. If you had something more like xAxis.tickValues([1, 2, 3, 4, 5, 6, 7]), it should work.
However, it sounds like you don't actually want a linear scale. d3 also has other scale types, and it sounds like the one you want is an Ordinal Scale. Ordinal scales are the scales you typically think of for bar charts; they're a type of scale that has a discrete domain. In this case, you could try changing your scale to something like:
.x(d3.scale.ordinal().domain(['One','two','three','four','five','six','seven']))
so it uses an ordinal scale instead. Since you're using dc.js, you'll also need to specify
.xUnits(dc.units.ordinal)
so that it knows to use ordinal marks.
Here is some simple code for adding an X axis; you will have to fiddle with the domain, and range to get the desired length, and number of ticks.
var XaxisScale = d3.scale.linear()
.domain([0,150]) //you will have to set the domain, not sure what you want
.range([0, magValue.length)
var xAxis = d3.svg.axis()
.scale(XaxisScale)
.ticks( magValue.length ) // this you will need to set as well
var xAxisGroup = $('"#dc-magnitude-chart').append("g")
.attr('class', 'axis')
.attr('transform', 'translate(5,8)')
.call(xAxis);
note, the .call(xAxis) is what is actually appending the X axis to your chart.
I want to crate a bar chart using strings as the labels for the ticks on the x-axis (e.g., Year 1, Year 2, etc instead of 0,1,2, etc).
I started by using the numeric values for the x-axis (e.g., 0,1,2,3, etc) as follows:
1) I generate my ranges:
x = d3.scale.ordinal()
.domain(d3.range(svg.chartData[0].length)) //number of columns is a spreadsheet-like system
.rangeRoundBands([0,width], .1);
y = d3.scale.linear()
.domain(Math.min(d3.min(svg.chartData.extent),0), Math.max(d3.min(svg.chartData.extent),0)])
.range([height, 0])
.nice();
2) Make the axes:
d3.svg.axis()
.scale(x);
3) Redraw the axes:
svg.select(".axis.x_axis")
.call(make_x_axis().orient("bottom").tickSubdivide(1).tickSize(6, 3, 0));
This works well with default numeric axis labels.
If I try to use an array of strings for the x tickValues like this....
d3.svg.axis()
.scale(x).tickValues(svg.pointsNames); //svg.pointsNames = ["Year 1", "year 2", etc.]
... when I redraw the chart (with or without changes to the data/settings), the labels swap like this.
Notice how Col 1 takes the place of Col 0 and vice versa.
Do you know why this happens?
Update
Just sort the svg.pointsNames before you apply them as tickValues. Make sure you sort them in exactly the same way that you sort your data. This way, a one-one mapping is always maintained between your labels and tick values.
Also if I may, check out the tickFormat` function here. This seems a better option to me.
//Tick format example
chart,xAxis.tickFormat(function(d, i){
return "Year" + d //"Year1 Year2, etc depending on the tick value - 0,1,2,3,4"
})
Thanks for that...I used this function with an inline if-clause to handle a different x-axis series with names instead of numbers.
The factions Array consists of all relevant names sorted by the indexes of the series who then just get matched with its corresponding index in the data.
xAxis = d3.svg.axis().scale(xScale)
.tickFormat(function(d) {
if(seriesX == 'seriesValue'){
return factions[d]}
else{
return d}
})
.orient("bottom");