Public
Edited
Dec 12, 2023
2 forks
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
shootings.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
import {Legend,legend, swatches} from "@d3/color-legend"
Insert cell
data = FileAttachment("shootings.csv").csv()
Insert cell
data_days = d3.rollups(
data,
group => group.length,
d => new Date(d.date)
)
Insert cell
daily_changes = {let result = [];

for (let i = 0; i < data_days.length; i++) {
if (i === 0) {
// For the first day, the change is 0
result.push([data_days[i][0], 0]);
} else {
// For subsequent days, calculate the change from the previous day
let change = data_days[i][1] - data_days[i - 1][1];
result.push([data_days[i][0], change]);
}
}

return result
}
Insert cell
data_month = d3.rollups(
data,
group => group.length,
d => d.date.substring(0,7)
)
Insert cell
data_year = d3.rollups(
data,
group => group.length,
d => d.date.substring(0,4)
)
Insert cell
years = d3.map(data_year, d=>d[0])
Insert cell
year_gender = {
data.forEach(d => d.year = d.date.split('-')[0]);

const countByYearRace = {};
data.forEach(d => {
const key = d.year + "_" + d.gender;
if (!countByYearRace[key]) {
countByYearRace[key] = { year: d.year, gender: d.gender, count: 0 };
}
countByYearRace[key].count += 1;
});


let result = Object.values(countByYearRace);

result = result.sort((a, b) => {
if (a.year === b.year) {
return a.gender.localeCompare(b.gender);
}
return a.year.localeCompare(b.year);
});

return result
}
Insert cell
year_race = {
data.forEach(d => d.year = d.date.split('-')[0]);

const countByYearRace = {};
data.forEach(d => {
const key = d.year + "_" + d.race;
if (!countByYearRace[key]) {
countByYearRace[key] = { year: d.year, race: d.race, count: 0 };
}
countByYearRace[key].count += 1;
});


let result = Object.values(countByYearRace);

result = result.sort((a, b) => {
if (a.year === b.year) {
return a.race.localeCompare(b.race);
}
return a.year.localeCompare(b.year);
});

return result
}
Insert cell
year_state = d3.rollup(data, v => v.length, d => d.state);
Insert cell
year_age = {
// Define age groups order
const ageGroupOrder = ['Children', 'Teenagers', 'Young Adults', 'Adults', 'Middle-aged Adults', 'Seniors', 'Elderly'];

// Helper function to categorize age
function categorizeAge(age) {
if (age <= 12) return 'Children';
if (age <= 19) return 'Teenagers';
if (age <= 29) return 'Young Adults';
if (age <= 39) return 'Adults';
if (age <= 54) return 'Middle-aged Adults';
if (age <= 64) return 'Seniors';
return 'Elderly';
}

// Extract year and categorize age
data.forEach(d => {
d.year = d.date.split('-')[0];
d.ageGroup = categorizeAge(d.age);
});

// Group and count
const countByYearAgeGroup = {};
data.forEach(d => {
const key = d.year + "_" + d.ageGroup;
if (!countByYearAgeGroup[key]) {
countByYearAgeGroup[key] = { year: d.year, ageGroup: d.ageGroup, count: 0 };
}
countByYearAgeGroup[key].count += 1;
});

// Convert to array and sort
let result = Object.values(countByYearAgeGroup);
result = result.sort((a, b) => {
if (a.year === b.year) {
return ageGroupOrder.indexOf(a.ageGroup) - ageGroupOrder.indexOf(b.ageGroup);
}
return a.year.localeCompare(b.year);
});

return result;
}
Insert cell
series_age ={//
const ageGroups = Array.from(new Set(year_age.map(d => d.ageGroup)));


const dataByYear = Array.from(d3.group(year_age, d => d.year), ([year, values]) => {
const yearData = { year };
ageGroups.forEach(ageGroup => {
yearData[ageGroup] = values.filter(v => v.ageGroup === ageGroup).reduce((sum, v) => sum + v.count, 0);
});
return yearData;
});

const series_age = d3.stack()
.keys(ageGroups)
.value((d, key) => d[key])
(dataByYear);

series_age.forEach(layer => {
layer.forEach(segment => {
const year = segment.data.year;
const ageData = year_age.find(d => d.year === year && d.ageGroup === layer.key);
segment.data = [year, new Map([[layer.key, ageData]])];
});
});


return series_age
}
Insert cell
series_race = d3.stack()
.keys(d3.union(year_race.map(d => d.race))) // distinct series keys, in input order
.value(([, D], key) => D.get(key).count) // get value for each series key and stack
(d3.index(year_race, d => d.year, d => d.race)); // group by stack then series key
Insert cell
series_gender = d3.stack()
.keys(d3.union(year_gender.map(d => d.gender))) // distinct series keys, in input order
.value(([, D], key) => D.get(key).count) // get value for each series key and stack
(d3.index(year_gender, d => d.year, d => d.gender)); // group by stack then series key
Insert cell
Insert cell
Insert cell
Insert cell
typeof(years[0])
Insert cell
months =
{let months = [];
for (let i = 0; i < 12; i++) {
let date = new Date(2020, i, 1);
let month = date.toLocaleString('en-US', { month: 'long' });
months.push(month);
}
return months}
Insert cell
monthData = {let aggregatedData = [];


data.forEach(item => {
let date = new Date(item.date);
let year = date.getFullYear().toString();
let month = date.toLocaleString('en-US', { month: 'long' });

let yearData = aggregatedData.find(y => y.index === year);
if (!yearData) {
yearData = { index: year };
months.forEach(m => yearData[m] = 0);
aggregatedData.push(yearData);
}

yearData[month]++;
});


return aggregatedData
}
Insert cell
month_Changes = {let monthDataChanges = [];

for (let i = 0; i < monthData.length; i++) {
let yearData = { index: monthData[i].index };
for (let j = 0; j < months.length; j++) {
const month = months[j];
const previousMonth = j === 0 ? "December" : months[j - 1];
const previousYear = j === 0 ? i - 1 : i;

if (i === 0 && j === 0) {
yearData[month] = 0;
} else {
const previousValue = previousYear >= 0 ? monthData[previousYear][previousMonth] : 0;
const change = monthData[i][month] - previousValue;
yearData[month] = change;
}
}
monthDataChanges.push(yearData);
}

return monthDataChanges;
}
Insert cell
monthData[5].June = 38
Insert cell
max_value = 106
Insert cell
fun1 = ()=> {
const width = 700;
const height = width+130;
const margin = ({top: width/10, right: width/10, bottom: width/5, left: width/5});

const squareSize = 50;

const xRange = [margin.left, margin.left + squareSize * years.length+16];
const yRange = [height - margin.bottom+20, height - margin.bottom - squareSize * months.length];


const x = d3.scaleBand()
.domain(years)
.range(xRange)
.padding(0.03);

const y = d3.scaleBand()
.domain(months)
.range(yRange)
.padding(0.03);




const color = d3.scaleSequential()
.domain([0,max_value])
.interpolator(d3.interpolateYlOrBr)

const xAxis = g => g
.attr("transform", `translate(0, ${height - margin.bottom+25})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call(g => g.selectAll(".domain").remove())
.selectAll("text")
.style("text-anchor", "start")
.style("font-size", "16px")
.attr("dx", "-.8em")
.attr("dy", ".8em")
const yAxis = g => g
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y).ticks(null, "s"))
.call(g => g.selectAll(".domain").remove())
.selectAll("text")
.style("font-size", "14px")
.text(d => d)


const svg = d3.select(DOM.svg(width, height))
.style("width", "100%")
.style("height", "auto")
.style("font", "1rem verdana");
const make_class = (item) => item.toLowerCase().split(' ').join('_').split('-').join('')
const make_id = d => `coords_${Math.floor(x(d.xval))}_${Math.floor(y(d.yval))}`
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("text-align", "center")
.style("padding", "8px")
.style("font", "12px sans-serif")
.style("background", "lightsteelblue")
.style("border", "0px")
.style("border-radius", "8px")
.style("pointer-events", "none");


const rects = svg.append("g")
.selectAll("g")
.data(monthData)
.enter().append("g")
.attr("class", (d, i) => `${i} bar`)
.selectAll("g")
.data(d => {
const innerData = [];
for (const month in d) {
if (month !== "index") {
innerData.push({
xval: d.index,
yval: month,
count: d[month]
});
}
}
console.log(innerData)
return innerData;
})
.enter().append("g");




rects.append("rect")
.attr("x", d => x(d.xval))
.attr("y", d => y(d.yval))
.attr("width", squareSize)
.attr("height", squareSize)
.style("fill", d => d.count === 0 ? "lightgray" : color(d.count))
.on("mouseover", function(event, d) {
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html(`${d.yval}<br/>${d.xval}<br/>Shootings: ${d.count}`)
.style("left", (event.pageX) + "px")
.style("top", (event.pageY - 28) + "px");

d3.select(this)
.style("stroke", "white")
.style("stroke-width", 4);
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
d3.select(this).style("stroke", "none");
})
;



svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);

return svg.node();



}
Insert cell
max = 61
Insert cell
fun2 = ()=>{
const width = 700;
const height = width+ 130;
const margin = ({top: width/10, right: width/10, bottom: width/5, left: width/5-50});
const max_value = 61


const squareSize = 50;

const xRange = [margin.left, margin.left + squareSize * years.length+16];
const yRange = [height - margin.bottom+20, height - margin.bottom - squareSize * months.length];


const x = d3.scaleBand()
.domain(years)
.range(xRange)
.padding(0.03);

const y = d3.scaleBand()
.domain(months)
.range(yRange)
.padding(0.03);






const color = d3.scaleSequential()
.domain([max_value, -max_value])
.interpolator(d3.interpolatePiYG)


const xAxis = g => g
.attr("transform", `translate(0, ${height - margin.bottom+25})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call(g => g.selectAll(".domain").remove())
.selectAll("text")
.style("text-anchor", "start")
.style("font-size", "16px")
.attr("dx", "-.8em")
.attr("dy", ".8em")
const yAxis = g => g
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y).ticks(null, "s"))
.call(g => g.selectAll(".domain").remove())
.selectAll("text")
.style("font-size", "14px")
.text(d => d)


