Published
Edited
May 28, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
table_info = [
{
technique: "Select",
description:
"<em>Users can mark something (a data item) as interesting. By making an item of interest visually distinctive, users can keep track of them / see any changes in representation even when looking at a large amount of data </em>",
example:
"<a href='https://www.researchgate.net/figure/A-full-screenshot-of-DnM-On-the-left-is-the-main-window-called-Dust-Magnet-where_fig5_220586633'>Selection</a>"
},
{
technique: "Explore",
description:
"<em>Enable users to examine a different subset of data cases.</em>",
example:
"<a href='https://www.visualthesaurus.com/wordlists/6770833'>Explore</a>"
},
{
technique: "Reconfigure",
description:
"<em>Provides a user with a different perspective on the data set by changing the spatial arrangement of representations. </em>",
example:
"<a href='http://www.b-eye-network.com/images/content/Rao-original-Figure-1.gif'>Reconfigure</a> "
},
{
technique: "Encode",
description:
"<em> Users can alter the fundamental visual representation of the data including visual appearance (color, size, and shape) of each data element. It allows users to basically see different representations of the same data. </em>",
example:
"<a href='https://community.tibco.com/wiki/mashup-example-multiple-views-using-tibco-spotfire-javascript-api'>Encode</a>"
},
{
technique: "Abstract/Elaborate",
description:
"<em>Allows users to have the ability to adjust the level of 'abstraction' of a data representation</em>",
example:
"<a href='https://www.google.com/search?q=cushion+treemaps&source=lnms&tbm=isch&sa=X&ved=2ahUKEwjZ3ayxjL_pAhXCqp4KHch_BSsQ_AUoAXoECAwQAw&biw=958&bih=736#imgrc=b2jLoXHmZRyxMM'> Abstract / Elaborate</a> "
},
{
technique: "Filter",
description:
"<em> A technique used to allow users to change what data they are seeing based off some type of condition they chose</em>",
example:
"<a href='https://observablehq.com/@elaval/coronavirus-worldwide-evolution'>Filter</a>"
},
{
technique: "Connect",
description:
"<em>This technique highlights relationships between data items that are already represented and/or can show hidden data items which are relevant to a specified item</em>",
example: "<a href='http://armsglobe.chromeexperiments.com/'>Connect</a>"
}
]
Insert cell
render_data_table(table_info)
Insert cell
Insert cell
md`## Visualization 1: Filter`
Insert cell
viewof movie_or_series = select(["Movie", "Series"])
Insert cell
chart1 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

if (movie_or_series == "Movie") {
//title
svg
.append("text")
.attr("class", "text")
.attr("transform", "translate(500,50)")
.style("text-anchor", "middle")
.style("font-size", "25px")
.style("font-weight", "bold")
.text("Count of ratings by genre for Disney movies");

//set x
const x_movie = d3
.scaleBand()
.domain(genre_movie.map(d => d.genre))
.range([margin.left, width - margin.right]);

//set y
const y_movie = d3
.scaleLinear()
.domain([d3.max(genre_movie, d => d.rating), 0])
.range([margin.bottom, height - margin.top]);

//setxAxis
const xAxis_movies = g =>
g
.call(d3.axisBottom(x_movie))
.attr('transform', `translate(0, ${height - margin.bottom + 10})`);

const yAxis_movie = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y_movie));

x_movie.padding(0.2);

svg
.append('g')
.call(xAxis_movies)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
svg.append('g').call(yAxis_movie);

svg
.selectAll('.bar')
.data(genre_movie)
.join(enter => enter.append("rect"))
.attr('x', d => x_movie(d.genre))
.attr('y', d => y_movie(d.rating))
.attr('width', x_movie.bandwidth())
.attr('height', d => y_movie(0) - y_movie(d.rating))
.attr('fill', "purple")
.attr('opacity', 0.7);
} else {
//title
svg
.append("text")
.attr("class", "text")
.attr("transform", "translate(500,50)")
.style("text-anchor", "middle")
.style("font-size", "25px")
.style("font-weight", "bold")
.text("Count of ratings by genre for Disney series");

//set x
const x_series = d3
.scaleBand()
.domain(genre_series.map(d => d.genre))
.range([margin.left, width - margin.right]);

//set y
const y_series = d3
.scaleLinear()
.domain([d3.max(genre_series, d => d.rating), 0])
.range([margin.bottom, height - margin.top]);

//setxAxis
const xAxis_series = g =>
g
.call(d3.axisBottom(x_series))
.attr('transform', `translate(0, ${height - margin.bottom + 10})`);

const yAxis_series = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y_series));
x_series.padding(0.2);

svg
.append('g')
.call(xAxis_series)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
svg.append('g').call(yAxis_series);

