Published
Edited
Mar 7, 2022
2 stars
Insert cell
Insert cell
Here is the link to the survey: https://docs.google.com/forms/d/e/1FAIpQLSekKwDEDwxTrIt2A7AyVkurNKVdPnq1oIfyD5iPVd6jzDaWVA/viewform?usp=sf_link
Insert cell
surveyData = {
let names = [];

reload; // refetch data from google form if reload button is clicked

await d3.tsv('https://docs.google.com/spreadsheets/d/e/2PACX-1vQuAClwZ6o-KplJGXGYr7r33rER3hdliGianyAxnfqCmhTJYhiju7GehKvcJTPicfj2zK3wZGBJZzwi/pub?output=tsv').then(data => {
names = []
data.forEach(d => names.push(d))
});
return names;
/* refetch data from google form every 30 seconds
while (true) {
await d3.tsv('https://docs.google.com/spreadsheets/d/e/2PACX-1vQuAClwZ6o-KplJGXGYr7r33rER3hdliGianyAxnfqCmhTJYhiju7GehKvcJTPicfj2zK3wZGBJZzwi/pub?output=tsv').then(data => {
names = []
data.forEach(d => names.push(d))
});
yield names;
await Promises.delay(30000);
}*/
}

Insert cell
mathTopics = ["Number and quantity", "Matrices / Vectors", "Algebra", "Functions - Polynomial", "Functions - Exponential", "Functions - Trigonometric", "Geometry", "Measurement - Area & Volume", "Statistics and Data Analysis", "Probability", "Calculus"]
Insert cell
Insert cell
Insert cell
Insert cell
scatter = {

const svg = d3.create("svg")
.attr("width", 450)
.attr("height", 450)
.attr("viewBox", [0, 0, 450, 450])
.attr("style", `max-width: 100%; height: auto; overflow: visible; margin-top: 30px; margin-left:${0.5*(Math.max(width-450,0))}; margin-right:${0.5*(Math.max(width-450))};`)

const gradient = DOM.uid();

const svgGrad = svg.append("linearGradient")
.attr("id", gradient.id)
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", 400)
.attr("y1", 450)
.attr("x2", 0)
.attr("y2", 0)
svgGrad.append("stop")
.attr("offset","0%")
.attr("stop-color","rgb(190, 255, 189)")
svgGrad.append("stop")
.attr("offset","50%")
.attr("stop-color","white")
svgGrad.append("stop")
.attr("offset","100%")
.attr("stop-color","rgb(255, 189, 244)")

svg.append("rect")
.attr("x",50)
.attr("y",0)
.attr("width",400)
.attr("height",400)
.attr("fill",gradient)
let sortedUse = [...topicData].sort((a, b) => d3.ascending(a.useful, b.useful))
let sortedEnj = [...topicData].sort((a, b) => d3.ascending(a.enjoyed, b.enjoyed))
const xScale = d3.scaleLinear(
[Math.min( Math.floor((+sortedUse[0].useful - 2.5) / 5) * 5 , Math.floor((+sortedEnj[0].enjoyed - 2.5) / 5) * 5 ),
Math.ceil((+sortedUse[topicData.length-1].useful + 2.5) / 5) * 5],
[50,450])

const yScale = d3.scaleLinear(
[Math.min( Math.floor((+sortedUse[0].useful - 2.5) / 5) * 5 , Math.floor((+sortedEnj[0].enjoyed - 2.5) / 5) * 5 ),
Math.max(Math.ceil((+sortedUse[topicData.length-1].useful + 2.5) / 5) * 5, Math.ceil((+sortedEnj[topicData.length-1].enjoyed + 2.5) / 5) * 5)],
[400,0])

svg.append("g") // the axis will be contained in an SVG group element
.attr("id","yAxis")
.call(d3.axisLeft(yScale)
.ticks(5)
.tickFormat(d3.format("d"))
.tickSizeOuter(0)
)
.attr("transform","translate(50,0)")
.attr("color","rgb(200, 0, 180)")
.style("font-family","Teko, sans-serif")
.style("font-weight","300")
.style("font-size",16)
svg.append("g")
.attr("transform", `translate(0,400)`) // translate x-axis to bottom of chart
.attr("id","xAxis")
.call(d3.axisBottom(xScale)
.ticks(5)
.tickFormat(d3.format("d"))
.tickSizeOuter(0)
)
.attr("color","rgb(30, 156, 60)")
.style("font-family","Teko, sans-serif")
.style("font-weight","300")
.style("font-size",16)

const xAxisLabel = svg.append("text")
.attr("x", 450)
.attr("y", 440)
.text("% who said: USEFUL")
.style("text-anchor","end")
.attr("fill", "rgb(0, 156, 18)")
.style("font-family","Yanone Kaffeesatz, sans-serif")
.style("font-size",20);

const yAxisLabel = svg.append("text")
.attr("x", 0)
.attr("y", 40)
.text("% who said: ENJOYABLE")
.style("text-anchor","end")
.attr("transform","translate(-20,0), rotate(-90)")
.attr("fill", "rgb(200, 0, 180)")
.style("font-family","Yanone Kaffeesatz, sans-serif")
.style("font-size",20);
svg.selectAll(".bubble")
.data(topicData)
.join("circle")
.attr("class", "bubble")
.attr("cx", d => xScale(d.useful))
.attr("cy", d => yScale(d.enjoyed))
.attr("r", 25)
.attr("stroke", (d,i) => colors11[i])
.attr("fill", (d,i) => colors11[i])
.attr("fill-opacity", 0.25)

svg.selectAll(".bubbleLabel")
.data(topicData) // bind each element of the data array to one SVG circle
.join("text")
.attr("class", "bubbleLabel")
.attr("x", d => xScale(d.useful))
.attr("y", d => yScale(d.enjoyed))
.style("text-anchor","middle")
.style("font-weight","700")
.attr("fill", (d,i) => colors11[i])
.text(d => d.topic.indexOf("Functions")>=0
? d.topic.split("Functions - ")[1].substr(0,4)
: d.topic.substr(0,4))
.style("font-family","Yanone Kaffeesatz, sans-serif")
return svg.node()
}
Insert cell
mathPractices = ["Make sense of problems and persevere in solving them.", "Reason abstractly and quantitatively.","Construct viable arguments and critique the reasoning of others.", "Model with mathematics.", "Attend to precision."]
Insert cell
mathPracticesShort = ["Problem solving and perseverance", "Abstract and quantitative reasoning","Construct and critique arguments", "Model with mathematics", "Attend to precision"]
Insert cell
practiceData = {

let filteredData = surveyData;
if(mathSciFilter.split("(")[0] !== "All "){
if(mathSciFilter.split("(")[0] == "Working in Math / Science field "){
filteredData = surveyData.filter(el => ["Accountant","Analyst (Business / Finance / Systems)","Data Analyst","Doctor / Dentist","Engineer","Mathematician","Scientist","Software Developer"].includes(el["Select your occupation (or select \"Other\" and type below) "]))
}
else if(mathSciFilter.split("(")[0] == "Working in other field "){
filteredData = surveyData.filter(el => (el["Which of these best describes your current employment?"] === "Working") && !["Accountant","Analyst (Business / Finance / Systems)","Data Analyst","Doctor / Dentist","Engineer","Mathematician","Scientist","Software Developer"].includes(el["Select your occupation (or select \"Other\" and type below) "]))
}
else if(mathSciFilter.split("(")[0] == "Uni students in Math / Science field "){
filteredData = surveyData.filter(el => ["Biology","Business","Commerce / Finance / Economics","Computer Science","Engineering","Mathematics","Medicine","Physics","Psychology"].includes(el["Select your field of study (or select \"Other\" and type below)"]))
}
else if(mathSciFilter.split("(")[0] == "Uni students in other field "){
filteredData = surveyData.filter(el => (el["Which of these best describes your current employment?"] === "University / college student") && !["Biology","Business","Commerce / Finance / Economics","Computer Science","Engineering","Mathematics","Medicine","Physics","Psychology"].includes(el["Select your field of study (or select \"Other\" and type below)"]))
}
else if(mathSciFilter.split("(")[0].substr(0,4) === "Uni:"){
filteredData = surveyData.filter(el => el["Select your field of study (or select \"Other\" and type below)"] === mathSciFilter.split(" (")[0].substr(5))
}
else{
filteredData = surveyData.filter(el => el["Select your occupation (or select \"Other\" and type below) "] === mathSciFilter.split(" (")[0]);
}
}
let data = mathPractices.map((practice, i)=>{
return { "practice" : mathPracticesShort[i],
"useful" :
filteredData.reduce(function (a, b) {
return ["4","5"].includes(b[practice]) ? a + 1 : a
}, 0)
,
"useless" :
filteredData.reduce(function (a, b) {
return ["1","2"].includes(b[practice]) ? a + 1 : a
}, 0),
}
})
data = data.map(el => {
el.useful = el.useful / filteredData.length * 100;
el.useless = el.useless / filteredData.length * 100;
return el})
data.sort((a, b) => d3.ascending(a.useful, b.useful))
return data
}
Insert cell
practiceChart = {
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic; margin-top: 50px; overflow: visible");
const yScale = d3.scaleBand(
Array.from(Array(mathPractices.length).keys()),
[300,0])
.paddingInner(2);

let sortedArr = [...practiceData].sort((a, b) => d3.ascending(a.useful, b.useful))
const xScale = d3.scaleLinear([0,sortedArr[practiceData.length-1].useful],[0,width/2 - 10])

const xAxis = d3.axisTop()
.scale(xScale)
.ticks(5)
.tickSizeOuter(0)

const xScale2 = d3.scaleLinear([0,sortedArr[practiceData.length-1].useful],[width/2 - 10, 0])

const xAxis2 = d3.axisTop()
.scale(xScale2)
.ticks(5)
.tickSizeOuter(0);

const axisLabel = svg.append("text")
.attr("x", width - 10)
.attr("y", -30)
.text("% who said: USEFUL")
.style("text-anchor","end")
.attr("fill", "rgb(10, 156, 252)")
.style("font-family","Yanone Kaffeesatz, sans-serif")
.style("font-size",20);

const axisLabel2 = svg.append("text")
.attr("x", 10)
.attr("y", -30)
.text("% who said: USELESS")
.style("text-anchor","start")
.attr("fill","grey")
.style("font-family","Yanone Kaffeesatz, sans-serif")
.style("font-size",20);
svg.append("g")
.attr("transform",`translate(${width/2},0)`)
.call(xAxis)
.attr("color", "rgb(10, 156, 252)")
.style("font-family","Teko, sans-serif")
.style("font-weight","300")
.style("font-size",16);

svg.append("g")
.attr("transform",`translate(10,0)`)
.attr("color","grey")
.call(xAxis2)
.style("font-family","Teko, sans-serif")
.style("font-weight","300")
.style("font-size",16);
const usefulBar = svg.selectAll(".rightBar")
.data(practiceData)
.join("rect")
.classed("rightBar",true)
.attr("fill", "rgb(10, 156, 252)")
.style("fill-opacity",0.25)
.attr("stroke", "rgb(10, 156, 252)")
.attr("x", width/2)
.attr("y", (d,i) => yScale(i) + 2)
.attr("height", yScale(0) - yScale(1) - 2)
.attr("width", d => xScale(d.useful));

const uselessBar = svg.selectAll(".leftBar")
.data(practiceData.sort((a, b) => d3.ascending(a.useful, b.useful)))
.join("rect")
.classed("leftBar",true)
.attr("fill", "rgb(200,200,200)")
.style("fill-opacity",0.25)
.attr("stroke","grey")
.attr("x", d=> width/2 - xScale(d.useless))
.attr("y", (d,i) => yScale(i) + 2)
.attr("height", yScale(0) - yScale(1) - 2)
.attr("width", d => xScale(d.useless));

const barLabels = svg.selectAll(".barLabel")
.data(practiceData)
.join("text")
.classed("barLabel",true)
.attr("fill", "black")
.attr("x", d => 0.5 * (width - xScale(d.useless) + xScale(d.useful)))
.style("text-anchor","middle")
.style("alignment-baseline","middle")
.attr("y", (d,i) => 2 + yScale(i) + 0.5 * (300 / mathPractices.length))
.text(d => d.practice)
.style("font-family","Yanone Kaffeesatz, sans-serif")
return svg.node()
}
Insert cell
height = 500
Insert cell
viewof reload = Inputs.button("Reload data")
Insert cell
colors11 = ["rgb(49,180,238)", "rgb(58,23,66)", "rgb(63,134,45)", "rgb(129,21,180)", "rgb(115,123,85)", "rgb(30,57,174)", "rgb(11,69,18)", "rgb(220,44,122)", "rgb(16,75,109)", "rgb(214,6,26)", "rgb(83,53,21)"].reverse();
Insert cell
Insert cell
d3 = require("d3@7")
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