const svg = d3.select(DOM.svg(width, height))
.style("width", "100%")
.style("height", "auto")
.style("font", "1rem verdana");
const make_class = (item) => item.toLowerCase().split(' ').join('_').split('-').join('')
const make_id = d => `coords_${Math.floor(x(d.xval))}_${Math.floor(y(d.yval))}`
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("text-align", "center")
.style("padding", "8px")
.style("font", "12px sans-serif")
.style("background", "lightsteelblue")
.style("border", "0px")
.style("border-radius", "8px")
.style("pointer-events", "none");


const rects = svg.append("g")
.selectAll("g")
.data(month_Changes)
.enter().append("g")
.attr("class", (d, i) => `${i} bar`)
.selectAll("g")
.data(d => {
const innerData = [];
for (const month in d) {
if (month !== "index") {
innerData.push({
xval: d.index,
yval: month,
count: d[month]
});
}
}
console.log(innerData)
return innerData;
})
.enter().append("g");




rects.append("rect")
.attr("x", d => x(d.xval))
.attr("y", d => y(d.yval))
.attr("width", squareSize)
.attr("height", squareSize)
.style("fill", d => d.count === 0 ? "lightgray" : color(d.count))
.on("mouseover", function(event, d) {
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html(`${d.yval}<br/>${d.xval}<br/>Changes: ${d.count}`)
.style("left", (event.pageX) + "px")
.style("top", (event.pageY - 28) + "px");

d3.select(this)
.style("stroke", "white")
.style("stroke-width", 4);
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
d3.select(this).style("stroke", "none");
});
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);

