How to change alignment of nodes in a Sankey diagram using D3? - node.js

I have generated a Sankey diagram as shown above using d3 code (.js file) mentioned below [the .html and .css files are not quoted here].
Now I want the Sankey diagram to look like below with node "Technology" and "Strategy" appearing apart as a fourth level:
What are the necessary changes to be done in the D3 code?
var svg = d3.select("svg").attr("style", "outline: thin solid grey;"),
width = +svg.attr("width"),
height = +svg.attr("height");
var formatNumber = d3.format(",.0f"),
format = function(d) { return formatNumber(d) + " TWh"; },
color = d3.scaleOrdinal(d3.schemeCategory10);
var school = {"nodes": [
{"name":"High School"}, // 0
{"name":"Community College"}, // 1
{"name":"Finance"}, // 2
{"name":"Accounting"}, // 3
{"name":"ITS"}, // 4
{"name":"Marketing"}, // 5
{"name":"Analytics"}, // 6
{"name":"Security"}, // 7
{"name":"Consulting"}, // 8
{"name":"Banking"}, // 9
{"name":"Internal"}, // 10
{"name":"Securities"}, // 11
{"name":"Public"}, // 12
{"name":"Audting"}, // 13
{"name":"Internal"}, // 14
{"name":"Retail"}, // 15
{"name":"Technology"}, // 16
{"name":"Strategy"} // 17
],
"links":[
// FirstYear
{"source":0,"target":2,"value":33},
{"source":0,"target":3,"value":42},
{"source":0,"target":4,"value":74},
{"source":0,"target":5,"value":60},
// Community College
{"source":1,"target":2,"value":7},
{"source":1,"target":3,"value":13},
{"source":1,"target":4,"value":11},
{"source":1,"target":5,"value":9},
// Finance
{"source":2,"target":9,"value":16},
{"source":2,"target":10,"value":14},
{"source":2,"target":11,"value":10},
// Accounting
{"source":3,"target":12,"value":20},
{"source":3,"target":13,"value":12},
{"source":3,"target":7,"value":8},
{"source":3,"target":14,"value":15},
// Marketing
{"source":5,"target":6,"value":30},
{"source":5,"target":15,"value":39},
// ITS
{"source":4,"target":6,"value":40},
{"source":4,"target":7,"value":20},
{"source":4,"target":12,"value":6},
{"source":4,"target":8,"value":19},
// ITS Consulting
{"source":8,"target":16,"value":10},
{"source":8,"target":17,"value":9},
]};
var sankey = d3.sankey()
.nodeWidth(15)
.nodePadding(10)
.extent([[1, 1], [width - 1, height - 6]]);
var link = svg.append("g")
.attr("class", "links")
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("path");
var node = svg.append("g")
.attr("class", "nodes")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.selectAll("g");
sankey(school);
link = link
.data(school.links)
.enter().append("path")
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function(d) { return Math.max(1, d.width); });
// link hover values
link.append("title")
.text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value); });
node = node
.data(school.nodes)
.enter().append("g");
node.append("rect")
.attr("x", function(d) { return d.x0; })
.attr("y", function(d) { return d.y0; })
.attr("height", function(d) { return d.y1 - d.y0; })
.attr("width", function(d) { return d.x1 - d.x0; })
.attr("fill", function(d) { return color(d.name.replace(/ .*/, "")); })
.attr("stroke", "#000");
node.append("text")
.attr("x", function(d) { return d.x0 - 6; })
.attr("y", function(d) { return (d.y1 + d.y0) / 2; })
.attr("dy", "0.35em")
.attr("text-anchor", "end")
.text(function(d) { return d.name; })
.filter(function(d) { return d.x0 < width / 2; })
.attr("x", function(d) { return d.x1 + 6; })
.attr("text-anchor", "start");
svg.append("text")
.attr("x", 10)
.attr("y", 30)
.attr("class", "graphTitle")
.text("STUDENT CHOICES");
svg.append("text")
.attr("x", width - 80)
.attr("y", height - 10)
.attr("class", "footnote")
.text("data is fictitious");
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/d3-sankey#0"></script>
<svg width="600" height="500"></svg>

