Public
Edited
Sep 10, 2024
3 forks
Importers
12 stars
SQLite Geospatial - intersections!London Isodemographic CartogramGolden Tonal Palette AnalysisHexbin Transform / Observable Plot
NBA Finals, Game 4
Disk Sampling on a SphereNBA Finals, Game 1Build your first scatterplot with Observable PlotDisplaying IBM Carbon charts in ObservableModelo de Cúpula CatenáriaBivariate Bubble MapAnalyzing Star Wars movies + the first anniversary of Observable Plot!Single-family homes owned by large corporate landlords in North CarolinaThe MoMA Collection DataMapping the Vietnamese DiasporaElection map as striped circles #2They heard the callDiagrammatic EmbeddingsHegel's Complete System of PhilosophyGeneration Arc DiagramVulnerability of Mountain PeoplesVisualizing Air Raid Sirens in UkraineUkrainian refugees welcomeConcentric Circles in D3Daylight Saving Time Gripe Assistant ToolPerceptually uniform color models and their implications
Top Notebooks of February 2022
Zillow Home Prices
Black History Month ActivityExamples from: Three Simple Flexible tools for Empowered Data VisualizationFull Covid Vaccination Rate in CA by County Over TimePlot CheatsheetsHunga Tonga–Hunga Ha'apai Global Pressure WaveVisualizing The New York Times’s Mini Crossword100 Beautiful and Informative Notebooks of 2021When Presidents Fade AwayThe Most Frequently Used Emoji of 2021Upset Plots with Observable PlotLucy mission animationSpreadsheet-based data collaboration gets supercharged with Observable30 Day Map Challenge: Day 4 - HexagonsDisputed territoriesMapping the Cities of US Highway SignsThirty day Map Challenge: Day 1 - PointsCube Pushing Loop 💪 🧊 🔄Guided Tour of an Infinite Sphere GridCanvas, P5.js and circle packing (collision and cluster force) on a mapWomen's History Month DataViz ContestWomen in DataViz EventSVG flowersMultiple Set Venn with Color BlendingA Stupid Emoji Utility FunctionQuilting with d3-curveCurvilinear RosettesStrange AttractorsSolo board gameDouble pendilumVertical SlidersDrawing and animating blobs with svg and javascriptOrbiting Particles III#genuary 2021 ~ Do some golf!Imitation Game 🖐Happy New Year (2019) (And other celebrations)Earth Temperature SpiralStar MapCorrelation FilteringBattle of ColorsPapercraft Christmas Ornaments GeneratorChristmas Tree Perspective 🎄bouncing circles2D (Non-physical) N-body Gravity with Poisson's EquationWaterfall Fill2020 TimelineTime Spiral with a COVID DemoTransition between Three.js camerasJulio Le Parc Replications and VariationsFlexible HeatmapSquare PackingSierpiński curve animationTrainsWhy use a radial data visualization?Earthquakes from the past 30 daysThe US COVID SyringeHello OGL - Minimal WebGL libraryVersor MultiplicationLabyrinthTruncated-octahedron-based 3D-space-filling curveNUTS regions (The Nomenclature of territorial units for statistics): PerspectiveStretchy FishTP3: Power Diagram and Semi-Discrete Optimal TransportEmoji ParticlesFirma de color de BogotáCovid19 WorldwideWeb 3.0 Explorable # 3: NFTs -- Game Items With Real World ValueUnemployment ExplorationSierpinski curveEight QueensVoronoi StipplingSpinning out of controlFragment shader raytracerSunny day, rainy day in SeattleAs votes are countedOrbit of the dayVoronoi ClothTry to impeach this? Challenge accepted!Dispersion in Water Surface WavesSelf-Organizing Maps meet DelaunayGenerative ArtReturn to a squareCreating a Canvas Noise Map for Generative ArtMARTINI: Real-Time RTIN Terrain MeshMunsell spinGenerative artAs votes come in, what would it take for the trailing candidate to win?3a. Historical participation in early voting vs. Election Day votingWaffle ChartSun settingRaymarch HelperF1 Constructor Standings 2010-2019Electoral College Decision TreeThe Woman Citizen's Wheel of Progress, 1917Equisurface bull's eye
Also listed in…
Economics
Maps
Insert cell
Insert cell
map = {
let div = d3
.create("div")
.style("width", `${w}px`)
.style("height", `${h}px`)
.style("overflow", "hidden");

let svg = div.append("svg").attr("width", w).attr("height", h);
let path = d3.geoPath().projection(proj);

let map = svg.append("g");
map
.append("g")
.selectAll("path")
.data(stategeo.features)
.join("path")
.attr("fill", "#eaeaea")
.attr("d", path);
let MSAs = map
.append("g")
.selectAll("path")
.data(msageo.features)
.join("path")
.attr("data-id", (d) => d.properties.ZillowRegionID)
.attr("fill", function (d) {
let result;
try {
result = get_current_price(
zillow_data.filter(
(o) => o.RegionID == d.properties.ZillowRegionID
)[0]
);
d.properties.got_it = true;
return d3.interpolateReds(result / max_current_house_price);
} catch {
d.properties.got_it = false;
console.log([d, "is bad"]);
}
})
.attr("stroke", "black")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 0.2)
.attr("d", (d) => (d.properties.got_it ? path(d) : null));

