Public
Edited
Feb 23, 2024
Insert cell
Insert cell
Insert cell
dataset = d3.csvParse(dataFile, (row) => ({
app_name: row.app_name,
downloads: parseInt(row.downloads),
average_rating: parseFloat(row.average_rating),
thirty_day_keep: parseFloat(row.thirty_day_keep) / 100
}))
Insert cell
makeChart(dataset)
Insert cell
makeChart = (dataset) => {
dataset = [...dataset]; // Copy dataset array so we don't modify the original
dataset.sort((a,b) => b.downloads - a.downloads);

const w = 600;
const h = dataset.length * 24;
const svg = d3.create('svg')
.attr('width', w)
.attr('height', h);
// our range is limited from 0 to width - 100,
// which is for the 80 pixels on left for axis and
// 20 pixels on right for padding
const xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, d => d.downloads)])
.rangeRound([0, w - 100]);

// using scale band to work with nominal values
// the Array.map() call allows us to get a new array
// by calling a function on each item of the source array
// here it pulls out the app_name
const yScale = d3.scaleBand()
.domain(dataset.map(d => d.app_name))
.rangeRound([20, h - 20]);

// d3 allows scaling between colors
const colorScale = d3.scaleLinear()
.domain(xScale.domain())
.range(['#88d', '#ccf']);
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
// AXES
const xAxisGroup = svg.append('g')
.attr('class', 'axis')
.attr('transform', `translate(80, ${h - 20})`)
.call(xAxis);

const yAxisGroup = svg.append('g')
.attr('class', 'axis-left1')
.attr('transform', `translate(80,0)`)
.call(yAxis);
const updateChartType = (newType) => {
console.log(newType);

// TODO
// Depending on newType
// update the chart by:
// 1. re-sorting the dataset
dataset.sort((a, b) => b[newType] - a[newType]);
// 2. update xscale domain
xScale.domain([0, d3.max(dataset, d => d[newType])]);
// 3. update yscale domain
yScale.domain(dataset.map(d => d.app_name));
// 4. Modifying the update parameter of .join, use transition to then
// update x,y, and width of bars
svg.selectAll('rect')
.data(dataset, d => d.app_name)
.join(
enter => enter.append('rect')
.attr('x', 80)
.attr('y', d => yScale(d.app_name))
.attr('width', 0) // Set initial width to 0 for the enter transition
.attr('height', 18)
.attr('fill', d => colorScale(d[newType]))
.transition()
.duration(500)
.attr('width', d => xScale(d[newType])),
update => update
.transition()
.duration(500)
.attr('x', 80)
.attr('y', d => yScale(d.app_name))
.attr('width', d => xScale(d[newType]))
.attr('height', 18)
.attr('fill', d => colorScale(d[newType]))
);
// After that, alway update xAxis scale, xAxisGroup with xAxis (call), and same for yAxis scale and yAxisGroup
xAxis.scale(xScale);
xAxisGroup.transition().call(xAxis);

yAxis.scale(yScale);
yAxisGroup.transition().call(yAxis);

svg.selectAll('rect')
.data(dataset, d => d.app_name)
.join(
enter => enter.append('rect')
.attr('x', 80)
.attr('y', d => yScale(d.app_name))
.attr('width', d => xScale(d.downloads))
.attr('height', 18)
.attr('fill', d => colorScale(d.downloads)),
// UPDATE the below
update => update
);

};
updateChartType('downloads');
const typeSelector = html`
<select id='chartType'>
<option value="downloads">Downloads</option>
<option value="average_rating">Popularity</option>
<option value="thirty_day_keep">Retention</option>
</select>
`;
d3.select(typeSelector).on('change', () => {
updateChartType(typeSelector.value);
});
return html`
<div class='chart-div'>
<label>Chart Type:</label>
${typeSelector}
${svg.node()}
</div>
`;
}
Insert cell
html`<style>


.chart-div {
text-align: center;
margin:auto;
padding:10px;
}

.chart-div svg {
display: block;
margin: auto;
padding: 20px;
}

.axis-left1 line,
.axis-left1 path {
stroke: none;
}

.axis-left1 text {
font-weight: bold;
font-size: 7pt;
text-transform: uppercase;
}

.dot-label {
font-size: 8pt;
}

</style>`
Insert cell
dataFile = await FileAttachment("fake_app_download_rating.csv").text()
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