The alignment of d3.sankey can be configured using .nodeAlign(), and for your requirement, you will need .nodeAlign(d3.sankeyLeft)
If it is not specified, the alignment defaults to d3.sankeyJustify, which is what you are currently seeing.
https://github.com/d3/d3-sankey#alignments

For those who are looking for a quick ans. Have a nice day!
var sankey = d3.sankey()
.nodeWidth(15)
.nodePadding(10)
.nodeAlign(function (node) {
// you may specify the horizatonal location here
// i.e. if your data structure contain node.horizontalPosition (an integer)
// you can return node.horizontalPosition
return node.depth; //align left
})
.extent([[1, 1], [width - 1, height - 6]]);

Related

Problems loading CSV data in D3. svg.selectAll(...).data(...).enter is not a function

I am trying to load a CSV data set into d3 by assigning it to a variable, but it seems that I keep receiving an error saying that enter() is not a function. I think the issue lies in the way I'm loading the CSV data.
For reference, I'm following this tutorial: http://duspviz.mit.edu/d3-workshop/scatterplots-and-more/
Here is my code for reference.
var ratData = [];
d3.csv("rat-data.csv", function(d) {
return {
city : d.city, // city name
rats : +d.rats // force value of rats to be number (+)
};
}, function(error, rows) { // catch error if error, read rows
ratData = rows; // set ratData equal to rows
console.log(ratData);
createVisualization(); // call function to create chart
});
function createVisualization(){
// Width and height of SVG
var w = 150;
var h = 175;
// Get length of dataset
var arrayLength = ratData.length; // length of dataset
var maxValue = d3.max(ratData, function(d) { return +d.rats;} ); // get maximum
var x_axisLength = 100; // length of x-axis in our layout
var y_axisLength = 100; // length of y-axis in our layout
// Use a scale for the height of the visualization
var yScale = d3.scaleLinear()
.domain([0, maxValue])
.range([0, y_axisLength]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Select and generate rectangle elements
svg.selectAll( "rect" )
.data( ratData )
.enter()
.append("rect")
.attr( "x", function(d,i){
return i * (x_axisLength/arrayLength) + 30; // Set x coordinate of rectangle to index of data value (i) *25
})
.attr( "y", function(d){
return h - yScale(d.rats); // Set y coordinate of rect using the y scale
})
.attr( "width", (x_axisLength/arrayLength) - 1)
.attr( "height", function(d){
return yScale(d.rats); // Set height of using the scale
})
.attr( "fill", "steelblue");
// Create y-axis
svg.append("line")
.attr("x1", 30)
.attr("y1", 75)
.attr("x2", 30)
.attr("y2", 175)
.attr("stroke-width", 2)
.attr("stroke", "black");
// Create x-axis
svg.append("line")
.attr("x1", 30)
.attr("y1", 175)
.attr("x2", 130)
.attr("y2", 175)
.attr("stroke-width", 2)
.attr("stroke", "black");
// y-axis label
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.text("No. of Rats")
.attr("transform", "translate(20, 20) rotate(-90)")
.attr("font-size", "14")
.attr("font-family", "'Open Sans', sans-serif");
}; // end of function

d3 - foreign object - getting the right scope

I am using foreignObject to use HTML inside a d3 circle.
What I want:
use HTML inside d3 objects
reach the normal scopes outside of HTML code
Now I have problems to get the right scope for using functions.
If I use "this" inside the
.html(".....")
I get the "g" element of d3. So is there a way to get to my normal scopes? Or is there even a more elegant way to solve this problem?
var foreign = that._foreignObject = d3.select(this).append("foreignObject")
.attr("width", 30)
.attr("height", 30)
.append("xhtml:div")
.style("font", "15px 'Arial'")
.html("<button name=Klickmich type=button value=play onclick=>");
EDIT: 16.12.2013:
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
//click once
.on("click", function (d) {
if (that._foreignObject) {
that._foreignObject.remove();
}
that.toggle(d);
that.update(d);
that.onNodeClick(d);
var circle = d3.select(this).select("circle")
.transition()
.duration(750)
.attr("r", 17)
.attr("width", 40)
.attr("heigth", 40)
.style("stroke-dasharray", ("5,2"))
.style("stroke-width", 2)
.style("stroke", "black");
var button = "<button name=Klickmich type=button value=play onclick=>";
var foreign = that._foreignObject = d3.select(this).append("foreignObject")
.attr("width", 30)
.attr("height", 30)
.append("xhtml:div")
.style("font", "15px 'Arial'")
.html(button)
.on("click", function (d) {
console.log("heyho");
});
d3.select(this).select("text").transition()
.duration(750)
.style("font-size", 15)
.style("font-weight", "bold")
.attr("x", function (d) {
return d.children || d._children ? -20 : -20;
});
})
If i Click on my html button i set
this._clicked = 1;
And the onClick function of the node is build as follows:
.on("click", function (d) {
if (this._clicked == null) {
// some code
}
}
this._clicked = null;

Setting up a click event in d3 that hides all other elements

I am fairly new to using d3, but what I am trying to do is make a chord diagram of some site traffic, and I am trying to make it interactive by changing the color of the paths when a user clicks on the group for a certain site.here is the style and script section of my code:
<style type="text/css">
.group text {
font: 11px sans-serif;
pointer-events: none;
}
#circle circle {
fill: none;
pointer-events: all;
}
.group path {
stroke: #000;
fill-opacity: .5;
}
path.chord {
stroke-width: .75;
fill-opacity: .75;
}
#circle:hover path.fade {
display: none;
}
</style>
</head>
<body>
<script type="text/javascript">
// Chart dimensions.
var w = 600,
h = 700,
r1 = Math.min(w, h) / 2 - 4,
r0 = r1 - 20,
format = d3.format(",.3r");
// Square matrices, asynchronously loaded; credits is the transpose of sitename.
var sitename = [];
// The chord layout, for computing the angles of chords and groups.
var layout = d3.layout.chord()
.sortGroups(d3.descending)
.sortSubgroups(d3.descending)
.sortChords(d3.descending)
.padding(.04);
// The color scale, for different categories of "worrisome" risk.
var fill = d3.scale.ordinal();
// The arc generator, for the groups.
var arc = d3.svg.arc()
.innerRadius(r0)
.outerRadius(r1);
// The chord generator (quadratic Bézier), for the chords.
var chord = d3.svg.chord()
.radius(r0);
// Add an SVG element for each diagram, and translate the origin to the center.
var svg = d3.select("body").selectAll("div")
.data([sitename])
.enter().append("div")
.style("display", "inline-block")
.style("width", w + "px")
.style("height", h + "px")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
// Load our data file…
d3.csv("data2.csv", function(data) {
var uniqueids = {},
array = [],
n = 0;
// Compute a unique id for each site.
data.forEach(function(d) {
d.siteid1 = uniqueIDMaker(d.siteid1);
d.siteid2 = uniqueIDMaker(d.siteid2);
d.valueOf = value; // convert object to number implicitly
});
// Initialize a square matrix of sitename and users
for (var i = 0; i < n; i++) {
sitename[i] = [];
for (var j = 0; j < n; j++) {
sitename[i][j] = 0;
}
}
// Populate the matrices, and stash a map from id to site.
data.forEach(function(d) {
sitename[d.siteid1.id][d.siteid2.id] = d;
array[d.siteid1.id] = d.siteid1;
array[d.siteid2.id] = d.siteid2;
});
// For each diagram…
svg.each(function(matrix, j) {
var svg = d3.select(this);
// Compute the chord layout.
layout.matrix(matrix);
// Add chords.
svg.selectAll(".chord")
.data(layout.chords)
.enter().append("svg:path")
.attr("class", "chord")
.style("fill", function(d) { return fill(d.source.value); })
.style("stroke", function(d) { return d3.rgb(fill(d.source.value)).darker(); })
.attr("d", chord)
.on("dblclick",function(){
d3.select(this)
.style("fill","red")
.style("stroke","yellow")
})
.append("svg:title")
.text(function(d) { return "site " + d.source.value.siteid2.name + " and site " + d.source.value.siteid1.name + " have " + format(d.source.value) + " common users"; })
;
// Add groups.
var g = svg.selectAll("g.group")
.data(layout.groups)
.enter().append("svg:g")
.attr("class", "group");
// Add the group arc.
g.append("svg:path")
.style("fill", function(d) { return fill(array[d.index]); })
.attr("id", function(d, i) { return "group" + d.index + "-" + j; })
.attr("d", arc)
.append("svg:title")
.text(function(d) { return "site " + array[d.index].name + " has " + format(d.value) + "users"; });
g.append("svg:text")
.attr("x", 6)
.attr("dy", 15)
.filter(function(d) { return d.value > 110; })
.append("svg:textPath")
.attr("xlink:href", function(d) { return "#group" + d.index + "-" + j; })
.text(function(d) { return array[d.index].name; });
});
function uniqueIDMaker(d) {
return uniqueids[d] || (uniqueids[d] = {
name: d,
id: n++
});
}
function value() {
return +this.count;
}});
</script>
any help would be greatly appreciated
http://jsfiddle.net/Rw3aK/2/ is a jsFiddle of the script, not sure how to make it read from a file, so here is the contents of data2.csv:
siteid1,siteid2,count,pubid1,pubid2
8,94,11132,57141,57141
201,94,10035,57141,57141
201,8,9873,57141,57141
0,94,8488,45020,57141
0,8,8258,45020,57141
0,201,7644,45020,57141
0,1,6973,45020,45020
94,1,5719,57141,45020
8,1,5670,57141,45020
1,201,5410,57141,45020
I forked your jsfiddle and converted your CSV data to JSON, now in a variable data: http://jsfiddle.net/mdml/K6FHW/.
I also modified your code slightly so that when you click on a group, all outgoing chords are highlighted red. When you click on a group again, the chords change back to their original color. Here're the relevant snippets:
When adding the chords, label each chord with a class according to the chord's source
svg.selectAll(".chord")
.data(layout.chords)
.enter().append("svg:path")
.attr("class", function(d){ return "chord chord-" + d.source.index; })
...
When clicking a group, check if that group's chords are highlighted.
If so, fill the chords with their default color
If not, fill the chords red
Then record whether or not the group's chords are highlighted in a variable d.chordHighlighted
g.append("svg:path")
...
.attr("id", function (d, i) {
return "group" + d.index + "-" + j;
})
...
.on("click", function(d){
if (d.chordHighlighted)
d3.selectAll(".chord-" + d.index)
.style("fill", fill(d.value));
else{
d3.selectAll(".chord-" + d.index)
.style("fill", "red");
}
d.chordHighlighted = d.chordHighlighted ? false : true;
})

