How to include csv data using `var data = d3.csvParse(csvdata, type)`?

Hi again, Community!

Tom and Mike were so nice in answering my previous question that I’d like to try another. My questions are so basic that I am still a bit fearful…

I am trying to re-create Mike’s D3.js donut chart example in observable. Here’s where I am at.

The challenges I am having now are in learning about converting data types, loading them, as well as telling Observable what to draw (so, everything really). Mike’s intro to data tutorial shows how D3.js can parse csv data (using D3.v4). I start with this. I fixed a few bugs resulting from version incompatibility (the donut example was written for D3.v3)… but when I add in the HTML <body> element to which the chart appears to bind in the example, nothing happens. This makes sense, right, because the chart no longer knows where to find the csv data, which is no longer referenced as data.csv?

I have been trying ways to include var data = d3.csvParse(csvdata, type); in to the javascript, but get errors like TypeError: t.charCodeAt is not a function. I feel like the more I dig around and hack for solutions, the more of a mess I have been making.

I’d appreciate any help, guidance, and feedback. I apologize for my basic questions. I’ve been enjoying Observable because it helps me to play around and experiment and learn how pieces fit together. I am in the very early days of my learning journey with Javascript, however, and it’s a steep learning curve. Thanks to everyone who has been patient with me, and for all the amazingly nice things that the Observable team is doing for teaching newbies like me and for encouraging friendly and helpful interactions on these help forums!

Sincerely,

Aaron

1 Like

Hey Aaron,

Sure, no problem - to break this down in to a few parts:

I have been trying ways to include var data = d3.csvParse(csvdata, type); in to the javascript, but get errors like TypeError: t.charCodeAt is not a function.

You’ve defined a cell (correctly) that parses a string into an array of objects using the d3.csvParse method, and assigns the resulting array of objects to the variable csvdata. Calling d3.csvParse(csvdata) is unnecessary, then, later on - it’s already a parsed array of objects, so calling csvParse on it throws this error. csvParse expects a string as input (with a String#charCodeAt method), rather than an array.

Re: constructing HTML - cells like

html`<body>
`

Unfortunately aren’t going to work, for a few reasons:

  • the html tagged template literal is less of a “write this to the page” method as a “create an element like this”, so if you give it just a starting tag like <body>, it’ll generate an empty <body> element rather than starting an element that later blocks can fill.
  • The other difficulty here is that cells don’t run in visual order: cells run when they need to based on their dependencies.
  • There’s already a body tag: notebooks render into a page that already has <html>, <meta>, and <body> tags, so trying to add another body in that body won’t be okay in HTML’s rules.

Here’s a tweaked, working example: Learning A Donut Chart from D3.js / Tom MacWright | Observable

The main changes to the chart cell are:

  • Observable is all about returning elements as values: so the chart cell itself creates an svg element and returns it: that’s how the chart is displayed from that cell. This is a bit different than web coding where you’ll primarily rely on selectors like d3.select('body') to add things to the page (but pretty similar to systems like React, Backbone, and so on where components create their own elements.
  • That Stackoverflow ticket seemed to be a bit off - scale.ordinal in d3 v3 is scaleOrdinal in d3 v4.

Hope that helps! Totally appreciate that it’s a steep learning curve - it’s easy to get lost in corners when learning a new thing. I feel the same way when I’m trying to learn Rust and some other languages… can get caught on ‘adding two numbers together’ for hours :slight_smile:

1 Like

Here’s my take on a donut chart:

1 Like

Thank you, Tom. This does help – a lot. I am still working through and reading about the changes you made to get everything working. I am still a bit stuck the part where you remove the final function from the JS code that concluded the example, namely:

function type(d) {
  d.population = +d.population;
  return d;
}

… I’ll keep reading (and will try out a few more examples) to see if I can figure it out.

Thank you for all the help and words of encouragement! Hopefully this will all come together for me one day. I can’t express how thankful I am for Observable! It’s a big help to have an interactive editor for JavaScript that works so beautifully. The immediate feedback really helps a lot!

The purpose of this function was to coerce the data’s population field from a string to a number. When you load a CSV file using d3-dsv, it doesn’t do any type inference, and treats all the columns as strings:

By passing a row function to as the second argument to d3.csvParse (see dsv.parse), you can alter the object that is used to represent each row. (This is almost equivalent to array.map, except the row function is applied during parsing, which can make it much more efficient than mapping the array after the entire CSV file is parsed.)

Here’s an example using a row function to coerce the population field to a number. Notice that the inspector now shows the population fields as numbers rather than strings.

Coercing strings to numbers isn’t always required; pie.value, for example, coerces whatever value you return to a number. So in this case you don’t need to coerce the population field to a number lazily, since the pie layout will do it for you implicitly.

However, it’s almost always a good idea to convert to more specific types (such as parsing dates and coercing numbers) when loading your data. Otherwise, it’s really easy to perform string operations accidentally. For example:

If you coerce the data on load, you get the expected result:

3 Likes

Thank you very much, came across this post and solved some stuff with your example on using d3.csvParse(string, callback) :partying_face:

1 Like