Public
Edited
Feb 25
Insert cell
import {vl} from "@vega/vega-lite-api"

Insert cell

diamonds = FileAttachment("diamonds.csv").csv()

Insert cell

// Visualization 1: Carat vs. Price Scatter Plot
vl.layer([
// Heatmap base layer
vl.markRect()
.data(diamonds)
.transform([
{bin: true, field: "carat", as: "carat_bin", maxbins: 40},
{bin: true, field: "price", as: "price_bin", maxbins: 40},
{aggregate: [{op: "count", as: "count"}],
groupby: ["carat_bin", "carat_bin_end", "price_bin", "price_bin_end"]}
])
.encode(
vl.x().fieldQ("carat_bin").title("Carat Weight").scale({zero: false}),
vl.y().fieldQ("price_bin").title("Price ($)").scale({zero: false}),
vl.color().fieldQ("count").scale({type: "log"}).title("Number of Diamonds")
),
// contour lines to highlight density regions
vl.markLine()
.data(diamonds)
.transform([
{density: "carat",
bandwidth: 0.1,
counts: true,
extent: [0, 5],
as: ["carat", "density"]}
])
.encode(
vl.x().fieldQ("carat"),
vl.y().fieldQ("density"),
vl.color().value("black"),
vl.opacity().value(0.5),
vl.size().value(0.5)
)
])
.width(600)
.height(400)
.title("Diamond Price vs. Carat (Density Heatmap)")
.config({view: {stroke: null}})
.render()

Insert cell

// Visualization 2
// shows the distribution of diamond proportions on price ranges
vl.markArea()
.data(diamonds)
.transform([
{calculate: "datum.x/datum.y", as: "length_width_ratio"},
{filter: "datum.length_width_ratio > 0.9 && datum.length_width_ratio < 1.1"},
{bin: {maxbins: 10, extent: [0, 20000]}, field: "price", as: "price_bin"},
{density: "length_width_ratio",
groupby: ["price_bin", "price_bin_end"],
extent: [0.9, 1.1],
as: ["length_width_ratio", "density"]}
])
.encode(
vl.x().fieldQ("length_width_ratio").title("Length/Width Ratio").scale({domain: [0.9, 1.1]}),
vl.y().fieldQ("density"),
vl.color().fieldQ("price_bin").title("Price Range ($)"),
vl.tooltip([
{field: "price_bin", title: "Min Price ($)"},
{field: "price_bin_end", title: "Max Price ($)"},
{field: "length_width_ratio", title: "Length/Width Ratio"}
])
)
.width(600)
.height(400)
.title("Distribution of Diamond Length-Width Ratios Across Price Ranges")
.render()

Insert cell

// Visualization 3: Diamond Cut Quality Analysis
diamonds.forEach(d => {
d.depth_percentage = (d.z / ((d.x + d.y) / 2)) * 100;
d.table_estimate = d.x * 0.6; // Rough estimate of table %
if (d.depth_percentage < 58) {
d.cut_quality = "Too Shallow";
} else if (d.depth_percentage > 64) {
d.cut_quality = "Too Deep";
} else if (d.depth_percentage >= 60.5 && d.depth_percentage <= 62.5) {
d.cut_quality = "Ideal";
} else {
d.cut_quality = "Good";
}
});

Insert cell

vl.layer([
// Bar chart showing count by cut quality
vl.markBar()
.data(diamonds)
.encode(
vl.x().fieldN("cut_quality").title("Cut Quality Estimate").sort(["Too Shallow", "Good", "Ideal", "Too Deep"]),
vl.y().aggregate("count").title("Number of Diamonds"),
vl.color().fieldN("cut_quality").title("Cut Quality")
.scale({
domain: ["Too Shallow", "Good", "Ideal", "Too Deep"],
range: ["#E15759", "#76B7B2", "#4E79A7", "#F28E2B"]
}),
vl.tooltip([
{aggregate: "count", title: "Count"},
{field: "cut_quality", title: "Cut Quality"}
])
),
// points to show average price by cut quality
vl.markPoint({size: 100, filled: true, color: "black"})
.data(diamonds)
.encode(
vl.x().fieldN("cut_quality").sort(["Too Shallow", "Good", "Ideal", "Too Deep"]),
vl.y().aggregate("mean", "price").title("Average Price ($)").scale({zero: false}),
vl.tooltip([
{aggregate: "mean", field: "price", title: "Avg Price ($)", format: ",.0f"},
{field: "cut_quality", title: "Cut Quality"}
])
)
])
.width(600)
.height(400)
.title("Diamond Cut Quality Distribution with Average Price")
.render()

Insert cell

// Visualization 4: shows price distribution by carat ranges
vl.markBoxplot({extent: "min-max"})
.data(diamonds)
.transform([
{bin: {step: 0.25}, field: "carat", as: "carat_bin"},
{filter: "datum.carat < 3"} // Filter out extremes
])
.encode(
vl.x().fieldN("carat_bin").title("Carat Range"),
vl.y().fieldQ("price").scale({type: "log"}).title("Price ($) - Log Scale"),
vl.color().fieldN("carat_bin").legend(null),
vl.tooltip([
{field: "carat_bin", title: "Carat Range"},
{aggregate: "min", field: "price", title: "Min Price", format: ",.0f"},
{aggregate: "q1", field: "price", title: "25th Percentile", format: ",.0f"},
{aggregate: "median", field: "price", title: "Median Price", format: ",.0f"},
{aggregate: "q3", field: "price", title: "75th Percentile", format: ",.0f"},
{aggregate: "max", field: "price", title: "Max Price", format: ",.0f"}
])
)
.width(700)
.height(400)
.title("Diamond Price Distribution by Carat Range (Log Scale)")
.render()
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