I need to display micromoles per liter (µmol/L) in my chart's tickFormat, but when I pass in "µmol/L", it shows the characters "µ" instead of the symbol for mu. How do I get it to render the symbol?
In that case, you shouldn't use an HTML entity. Once you're dealing with an SVG , use this:
\u00B5
Check this snippet:
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 200);
var scale = d3.scaleLinear()
.range([40, 460])
.domain([0, 100]);
var axis = d3.axisBottom(scale)
.tickFormat(function(d){ return d + "\u00B5mol/L"})
.ticks(5);
svg.append("g")
.attr("transform", "translate(0,100)")
.call(axis);
text { font-size: 14px;}
<script src="https://d3js.org/d3.v4.min.js"></script>
Related
The multiple line chart example at https://www.d3-graph-gallery.com/graph/line_smallmultiple.html quite clearly provides the examples I need for what I'm trying to do...
except...
I need the y-axis scale for each of the charts to be appropriate for the data associated with the individual keys. As is, the example does d3.max on the entire data set, not the filtered data set controlling the individual lines.
I've tried various ways to apply the filter in the y-axis definition and can't get anything to work.
The closest I've been able to get is to make it use the max value from one of the specific keys for all the charts.
var y = d3.scaleLinear()
// .domain([0, d3.max(data, function(d) { return +d.n; })])
.domain([0, d3.max(data.filter(d => d.name === "Helen"), e => +e.n)])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y).ticks(5));
I think I want it to filter d.name against the CURRENT-CHART key (whatever it might be) rather than a specific one (like "Helen" above), but can't figure out how to do it. Is it some feature of nesting that I haven't found yet? Something amazingly simple that I can't see??
Any suggestions?
Thanks in advance
I have built a demo for you, i hope you are looking for something like this. Please let me know if there is any issue.
// set the dimensions and margins of the graph
var margin = {top: 30, right: 0, bottom: 30, left: 50},
width = 210 - margin.left - margin.right,
height = 210 - margin.top - margin.bottom;
//Read the data
d3.csv("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/5_OneCatSevNumOrdered.csv", function(data) {
// group the data: I want to draw one line per group
var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
.key(function(d) { return d.name;})
.entries(data);
// What is the list of groups?
allKeys = sumstat.map(function(d){return d.key})
// Add X axis --> it is a date format
var x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.year; }))
.range([ 0, width ]);
// color palette
var color = d3.scaleOrdinal()
.domain(allKeys)
.range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'])
// Add an svg element for each group. The will be one beside each other and will go on the next row when no more room available
var svg = d3.select("#my_dataviz")
.selectAll("uniqueChart")
.data(sumstat)
.enter()
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")")
.each(multiple);
svg
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(3));
// Add titles
svg
.append("text")
.attr("text-anchor", "start")
.attr("y", -5)
.attr("x", 0)
.text(function(d){ return(d.key)})
.style("fill", function(d){ return color(d.key) })
function multiple(item) {
var svg = d3.select(this);
var y = d3.scaleLinear()
.domain([0, d3.max(item.values, function(d) { return +d.n; })])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y).ticks(5));
var line = d3.line()
.x(function(d) { return x(+d.year); })
.y(function(d) { return y(+d.n); });
// Draw the line
svg
.append("path")
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 1.9)
.attr("d", line(item.values))
}
})
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
I need to display micromoles per liter (µmol/L) in my chart's tickFormat, but when I pass in "µmol/L", it shows the characters "µ" instead of the symbol for mu. How do I get it to render the symbol?
In that case, you shouldn't use an HTML entity. Once you're dealing with an SVG , use this:
\u00B5
Check this snippet:
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 200);
var scale = d3.scaleLinear()
.range([40, 460])
.domain([0, 100]);
var axis = d3.axisBottom(scale)
.tickFormat(function(d){ return d + "\u00B5mol/L"})
.ticks(5);
svg.append("g")
.attr("transform", "translate(0,100)")
.call(axis);
text { font-size: 14px;}
<script src="https://d3js.org/d3.v4.min.js"></script>
I have currently have a quick test for a graph I'm about to create for website and I have made the most basic functionality. I have a graph, a 4 elements and an x and a y axis and a zoom functionality.
My problem lies in the fact that when I zoom on the graph, the elements are able to reach the axis and overlap it. I've pasted my source code below
//Setting generic width and height values for our SVG.
var margin = {top: 60, right: 0, bottom: 60, left: 40},
width = 1024 - 70 - margin.left - margin.right,
height = 668 - margin.top - margin.bottom;
//Other variable declarations.
//Creating scales used to scale everything to the size of the SVG.
var xScale = d3.scale.linear()
.domain([0, 1024])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([1, 768])
.range([height, 0]);
//Creates an xAxis variable that can be used in our SVG.
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
//Zoom command ...
var zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([1, 10])
.on("zoom", zoomTargets);
// The mark '#' indicates an ID. IF '#' isn't included argument expected is a tag such as "svg" or "p" etc..
var SVG = d3.select("#mainSVG")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
//Create background. The mouse must be over an object on the graph for the zoom to work. The rectangle will cover the entire graph.
var rect = SVG.append("rect")
.attr("width", width)
.attr("height", height);
//Showing the axis that we created earlier in the script for both X and Y.
var xAxisGroup = SVG.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var yAxisGroup = SVG.append("g")
.attr("class", "y axis")
.call(yAxis);
//This selects 4 circles (non-existent, there requires data-binding) and appends them all below enter.
//The amount of numbers in data is the amount of circles to be appended in the enter() section.
var circle = SVG
.selectAll("circle")
.data([40,100,400,1900])
.enter()
.append("circle")
.attr("cx",function(d){return xScale(d)})
.attr("cy",function(d){return yScale(d)})
.attr("r",20);
//Resets zoom when click on circle object. Zoom work now, should be changed to a button instead of click on circle though.
SVG.selectAll("circle").on("click", function() {
zoom.scale(1);
zoom.translate([0,0]);
zoomTargets();
});
function zoomTargets() {
SVG.select(".x.axis").call(xAxis);
SVG.select(".y.axis").call(yAxis);
SVG.selectAll("circle").attr("cx",function(d){return xScale(d)}).attr("cy",function(d){return yScale(d)});
}
function resetZoom() {
zoom.scale(1);
zoom.translate([0,0]);
zoomTargets();
}
I've tried using "append("g2") before creating a circle to I can make g2 smaller than the entire svg, but that doesn't seem to work. As far as I have understood, you can just append a new element inside your existing one. I'm guessing I'm wrong since it hasn't worked for me.
Thank you in advance for your help.
Leave a small gap between the most extreme data point and the axis. In particular, you may want the range of your domain to take the margins into account:
var xScale = d3.scale.linear()
.domain([0, 1024])
.range([0, width-margin.right]);
var yScale = d3.scale.linear()
.domain([1, 768])
.range([height, margin.bottom]);
I am trying to develop a scatterplot using d3 but the domain for y-axis is confusing me. y-axis are gonna display patient names and x-axis display their appointment dates. x-axis are working fine, but y-axis are displaying only two patient names.
function graph() {
var num_patient = Object.keys(patientList).length;
var patient_names = Object.keys(patientList);
console.log(patient_names);
var x = d3.time.scale().range([0, width]);
var y = d3.scale.ordinal().range([height, 0]);
x.domain(d3.extent(data, function(d) {return parseDate(d.dates); }));
//y.domain(patient_names.map(function(d) { return d.name;}));
y.domain(patient_names);
console.log(y.domain());
var xAxis = d3.svg.axis()
.scale(x)
.ticks(d3.time.year, 1)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#punchcard")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.value); });
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
}
console.log(patient_names) display the names correctly:
`["Andrew","Fred","Steve","John"]`
console.log(y.domain()) displays an extra undefined object:
["Andrew", "Fred","Steve" , "John", undefined]
But the y-axis only display Andrew at 0 and Fred at height h. How can I get to display all four names? I cannot hard code them as they are user input values. BTW: I am a beginner with d3 and js.
Thanks in advance!
With ordinal scales, you need to define the range points for the inputs explicitly (see the documentation). That is, you need to tell the scale explicitly which input value to map to which output. For example:
var y = d3.scale.ordinal()
.domain(["Andrew","Fred","Steve","John"])
.range([height, height * 2/3, height * 1/3, 0]);
You probably want to use the .rangePoints() method instead, which allows you to specify an interval that D3 automatically divides based on the number of values in the domain:
var y = d3.scale.ordinal()
.domain(["Andrew","Fred","Steve","John"])
.rangePoints([height, 0]);
Note that for .rangePoints() to work properly, you need to set the domain before the output range.
I am just starting to learn D3, and have been following a tutorial to create this piece of code.
I created a couple of bars and intend to create an x axis for my graph.
The problem is when I add the ".call(xAxis)" to my canvas the browsers won't show me anything and I get the following error in the console:
Uncaught TypeError: Cannot call method 'copy' of undefined d3.min.js:5
(anonymous function) d3.min.js:5
(anonymous function) d3.min.js:3
R d3.min.js:1
da.each d3.min.js:3
n d3.min.js:5
da.call d3.min.js:3
(anonymous function)
Can anyone please help me with what's wrong? I really can't understand what's missing or what I'm doing wrong!
<!doctype html>
<html>
<head>
<title>Intro to D3</title>
<script src="d3.min.js"></script>
<head>
<body>
<script>
var width = 1024;
var height = 768;
var dataArray = [20, 40, 60, 120];
var xAxis = d3.svg.axis()
.scale(widthScale);
var widthScale = d3.scale.linear()
.domain([0, 120])
.range([0, width]);
var color = d3.scale.linear()
.domain([0, 120])
.range(["red", "blue"]);
var canvas = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(20, 0)")
.call(xAxis);
var bars = canvas.selectAll("rect")
.data(dataArray)
.enter()
.append("rect")
.attr("width", function(d){ return widthScale(d); })
.attr("height", 20)
.attr("fill", function(d){ return color(d); })
.attr("y", function(d, i){ return i*30; });
</script>
</body>
</html>
The problem is that you're assigning the scale to the axis before defining it. Doing it in this order works fine:
var widthScale = d3.scale.linear()
.domain([0, 120])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(widthScale);
You probably also want to append the bars to the SVG itself, not the g element that contains the axis. To do that, simply split the definition of canvas and the appending of the axis:
var canvas = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
canvas.append("g")
.attr("transform", "translate(20, 0)")
.call(xAxis);
Complete demo here.