D3.js 4 histogram with JSON from AJAX - d3.js

With help from https://bl.ocks.org histogram example I try to create a histogram with JSON from AJAX.
It seems like my data is not suitable for the histogram() function.
My Data in dev tools (top = my data; bottom = bins from the histogram):
My data is not in histogram bins. The array objects are missing.
Here are the data from bl.ocks.org working example:
...and the bins from histogram from bl.ocks.org example:
You can see it clearly. In my experiment, the data is not in the bins. In the working example of bl.ocks.org you can see the additional objects as an array from index 1 to 13 in the histogram bins.
Here is my full source code:
$(function () {
var updateStatistic = function () {
var dateFrom = $('#date_from').val();
var dateTo = $('#date_to').val();
var parseDate = d3.timeParse('%Y-%m-%d %H:%M:%S'), formatCount = d3.format(',.0f');
var margin = {top: 10, right: 10, bottom: 20, left: 10},
width = 1800 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var dataset = [];
d3.json('/statistic-sizearchive/' + dateFrom + '/' + dateTo, function (data) {
dataset = data.sizeArchive;
dataset.columns = ['date'];
var datetimeFrom = parseDate(dataset[0].archive_time_sql);
var datetimeTo = parseDate(dataset[dataset.length - 1].archive_time_sql);
$(dataset).each(function (index, element) {
element.date = parseDate(element.archive_time_sql);
delete element.archive_time_sql;
});
console.log(dataset);
var x = d3.scaleTime()
.domain([datetimeFrom, datetimeTo])
.rangeRound([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var histogram = d3.histogram()
.value(function (d) {
return d.length;
})
.domain(x.domain())
.thresholds(x.ticks(d3.timeWeek));
var bins = histogram(dataset);
console.log(bins);
y.domain([0, d3.max(bins, function (d) {
return d.length;
})]);
/*
* ### SVG
*/
var svg = d3.select('#statistic_size_archive').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.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var bar = svg.selectAll(".bar")
.data(bins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function (d) {
return "translate(" + x(d.x0) + "," + y(d.length) + ")";
})
;
bar.append("rect")
.attr("x", 1)
.attr("width", function (d) {
return x(d.x1) - x(d.x0); // x(d.x1) - x(d.x0) - 1
})
.attr("height", function (d) {
return height - y(d.length); // height - y(d.length)
});
bar.append("text")
.attr("dy", ".75em")
.attr("y", 0)
.attr("x", function (d) {
return (x(d.x1) - x(d.x0)) / 2;
})
.attr("text-anchor", "middle")
.text(function (d) {
return formatCount(d.length);
});
});
};
updateStatistic();
$('button#update_statistic').click(function () {
updateStatistic();
});
});
I do not see anything that I'm doing wrong.

Without your actual data, I'm not able to test this code... however, it appears that your histogram call function is returning the wrong value from the data. Instead of returning d.length, shouldn't the code be:
var histogram = d3.histogram()
.value(function (d) {
return d.date;
})
...
This way, the histogram will put each data point into a bin determined by its date?

Related

D3 Typeerror cannot read property of undefined

I'm trying to follow this guide, but implementing my own data:
https://www.d3-graph-gallery.com/graph/stackedarea_basic.html
Here is my function
getStackedAreaChart: function(pod) {
//eval is sligtly heavily used here.
var cssName = ".stackedareachart-" + pod;
var podData = eval("this.StackedAreaChartData" + pod);
var ListName = eval("this.List" + pod);
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 60 },
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3
.select(cssName)
.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 + ")");
var sumstat = d3
.nest()
.key(function(d) {
return d.time;
})
.entries(podData);
console.log(sumstat);
// Stack the data: each group will be represented on top of each other
var mygroups = ListName; // list of group names
var mygroup = []; // list of group names
for (let i = 1; i <= mygroups.length; i++) {
mygroup.push(i);
}
console.log(mygroups);
console.log(mygroup);
var stackedData = d3
.stack()
.keys(mygroup)
.value(function(d, key) {
return d.values[key].interactionCount;
})(sumstat);
// Add X axis --> it is a date format
var x = d3
.scaleLinear()
.domain(
d3.extent(podData, function(d) {
return d.time;
})
)
.range([0, width]);
svg
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(5));
// Add Y axis
var y = d3
.scaleLinear()
.domain([
0,
d3.max(podData, function(d) {
return +d.interactionCount;
})
])
.range([height, 0]);
svg.append("g").call(d3.axisLeft(y));
// color palette
var color = d3
.scaleOrdinal()
.domain(mygroups)
.range([
"#e41a1c",
"#377eb8",
"#4daf4a",
"#984ea3",
"#ff7f00",
"#ffff33",
"#a65628",
"#f781bf",
"#999999"
]);
// Show the areas
svg
.selectAll("mylayers")
.data(stackedData)
.enter()
.append("path")
.style("fill", function(d) {
name = mygroups[d.key - 1];
return color(name);
})
.attr(
"d",
d3
.area()
.x(function(d, i) {
return x(d.data.key);
})
.y0(function(d) {
return y(d[0]);
})
.y1(function(d) {
return y(d[1]);
})
);
}
}
Here is where I get the error:
TypeError: Cannot read property 'interactionCount' of undefined
var stackedData = d3
.stack()
.keys(mygroups)
.value(function(d, key) {
return d.values[key].interactionCount;
})(sumstat);
For some reason if I make the list mygroup have one less element in the array, I don't get this error. BUT, my chart doesn't come out looking right.
I've followed the guide word for word line by line and I have no problems replicating the chart. But, when using my own data, I run into issues. Here is the json data:
[{"interactionCount":0,"time":951,"pod":"POD2","client":"C1"},{"interactionCount":6,"time":951,"pod":"POD2","client":"C2"},{"interactionCount":0,"time":951,"pod":"POD2","client":"C3"},{"interactionCount":14,"time":951,"pod":"POD2","client":"C4"},{"interactionCount":44,"time":951,"pod":"POD2","client":"C5"},{"interactionCount":0,"time":951,"pod":"POD2","client":"C6"},{"interactionCount":8,"time":951,"pod":"POD2","client":"C7"},{"interactionCount":0,"time":951,"pod":"POD2","client":"C8"},{"interactionCount":5,"time":951,"pod":"POD2","client":"C9"},{"interactionCount":2,"time":951,"pod":"POD2","client":"C10"},{"interactionCount":0,"time":951,"pod":"POD2","client":"C11"},{"interactionCount":13,"time":951,"pod":"POD2","client":"C12"},{"interactionCount":6,"time":951,"pod":"POD2","client":"C13"},{"interactionCount":0,"time":951,"pod":"POD2","client":"C14"},{"interactionCount":6,"time":951,"pod":"POD2","client":"C15"}]
I was thinking maybe the error was when interactionCount was 0. This is not a problem. I did test this out.
Although I'm following the guide line by line. What am I doing wrong to receive the error?
NOTE my data is json data. The user uses CSV data. Could this be my problem?