return svg.node();



}
Insert cell
legend1 = ()=>{
return Legend(d3.scaleSequential([0, max_value], d3.interpolateYlOrBr), {
title: "Shooting number",
})
}
Insert cell
legend2 = ()=>{
return Legend(d3.scaleSequential([max,-max], d3.interpolatePiYG), {
title: "Shooting number changes",
})
}
Insert cell
{
const ages = fun1();
const barchart_gender = fun2()
const legend_2 = legend2()
const legend_1 = legend1();
return html`

<div style="display: flex; width: 120%; max-width: 1500px; margin: auto; height: 100%; ">

<div style="width: 50%; padding: 0px; margin-left:3% ">
${legend_1}
</div>
<div style="width: 50%; padding: 0px;">
${legend_2}
</div>
</div>
<div style="display: flex; width: 120%; max-width: 1500px; margin: auto; height: 100%; ">

<div style="width: 50%; padding: 0px;">
${ages}
</div>
<div style="width: 50%; padding: 0px;">
${barchart_gender}
</div>
</div>
`;



}
Insert cell
// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/calendar-view
function Calendar(data, {
x = ([x]) => x, // given d in data, returns the (temporal) x-value
y = ([, y]) => y, // given d in data, returns the (quantitative) y-value
title, // given d in data, returns the title text
width = 928, // width of the chart, in pixels
cellSize = 17, // width and height of an individual day, in pixels
weekday = "monday", // either: weekday, sunday, or monday
formatDay = i => "SMTWTFS"[i], // given a day number in [0, 6], the day-of-week label
formatMonth = "%b", // format specifier string for months (above the chart)
yFormat, // format specifier string for values (in the title)
colors = d3.interpolateYlOrBr
} = {}) {
// Compute values.
const X = d3.map(data, x);
const Y = d3.map(data, y);
const I = d3.range(X.length);

const countDay = weekday === "sunday" ? i => i : i => (i + 6) % 7;
const timeWeek = weekday === "sunday" ? d3.utcSunday : d3.utcMonday;
const weekDays = weekday === "weekday" ? 5 : 7;
const height = cellSize * (weekDays + 2);

// Compute a color scale. This assumes a diverging color scheme where the pivot
// is zero, and we want symmetric difference around zero.
const max = d3.quantile(Y, 0.9975, Math.abs);
const color = d3.scaleSequential([0,max], colors).unknown("none");

// Construct formats.
formatMonth = d3.utcFormat(formatMonth);

// Compute titles.
if (title === undefined) {
const formatDate = d3.utcFormat("%B %-d, %Y");
const formatValue = color.tickFormat(100, yFormat);
title = i => `${formatDate(X[i])}\n${formatValue(Y[i])}`;
} else if (title !== null) {
const T = d3.map(data, title);
title = i => T[i];
}

// Group the index by year, in reverse input order. (Assuming that the input is
// chronological, this will show years in reverse chronological order.)
const years = d3.groups(I, i => X[i].getUTCFullYear()).reverse();

function pathMonth(t) {
const d = Math.max(0, Math.min(weekDays, countDay(t.getUTCDay())));
const w = timeWeek.count(d3.utcYear(t), t);
return `${d === 0 ? `M${w * cellSize},0`
: d === weekDays ? `M${(w + 1) * cellSize},0`
: `M${(w + 1) * cellSize},0V${d * cellSize}H${w * cellSize}`}V${weekDays * cellSize}`;
}

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height * years.length)
.attr("viewBox", [0, 0, width, height * years.length])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "sans-serif")
.attr("font-size", 10);

