Published
Edited
Jun 21, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
table_info = [
{
technique: "Select",
description: "<em>To mark something as important</em>",
example:
"<a href='https://observablehq.com/@elaval/coronavirus-worldwide-evolution' target='_blank'>The lines</a> in this chart are clickable for the user to be able to highlight specific countries of interest."
},
{
technique: "Explore",
description:
"<em>To show details about a specific part or point in the data</em>",
example:
"<a href='https://observablehq.com/@oluckyman/point-on-a-path-detection' target='_blank'>The sliding pointer</a> allows the user to explore different points on the dataset."
},
{
technique: "Reconfigure",
description: "<em>Showing or altering the information being displayed</em>",
example:
"<a href='https://observablehq.com/@anjana/anjanas-world-earthquake-map' target='_blank'>The controls</a> on this globe allow the user to reconfigure the visualization for different time periods or settings."
},
{
technique: "Encode",
description:
"<em>The form in which information is presented to the user</em>",
example:
"<a href='https://observablehq.com/@autopaideia/covid-19-in-nyc-by-zip-code-income' target='_blank'>The color and metric controls</a> in this map show different encodings of the same covid data."
},
{
technique: "Abstract/Elaborate",
description: "<em>Control of the amount of detail shown to the user</em>",
example:
"The <a href='https://observablehq.com/@bartok32/connection-between-songs-features-and-popularity' target='_blank'>bins selector</a> shows a user more or less detail as he or she desires."
},
{
technique: "Filter",
description: "<em>To hide or partially show part of the information</em>",
example:
"The <a href='https://observablehq.com/@mbostock/ancestry-by-u-s-county' target='_blank'>ethnicity selector</a> filters for information the user specifically requests."
},
{
technique: "Connect",
description: "<em>Connecting the user to another resource</em>",
example:
"Each bubble<a href='https://www.w3schools.com/html/html_links.asp' target='_blank'>in this graph</a> connects the user to a new social media entity."
}
]
Insert cell
render_data_table(table_info)
Insert cell
Insert cell
md`## Visualization 1
### Lanugage isolate map using data from https://glottolog.org/glottolog/family
`
Insert cell
/*viewof categoryFilterString = select([
'All',
'Family',
'Pseudo Family',
'Spoken L1 Language'
])*/
Insert cell
pointChart = {
let svg = d3.create("svg");
svg.attr("viewBox", [0, 0, width, height]);

let map = svg
.append("g")
.selectAll("path")
.data(topoJsonFeature.features)
.enter()
.append("path")
.attr("d", path);
let pointContainer = svg.append("g");
let points = pointContainer.selectAll("g");

let pointsSelection = points.data(languageIsolates);

let pointsGroup = pointsSelection
.enter()
.append("g")
.attr("transform", element => {
let elementLatLong = [-element.latitude, -element.longitude];
let projectionCoords = projection(elementLatLong);
return (
"translate(" + projectionCoords[1] + "," + projectionCoords[0] + ")"
);
})
.on("mouseover", languageIsolateOnMouseOver)
.on("mouseout", (element, index) => {
let hiddenElement = d3.select("#" + element.name);
hiddenElement.attr("style", "visibility: hidden;");
});
let circles = pointsGroup
.append("circle")
.attr("fill", "green")
.attr("r", "4");

let hiddenDescriptions = pointsGroup
.append("g")
.attr("style", "visibility: hidden;")
.attr("id", element => {
return element.name;
});
let hiddenDescriptionText = hiddenDescriptions
.append("text")
.attr("style", "font-size:19pt;")
.attr("fill", "red")
.text(element => {
return element.name;
});
//points.on('mouseover', element => {});

let titleDiv = svg
.append("g")
.attr("transform", "translate(" + width / 4 + ", " + 30 + ")")
.append("text")
.text("Location of each language family");

return svg.node();
}
Insert cell
languageIsolateOnMouseOver = (element, index) => {
let hiddenElement = d3.select("#" + element.name);
hiddenElement.attr("style", "visibility: visible;");
}
Insert cell
md`## Visualization 1 Appendix
`
Insert cell
languageFamiliesRaw = FileAttachment("languageTopLevelFamilies.csv").text()
Insert cell
languageFamiliesParsed = d3.csvParse(languageFamiliesRaw, d3.autoType)
Insert cell
/*filteredLanguage = languageFamiliesParsed.filter(element => {
return (
element.category == categoryFilterString || categoryFilterString == "All"
);
})*/
Insert cell
languageIsolates = languageFamiliesParsed.filter(element => {
return (
element.category == "Spoken L1 Language" &&
element.latitude &&
element.longitude
);
})
Insert cell
worldmapTopoJson = FileAttachment("worldmapSimplifiedTopoJson.json").json()
Insert cell
path = d3.geoPath().projection(projection)
Insert cell
topoJsonFeature = topojson.feature(
worldmapTopoJson,
worldmapTopoJson.objects[
"99bfd9e7-bb42-4728-87b5-07f8c8ac631c2020328-1-1vef4ev"
]
)
Insert cell
projection = d3
.geoIdentity()
.reflectY(true)
.fitSize([width, height], topoJsonFeature)
Insert cell
md`## Visualization 2
### The philips curve

`
Insert cell
maxI=.2;
Insert cell
getR=(iValue,piValue)=>{
return iValue+piValue;
}
Insert cell
minInterestRate = getRFromY(maxY)
Insert cell
maxInterestRate = getRFromY(minY)
Insert cell
getRFromY = yValue => {
return (yValue - baseY) / ySlope;
}
Insert cell
ySlope = baseY * -1.69;
Insert cell
maxY=y(getR(0,getPi(0)))
Insert cell
minY = y(maxI)
Insert cell
r = i + getPi(m)
Insert cell
baseY = 100000
Insert cell
y = rVal => {
return baseY + ySlope * rVal;
}
Insert cell
piSlope = .001
Insert cell
getPi =(mValue)=>{ return piSlope * mValue}
Insert cell
getPiFromY = yVal => {
return -(yVal - baseY) / ySlope + i;
}
Insert cell
mSupplyDomain = [0, 10000]
Insert cell
m = 5000
Insert cell
iSlope = maxI / mSupplyDomain[1]
Insert cell
i=maxI-iSlope*m;
Insert cell
philipsIncomeXScale = d3
.scaleLinear()
.domain([minY, maxY])
.range([0, graphWidth])
Insert cell
philipsInterestRateYScale = d3
.scaleLinear()
.domain([maxI, 0])
.range([0, graphHeight])
Insert cell
philipsRealInterestRateYScale = d3
.scaleLinear()
.domain([minInterestRate, maxInterestRate])
.range([graphHeight, 0])
Insert cell
getU = yValue => {
return 1 - (1 / maxY) * yValue + .01;
}
Insert cell
minUnemployment = getU(maxY)
Insert cell
maxUnemployment = getU(minY)
Insert cell
philipsUnemploymentYScale = d3
.scaleLinear()
.domain([minUnemployment, maxUnemployment])
.range([graphHeight, 0])
Insert cell
philipsInflationYScale = d3
.scaleLinear()
.domain([getPiFromY(minY), getPiFromY(maxY)])
.range([graphHeight, 0])
Insert cell
yMultiples = [
{
scale: philipsRealInterestRateYScale,
func: getRFromY,
name: "Real interest rates",
symbol: 'r',
yVal: 70000
},
{
scale: philipsUnemploymentYScale,
func: getU,
name: "Unemployment rate",
symbol: 'u',
yVal: 70000
},
{
scale: philipsInflationYScale,
func: getPiFromY,
name: "Inflation rate",
symbol: 'pi',
yVal: 70000
}
]
Insert cell
makeYMultiple=(selection, chartData)=>{
}
Insert cell
//Returns the
mousePositionToYPercentage = mouseX => {
let incomeFromPixel = philipsIncomeXScale.invert(mouseX);
let newY = Math.max(Math.min(incomeFromPixel, maxY), minY);
return newY;
}
Insert cell
philipsChart = {
let svg = d3.create("svg");
svg.attr("viewBox", [0, 0, width, height]);
let currentY = { cur: 80000 };
let currentYCont = [currentY];
let graphContainer = svg.append("g").attr("transform", element => {
let yTrans = 200;
let xTrans = 20;
return "translate(" + xTrans + "," + yTrans + ")";
});
let yMultiplesCopy = _.cloneDeep(yMultiples);

let slidingBar = svg
.append("line")
.attr("id", "slider")
.attr("y1", element => {
return 0;
})
.attr("x1", element => {
console.log(currentY);
return philipsIncomeXScale(currentY.cur);
})
.attr("y2", element => {
return height;
})
.attr("x2", element => {
return philipsIncomeXScale(currentY.cur);
})

.attr("style", "stroke:rgb(255,0,0);stroke-width:3");
let graphs = graphContainer
.selectAll("g.yMult")
.data(yMultiplesCopy)
.enter()
.append("g")
.attr("id", (element, index) => {
"ph" + index;
})
.attr("transform", (element, index) => {
let yTrans = index * (graphHeight + graphMargin.top);
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
})
.attr("class", "yMult");

graphs.append("text").text(element => {
return element.name;
});
graphs
.append("line")
.attr("y1", element => {
return element.scale(element.func(minY));
})
.attr("x1", element => {
return philipsIncomeXScale(minY);
})
.attr("y2", element => {
return element.scale(element.func(maxY));
})
.attr("x2", element => {
return philipsIncomeXScale(maxY);
})
.attr("style", "stroke:rgb(255,0,0);stroke-width:3");
graphs.append("text").attr("transform", (element, index) => {
let yTrans = 100;
let xTrans = 300;
return "translate(" + xTrans + "," + yTrans + ")";
}) /*
.text(element => {
return "is:" + element.func(currentY.cur);
})*/;
graphs
// .each(drawPhilipsYAxis);
.append("g")
.attr("transform", (element, index) => {
let yTrans = graphHeight;
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
})
.call(d3.axisBottom(philipsIncomeXScale));
graphContainer
.append("g")
.attr("transform", (element, index) => {
let yTrans = 0 * (graphHeight + graphMargin.top);
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
})
.call(d3.axisLeft(philipsRealInterestRateYScale));
graphContainer
.append("g")
.attr("transform", (element, index) => {
let yTrans = 1 * (graphHeight + graphMargin.top);
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
})
.call(d3.axisLeft(philipsUnemploymentYScale));
graphContainer
.append("g")
.attr("transform", (element, index) => {
let yTrans = 2 * (graphHeight + graphMargin.top);
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
})
.call(d3.axisLeft(philipsInflationYScale));
let titleDiv = svg
.append("g")
.attr("transform", "translate(" + width / 4 + ", " + 30 + ")")
.append("text")
.text("Philips curve");
svg.on("mouseover", function() {
currentY.cur = mousePositionToYPercentage(d3.mouse(this)[0]);
d3.select("line#slider")
.attr("x1", philipsIncomeXScale(currentY.cur))
.attr("x2", philipsIncomeXScale(currentY.cur));

/*yMultiplesCopy = yMultiples.map(element => {
element.yVal = currentY;
return element;
});*/
});
return svg.node();
}
Insert cell
drawPhilipsYAxis = (element, index) => {
let selection = d3.select("#ph" + index);
let axisFunction = d3.axisLeft(element.scale);
axisFunction(selection);
//console.log(selection);
//selection.call(axisFunction);
}
Insert cell
md`## Visualization 2 Appendix
`
Insert cell
graphWidth=200;
Insert cell
graphHeight = 200
Insert cell
graphMargin = ({
top: 70
})
Insert cell
md`## Visualization 3
### Spotify Song scalar scatterplot comparisons.

Compares 2 different scalars of top spotify songs.

`
Insert cell
md`Select X variable`
Insert cell
viewof scalarFilterStringX = select(
scalarColumnNames.map(name => {
return name;
})
)
Insert cell
md`Select Y variable`
Insert cell
yColumnNames = scalarColumnNames.filter(element => {
return element != scalarFilterStringX;
})
Insert cell
viewof scalarFilterStringY = select(
yColumnNames.map(name => {
return name;
})
)
Insert cell
metricsComparisonChart = {
let svg = d3.create("svg");
svg.attr("viewBox", [0, 0, width + 400, height + 200]);

let graphContainer = svg.append("g").attr("transform", element => {
let yTrans = 100;
let xTrans = 80;
return "translate(" + xTrans + "," + yTrans + ")";
});

let graphs = graphContainer
.selectAll("g.point")
.data(metricComparisonList)
.enter()
.append("g")
.attr("id", (element, index) => {
return "c" + index;
})
.attr("class", "point")
.on("mouseover", (element, index) => {
console.log(element, index);
let hiddenElement = d3.select("#c" + index);
hiddenElement.select("circle").attr("style", "visibility: hidden;");
hiddenElement
.select(".hiddenTooltip")
.attr("style", "visibility: visible;");
})
.on("mouseout", (element, index) => {
let hiddenElement = d3.select("#c" + index);
hiddenElement.select("circle").attr("style", "visibility: visible;");
hiddenElement
.select(".hiddenTooltip")
.attr("style", "visibility: hidden;");
});

graphContainer
.append("g")
.attr("transform", element => {
let yTrans = height;
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
})
.call(d3.axisBottom(scalarX));

graphContainer.append("g").call(d3.axisLeft(scalarY));
graphContainer
.append("text")
.text(scalarFilterStringX)
.attr("style", "font-size:18pt")
.attr("transform", element => {
let yTrans = 0;
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
});
graphContainer
.append("text")
.text(scalarFilterStringY)
.attr("style", "font-size:18pt;")

.attr("transform", element => {
let yTrans = height - 20;
let xTrans = width - 80;
return "translate(" + xTrans + "," + yTrans + ")";
});

graphs
.append("circle")
.attr("fill", "black")
.attr("r", 5)
.attr("cx", element => {
return scalarX(element.x);
})
.attr("cy", element => {
return scalarY(element.y);
});
let hiddenTooltips = graphs
.append("g")
.attr("class", "hiddenTooltip")
.attr("style", "visibility: hidden;")
.attr("transform", element => {
let yTrans = scalarY(element.y);
let xTrans = scalarX(element.x);
return "translate(" + xTrans + "," + yTrans + ")";
});
hiddenTooltips
.append("rect")
.attr("width", element => {
return 10 * element.artist.length;
})
.attr("height", 30)
.attr("fill", "red");
hiddenTooltips
.append("text")
.attr("fill", "white")
.attr("transform", element => {
let yTrans = 20;
let xTrans = 0;
return "translate(" + xTrans + "," + yTrans + ")";
})
.text(element => {
return element.artist;
});

let titleDiv = svg
.append("g")
.attr("transform", "translate(" + width / 4 + ", " + 30 + ")")
.append("text")
.text(scalarFilterStringX + " X " + scalarFilterStringY);
return svg.node();
}
Insert cell
md`## Visualization 3 appendix`
Insert cell
top50NoNans
Insert cell
metricComparisonList = top50NoNans.map(element => {
return {
x: element[scalarFilterStringX],
y: element[scalarFilterStringY],
name: element.title,
artist: element.artist
};
})
Insert cell
getScalarScale = (min, max, rangeMin, rangeMax) => {
return d3
.scaleLinear()
.domain([min, max])
.range([rangeMin, rangeMax]);
}
Insert cell
xMin = d3.min(metricComparisonList, element => {
return element.x;
})
Insert cell
xMax = d3.max(metricComparisonList, element => {
return element.x;
})
Insert cell
yMin= d3.min(metricComparisonList, element => {
return element.y;
})
Insert cell
yMax = d3.max(metricComparisonList, element => {
return element.y;
})
Insert cell
scalarX = getScalarScale(xMin, xMax, 0, width)
Insert cell
scalarY = getScalarScale(yMin, yMax, height, 0)
Insert cell
import {
top50NoNans,
scalarColumnNames
} from '@info474/exploratory-data-analysis/2'
Insert cell
Insert cell
Insert cell
Insert cell
_ = require('lodash')
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3")
Insert cell
height = 1200
Insert cell
width = 1200
Insert cell
margin = ({ left: 40, right: 40, top: 70, bottom: 30 })
Insert cell
import { select, radio, checkbox } from "@jashkenas/inputs"
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more