svg
.selectAll('.bar')
.data(genre_movie)
.join(enter => enter.append("rect"))
.attr('x', d => x_series(d.genre))
.attr('y', d => y_series(d.rating))
.attr('width', x_series.bandwidth())
.attr('height', d => y_series(0) - y_series(d.rating))
.attr('fill', "red")
.attr('opacity', 0.7);
}

svg
.append("text")
.attr("class", "text")
.attr("transform", `translate(500, ${height - margin.bottom + 45})`)
.style("text-anchor", "middle")
.style("font-size", "14px")
.text("Genre");
svg
.append("text")
.attr("class", "text")
.attr("transform", `translate(10, ${height / 2})rotate(-90)`)
.style("text-anchor", "middle")
.style("font-size", "14px")
.text("Count");

return svg.node();
}
Insert cell
md`## Visualization 2: Encode (by color)`
Insert cell
x_encode = d3
.scaleLinear()
.domain(d3.extent(disney, d => +d.imdb_rating))
.range([margin.left, width - margin.right])
Insert cell
y_encode = d3
.scaleLinear()
.domain(d3.extent(disney, d => +d.imdb_votes))
.range([height - margin.bottom, margin.top])
Insert cell
viewof color = select(["Purple", "Pink", "Blue"])
Insert cell
chart_encode = {
const svg = d3.create('svg').attr('viewBox', [0, 0, width, height]);
const margin = { top: 50, right: 50, bottom: 50, left: 60 };

const xAxis = g =>
g
.call(d3.axisBottom(x_encode).ticks(50))
.attr('transform', `translate(0, ${height - margin.bottom - 7})`);

svg.append('g').call(xAxis);

const yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y_encode));
svg.append('g').call(yAxis);

svg
.append('g')
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(xAxis);
svg
.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(yAxis);

if (color == "Purple") {
svg
.append("g")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("circle")
.data(disney)
.enter()
.append("circle")
.attr("cx", d => x_encode(d.imdb_rating))
.attr("cy", d => y_encode(d.imdb_votes))
.attr('fill', "purple")
.attr("r", 1.5);

svg
.append("circle")
.attr("transform", "translate(600,-30)")
.attr("cx", 200)
.attr("cy", 160)
.attr("r", 6)
.style("fill", "purple");
} else if (color == "Pink") {
svg
.append("g")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("circle")
.data(disney)
.enter()
.append("circle")
.attr("cx", d => x_encode(d.imdb_rating))
.attr("cy", d => y_encode(d.imdb_votes))
.attr('fill', "pink")
.attr("r", 1.5);

svg
.append("circle")
.attr("transform", "translate(600,-30)")
.attr("cx", 200)
.attr("cy", 160)
.attr("r", 6)
.style("fill", "pink");
} else {
svg
.append("g")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("circle")
.data(disney)
.enter()
.append("circle")
.attr("cx", d => x_encode(d.imdb_rating))
.attr("cy", d => y_encode(d.imdb_votes))
.attr('fill', "blue")
.attr("r", 1.5);

svg
.append("circle")
.attr("transform", "translate(600,-30)")
.attr("cx", 200)
.attr("cy", 160)
.attr("r", 6)
.style("fill", "blue");
}

svg
.append("text")
.attr("class", "text")
.attr("transform", `translate(500, ${height - margin.bottom + 45})`)
.style("text-anchor", "middle")
.style("font-size", "14px")
.text("Rating");
svg
.append("text")
.attr("class", "text")
.attr("transform", `translate(10, ${height / 2})rotate(-90)`)
.style("text-anchor", "middle")
.style("font-size", "14px")
.text("Vote");

svg
.append("text")
.attr("class", "text")
.attr("transform", "translate(500,50)")
.style("text-anchor", "middle")
.style("font-size", "25px")
.style("font-weight", "bold")
.text("Rating VS Vote: Disney+ ");

svg
.append("text")
.attr("transform", "translate(600,-30)")
.attr("x", 220)
.attr("y", 160)
.text("Disney")
.style("font-size", "15px")
.attr("alignment-baseline", "middle");

