Public
Edited
Feb 28, 2024
Insert cell
Insert cell
Insert cell
dataset = d3.csvParse(await FileAttachment("data.csv").text(), rowConverter);
Insert cell
rowConverter = (row) => {
// Modify code here for row conversion
return {
date: d3.timeParse("%Y-%m-%d")(row.date),
hours_of_sleep: parseFloat(row.hours_of_sleep),
};
}
Insert cell
w = 600
Insert cell
h = 500
Insert cell
makeChart()
Insert cell
function makeChart() {
// sort by date ascending
dataset.sort((a, b) => a.date - b.date);

console.log(dataset);

const margin = { top: 20, right: 80, bottom: 20, left: 20 };

const numDaysSlider = html`
<input id="numDaysSlider" type="range" min="1" max="7" value="7"></input>
`;

const daysText = html`
<span id="daysText">7</span>
`;

const svg = d3.create('svg')
.attr('width', w)
.attr('height', h);

// Setup scales
const xScale = d3.scaleTime()
.domain(d3.extent(dataset, d => d.date))
.range([margin.left, w - margin.right]);

const yScale = d3.scaleLinear()
.domain([0, 12])
.range([h - margin.top, margin.bottom]);

const cScale = d3.scaleLinear()
.domain([0, 12])
.range(['red', 'orange']);

// Append x-axis
const xAxis = d3.axisBottom(xScale)
.ticks(d3.timeDay.every(1))
.tickFormat(d3.timeFormat('%a'));

const xAxisGroup = svg.append('g')
.attr('transform', `translate(0, ${h - 20})`)
.call(xAxis);

// Append y-axis
const yAxis = d3.axisLeft(yScale);
svg.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(yAxis);

// Create the initial bars
updateGraph();

function updateGraph() {
const numDays = +numDaysSlider.value;

// Update the displayed number of days
daysText.innerText = numDays;

// Create a subset of data based on the selected number of days
const subsetData = dataset.slice(dataset.length - numDays, dataset.length);

// Update xScale domain based on the subsetData
xScale.domain(d3.extent(subsetData, d => d.date));

// Adjust bar width based on the selected number of days
const barWidth = (w - 40) / numDays;

const bars = svg.selectAll('rect')
.data(subsetData, d => d.date);

// Exit, Enter, Update pattern
bars.exit()
.transition()
.duration(500)
.attr('x', -barWidth) // Move offscreen before removal
.remove();

bars.enter()
.append('rect')
.attr('x', -barWidth) // Move offscreen before entering
.merge(bars)
.transition()
.duration(500)
.attr('x', d => xScale(d.date))
.attr('y', d => yScale(d.hours_of_sleep))
.attr('width', barWidth)
.attr('height', d => h - margin.bottom - yScale(d.hours_of_sleep))
.attr('fill', d => cScale(d.hours_of_sleep));

// Update x-axis ticks and labels
xAxisGroup.transition().call(xAxis);

// Draw red line at 7.5 on the y-axis
svg.selectAll('.target-line').remove(); // Remove existing lines
svg.append('line')
.attr('class', 'target-line')
.attr('x1', margin.left)
.attr('x2', w - 80)
.attr('y1', yScale(7.5))
.attr('y2', yScale(7.5))
.attr('stroke', 'red')
.attr('stroke-width', 2)
.attr('stroke-dasharray', '4');
}

numDaysSlider.addEventListener('input', updateGraph);

return html`
<h1>Sleep</h1>
<label>Days to Show</label>
${numDaysSlider}
${daysText}
<div style="margin-top: 20px;">
${svg.node()}
</div>
`;
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@7")
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