map
.append("path")
.datum(stategeo)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-linejoin", "round")
.attr("pointer-events", "none")
.attr("stroke-width", 1)
.attr("d", path);

let tippy_instances = new Map();
MSAs.nodes().forEach(function (city) {
let city_id = city.getAttribute("data-id");
let tippy_instance = tippy(city, {
maxWidth: 500,
theme: "light",
content: city.getAttribute("data-id")
});
tippy_instances.set(city_id, tippy_instance);
});

MSAs.on("pointerenter", function (a, d) {
tippy_instances.get(d.properties.ZillowRegionID.toString()).setContent(
make_time_series_graphs({
highlight: d.properties.ZillowRegionID,
max_size: 0.8 * width < 500 ? 0.8 * width : 500,
opacity: 0.02
})
);
d3.select(this).attr("stroke-width", 2).raise();
}).on("pointerleave mouseleave", function (a, d) {
d3.select(this).attr("stroke-width", 0.2);
// MSAs.attr("stroke-width", 0.2);
});

svg.call(
d3
.zoom()
.extent([
[0, 0],
[w, h]
])
.translateExtent([
[0, 0],
[w, h]
])
.scaleExtent([1, 8])
.duration(500)
.on("zoom", function (evt, a, b) {
map.attr("transform", evt.transform);
})
);

return div.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Object.keys(zillow_data[0]).slice(-8)
Insert cell
zillow_data[0].time_series
Insert cell
// https://files.zillowstatic.com/research/public_csvs/mlp/Metro_mlp_uc_sfrcondo_sm_month.csv
// https://files.zillowstatic.com/research/public_csvs/zhvi/Metro_zhvi_uc_sfrcondo_tier_0.33_0.67_sm_sa_month.csv

zillow_data = {
let zillow_data = d3.csvParse(
await (
await fetch(
"https://files.zillowstatic.com/research/public_csvs/mlp/Metro_mlp_uc_sfrcondo_sm_month.csv"
)
).text()
);

// return zillow_data;

// // Temporarily doing a static version
// let zillow_data = d3.csvParse(
// await FileAttachment("static_zillow_data.csv").text()
// );

let us_data = zillow_data[0];
us_data = Object.keys(us_data);
let start_date = parseDate(us_data[5]);
// start_date = parseDate("2012-01-01");
let end_date = parseDate(us_data.slice(-1)[0]);

zillow_data = zillow_data.map(function (d) {
let keys = Object.keys(d).slice(5);
let time_series = keys
.map((key) => [parseDate(key), parseInt(d[key])])
.filter((a) => !isNaN(a[1]) && a[0] > start_date);
d.time_series = time_series;
return d;
}); //.slice(0, 200);

zillow_data = d3.sort(zillow_data, get_current_price);
zillow_data.reverse();

zillow_data = zillow_data; //.slice(0, 400);

zillow_data.start_date = start_date; // d3.sort(zillow_data, get_current_price).slice(0, 101);
zillow_data.end_date = end_date;

return zillow_data;
}