D3 How to update the chart after selection from drop down menu with new data

I'm building a waterfall chart in D3. When the page will load, it will render the default page but user will have choice to select different
'Company' and 'Year' from the drop down menu. I have been able to create the chart what I want. But when I select any different Company or Year, D3 adds another chart on top of the existing instead of replacing it and thats because I'm targeting a particular div / svg from the HTML. How can I use D3 to update the chart with new data instead add another one of top? And if I can have that movement of chart bars with transition, that will be awesome.
HTML is a simple svg:
<svg class="chart"></svg>
Here is the function to create the chart which I call when Ajax call is successful:
function waterfallChart (dataset) {
var data = [];
for (var key in dataset[0]) {
data.push({
name: key,
value: dataset[0][key]
})
}
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 0.3;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width])
.padding(padding);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x)
var yAxis = d3.axisLeft(y)
.tickFormat(function(d) {
return dollarFormatter(d);
});
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cumulative = 0;
for (var i = 0; i < data.length; i++) {
data[i].start = cumulative;
cumulative += data[i].value;
data[i].end = cumulative;
data[i].class = (data[i].value >= 0) ? 'positive' : 'negative'
}
data.push({
name: 'Total',
end: cumulative,
start: 0,
class: 'total'
});
x.domain(data.map(function(d) {
return d.name;
}));
y.domain([0, d3.max(data, function(d) {
return d.end;
})]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", function(d) {
return "bar " + d.class
})
.attr("transform", function(d) {
return "translate(" + x(d.name) + ",0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(Math.max(d.start, d.end));
})
.attr("height", function(d) {
return Math.abs(y(d.start) - y(d.end));
})
.attr("width", x.bandwidth());
bar.append("text")
.attr("x", x.bandwidth() / 2)
.attr("y", function(d) {
return y(d.end) + 5;
})
.attr("dy", function(d) {
return ((d.class == 'negative') ? '-' : '') + ".75em"
})
.text(function(d) {
return dollarFormatter(d.end - d.start);
});
bar.filter(function(d) {
return d.class != "total"
}).append("line")
.attr("class", "connector")
.attr("x1", x.bandwidth() + 5)
.attr("y1", function(d) {
return y(d.end)
})
.attr("x2", x.bandwidth() / (1 - padding) - 5)
.attr("y2", function(d) {
return y(d.end)
})
function dollarFormatter(n) {
n = Math.round(n);
var result = n;
if (Math.abs(n) > 1000) {
result = Math.round(n/1000) + 'B';
}
return '$ ' + result;
}
}
Here is code where I have event listener and on selection it will run the above function:
$("#airline-selected, #year-selected").change(function chartsData(event) {
event.preventDefault();
var airlineSelected = $('#airline-selected').find(":selected").val();
var yearSelected = $('#year-selected').find(":selected").val();
$.ajax({
url: "{% url 'airline_specific_filtered' %}",
method: 'GET',
data : {
airline_category: airlineSelected,
year_category: yearSelected
},
success: function(dataset){
waterfallChart(dataset)
},
error: function(error_data){
console.log("error")
console.log(error_data)
}
})
});
You are missing some pretty important things here. If you are going to do updates on your data you need to do a couple things.
Give a key to the data() function. You need to give D3 a way to identify data when you update it so it knows if it should add, remove, or leave existing data. The key does this. For instance you might do something like this:
.data(data, function(d) { return d.name })
Now d3 will be able to tell you data items apart assuming d.name is a unique identifier.
You need an exit() for data that is removed during update. You need to save the data joined selection so you can call enter and exit on it:
var bar = chart.selectAll(".bar")
.data(data, function(d) { return d.name})
now you can call: bar.exit().remove() to get rid of deleted items and bar.enter() to add items.
You need to make a selection that hasn't had enter() called on it to update attributes.
Probably more a matter of style, but you should set up the SVG and margins outside the update function since they state the same. You can still update the axis and scales by calling the appropriate functions in the update.
The code you posted is a little hard for other people to run — you'll always get better faster answers if you post code that has been reduced to the main problem and that others can run without needing access to offsite data or apis.
Here's an example that updates on a setInterval between two data sets based on your code. But you should also look at the General Update Patterns - they are very simple but have almost everything you need to know. (https://bl.ocks.org/mbostock/3808234)
dataset = [
{name: "Albert", start: 0, end:220},
{name: "Mark", start: 0, end:200},
{name: "Søren", start: 0, end:100},
{name: "Immanuel", start: 0, end:60},
{name: "Michel", start: 0, end:90},
{name: "Jean Paul", start: 0, end: 80}
]
dataset2 = [
{name: "Albert", start: 0, end:20},
{name: "Immanuel", start:0, end:220},
{name: "Jaques", start: 0, end:100},
{name: "Gerhard", start:0 , end:50},
{name: "Søren", start: 0, end:150},
{name: "William", start: 0, end: 180}
]
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 40
},
width = 400 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom,
padding = 0.3;
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.range([0, width])
.padding(padding);
var y = d3.scaleLinear()
.range([height, 0])
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
chart.append("g")
.attr("class", "y axis")
var currentData = dataset
waterfallChart(currentData)
setInterval(function() {
currentData = currentData === dataset ? dataset2 : dataset
waterfallChart(currentData)
}, 3000)
function waterfallChart(data) {
var t = d3.transition()
.duration(750)
x.domain(data.map(function(d) {
return d.name
}))
y.domain([0, d3.max(data, function(d) {
return d.end
})])
var xAxis = d3.axisBottom(x)
var yAxis = d3.axisLeft(y)
d3.select('g.x').transition(t).call(xAxis)
d3.select('g.y').call(yAxis)
var bar = chart.selectAll(".bar")
.data(data, function(d) {
return d.name
})
// ENTER -- ADD ITEMS THAT ARE NEW IN DATA
bar.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x(d.name) + ",0)"
})
.attr("class", 'bar')
.append("rect")
.attr("y", function(d) {
return y(Math.max(d.start, d.end));
})
.attr("height", function(d) {
return Math.abs(y(d.start) - y(d.end));
})
.attr("width", x.bandwidth())
// UPDATE EXISTING ITEMS
chart.selectAll(".bar")
.transition(t)
.attr("transform", function(d) {
return "translate(" + x(d.name) + ",0)"
})
.select('rect')
.attr("y", function(d) {
return y(Math.max(d.start, d.end))
})
.attr("height", function(d) {
return Math.abs(y(d.start) - y(d.end))
})
.attr("width", x.bandwidth())
// REMOVE ITEMS DELETED FROM DATA
bar.exit().remove()
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg class="chart"></svg>