add images to bubble layout in D3.js

Unable to add images to bubble layout in D3.js . I am trying to append images to the circles in bubble layout but it doesnt works out . the image is not getting transformed.
I want to have look and feel of this:-
http://www.cloudshapes.co.uk/labs/attention-hungry-cabinet-ministers/
here is the fiddle link for what I have been trying to do:
http://jsfiddle.net/Ankitb/eYGCY/4/
var force = d3.layout.force()
.charge(-300)
.size([w, h])
.nodes(nodes)
.on("tick", tick)
.start();
function tick() {
svg.selectAll("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
}
var interval = setInterval(function () {
var d = {
x: w / 4 + 2 *( Math.random() - 1),
y: h / 4 + 2 *( Math.random() - 1)
};
var personDot = svg.append("g")
.attr("class", "g-person-dots")
.selectAll("g")
.data([d])
.enter().append("g")
.attr("transform", function (d) { return "translate(" + d.x+ "," + d.y + ")"; });
personDot.append("circle")
.data([d]).attr("r", 40)
//.attr("r", 1e-6)
.attr("cx",0).attr("cy",0)
.transition().style("stroke", "gray").style("fill","white")
.ease(Math.sqrt);
personDot.append("image").data([d])
.attr("xlink:href", "PeopleProfilePicture.jpg")
// .attr("x", function (d, i) { return -mugDiameter / 2 - mugDiameter * (i % 9); })
//.attr("y", function (d, i) { return -mugDiameter / 2 - mugDiameter * (i / 9 | 0); })
.attr("width", 80)
.attr("height", 80)
.attr("transform", function (d) { return "translate(" + -d.x / 10 + "," + -d.y / 10 + ")"; });
if (nodes.push(d) > 10) clearInterval(interval);
else { force.start(); }
}, 30);
The translation of an element is relative to its parent element. That is, by default the element will be in the same position as its parent. Therefore, the translation you need to do does not depend on the dynamic data that you pass in, but only on the dimensions of the image. You need to set transform as follows.
.attr("transform", "translate(-40,-40)");
You may also want to make the background of your images transparent such that you can still see the circle.

d3 Gives a 406 Error when trying to run d3js example on IIS

I have setup a basic IIS server and am trying to demonstrate a d3js example. First I create an html page with the example code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").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 + ")");
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
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("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
and then i create the data.csv file:
State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
AL,310504,552339,259034,450818,1231572,1215966,641667
AK,52083,85640,42153,74257,198724,183159,50277
AZ,515910,828669,362642,601943,1804762,1523681,862573
AR,202070,343207,157204,264160,754420,727124,407205
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
CO,358280,587154,261701,466194,1464939,1290094,511094
CT,211637,403658,196918,325110,916955,968967,478007
DE,59319,99496,47414,84464,230183,230528,121688
DC,36352,50439,25225,75569,193557,140043,70648
FL,1140516,1938695,925060,1607297,4782119,4746856,3187797
GA,740521,1250460,557860,919876,2846985,2389018,981024
HI,87207,134025,64011,124834,356237,331817,190067
ID,121746,201192,89702,147606,406247,375173,182150
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
IN,443089,780199,361393,605863,1724528,1647881,813839
IA,201321,345409,165883,306398,750505,788485,444554
KS,202529,342134,155822,293114,728166,713663,366706
KY,284601,493536,229927,381394,1179637,1134283,565867
LA,310716,542341,254916,471275,1162463,1128771,540314
ME,71459,133656,69752,112682,331809,397911,199187
MD,371787,651923,316873,543470,1556225,1513754,679565
MA,383568,701752,341713,665879,1782449,1751508,871098
MI,625526,1179503,585169,974480,2628322,2706100,1304322
MN,358471,606802,289371,507289,1416063,1391878,650519
MS,220813,371502,174405,305964,764203,730133,371598
MO,399450,690476,331543,560463,1569626,1554812,805235
MT,61114,106088,53156,95232,236297,278241,137312
NE,132092,215265,99638,186657,457177,451756,240847
NV,199175,325650,142976,212379,769913,653357,296717
NH,75297,144235,73826,119114,345109,388250,169978
NJ,557421,1011656,478505,769321,2379649,2335168,1150941
NM,148323,241326,112801,203097,517154,501604,260051
NY,1208495,2141490,1058031,1999120,5355235,5120254,2607672
NC,652823,1097890,492964,883397,2575603,2380685,1139052
ND,41896,67358,33794,82629,154913,166615,94276
OH,743750,1340492,646135,1081734,3019147,3083815,1570837
OK,266547,438926,200562,369916,957085,918688,490637
OR,243483,424167,199925,338162,1044056,1036269,503998
PA,737462,1345341,679201,1203944,3157759,3414001,1910571
RI,60934,111408,56198,114502,277779,282321,147646
SC,303024,517803,245400,438147,1193112,1186019,596295
SD,58566,94438,45305,82869,196738,210178,116100
TN,416334,725948,336312,550612,1719433,1646623,819626
TX,2027307,3277946,1420518,2454721,7017731,5656528,2472223
UT,268916,413034,167685,329585,772024,538978,246202
VT,32635,62538,33757,61679,155419,188593,86649
VA,522672,887525,413004,768475,2203286,2033550,940577
WA,433119,750274,357782,610378,1850983,1762811,783877
WV,105435,189649,91074,157989,470749,514505,285067
WI,362277,640286,311849,553914,1487457,1522038,750146
WY,38253,60890,29314,53980,137338,147279,65614
When I access the page, nothing displays...A quick look through fiddler shows the html content downloads fine (you can also see it in show source). You can also look at the data.csv file directly by accessing it from the url
http://localhost/data.csv
The problem shown in fiddler is a 406 error when d3js attempts to load the CSV file. Any ideas?
Thanks
What does your HTTP Request's Accept header contain? Apache is probably configured in such a way so as to return a 406 because the value in the Accept header does not include whatever MIME type your CSV is returning.
See
http://blogs.msdn.com/b/ieinternals/archive/2011/03/27/http-406-not-acceptable-php-ie9-standards-mode-accepts-only-text_2f00_css-for-stylesheets.aspx for a similar problem sometimes seen in browsers.
Change the build action for the csv file to "Resource" in the file's properties using your Visual Studio.

Resources