// zillow_data = FileAttachment("zillow_data.csv").csv({ typed: true })
Insert cell
Insert cell
max_current_house_price = d3.max(
zillow_data.map((d) => d.time_series.slice(-1)[0][1])
)
Insert cell
get_current_price = function (v) {
return v.time_series.slice(-1)[0][1];
}
Insert cell
parseDate = d3.timeParse("%Y-%m-%d")
Insert cell
function make_time_series_graphs(opts = {}) {
let {
max_size = 1200,
margin = { left: 70, right: 30, bottom: 25, top: 20 },
opacity = 0.2,
highlight = "102001",
interactive = true
} = opts;
let graph_width = width < max_size ? width : max_size;
let graph_height = 0.625 * graph_width;

let svg = d3
.create("svg")
.attr("width", graph_width)
.attr("height", graph_height)
.attr("viewBox", [0, 0, graph_width, graph_height]);

let x_scale = d3
.scaleTime()
.domain([zillow_data.start_date, zillow_data.end_date])
.range([margin.left, graph_width - margin.right]);
let y_scale = d3
.scaleLinear()
.domain([0, max_current_house_price])
.range([graph_height - margin.bottom, margin.top]);
let pts_to_path = d3
.line()
.x((d) => x_scale(d[0]))
.y((d) => y_scale(d[1]));

let axes = svg.append("g");
let x_axis = d3.axisBottom(x_scale);
axes
.append("g")
.attr("transform", `translate(0,${graph_height - margin.bottom})`)
.call(x_axis);
let y_axis = d3.axisLeft(y_scale);
axes.append("g").attr("transform", `translate(${margin.left})`).call(y_axis);

let time_series_plots = svg.append("g");
let text_group = svg
.append("text")
.attr("x", 1.3 * margin.left)
.attr("y", 1.5 * margin.top)
.attr("font-size", 16)
.attr("text-anchor", "start")
.attr("fill", "steelblue");
let text_group2 = svg
.append("text")
.attr("x", 1.3 * margin.left)
.attr("y", 1.5 * margin.top + 20)
.attr("font-size", 16)
.attr("text-anchor", "start")
.attr("fill", "steelblue");

zillow_data.forEach(function (d) {
time_series_plots
.append("path")
.attr("d", pts_to_path(d.time_series))
.attr("class", "time_series")
.attr("id", `city${d.RegionID}`)
.attr("stroke", d.RegionID == highlight ? "steelblue" : "black")
.attr("stroke-opacity", d.RegionID == highlight ? 1 : opacity)
.attr("stroke-width", d.RegionID == highlight ? 4 : 0.7)
.attr("stroke-linejoin", "round")
.attr("fill", "none");
if (d.RegionID == highlight) {
text_group.text(d.RegionName);
text_group2.text(
`Current median price: $${d.time_series.slice(-1)[0][1]}`
);
}
});
if (highlight) {
time_series_plots.select(`#city${highlight}`).raise();
}

if (interactive) {
svg
.on("touchmove", (e) => e.preventDefault()) // prevent scrolling
.on("pointermove", function (evt) {
let p = d3.pointer(evt);
let x = p[0];
let y = p[1];
let day = x_scale.invert(x);
let price = y_scale.invert(y);
let closest = get_closest(day, price);
if (closest) {
time_series_plots
.selectAll("path.time_series")
.attr("stroke-opacity", opacity)
.attr("stroke-width", 0.7)
.attr("stroke", "black");
time_series_plots
.select(`#city${closest.cityID}`)
.attr("stroke-opacity", 1)
.attr("stroke-width", 4)
.attr("stroke", "steelblue")
.raise();
let zd = zillow_data.filter((o) => o.RegionID == closest.cityID)[0];
text_group.text(zd.RegionName);
text_group2.text(
`Current median price: $${zd.time_series.slice(-1)[0][1]}`
);
}
})
.on("pointerleave mouseleave", function () {
text_group.text("");
time_series_plots
.selectAll("path.time_series")
.attr("stroke-opacity", opacity)
.attr("stroke-width", 0.7)
.attr("stroke", "black");
if (highlight) {
time_series_plots
.select(`#city${highlight}`)
.attr("stroke-opacity", 1)
.attr("stroke-width", 4)
.attr("stroke", "steelblue")
.raise();
let zd = zillow_data.filter((o) => o.RegionID == highlight)[0];
text_group.text(zd.RegionName);
text_group2.text(
`Current median price: $${zd.time_series.slice(-1)[0][1]}`
);
}
});
}

return svg.node();
}
Insert cell
function get_closest(time, price) {
let results = [];

zillow_data.forEach(function (city_data) {
let these_dates = city_data.time_series.map((pt) => pt[0]);
try {
let idx = d3.bisect(these_dates, time);
let t1 = these_dates[idx - 1].getTime();
let t2 = these_dates[idx].getTime();
let p1 = city_data.time_series[idx - 1][1];
let p2 = city_data.time_series[idx][1];
let p = p1 + ((p2 - p1) * (time.getTime() - t1)) / (t2 - t1);
results.push({
time: time,
value: p,
err: Math.abs(p - price),
cityID: city_data.RegionID
});
} catch {
("pass");
}
});
return results.sort((o1, o2) => o1.err - o2.err)[0];
}
Insert cell
tippy_style = html`<link rel="stylesheet" href="${await require.resolve(
`tippy.js/themes/light.css`
)}">`
Insert cell
tippy = require("tippy.js@6")
Insert cell
proj = d3.geoIdentity().reflectY(true).fitSize([w, h], stategeo)
Insert cell
stategeo = topojson.feature(topo, topo.objects.tl_2019_us_state)
Insert cell
msageo.features
.map((o) => o.properties)
.filter(
(o) => o.NAMELSAD.slice(-10) == "Metro Area" //&& o.NAME.slice(-2) == "NC"
)
.map((o) => o.NAME)
Insert cell
smaller_zillow_data
Insert cell
// smaller_zillow_data.forEach(function (o) {
// delete o.time_series;
// })
Insert cell
smaller_zillow_data = zillow_data.filter((o) =>
region_ids.find((id) => id == o.RegionID)
)
Insert cell
region_ids = msageo.features
.map((o) => o.properties)
.filter((o) => o.LSAD == "M1")
.map((o) => o.ZillowRegionID)
Insert cell
msageo = topojson.feature(topo, topo.objects.tl_2019_us_cbsa)
Insert cell
topo = FileAttachment("AlberseStatesAndMSAsWithZillowIDs@1.json").json()
Insert cell
// w = width < 1200 ? width : 1200

w = 800
Insert cell
h = 0.625641 * w
Insert cell
d3.zip([1, 2], [3, 4])
Insert cell
m = new Map([["1", 2]])
Insert cell
d3.format(",")(3948739)
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