Rescale axis ticks and text in d3.js multi y-axis plot - javascript

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?

Related

Issue with displaying correct domain values in legend tick

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());

How to set y domain for dc js box plot? Does it handle negative values?

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.

D3 Bounded Panning

I've been at this for a few days now and I can't get this chart to obey panning bounds. Initially, the data could be pulled off the page both negatively and positively, but I've been able to stop the negative by following this blog post. I'll paste what I believe is the relevant code here, but the file is way to long to include.
The chart in question is an elevation chart made up of concatenated area objects that are colored according to their gradient.
There's a commented out line that is the one giving trouble. I've put question marks in place of what is supposed to be a max bound. For some reason, I can't find the max bound of the data area.
Here's a Plunkr
// Set up the size of the chart relative to the div
var x = d3.scale.linear().range([0, (width-80)]);
var y = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);
// Define the look of the axis
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5);
var yAxisRight = d3.svg.axis().scale(y1).orient("left").ticks(5);
// Areas are segments of the chart filled with color
var area = d3.svg.area()
.x(function(d) { return x(d.distance); })
.y0(height)
.y1(function(d) { return y(d.elevation); });
// Functions for handling zoom events
var gradientZoomListener = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", gradientZoomHandler);
function gradientZoomHandler() {
var t = gradientZoomListener.translate(),
tx = t[0],
ty = t[1];
tx = Math.min(tx, 0);
// tx = Math.max(tx, ??);
gradientZoomListener.translate([tx,ty])
gradientChart.select(".x.axis").call(xAxis);
gradientChart.select(".y.axis").call(yAxis);
gradientChart.selectAll('.area').attr('d', area);
}
The most intuitive way to do it in this case is to check what the scale maps the bounds of the input domain to, rather than checking the pixel values. The idea is that the lower bound of the domain should map to 0 (the lower bound of the output range) or less, and the upper bound to the upper bound of the output range or more.
If the lower bound maps to less than 0, the value is to the left of the graph, i.e. the lowest shown value is more than the bound. If it is larger than 0, there must be a gap between the y axis and the first value. Similarly for the upper bound, if it maps to less than the upper bound of the output range, there must be a gap between it and the end of the graph.
In code, this looks as follows.
if(x(xExtent[0]) > 0) {
tx = 0;
} else if(x(xExtent[1]) < x.range()[1]) {
tx -= x(xExtent[1]) - x.range()[1];
}
The only non-trivial thing is the adjusting of the translation value if there's a gap between the largest value and the end of the graph. The size of this gap is the difference between where the largest input value is projected to and the largest output value. The gap is then subtracted from the current translation value to close it.
Complete example here. Note that I've moved some code around to get access to the values which are only known when the data has been read.
It works the same way for the y axis.

Add labels to a d3 bar chart

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.

D3 - using strings for axis ticks

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");

Categories

Resources