getting error with d3 (V4) Stacked Bar Chart

Getting error while creating Stacked Bar Chart using D3 JS in a Angular 2 application,
here is the code,
//data
var data = [
{ month: 'Jan', A: 20, B: 5, C: 10 },
{ month: 'Feb', A: 30, B: 10, C: 20 }
];
var xData = ["A", "B", "C"];
var margin = { top: 20, right: 50, bottom: 30, left: 0 },
width = 350 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.35);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var xAxis = d3.axisBottom(x);
var svg = d3.select("#chart").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 + ")");
var dataIntermediate = xData.map(function (c) {
return data.map(function (d) {
return { x: d.month, y: d[c] };
});
});
var dataStackLayout = d3.stack().keys([dataIntermediate]);
x.domain(dataStackLayout[0].map(function (d) {
return d.x;
}));
y.domain([0,
d3.max(dataStackLayout[dataStackLayout.length - 1],
function (d) { return d.y0 + d.y; })
])
.nice();
var layer = svg.selectAll(".stack")
.data(dataStackLayout)
.enter().append("g")
.attr("class", "stack")
.style("fill", function (d, i) {
return color(i);
});
layer.selectAll("rect")
.data(function (d) {
return d;
})
.enter().append("rect")
.attr("x", function (d) {
return x(d.x);
})
.attr("y", function (d) {
return y(d.y + d.y0);
})
.attr("height", function (d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.range());
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
Error are,
(51,41): error TS7017: Index signature of object type implicitly has an 'any' type.
# below line,
return { x: d.month, y: d[c] };
(76,19): error TS2345: Argument of type '(this: BaseType, d: {}) => {}' is not assignable to parameter of type 'ValueFn'.
Type '{}' is not assignable to type '{}[]'.
Property 'find' is missing in type '{}'.
# below line,
var dataStackLayout = d3.stack().keys([dataIntermediate]);
im using same example to implement stacked barchart in angular2.
i think your issue is
var dataStackLayout = d3.stack().keys([dataIntermediate]);
dataStackedLayout should be array instead of function.
were you able to resolve this issue yet?

2 completely different d3 charts on same page

I'm trying to get 2 completely different d3 charts (2 line charts but totally different data - one with several lines and negative data, other with one line positive data) on the same page.
Right now, I only get the first one to be generated and shown correctly on the HTML page, the second chart doesn't show at all (not even svg container is generated).
Here is my code:
(function() {
// Get the data
d3.json("../assets/js/json/temperature.json", function(data) {
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 25},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.temps); })
.y(function(d) { return y(d.temperature); });
// prepare data
data.forEach(function(d) {
d.temps = parseDate(d.temps);
d.temperature = +d.temperature;
});
// Adds the svg canvas
var svg = d3.select("#graphTemp")
.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 + ")");
// Scale the range of the data on domain
x.domain(d3.extent(data, function(d) { return d.temps; }));
y.domain([0, d3.max(data, function(d) { return d.temperature; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperatures");
});
})();
(function(){
// loads the data and loads it into chart - main function
d3.json("../assets/js/json/maitrise.json", function(data) {
var m = {top: 20, right: 5, bottom: 30, left: 40},
w = 70 - m.left - m.right,
h = 30 - m.top - m.bottom;
var x = d3.scale.linear().domain([0, data.length]).range([0 + m.left, w - m.right]);
var y = d3.scale.linear()
.rangeRound([h, 0]);
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d,i) { return x(i); })
.y(function (d) { return y(d.value); });
var color = d3.scale.ordinal()
.range(["#28c6af","#ffd837","#e6443c","#9c8305","#d3c47c"]);
var svg2 = d3.select("#maitrisee").append("svg")
.attr("width", w + m.left + m.right)
.attr("height", h + m.top + m.bottom)
.append("g")
.attr("transform", "translate(" + m.left + "," + m.top + ")");
// prep axis variables
var xAxis2 = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis2 = d3.svg.axis()
.scale(y)
.orient("left");
//console.log("Inital Data", data);
var labelVar = 'id'; //A
var varNames = d3.keys(data[0])
.filter(function (key) { return key !== labelVar;}); //B
color.domain(varNames); //C
var seriesData = varNames.map(function (name) { //D
return {
name: name,
values: data.map(function (d) {
return {name: name, label: d[labelVar], value: +d[name]};
})
};
});
console.log("seriesData", seriesData);
y.domain([
d3.min(seriesData, function (c) {
return d3.min(c.values, function (d) { return d.value; });
}),
d3.max(seriesData, function (c) {
return d3.max(c.values, function (d) { return d.value; });
})
]);
var series = svg2.selectAll(".series")
.data(seriesData)
.enter().append("g")
.attr("class", function (d) { return d.name; });
series.append("path")
.attr("class", "line")
.attr("d", function (d) { return line(d.values); })
.style("stroke", function (d) { return color(d.name); })
.style("stroke-width", "2px")
.style("fill", "none");
});
})();
OK, I found where the error was coming from. There was a piece of javascript in the middle of the HTML page that stopped d3 to generate the second graph further down in the page.
Thanks for all the help!

