Published
Edited
Jun 27, 2018
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require('d3')
Insert cell
Insert cell
Insert cell
usWeatherHistoryDataInput = d3.csv('https://raw.githubusercontent.com/fivethirtyeight/data/master/us-weather-history/KIND.csv').then(data => data)
Insert cell
Insert cell
usWeatherHistoryData = d3.csv('https://raw.githubusercontent.com/fivethirtyeight/data/master/us-weather-history/KIND.csv').then(data => {
const mappedData = _.map(data, d => {
return {
date: moment(d.date, 'YYYY-M-D'),
dateDisplay: moment(d.date, 'YYYY-M-D').format('YYYY-MM-DD'),
actualMaxTemperature: _.round(fahrenheit2Celsius(_.toNumber(d.actual_max_temp)), 0),
actualMinTemperature: _.round(fahrenheit2Celsius(_.toNumber(d.actual_min_temp)), 0),
actualMeanTemperature: _.round(fahrenheit2Celsius(_.toNumber(d.actual_mean_temp)), 0),
actualPrecipitation: _.toNumber(d.actual_precipitation)
};
});
return mappedData;
})
Insert cell
Insert cell
Insert cell
x2 = d3.scaleBand() // Each value needs a "space" across the graph width, so that everything can fit
.domain(usWeatherHistoryData.map(d => d.dateDisplay)) // Pass an array of dates you want to use for domain
.range([usWeatherHistoryDataMargin.left, width - usWeatherHistoryDataMargin.right]) // And map it into desired range and put them across the whole width of the graph
Insert cell
y2 = d3.scaleLinear() //Transform values into defined interval
.domain([d3.min(usWeatherHistoryData, d => d.actualMeanTemperature), d3.max(usWeatherHistoryData, d => d.actualMeanTemperature)]).nice() //Check values, create interval for domain and give me nice numbers
.range([usWeatherHistoryHeight - usWeatherHistoryDataMargin.bottom, usWeatherHistoryDataMargin.top]) //And then convert each number into this interval (range), so that it can fit inside of graph height.
Insert cell
Insert cell
Insert cell
Insert cell
xAxis2 = g => g //I want to add X-axis, so that I can know that is what
.call(d3.axisBottom(x2).tickValues(x2.domain().filter((d, i) => !(i % 10)))) //We have 365 values, we have to somehow filter the values. We will use the tickValues function which basically returns the values which need to be shown in axiss. Additionally, we are filtering the items by taking every 10th element.
.attr("transform", `translate(0,${usWeatherHistoryHeight - usWeatherHistoryDataMargin.bottom})`) //Put X-axis on this location
Insert cell
yAxis2 = g => g //Same for Y-axis
.attr("transform", `translate(${usWeatherHistoryDataMargin.left},0)`)
.attr("class", "axis axis--y")
.call(d3.axisLeft(y2))
.call(g => g.select(".domain")) //Select a point from where we'll start building our text element
.call(g => g.append("text") //Let's say that I wanna add some additional text
.attr("fill", "#000") //And then I have to define all additional values
.attr("x", 2) //Set X position of the text (from left to right) in comparison to available D3 canvas
.attr("y", usWeatherHistoryDataMargin.top - 5) //Set Y position of the text (from top to bottom) in comparison to available D3 canvas
.attr("dy", "0.32em") //Set offset in comparison to Y values (additional moving of element). Em means relative value in comparison to font value (2em -> twice the current font size).
.attr("text-anchor", "start")
.text("°C (avg)"))
Insert cell
Insert cell
{
const svg = d3.select(DOM.svg(width, usWeatherHistoryHeight + 35)); //Standard start :)
svg.append("g")
.call(xAxis2)
.selectAll("text") //We are selecting all those labels we have appended
.attr("transform", "translate(-48, 48) rotate(-48)") //Let's say that we want to fit more values. That means that we need to rotate the values. We can do that by using the transform attribute where you can assign for how much you want to nudge the values and how much degrees they should be rotated.
.style("text-anchor", "start")
.style("font-size", 12);
svg.append("g")
.call(yAxis2)
.selectAll("text")
.style("font-size", 12);
return svg.node();
}
Insert cell
Insert cell
{
const svg = d3.select(DOM.svg(width, usWeatherHistoryHeight + 35)); //Standard start :)
svg.append("g") //This part adds circles ... also required if you want to create a scatter chart!
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("circle")
.data(usWeatherHistoryData)
.enter().append("circle")
.attr("cx", d => x2(d.dateDisplay)) //Move specified number of units from the left to the right of the SVG element
.attr("cy", d => y2(d.actualMeanTemperature)) //Move specified number of units from the top to the bottom of the SVG element
.attr("fill", "red")
.attr("r", 1.75); //Radius of element. Since you only tell with cx and cy where to put a dot, you have to set its radius with "r" attribute.
svg.append("g")
.call(xAxis2)
.selectAll("text")
.attr("transform", "translate(-48, 48) rotate(-48)")
.style("text-anchor", "start")
.style("font-size", 12);
svg.append("g")
.call(yAxis2)
.selectAll("text")
.style("font-size", 12);
return svg.node();
}
Insert cell
Insert cell
Insert cell
usWeatherHistoryAvgLine = d3.line() //Construct a line
.x(d => x2(d.dateDisplay)) //For X-axis, please take a date I'm looking at
.y(d => y2(d.actualMeanTemperature)) //For Y-axis, please take a mean temperature for that day
Insert cell
Insert cell
{
const svg = d3.select(DOM.svg(width, usWeatherHistoryHeight + 35)); //Standard start :)
svg.append("g") //This part adds circles ... also required if you want to create a scatter chart!
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("circle")
.data(usWeatherHistoryData)
.enter().append("circle")
.attr("cx", d => x2(d.dateDisplay)) //Move specified number of units from the left to the right of the SVG element
.attr("cy", d => y2(d.actualMeanTemperature)) //Move specified number of units from the top to the bottom of the SVG element
.attr("fill", "red")
.attr("r", 1.75); //Radius of element. Since you only tell with cx and cy where to put a dot, you have to set its radius with "r" attribute.
svg.append("path") //If you don't want only scatter chart, you most likely want a line chart, so you need to connect all those dots!
.datum(usWeatherHistoryData) //If you want to create a single element for all values, use datum (you don't need enter and append then). If you want a DOM element per array value, use data (which then requires enter and append!).
.attr("stroke", "red")
.attr("stroke-opacity", 0.8)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.attr("d", usWeatherHistoryAvgLine); //So, we wanna create an actual line chart, so we need to pass
svg.append("g")
.call(xAxis2)
.selectAll("text") //We are selecting all those labels we have appended
.attr("transform", "translate(-48, 48) rotate(-48)") //Let's say that we want to fit more values. That means that we need to rotate the values. We can do that by using the transform attribute where you can assign for how much you want to nudge the values and how much degrees they should be rotated.
.style("text-anchor", "start")
.style("font-size", 12);
svg.append("g")
.call(yAxis2)
.selectAll("text")
.style("font-size", 12);
svg.append("text") //Let's say that I want a title, I can simply append text element, add some text to it and transform/style it. :)
.text("US Mean Temperature Statistics (July 2014 - July 2015)")
.attr("transform", `translate (${width / 4}, 20)`)
.style("font-family", "Calibri")
.style("font-size", 20)
.style("font-weight", "bold");
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
html`<svg width="${circleGraphSize}" height="${circleGraphSize}" fill="none" stroke="black">${
d3.range(circles/2, circleGraphSize/2, circles/2).map(radius => `<circle r="${radius}" transform="translate(${circleGraphSize/2}, ${circleGraphSize/2})"></circle>`)
}</svg>`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
x1 = d3.scaleBand() //Bar column for each letter :)
.domain(letterBarChartData.map(d => d.data)) //Call those letters like this
.range([letterBarChartMargin.left, width - letterBarChartMargin.right]) //And put them across the whole width
.padding(0.15) //Bars shouldn't be so close like sardines in a can :)
Insert cell
y1 = d3.scaleLinear() //Transform values into this interval
.domain([0, d3.max(letterBarChartData, d => d.frequency)]).nice() //Give me a domain with nice numbers (without something like 12.354251364)
.range([letterBarChartHeight - letterBarChartMargin.bottom, letterBarChartMargin.top]) //And transform into this range
Insert cell
xAxis1 = g => g //I want to add X-axis, so that I can know that is what
.attr("transform", `translate(0,${letterBarChartHeight - letterBarChartMargin.bottom})`) //Where should the axis be
.call(d3.axisBottom(x1) //Call our x function
.tickSizeOuter(0))
Insert cell
yAxis1 = g => g //Same for Y-axis
.attr("transform", `translate(${letterBarChartMargin.left},0)`)
.call(d3.axisLeft(y1).ticks(10, "%")) //Give me nice percentages, so that we can continue with nice numbers :)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
var svg = d3.select(DOM.svg(width, letterBarChartHeight)); //You start from some element and you select it
svg.append("g") //This part gives us bars for each letter
.selectAll("rect").data(letterBarChartData).enter().append("rect") //Generate bar for each row in data ... this row's the one you should blame for all those beautiful data! :)
.attr("class", "bar")
.attr("x", d => x1(d.data)) //Where should be each value
.attr("y", d => y1(d.frequency)) //How high should be each bar
.attr("height", d => y1(0) - y1(d.frequency)) //Height from bottom to top (value)
.attr("width", x1.bandwidth()); //How wide should be each bar?
svg.append("g") //This part gives us X-axis
.call(xAxis1)
.selectAll("text")
.style("font-size", 13);
svg.append("g") //This part gives us Y-axis
.call(yAxis1)
.selectAll("text")
.style("font-size", 13);
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more