D3.js Cannot read property 'length' of undefined

12,771

Your mistake is assuming that calling d3.csv(...) returns your CSV data.

d3.csv makes an AJAX call to load your data and then calls a callback function when that data has been loaded. Your code continues running while the data loads in the background. When you write var data = [d3.csv(...)], data doesn't contain the data loaded from your CSV file, it contains only a d3 object, and you cannot plot that.

Instead, your call to d3.csv should look something like the following:

d3.csv("refugee_data.csv", function(d) {
  return {
        date: d.Year + "/" +d.Month,
        origin: d.Origin,
        asylum: d.Asylum,
        value: +d.Value
  };
}, function(error, rows) {
  console.log(rows);
  render(rows);
});

where render is a function that d3 will call to draw your graph when the data has finished loading.

I created a render function that contained the contents of your code from the line var w = 800; to the lines beginning var y = ..., followed by the contents of the plot function. I also replaced this.selectAll with svg.selectAll, and remove the use of plot.call. The render function I wrote ended up as follows:

function render(data) {
    var w = 800;
    var h = 450;
    var margin = {
        top: 58,
        bottom: 100,
        left: 80,
        right: 40
    };
    var width = w - margin.left - margin.right;
    var height = h - margin.top - margin.bottom;

    var svg = d3.select("body").append("svg")
                .attr("id", "chart")
                .attr("width", w)
                .attr("height", h);
    var dateParser = d3.time.format("%Y/%B").parse;
    var x = d3.time.scale()
             .domain(d3.extent(data, function(d){
                var date = dateParser(d.date);
                return date;
            }))
            .range([0,width]);

    var y = d3.scale.linear()
            .domain([0, d3.max(data, function(d){
                return d.value;
            })])
            .range([height,0]);   

        //enter()
        svg.selectAll(".point")
                .data(data)
                .enter()
                    .append("circle")
                    .classed("point", true)
                    .attr("r", 2);
        //Update
        svg.selectAll(".point")
        .attr("cx", function(d){
            var date = dateParser(d.date);
            return x(date);
        })
        .attr("cy", function(d){
            return y(d.value);
        })

        //Exit()
        svg.selectAll(".point")
        .data(data)
        .exit()
        .remove();
}

After making these changes I was able to see your graph working.

Share:
12,771
basedian
Author by

basedian

Newb and designer. IxD and Interface. I'm interested in coding and design.

Updated on June 04, 2022

Comments

  • basedian
    basedian almost 2 years

    I'm pretty new to d3.js and to web too. I wanted to ue a csv file to plot a line graph but I got some errors I can't really fix:

    enter image description here

    Here is the code:

    body,html{
    	margin: 0;
    	padding: 0;
    	font-family: "Arial", sans-serif;
    	font-size: 0.95em;
    	text-align: center;
    }
    #chart{
    	background-color: #F5F2EB;
    	border: 1px solid #CCC;
    }
    .bar{
    	fill: purple;
    	shape-rendering: crispEdges;
    }
    .bar-label{
    	fill: black;
    	text-anchor: middle;
    	font-size: 18px;
    }
    .axis path,
    .axis line{
    	fill: none;
    	stroke: #000;
    	shape-rendering: crispEdges;
    }
    .gridline path,
    .gridline line{
    	fill: none;
    	stroke: #ccc;
    	shape-rendering: crispEdges;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Learning D3</title>
    	<link rel="stylesheet" href="main.css">
    	<script  type="text/javascript" src="d3.min.js"></script>
    </head>
    <body>
    <!--Place all DOM elements here -->
    <script>
    
    var data= [
    	d3.csv("refugee_data.csv", function(d) {
      return {
        /*year: new Date(+d.Year, 0, 1), // convert "Year" column to Date
        make: d.Make,
        model: d.Model,
        length: +d.Length // convert "Length" column to number*/
    		date: d.Year + "/" +d.Month,
    		origin: d.Origin,
    		asylum: d.Asylum,
    		value: +d.Value
      };
    }, function(error, rows) {
      console.log(rows);
    })
    ];
    
    var w = 800;
    var h = 450;
    var margin = {
    	top: 58,
    	bottom: 100,
    	left: 80,
    	right: 40
    };
    var width = w - margin.left - margin.right;
    var height = h - margin.top - margin.bottom;
    
    var svg = d3.select("body").append("svg")
    			.attr("id", "chart")
    			.attr("width", w)
    			.attr("height", h);
    var dateParser = d3.time.format("%Y/%B").parse;
    var x = d3.time.scale()
    		 .domain(d3.extent(data, function(d){
    			var date = dateParser(d.date);
    			return date;
    		}))
    		.range([0,width]);
    
    var y = d3.scale.linear()
    		.domain([0, d3.max(data, function(d){
    			return d.value;
    		})])
    		.range([height,0]);
    
    function plot(params){
    		//enter()
    		this.selectAll(".point")
    				.data(params.data)
    				.enter()
    					.append("circle")
    					.classed("point", true)
    					.attr("r", 2);
    		//Update
    		this.selectAll(".point")
    		.attr("cx", function(d){
    			var date = dateParser(d.date);
    			return x(date);
    		})
    		.attr("cy", function(d){
    			return y(d.value);
    		})
    
    		//Exit()
    		this.selectAll(".point")
    		.data(params.data)
    		.exit()
    		.remove();
    }
    plot.call(chart, {
    	data: data
    })
    </script>
    </body>
    </html>

    And this ist my a part of my csv, pretty basic so I don't get why it does not work.

    Asylum,Origin,Year,Month,Value

    Germany,Afghanistan,2014,January,981 Germany,Afghanistan,2014,February,781 Germany,Afghanistan,2014,March,675 Germany,Afghanistan,2014,April,673 Germany,Afghanistan,2014,May,523 Germany,Afghanistan,2014,June,621 Germany,Afghanistan,2014,July,752 Germany,Afghanistan,2014,August,743 Germany,Afghanistan,2014,September,922

    I assume it is a really stupid mistake can you please help me out?

  • basedian
    basedian over 8 years
    Thank you, it worked. Is there some besides d3.keys to filter some entries of the csv? I want to filter the origin countries, like afghanistan an syria for interavtive purposes. In other terms I'd like to hover with the mous over the points to get the origin country. This is the source to get an idea of the real data
  • Luke Woodward
    Luke Woodward over 8 years
    @basedian: if you have a separate question, the thing to do is to ask a separate question. This is something I see that you have now done.