return svg.node();
}
Insert cell
md`## Visualization 3: Hover`
Insert cell
x_hover = d3
.scaleLinear()
.domain(d3.extent(disney, d => +d.imdb_rating))
.range([margin.left, width - margin.right])
Insert cell
y_hover = d3
.scaleLinear()
.domain(d3.extent(disney, d => +d.imdb_votes))
.range([height - margin.bottom, margin.top])
Insert cell
tooltip = {
chart_hover;

const tooltip = d3
.select("body")
.append("div")
.attr("class", "svg-tooltip")
.style("position", "absolute")
.style("visibility", "hidden");

d3.selectAll("#select")
.on("mouseover", function(d) {
d3.select(this)
.attr('stroke-width', '1')
.style('fill', 'steelblue')
.attr("stroke", "black");
tooltip
.style("visibility", "visible")
.style("font", "9px")
.text(`Total Votes: ${d.imdb_votes}`);
})
.on("mousemove", function() {
tooltip
.style("top", d3.event.pageY - 10 + "px")
.style("left", d3.event.pageX + 10 + "px");
})
.on("mouseout", function() {
d3.select(this)
.attr('stroke-width', '1')
.style('fill', '#f28e2c');

tooltip.style("visibility", "hidden");
});
}
Insert cell
chart_hover = {
const svg = d3.create('svg').attr('viewBox', [0, 0, width, height]);
const margin = { top: 50, right: 50, bottom: 50, left: 60 };

const xAxis = g =>
g
.call(d3.axisBottom(x_hover).ticks(50))
.attr('transform', `translate(0, ${height - margin.bottom - 7})`);

svg.append('g').call(xAxis);

const yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y_hover));
svg.append('g').call(yAxis);

svg
.append('g')
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(xAxis);
svg
.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(yAxis);

svg
.append("g")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("circle")
.data(disney)
.enter()
.append("circle")
.attr("cx", d => x_hover(d.imdb_rating))
.attr("cy", d => y_hover(d.imdb_votes))
.attr('fill', "purple")
.attr("r", 1.5)
.attr("id", "select");

svg
.append("text")
.attr("class", "text")
.attr("transform", `translate(500, ${height - margin.bottom + 45})`)
.style("text-anchor", "middle")
.style("font-size", "14px")
.text("Rating");
svg
.append("text")
.attr("class", "text")
.attr("transform", `translate(10, ${height / 2})rotate(-90)`)
.style("text-anchor", "middle")
.style("font-size", "14px")
.text("Vote");

svg
.append("text")
.attr("class", "text")
.attr("transform", "translate(500,50)")
.style("text-anchor", "middle")
.style("font-size", "25px")
.style("font-weight", "bold")
.text("Rating VS Vote: Disney+");

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
import { select } from "@jashkenas/inputs"
Insert cell
d3 = require('d3')
Insert cell
disney = d3.csvParse(
await FileAttachment("disney_plus_shows.csv").text(),
({ imdb_id, title, type, genre, imdb_rating, imdb_votes, year }) => ({
imdb_id: imdb_id,
type: type,
title: title,
genre: genre,
imdb_rating: imdb_rating,
imdb_votes: imdb_votes.split(",").join(""),
year: year
})
)
Insert cell
disney_movie = disney.filter(d => d.type == 'movie')
Insert cell
disney_series = disney.filter(d => d.type == 'series')
Insert cell
genre_movie = {
let genre_count = [];
disney_movie.forEach(row => {
let genres = row.genre.split(",");
let rating = row.imdb_rating;

for (let i = 0; i < genres.length; i++) {
let this_genre = genres[i].trim();
if (this_genre != "N/A") {
let find_genre = genre_count.find(d => d.genre === this_genre);

// if not in
if (find_genre === undefined) {
let rating_arr = [];
rating_arr.push(rating);
genre_count.push({
genre: this_genre,
count: 1,
rating: rating,
rating_all: rating_arr
});
} else {
find_genre.rating_all.push(rating);
find_genre.count++;
if (rating != "N/A" && rating != "" && rating != undefined) {
find_genre.rating += +rating;
}
}
}
}
});
genre_count.forEach(row => {
row.rating = d3.sum(row.rating_all) / row.count;
});
return genre_count;
}
Insert cell
genre_series = {
let genre_count = [];
disney_series.forEach(row => {
let genres = row.genre.split(",");
let rating = row.imdb_rating;

for (let i = 0; i < genres.length; i++) {
let this_genre = genres[i].trim();
if (this_genre != "N/A") {
let find_genre = genre_count.find(d => d.genre === this_genre);

// if not in
if (find_genre === undefined) {
let rating_arr = [];
rating_arr.push(rating);
genre_count.push({
genre: this_genre,
count: 1,
rating: rating,
rating_all: rating_arr
});
} else {
find_genre.rating_all.push(rating);
find_genre.count++;
if (rating != "N/A" && rating != "" && rating != undefined) {
find_genre.rating += +rating;
}
}
}
}
});
genre_count.forEach(row => {
row.rating = d3.sum(row.rating_all) / row.count;
});
return genre_count;
}
Insert cell
disney_rating = disney
.filter(d => d.imdb_rating != "N/A")
.map(d => d.imdb_rating)
Insert cell
height = 500
Insert cell
margin = ({ top: 50, right: 10, bottom: 60, left: 60 })
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