const year = svg.selectAll("g")
.data(years)
.join("g")
.attr("transform", (d, i) => `translate(40.5,${height * i + cellSize * 1.5})`);

year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(([key]) => key);

year.append("g")
.attr("text-anchor", "end")
.selectAll("text")
.data(weekday === "weekday" ? d3.range(1, 6) : d3.range(7))
.join("text")
.attr("x", -5)
.attr("y", i => (countDay(i) + 0.5) * cellSize)
.attr("dy", "0.31em")
.text(formatDay);

const cell = year.append("g")
.selectAll("rect")
.data(weekday === "weekday"
? ([, I]) => I.filter(i => ![0, 6].includes(X[i].getUTCDay()))
: ([, I]) => I)
.join("rect")
.attr("width", cellSize - 1)
.attr("height", cellSize - 1)
.attr("x", i => timeWeek.count(d3.utcYear(X[i]), X[i]) * cellSize + 0.5)
.attr("y", i => countDay(X[i].getUTCDay()) * cellSize + 0.5)
.attr("fill", i => color(Y[i]));

if (title) cell.append("title")
.text(title);

const month = year.append("g")
.selectAll("g")
.data(([, I]) => d3.utcMonths(d3.utcMonth(X[I[0]]), X[I[I.length - 1]]))
.join("g");