Problems parsing data with the D3.js Sankey layout

I'm looking for some hints as to what I am doing wrong with a Sankey diagram I'm creating. I am charting changes in food consumption over time, and using the Sankey layout to visualize how these values changed over a period of forty years.
The bl.ock and small dataset are here. The relevant code:
var margin = {top: 1, right: 1, bottom: 6, left: 1},
width = 1260 - margin.left - margin.right,
height = 1000 - margin.top - margin.bottom;
var formatNumber = d3.format(",.0f"),
format = function(d) { return formatNumber(d) + " TWh"; },
color = d3.scale.category20();
var svg = d3.select("#chart").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 + ")");
var sankey = d3.sankey()
.nodeWidth(15)
.nodePadding(10)
.size([width, height]);
var path = sankey.link();
// ========================== Prepare data ==========================
queue()
.defer(d3.csv, "grains.csv")
.await(ready);
// ========================== Start viz ==========================
function ready(error, csv_data) {
nodes = [];
edges = [];
nodesArray = [];
// Scales
yearScale = d3.scale.linear().domain([1640,1688]).range([20,width -20]);
radiusScale = d3.scale.linear().domain([0,300]).range([2,12]).clamp(true);
chargeScale = d3.scale.linear().domain([0,100]).range([0,-100]).clamp(true);
uniqueValues = d3.set(nodesArray.map(function(d) {return d.name})).values();
colorScale = d3.scale.category20b(uniqueValues);
sortScale = d3.scale.ordinal().domain(uniqueValues).rangePoints([-0.001,.001]);
// Create a JSON link array
// This creates unique nodes for each item and its corresponding date.
// For example, nodes are rendered as "peas-1640," "peas-1641," etc.
csv_data.forEach(function(link) {
key = link.translation + '-' + link.date;
link.source = nodes[key] || (nodes[key] = {name: link.translation, date: link.date, origX: yearScale(parseInt(link.date)), value: link.value || 0});
});
// Build the edgesArray array
// This creates the edgesArray to correspond with unique nodes. We're telling
// items and dates to remain together. So, the code below tells the graph
// layout that `1641` is preceded by `1640` and followed by `1642`, etc.
var y = "→";
for (x in nodes) {
nodesArray.push(nodes[x])
if(nodes[y]) {
nodes[y].date = parseInt(nodes[y].date);
if (nodes[y].name == nodes[x].name) {
var newLink = {source:nodes[y], target:nodes[x]}
edges.push(newLink);
}
}
y = x;
}
sankey
.nodeWidth(10)
.nodePadding(10)
.size([1200, 1200])
.nodes(nodesArray.filter(function(d,i) {return d.date < 1650}))
.links(edges.filter(function(d,i) { return i < 50 && d.source.date < 1650 && d.target.date < 1650} )) // filtering to test a smaller data set
.layout(32);
var link = svg.append("g").selectAll(".link")
.data(edges.filter(function(d,i) { return i < 50 && d.source.date < 1650 && d.target.date < 1650} )) // filtering to test a smaller data set
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
link.append("title")
.text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value); });
var node = svg.append("g").selectAll(".node")
.data(nodesArray.filter(function(d,i) {return d.date < 1650})) // filtering to test a smaller data set
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.call(d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", function() { this.parentNode.appendChild(this); })
.on("drag", dragmove));
node.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
.append("title")
.text(function(d) { return d.name + "\n" + format(d.value); });
node.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function(d) { return d.name; })
.filter(function(d) { return d.x < width / 2; })
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
function dragmove(d) {
d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");
sankey.relayout();
link.attr("d", path);
}
};
Unfortunately, I'm getting an error as you can see in the bl.ock. The Boss suggested it might be a circular link but I'm at a bit of a loss. Any hints or suggestions?
EDIT: For some clarity, I'm after something like this:
(Source)
From what I can tell, I think I'm building the nodes and edges correctly. If we look at the console for the nodes array and edges array:
It's not like a usual Sankey or alluvial diagram, which, as I've often seen them, shows collapses and expansions of items. In my case the date, food item, and value are all a single stream throughout the length of the visualization but are resized/repositioned based on the value for a given year (like the example image above).

Resources