I'm just starting out with D3 and am quickly understanding that it's a pretty low level tool.
I'm using D3 to produce a Marimekko chart using this great example by Mike Bostock in
b.locks, which is in all honestly a way too advanced place to start for me, but I started using D3 because I need a Marimekko chart, so here I am.
The x-axis here has ticks, 0 to 100% with 10% intervals. If my understanding of these code excerpts is correct...
Set the x axis to a linear scale
var x = d3.scale.linear().range([0, width - 3 * margin]);
Give the x-axis 10 ticks
var xtick = svg.selectAll(".x").data(x.ticks(10))
In my usage case , I'd like to have the x-axis ticks at the irregular intervals inherent to a Marimekko chart, and the axis labels to be the category, rather than a percentage.
The desired behaviour, as far as x-axis labelling, can be illustrated by this b.locks example by 'cool Blue'
I've got as far as understanding that I need a ordinal axis rather than a linear one (as in this excerpt of cool Blue's code)
var padding = 0.1, outerPadding = 0.3,
x = d3.scale.ordinal().rangeRoundBands([0, width], padding, outerPadding);
How can I modify Mike Bostock's code to give me an example where the x-axis ticks label the column (ideally centrally), as opposed to providing a %age of the width?
I wouldn't say that D3 is that low level, since it has a lot of abstractions. However, D3 is not a charting tool (and, in that sense, it is low level...), which makes things hard for a beginner.
However, you're lucky: the changes needed here are minimal. First, you'll pass the correct data to the selection that generates the axis...
var xtick = svg.selectAll(".x")
.data(segments)
//etc..
... and then use the same math for the translate, but adding half the sum:
.attr("transform", function(d, i) {
return "translate(" + x((d.offset + d.sum / 2) / sum) + "," + y(1) + ")";
});
Of course, you'll print the key, not a percentage:
xtick.text(function(d) {
return d.key
});
Here is the updated bl.ocks: https://bl.ocks.org/anonymous/09a8881e5bab2b12e7fd46c90a63b3fd/fd7b1a7b20f8436666f1544b6774778e748934ba
Related
I've made a chart similar to the one pictured here.
Of course d3 can create the y axis, but it turns out that d3 can also give us the logic for making these arcs. Example snippet below:
var dScale = d3.scaleLinear()
.domain([-90,90])
.range([0,Math.PI]);
let arc = d3.arc()
.innerRadius(0)
.outerRadius(function(d) {return yScale(d.y)})
.startAngle(function(d) {return dScale(d.top)})
.endAngle(function(d) {return dScale(d.bottom)});
However, creating an "axis" for these degrees (dScale()) in my snippet seems way harder -- to the point where it doesn't feel like d3 anymore.
Question
Unless I'm mistaken, we'd have to get real hacky at this point and hardcode lines, ticks and axis labels using dScale, but is anyone able to help me see a more "d3-ic" (analog of pythonic?) way of achieving a degree scale?
I have a scatterplot that uses constraint relaxation to de-conflict the labels for the points that it graphs (Plunker here). The problem is that, when I relax the constraints, this causes collisions between the point labels and the x-axis labels. The axes are generated using d3.extent and d3.scale.linear.
I've tried to de-conflict the point labels and the x-axis by extending the length of the y-axis, but the closest I've come to achieving this is by changing the original value of 0 to 30 in the following stanza:
var yext = d3.extent(data, d => d[1]);
var sy = d3.scale.linear()
.domain(yext)
.range([height, 30]) // flip y-axis
.nice();
The result is less than ideal, leaving an awkward gap instead of an intersection between the x and y axes:
What I want to achieve is something like this:
(Except I want to achieve this through code, rather than Photoshop).
Can anyone demonstrate a solution? (Plunker here)
Why don't you add a padding in the domain? Like:
.domain([yext[0] * 0.95, yext[1] * 1.05])
//less here---------^-- more here----^
Here is the plunker with that solution: http://plnkr.co/edit/rKArjn7DwQa9g1X5CaNW?p=preview
Consider a sequence of data along the following lines:
data = [{angle:1.2,value:1.2},...,{angle:355.2: value:5.6}];
I'd like to display this data on a radially scaled plot (i.e. circular bands indicating how high the value of each point is) to show angle vs value. Angles will change by a small but uncontrollable quantity for each data set but there will always be ~50 of them spaced fairly evenly around the chart.
It looks like chart.js has two options which don't quite fit the bill:
A Radar plot which appears to require a label per point but without an obvious way to control where those labels are applied.
An x-y scatter which I could calculate x/y co-ordinates for but which doesn't have the radial scale to help visualise the value of each point.
Is there a way to combine the two perhaps or some option I've missed to control them to achieve the result I'm looking for here?
Edit - for example, this shows the data but lacks a radial scale:
https://jsfiddle.net/7d7ghaxx/4/
**Edit2 - This is the sort of thing I Would expect to see as a result:
Demo & Code :
https://stackblitz.com/edit/js-jp4xm4?file=index.js
Explanation:
Used a scatter chart to plot points
Added (wrote) a chartjs plugin that converts points from polar to cartesian on beforeUpdate so you don't have to worry about converting before every update
Made x & y grid (not axes through origin) hide by adding gridLines: { color: 'transparent' } and ticks: { display: false }
Made min and max (options in ticks) of both axes equal so that the orgin is at the center
Added a radialLinear scale for the polar grid
(Update 1)
Added a tooltip label callback to show tooltip as (r,θ) instead of default (x,y)
(Update 2)
Added (wrote) a beforeDraw plugin to fill the ctx with light blue color as the OP wanted
PS: (Pointing out just to be a little competitive) I have used chartjs (unlike other answers) because the OP wants a chartjs solution as it's clearly written in the question: "using chart.js". There might be solutions better than chartjs but that's irrelevant.
You can use D3 js Charts is usefull for radar chart check the example link bellow :
//////////////////////////////////////////////////////////////
//////////////////////// Set-Up //////////////////////////////
//////////////////////////////////////////////////////////////
var margin = {top: 100, right: 100, bottom: 100, left: 100},
width = Math.min(700, window.innerWidth - 10) - margin.left - margin.right,
height = Math.min(width, window.innerHeight - margin.top - margin.bottom - 20);
//////////////////////////////////////////////////////////////
////////////////////////// Data //////////////////////////////
//////////////////////////////////////////////////////////////
var data = [
[//Yourchart values
{axis:"",value:0.052},
{axis:"",value:0.052},
{axis:"",value:0.012},
{axis:"",value:0.012},
{axis:"",value:0.022},
{axis:"",value:0.052},
{axis:"",value:0.052},
{axis:"",value:0.021}
]
];
//////////////////////////////////////////////////////////////
//////////////////// Draw the Chart //////////////////////////
//////////////////////////////////////////////////////////////
var color = d3.scale.ordinal()
.range(["#6cbb69","#CC333F","#00A0B0"]);
var radarChartOptions = {
w: 500,
h: 300,
maxValue: 0.15,
levels: 5,
roundStrokes: true,
color: color
};
//Call function to draw the Radar chart
RadarChart(".radarChart", data, radarChartOptions);
https://codepen.io/Nighamza/pen/bKmjGE
I'm using Mike Bostock's example as a template and building on it. My bar chart here.
After transition to stacked version, I am unable to get the y position of the bars. Bars of higher height overshadow the smaller ones. Most likely because of the valueOffset attribute of the stack. I am stuck on this issue for few days now.
Changes from Mike's example:
removed group labels in stacked chart
new y-axis y2 on linear scale. The domain for this axis is from 0 to the maximum of all the sums of values in each year which is 141.
defined new stack stack_year for relative positions of the bars.
Relevant code:
// y2 definition
y2.domain([0, d3.max(dataByGroup_year, function(d) { return d.year_wise_sum; })]).range([height, 0]);
// calculates sum of all wins per year
dataByGroup_year.forEach(function(d) {
var order = d.values.map(function(d) { return d.value; });
d.year_wise_sum = d3.sum(order);
});
function transitionStacked() {
var t = svg.transition().duration(750),
g = t.selectAll(".group").attr("transform", "translate(0," + y0(y0.domain()[0]) + ")");
g.selectAll("rect").attr("x", function(d) { return x(d.year); })
.attr("y", function(d) { return height - y2(d.valueOffset); })
.attr("height", function(d) { return height - y2(d.value); });
g.selectAll(".group-label").text("");
}
y0 is the ordinal scale used for multiple charts. y1 is the linear scale used for each chart in multiple charts.
Full HTML code at github
Data used: input file. I disabled tips for each bar.
Update: JSFIDDLE
Any help is much appreciated! Thank you
There were a number of issues here, which I've fixed up in this fiddle: http://jsfiddle.net/henbox/wL9x6cjk/4/
Part of the problems was the data itself (as per my comment above). There were some repeated values, which was causing issues when calculating the valueOffset correctly (using the d3.layout.stack)
I've also made some changes to how the y and attribute for each rect are calculated in the transitionStacked function. I changed what you had:
.attr("y", function(d) {
return height - y2(d.valueOffset);
})
to:
.attr("y", function (d) {
return y2(d.value + d.valueOffset) - height;
})
Note that you need to sum the d.value and d.valueOffset, before applying the scaling, to calculate the top left corner position of the rect. Additionally, you don't need to recalculate the x attribute value since this doesn't change between the two chart views, so I removed it
I also removed the call to stack_year(dataByGroup_year);. You don't need to build the stack layout here, just to calculate the maximum sum per year.
Finally I also tidied up the y-axis positioning a bit so there's enough space for the x-axis labels, and simplified the positioning of group elements in the stacked view. I also moved the x-axis to be appended to svg rather than group, which simplified positioning of elements
I created a bar chart (fiddle) using D3.js.
I have one (probably very simple) question for which I would be glad to get some support:
Why is the x-axis too short?
Older question, and my first answer attempt on Stack, but hopefully still useful:
From what I can tell your X domain only goes to XMax, which will be the bottom-left point of the last bar of the graph - it's not coming up "short", it just doesn't know how wide the bar is after XMax.
(It does not account for you adding the barWidth of 20 pixels, and your text placement does account for the additional width, which seems to be why you're seeing the problem only with the Axis.)
In order to extend the axis all the way to the end, you'll have to account for the additional barWidth in your domain, or append the additional barWidth another way. That might be possible by adding another half day or so on to your xMax, or by shifting each bar to the left of its X position (-barWidth) instead of +barWidth.
The x-axis isn't too short (it's exactly tall enough for your max value) but you might have wanted it to be nice.
var y = d3.scale.linear()
.domain([0, yMax])
.range([h - margin.top - margin.bottom, 0])
.nice();