month.filter((d, i) => i).append("path")
.attr("fill", "none")
.attr("stroke", "#fff")
.attr("stroke-width", 3)
.attr("d", pathMonth);

month.append("text")
.attr("x", d => timeWeek.count(d3.utcYear(d), timeWeek.ceil(d)) * cellSize + 2)
.attr("y", -5)
.text(formatMonth);

return Object.assign(svg.node(), {scales: {color}});
}
Insert cell
Calendar(data_days, {
x: d => d[0],
y: d => d[1],
weekday:"sunday",
width
})
Insert cell
// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/calendar-view
function Calendar1(data, {
x = ([x]) => x, // given d in data, returns the (temporal) x-value
y = ([, y]) => y, // given d in data, returns the (quantitative) y-value
title, // given d in data, returns the title text
width = 928, // width of the chart, in pixels
cellSize = 17, // width and height of an individual day, in pixels
weekday = "monday", // either: weekday, sunday, or monday
formatDay = i => "SMTWTFS"[i], // given a day number in [0, 6], the day-of-week label
formatMonth = "%b", // format specifier string for months (above the chart)
yFormat, // format specifier string for values (in the title)
colors = d3.interpolatePiYG
} = {}) {
// Compute values.
const X = d3.map(data, x);
const Y = d3.map(data, y);
const I = d3.range(X.length);

const countDay = weekday === "sunday" ? i => i : i => (i + 6) % 7;
const timeWeek = weekday === "sunday" ? d3.utcSunday : d3.utcMonday;
const weekDays = weekday === "weekday" ? 5 : 7;
const height = cellSize * (weekDays + 2);

// Compute a color scale. This assumes a diverging color scheme where the pivot
// is zero, and we want symmetric difference around zero.
const max = d3.quantile(Y, 0.9975, Math.abs);
const color = d3.scaleSequential([max,-max], colors).unknown("none");

// Construct formats.
formatMonth = d3.utcFormat(formatMonth);

// Compute titles.
if (title === undefined) {
const formatDate = d3.utcFormat("%B %-d, %Y");
const formatValue = color.tickFormat(100, yFormat);
title = i => `${formatDate(X[i])}\n${formatValue(Y[i])}`;
} else if (title !== null) {
const T = d3.map(data, title);
title = i => T[i];
}

// Group the index by year, in reverse input order. (Assuming that the input is
// chronological, this will show years in reverse chronological order.)
const years = d3.groups(I, i => X[i].getUTCFullYear()).reverse();

function pathMonth(t) {
const d = Math.max(0, Math.min(weekDays, countDay(t.getUTCDay())));
const w = timeWeek.count(d3.utcYear(t), t);
return `${d === 0 ? `M${w * cellSize},0`
: d === weekDays ? `M${(w + 1) * cellSize},0`
: `M${(w + 1) * cellSize},0V${d * cellSize}H${w * cellSize}`}V${weekDays * cellSize}`;
}

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height * years.length)
.attr("viewBox", [0, 0, width, height * years.length])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "sans-serif")
.attr("font-size", 10);

const year = svg.selectAll("g")
.data(years)
.join("g")
.attr("transform", (d, i) => `translate(40.5,${height * i + cellSize * 1.5})`);

