Published
Edited
Sep 2, 2022
Importers
21 stars
Andy's Walgreens COVID-19 Tracker TrackerBelgium Vaccination Tracker - Progress of the vaccination campaignVaccination against covid-19 in France500,000 COVID-19 DeathsCovid-19 vaccinations: BubblesTime Spiral with a COVID DemoCOVID MasksGrid Cartogram component with live COVID demos (plus a MapEditor)The US COVID SyringeCOVID-19 DKCOVID-19 World Community Mobility Report by GoogleCOVID 19Chicago COVID ZIP SparklinesThe COVID Syringeభారతదేశంలో కోవిడ్-19SVG DataGrid with many features and a live COVID Dashboard demoThe spread of Covid-19 in New MexicoHeatmap of COVID-19 Confirmed Cases by Age over Time in JapanThe CoViD-19 ReportCovid19 WorldwideMassachusetts Coronavirus Cases by TownChoropleth map about Covid19 in FranceWell ordered coronavirus heatmaps for US and the WorldCOVID-19 Racial/Ethnic Mortality AnalysisProPublica's COVID Arrow Map
Clustering students to slow virus spread inside schools
COVID-19 in the USAWho Is Wearing Masks in the U.S.Covid-19 Viz RoundupCoronavirus StatsThe Covid-19 Crisis' Impact on the Number of US Flight PassengersCOVID-19 Daily New CasesCOVID-19 CasesCOVID–19 Bubble Chart with D3 RenderCoronavirus Deaths by Race / EthnicityHow many SARS-CoV-2 tests are we running in the U.S.?COVID-19 Onset vs. ConfirmationPeaks in confirmed daily deaths due to COVID-19 so farCOVID-19 in the U.S.Recreating John Burn-Murdoch’s Coronavirus trackerTracking COVID-19 Cases in VietnamCOVID-19 in NYC by Zip Code & IncomeVisualizing the Network Meta-Analysis of Covid-19 Studiesxkcd COVID-19 spread sketchCOVID-19's deaths in EuropeCovid-19 (corona virus) deaths per 1,000,000 peopleCOVID-19 Bubble map or spike map? (Twitter debate)A Timeline of Shelter-in-PlaceWhere’s that $2 trillion going?Estimating SARS-COV-2 infectionsCODAVIM - CountySARS-CoV-2 Epi CurveCOVID-19 Curves (U.S.)COVID-19 Cases by CountyCOVID-19 world growth rateA graphical experiment of exponential spreadCOVID-19 by US countyCOVID-19 Confirmed vs. New cases"Live" Logistic Coronavirus Death CounterInfografiche: COVID-19 in ItaliaCoronavirus (COVID-19) GlobeBar Chart Race, COVID-19 outbreak Worldwide to 24th March 2020US Coronavirus testing by statesUnited States Coronavirus Daily Cases Map (COVID-19)COVID-19 Numbers by State, Side by SideRecreating NYT U.S. Cases MapCOVID-19 in Washington stateCOVID-19 outbreak in maps and chartsCOVID-19 Spreading trendsRestaurants during COVID-19 social distancingCOVID-19 Countries Trajectories in 3DStates that aren't reporting aspects of their COVID-19 testing processNextstrain Prototyping - Issue 817Reviewing COVID-19 SARS-CoV-2 preprints from medRxiv and bioRxivCoronavirus worldwide evolutionCovid-19 New Cases PunchcardCovid-19 cases per district in Germany.COVID-19 Cases, Deaths, and Recoveries (Select Country)Quarantine NowEmissions in WuhanCOVID-19(nCOV-2019) Outbreak in S.KoreaMovement of population between provinces in 2019-nCoVComparing COVID-19 GrowthCovid-19 derived chartCoronavirus Trends (COVID-19)Netherlands Coronavirus Daily Cases Map (COVID-19)Map and timeline of Corona outbreakSARS-CoV-2 Phylogenetic TreeCoronavirus data (covid-19)Visualizing the Logic of Exponential Viral SpreadItaly Coronavirus Daily Cases Map (COVID-19)COVID-19 Fatality Rate
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pop = {
// Here I'm assuming that 66% of the 720 student population opts for blended learning.
const pop = new Random_School_Population(["K", "1", "2", "3", "4", "5"], 720 * .66)
return pop
}
Insert cell
Insert cell
Insert cell
Insert cell
import { vl } from "@vega/vega-lite-api"
Insert cell
Insert cell
simulations = {
// Simulate 100 outbreaks
const runs = []
const saved_state = [...pop.classrooms]
//return saved_state

for (let method of ['random', 'network']) {
pop.build_classrooms(method)
for (let i of d3.range(100)) {
if (Math.random() < .1) {
// Rebuild the classrooms occasionally
pop.build_classrooms(method)
}
let total
for (let n_infected of simulate_outbreak(infectivity, false)) {
total = n_infected
}
runs.push({
method,
infected: total
})
}
}
pop.remove_all_classrooms()
pop.set_classrooms(saved_state)
return runs
}
Insert cell
classes = pop.build_classrooms(assign_students_to_classes)
Insert cell
import { drag } from '@d3/force-directed-graph@149';
Insert cell
color = d3.scaleOrdinal(d3.schemeCategory10);
Insert cell
make_data = function(person_filter = (x) => true) {
// Create d3-suitable network data.
const links = [];
pop.make_links()
const students = pop.students.filter(person_filter)
const set_ids = new Set(students.map(student => student.id))
const nodes = students.map(student => {
student.links.forEach(linkage => {
const studentB = linkage.student
const link_type = linkage.link_type
if (studentB.id < student.id) {
if (set_ids.has(studentB.id)) {
links.push(
{"source": student.id, "target": studentB.id, link_type}
)
}
}
})
return student
})
return {nodes, links}
}
Insert cell
Insert cell
class Random_School_Population extends School_Population {
constructor(grades = ["K", "1", "2", "3", "4", "5"], students = 720) {
super()
this.grades = grades
for (let i of d3.range(students)) {
d3.shuffle(grades)
this.students.push(new Student(grades[0]))
}
this.give_students_random_links()
this.n_classrooms = {
"K": 4,
"1": 4,
"2": 3,
"3": 4,
"4": 3,
"5": 3
}
}
get classrooms() {

if (this._classrooms) {return this._classrooms}
this._classrooms = []
for (let grade of this.grades) {
for (let i of d3.range(this.n_classrooms[grade])) {
for (let day of ["W", "Th", "Fr"]) {
this._classrooms.push({
day: day,
teacher_number: i,
grade: grade,
students: [],
max_size: 9
})
}
}
}
return this._classrooms
}
assign_learning_pods(fraction = SHARE_OF_STUDENTS_IN_PODS, sizes = [2, 3, 4, 5, 6, 7]) {
for (let grade of this.grades) {
const kids = this.get_grade(grade).filter(kids => kids.format != "remote")
const pods = [[]]
d3.shuffle(kids)
d3.shuffle(sizes)
let next_pod_size = sizes[0]
for (let kid of kids) {
if (Math.random() > fraction) {
continue
}
if (pods[0].length == next_pod_size) {
pods.unshift([])
next_pod_size = d3.shuffle(sizes)[0]
}
pods[0].unshift(kid)
}
for (let pod of pods) {
this.groups.push(new Pod(pod))
}
}
}
assign_siblings(sibshare = SHARE_OF_STUDENTS_WITH_SIBLINGS) {
d3.shuffle(this.students)
const families = this.students.reduceRight((full_list, next_student) => {
// 1 in four times, a student is assumed to be related to the one
// next to them; the rest of the time, they're a new family.
if (Math.random() < sibshare) {
full_list[0].unshift(next_student)
} else {
full_list.unshift([next_student])
}
return full_list
}, [[]])
families.filter(family => family.length > 1).map(family => {
// For prettiness, give them all a last name.
family.map(student => student.last = family[0].last)
this.groups.push(new Family(family))
})
}
give_students_random_links() {
// Learning pods
this.assign_learning_pods()
this.assign_siblings()
}
}
Insert cell
import {checkbox, radio, slider} from "@jashkenas/inputs"
Insert cell
class School_Population {
constructor() {
this.students = []
this.groups = []
}
make_links() {
for (const student of this.students) {
student.links = []
}
for (const group of this.groups) {
group.make_links()
}
}
get_grade(grade) {
return this.students.filter(student => student.grade == grade)
}
remove_all_classrooms() {
this._classrooms = undefined
this.groups = this.groups.filter(d => d.link_type != "classroom")
this.students.forEach(student => {
student.links = student.links.filter(d => d.link_type != "classroom")
})

}

set_classrooms(list_of_rooms) {
this._classrooms = list_of_rooms
for (let classroom of list_of_rooms) {
this.groups.push(new Classroom(classroom.students))
}

}
build_classrooms(order = "random") {
let students
this.remove_all_classrooms()
if (order === "random") {
students = this.grades.map(grade => {
const students_in_grade = this.get_grade(grade);
d3.shuffle(students_in_grade);
return students_in_grade
})
}
if (order === "network") {
// Get the order from the current network.
students = this.networked_groups
}
// Builds empty classrooms.
const classrooms = this.classrooms;
for (let set_of_students of students) {
if (set_of_students === undefined) {continue}
// Sort the classrooms by how filled they are. This sorting will persist through the currently
// active group of students--for instance, a pod or something. Unless the classroom is filled.
classrooms.sort((a, b) => a.students.length - b.students.length)
for (let student of set_of_students) {
const grade_classes = classrooms.filter(room => room.grade === student.grade)
const unfilled_classes = grade_classes.filter(room => room.students.length < room.max_size)
let destination_class;
if (unfilled_classes.length > 0) {
destination_class = unfilled_classes[0]
} else {
grade_classes.sort((a, b) => a.length - b.length);
destination_class = grade_classes[0]
}
destination_class.students.push(student)
}
}
this.set_classrooms(classrooms)
return this._classrooms
}

get networked_groups() {
let current_group = -1
const completed = new Set()
// Approach in a random order.
d3.shuffle(this.students)
// Reset values.
this.students.forEach(student => student._assigned_group = undefined)
const queue = [...this.students]
const groups = []
while (queue.length > 0) {
// pop the first element off the queue.
const kid = queue.shift()
if (completed.has(kid.id)) {
// We've already done the kid.
continue
}
// All kids start unassigned; but students added
// b/c of links will get added earlier.
if (kid._assigned_group === undefined) {
kid._assigned_group = current_group++
groups[current_group] = []
}
for (const link of kid.links) {
const {student, link_type} = link
// Put the kid in this group;
student._assigned_group = current_group;
// move the kid to the top of the queue, so that all of
// their linkages will be followed before we move on.
queue.unshift(student)
}
completed.add(kid.id);
if (groups[current_group] === undefined) {
groups[current_group] = []
}
groups[current_group].push(kid);
}
return groups
}

}
Insert cell
class Pod extends Group {
constructor(students) {
super("pod", ...students)
}
}
Insert cell
class Family extends Group {
constructor(people) {
super("family", ...people)
}
}
Insert cell
class Classroom extends Group {
constructor(students, teacher) {
if (teacher === undefined) {
teacher = new Person()
}
super("classroom", teacher, ...students)
this.teacher = teacher;
}
}
Insert cell
d3.shuffle(pop.groups)
Insert cell
class Group {
constructor(type_of_link, ...students) {
this.students = [...students]
this.link_type = type_of_link
}
make_links() {
for (let studentA of this.students) {
for (let studentB of this.students) {
if (studentA.id == studentB.id) {
continue // Don't link students to themselves.
}
studentA.links.push({student: studentB, link_type: this.link_type})
}
}
}
}
Insert cell
new Student().name
Insert cell
random_name()
Insert cell
class Person {
constructor(name) {
this.id = uuid.v4()
this.links = []
if (name === undefined) {
let [f, l] = random_name()
this.first = f;
this.last = l;
} else {
this.first, this.last = name
}
}
get name() {
return `${this.first} ${this.last}`
}
}
Insert cell
class Teacher extends Person {
constructor(name, grade) {
}
}
Insert cell
class Student extends Person {
constructor(grade, name) {
super(name)
if (grade == undefined) {
grade = d3.shuffle(["K", "1", "2", "3", "4", "5"])[0]
}
this.grade = grade
// All students that this child is linked to.
}
}
Insert cell
G = {
return
const G = new jsnx.Graph();
const students = [];
let i = 0;
for (let student of pop.students) {
students.push([i++, student])
student._jsnxid = i
}
const edges = []
const link_set = new Set(link_restrictions)
for (let student of pop.students) {
for (let link of student.links) {
if (link_set.has(link.link_type)) {
edges.push([student._jsnxid, link.student._jsnxid])
}
}
}
G.addNodesFrom(students);
G.addEdgesFrom(edges);

jsnx.allPairsShortestPathLength(G)
return G
}
Insert cell
md`# General imports and functions`
Insert cell
function random_name() {
const i = parseInt(Math.random() * first_names.length)
const j = parseInt(Math.random() * last_names.length)
return [first_names[i], last_names[j]]
}
Insert cell
uuid = require('uuid@8.3.0/dist/umd/uuid.min.js')
Insert cell
first_names = {
const promises = []
for (let gender of ["male", "female"]) {
for (let language of ["fr", "es", "en"]) {
promises.push(d3.json(`https://raw.githubusercontent.com/AlessandroMinoccheri/human-names/master/data/${gender}-human-names-${language}.json`))
}
}
let first_names = Promise.all(promises).then(
l => l.flat()
)
return first_names
}
Insert cell
d3 = require("d3@5")
Insert cell
last_names = d3.json("https://raw.githubusercontent.com/dominictarr/random-name/master/names.json")
Insert cell
jsnx = require('https://bundle.run/jsnetworkx@0.3.4')
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