year.append("text")
.attr("x", -5)
.attr("y", -5)
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(([key]) => key);

year.append("g")
.attr("text-anchor", "end")
.selectAll("text")
.data(weekday === "weekday" ? d3.range(1, 6) : d3.range(7))
.join("text")
.attr("x", -5)
.attr("y", i => (countDay(i) + 0.5) * cellSize)
.attr("dy", "0.31em")
.text(formatDay);

const cell = year.append("g")
.selectAll("rect")
.data(weekday === "weekday"
? ([, I]) => I.filter(i => ![0, 6].includes(X[i].getUTCDay()))
: ([, I]) => I)
.join("rect")
.attr("width", cellSize - 1)
.attr("height", cellSize - 1)
.attr("x", i => timeWeek.count(d3.utcYear(X[i]), X[i]) * cellSize + 0.5)
.attr("y", i => countDay(X[i].getUTCDay()) * cellSize + 0.5)
.attr("fill", i => color(Y[i]));

if (title) cell.append("title")
.text(title);

const month = year.append("g")
.selectAll("g")
.data(([, I]) => d3.utcMonths(d3.utcMonth(X[I[0]]), X[I[I.length - 1]]))
.join("g");

month.filter((d, i) => i).append("path")
.attr("fill", "none")
.attr("stroke", "#fff")
.attr("stroke-width", 3)
.attr("d", pathMonth);

month.append("text")
.attr("x", d => timeWeek.count(d3.utcYear(d), timeWeek.ceil(d)) * cellSize + 2)
.attr("y", -5)
.text(formatMonth);

return Object.assign(svg.node(), {scales: {color}});
}
Insert cell
Calendar1(daily_changes, {
x: d => d[0],
y: d => d[1],
weekday:"sunday",
width
})
Insert cell
// {
// const height = width;
// const svg = d3.create("svg")
// .attr("viewBox", [0, 0, width, height]);

// const margin = ({top: 20, right: 30, bottom: 80, left: 40});
// const x = d3.scaleBand()
// .domain(data_month.map(d => d[0]))
// .rangeRound([margin.left, width - margin.right])
// .padding(0.1)

// const y = d3.scaleLinear()
// .domain([20, d3.max(data_month, d => d[1])]) // 将起始值设为 20
// .rangeRound([height - margin.bottom, margin.top]);

// const xAxis = g => g
// .attr("transform", `translate(0,${height - margin.bottom})`)
// .call(d3.axisBottom(x)
// .tickValues(data_month.map(d => d[0]).filter(v => v.endsWith("-01") || v.endsWith("-07")))
// .tickSizeOuter(0))

// const yAxis = g => g
// .attr("transform", `translate(${margin.left},0)`)
// .style("color", "steelblue")
// .call(d3.axisLeft(y).ticks(null, "s"))
// .call(g => g.select(".domain").remove())
// .call(g => g.append("text")
// .attr("x", -margin.left)
// .attr("y", 10)
// .attr("fill", "currentColor")
// .attr("text-anchor", "start")
// .text(data_month.y))

// svg.append("g")
// .attr("fill", "steelblue")
// .attr("fill-opacity", 0.8)
// .selectAll("rect")
// .data(data_month)
// .join("rect")
// .attr("x", d => x(d[0]))
// .attr("width", x.bandwidth())
// .attr("y", d => y(d[1]))
// .attr("height", d =>(y(20) - y(d[1])));



// svg.append("g")
// .attr("fill", "none")
// .attr("pointer-events", "all")
// .selectAll("rect")
// .data(data_month)
// .join("rect")
// .attr("x", d => x(d.year))
// .attr("width", x.bandwidth())
// .attr("y", 0)
// .attr("height", height)
// // .append("title")
// // .text(d => `${d.year}
// // ${d[0].toLocaleString("en")} new cars sold`);

// svg.append("g")
// .call(xAxis);

// svg.append("g")
// .call(yAxis);



// return svg.node();
// }
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