Public
Edited
Feb 15, 2024
Fork of Homework 1
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
img_00201 = FileAttachment("IMG_0020@1.jpg").image()
Insert cell
Insert cell
Insert cell
Insert cell
FHV_data = {
const scsv = d3.dsvFormat(",") // Important to define the separator of your CSV file
return scsv.parse(await FileAttachment("FHV_Base_Aggregate_Report_20240205.csv").text())
}
Insert cell
Insert cell
dispatchesGroupedByCompany = d3.rollup(FHV_data,
v => d3.sum(v, d => +d["Total Dispatched Trips"]),
d => d["Base Name"])


Insert cell
top5_companies = Array.from(dispatchesGroupedByCompany.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 5);

Insert cell
Insert cell
Insert cell
top5_company_dispatches = FHV_data.filter(d => top5_companies.map(c => c[0]).includes(d["Base Name"]))
.map(d => ({
...d,
timestamp: new Date(`${d.Year}-${d.Month.padStart(2, '0')}`).toISOString()
}));



Insert cell
// Sort the top5_company_dispatches array based on the timestamp field
top5_company_dispatches.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));

Insert cell
Insert cell
margin = ({top: 20 , right:20 , bottom: 30, left: 40})
Insert cell
visWidth = width
Insert cell
visHeight = 800
Insert cell
Insert cell
dateExtent = d3.extent(top5_company_dispatches, d => d.timestamp)
Insert cell

startDate = new Date(dateExtent[0]);

Insert cell
endDate = new Date(dateExtent[1]);
Insert cell
//TO DO Scale 1
x=d3.scaleTime()
.domain([startDate,endDate])
.range([margin.left, width - margin.right])

Insert cell
// Get the maximum value of 'Total Dispatched Trips'
maxdispatchedtrips = top5_company_dispatches.reduce((max, current) => {
const currentTrips = parseInt(current['Total Dispatched Trips']);
return Math.max(max, currentTrips);
}, 0);



Insert cell
//TO DO Scale 2
y = d3.scaleLinear()
.domain([0, maxdispatchedtrips])
.range([visHeight - margin.bottom, margin.top]);
Insert cell
//TO DO Scale 3
color = d3.scaleOrdinal()
.domain(top5_companies.map(c => c[0]))
.range(d3.schemeCategory10);
Insert cell
Insert cell
line = d3.line()
.x(d => x(new Date(d.timestamp)))
.y(d => y(+d['Total Dispatched Trips']))
Insert cell
line(top5_company_dispatches)
Insert cell
top5_company_dispatches
Insert cell

//used ChatGPT for help with generating code for legend and titles
//create SVG, add any axes, draw any marks, etc
{
// Setup
const svg = d3.create('svg')
.attr('width', width)
.attr('height', visHeight);

svg.append("text")
.attr("x", (visWidth + margin.left + margin.right) / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-weight", "bold")
.text("Total Dispatched Vehicles Over Time");
// Create the axes
const xAxis = d3.axisBottom(x);

svg.append("text")
.attr("x", visWidth / 2) // Position text in the middle of the visualization
.attr("y", visHeight - 20) // Adjust vertical position as needed
.attr("dy", "0.71em") // Adjust vertical alignment as needed
.attr("text-anchor", "middle") // Align text to the middle
.text("Timestamp");

svg.append('g')
// Move x-axis down to the bottom
.attr('transform', `translate(0,${visHeight - margin.bottom})`)
// Append x-axis
.call(xAxis);


const yAxis = d3.axisLeft(y).tickValues(d3.range(0, maxdispatchedtrips, 1000000)) // Set tick values at intervals of 100,000
.tickFormat(d3.format(".0s"));


svg.append('g')
// move y-axis to the left to account for left margin
.attr('transform', `translate(${margin.left})`)
.call(yAxis)
// add axis label
.append('text')
.attr('fill', 'black')
.attr('text-anchor', 'start')
.attr('dominant-baseline', 'hanging')
.attr('font-weight', 'bold')
.text('Total Dispatched Vehicles');

// Define legend dimensions and positioning
var legendWidth = 120;
var legendHeight = 20 * top5_companies.length; // Adjust height based on the number of companies
var legendX = width - legendWidth - 100; // Adjust position as needed
var legendY = 20; // Adjust position as needed

// Append legend group
var legend = svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(" + legendX + "," + legendY + ")");

// Add colored rectangles and company names to the legend
var legendItems = legend.selectAll(".legend-item")
.data(top5_companies)
.enter().append("g")
.attr("class", "legend-item")
.attr("transform", (d, i) => "translate(0," + i * 20 + ")");

legendItems.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 10)
.attr("height", 10)
.attr("fill", d => color(d[0]));

legendItems.append("text")
.attr("x", 20)
.attr("y", 9)
.attr("dy", "0.35em")
.text(d => d[0]);



// // Draw the line
// // We are only drawing one line, so we can append one path

svg.selectAll(".line")
.data(top5_companies.map(c => c[0])) // Use company names from top5_companies
.join("path")
.attr("class", "line")
.attr("d", function(companyName) {
return line(top5_company_dispatches.filter(d => d["Base Name"] === companyName));
})
.attr("fill", "none")
.attr("stroke", function(companyName) {
return color(companyName);
})
.attr("stroke-width", 2);


return svg.node();

}
Insert cell
Insert cell
Insert cell
Insert cell
img_00212 = FileAttachment("IMG_0021@2.jpg").image()
Insert cell
Insert cell
Insert cell
margin2 = ({top: 20 , right:20 , bottom: 30, left: 10})
Insert cell
width2 = width/2
Insert cell
height2 = 500
Insert cell
marginBar3 = ({top: 20 , right:20 , bottom: 30, left: 40})
Insert cell
width3 = width2
Insert cell
height3 = 500
Insert cell
Insert cell
uniquedispatchExtent = top5_company_dispatches.reduce((maxValue, currentObj) => {
const currentValue = parseInt(currentObj["Unique Dispatched Vehicles"]);
return Math.max(maxValue, currentValue);
}, 0);
Insert cell
//TO DO Scale
x2 = d3.scaleLinear()
.domain([0, d3.max(top5_company_dispatches, d => +d['Unique Dispatched Vehicles'])])
.range([margin2.left, width - margin2.right]);







Insert cell
y2 = d3.scaleLinear()
.domain([0, maxdispatchedtrips])
.range([height2 - margin2.bottom, margin2.top]);

// Define a color scale based on the 'Month' attribute using the custom color scheme array

Insert cell
customColorScheme = [
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
"#bcbd22", "#17becf", "#aec7e8", "#ffbb78"
];


Insert cell
colorScale = d3.scaleOrdinal()
.domain(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'])
.range(customColorScheme);
Insert cell
Insert cell
function leftVisualization() {
const initialValue=top5_company_dispatches;
const svg2 = d3.create('svg')
.attr('width', width2)
.attr('height', height2)
.property('value', initialValue);

// Define the scales
const x2 = d3.scaleLinear()
.domain([0, d3.max(top5_company_dispatches, d => +d['Unique Dispatched Vehicles'])])
.range([margin2.left, width2 - margin2.right]);

const y2 = d3.scaleLinear()
.domain([0, d3.max(top5_company_dispatches, d => +d['Total Dispatched Trips'])])
.range([height2 - margin2.bottom, margin2.top]);

// Define a color scale based on the 'Month' attribute using the custom color scheme array
const customColorScheme = [
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
"#bcbd22", "#17becf", "#aec7e8", "#ffbb78"
];

const colorScale = d3.scaleOrdinal()
.domain(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'])
.range(customColorScheme);

// Create x-axis
const xAxis2 = d3.axisBottom(x2).tickFormat(d3.format(".0s"));
svg2.append('g')
.attr('transform', `translate(0, ${height2 - margin2.bottom})`)
.call(xAxis2);

svg2.append("text")
.attr("x", width2 / 2) // Position text in the middle of the visualization
.attr("y", height2 - 20) // Adjust vertical position as needed
.attr("dy", "0.71em") // Adjust vertical alignment as needed
.attr("text-anchor", "middle")
.style("font-size", "12px")// Align text to the middle
.text("Unique Vehicles Dispatched");

// Create y-axis
const yAxis2 = d3.axisLeft(y2).tickFormat(d3.format(".0s"));


svg2.append('g')
// move y-axis to the left to account for left margin
.attr('transform', `translate(${margin2.left+15})`)
.call(yAxis2)
// add axis label
.append('text')
.attr('fill', 'black')
.attr('text-anchor', 'start')
.attr('dominant-baseline', 'hanging')
.attr('font-weight', 'bold')
.text('Total Dispatched Trips');

// Add legend
const legend = svg2.append('g')
.attr('class', 'legend')
.attr('transform', `translate(${margin2.left}, ${margin2.top})`); // Position the legend at the top-left corner

const colorLegend = legend.selectAll('.color-legend')
.data(customColorScheme)
.enter().append('g')
.attr('class', 'color-legend')
.attr('transform', (d, i) => `translate(50, ${i * 20})`);

// Add color rectangles
colorLegend.append('rect')
.attr('x', 0)
.attr('width', 15)
.attr('height', 15)
.attr('fill', d => d);
const monthNames = [
"January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"
];

// Add legend text
colorLegend.append('text')
.attr('x', 20)
.attr('y', 10)
.text((d, i) => monthNames[i]);


// Create circles for each data point and color code based on the 'Month' attribute
const dots=svg2.selectAll('circle')
.data(top5_company_dispatches)
.enter()
.append('circle')
.attr('cx', d => x2(+d['Unique Dispatched Vehicles']))
.attr('cy', d => y2(+d['Total Dispatched Trips']))
.attr('r', 5)
.attr('fill', d => colorScale(d['Month']))
.attr('opacity', 0.7);
const container = d3.select('#container-id');
//creating brush
const brush = d3.brush()
// set the space that the brush can take up
.extent([[margin2.left, margin2.top], [width2 - margin2.right, height2 - margin2.bottom]])
// handle events
.on('brush', onBrush)
.on('end', onEnd);
svg2.append('g')
.call(brush);
function onBrush(event) {
// event.selection gives us the coordinates of the
// top left and bottom right of the brush box
const [[x_1, y_1], [x_2, y_2]] = event.selection;
console.log(x_1,y_1,x_2,y_2)
// Log filtered data to console

const brushedData = top5_company_dispatches.filter(d => {
const cx = x2(d['Unique Dispatched Vehicles']);
const cy = y2(d['Total Dispatched Trips']);
console.log('ppp',cx,cy)
return cx >= x_1 && cx <= x_2 && cy >= y_1 && cy <= y_2;
});
dots.attr('fill', d => brushedData.includes(d) ? colorScale(d['Month Name']) : 'gray');
console.log('Filtered Data:',brushedData );
svg2.property('value', brushedData).dispatch('input');
}
function onEnd(event) {
// if the brush is cleared
if (event.selection === null) {
// reset the color of all of the dots
dots.attr('fill', d => colorScale(d['Month Name']));
svg2.property('value', initialValue).dispatch('input');
}
}
return svg2.node();

}
Insert cell
Insert cell
function rightVisualization() {
//create SVG, add any axes, draw any marks, etc for initial view

const svg3 = d3.create('svg')
.attr('width', width3)
.attr('height', height3 + marginBar3.top + marginBar3.bottom);
// create scales
const x3 = d3.scaleLinear()
.range([marginBar3.left, width3 - marginBar3.right]);
const y3 = d3.scaleBand()
.domain(colorScale.domain())
.range([marginBar3.top, height3 - marginBar3.bottom])
.padding(0.2);
// create and add axes
const xAxis3 = d3.axisBottom(x3).tickSizeOuter(0).tickFormat(d3.format(".0s"));
const xAxisGroup3 = svg3.append("g")
.attr("transform", `translate(0, ${height3 - marginBar3.bottom})`);
xAxisGroup3.append("text")
.attr('x', marginBar3.left + (width3 - marginBar3.left - marginBar3.right) / 2)
.attr("y", 40)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Total Dispatched Trips");
const yAxis3 = d3.axisLeft(y3);
const yAxisGroup3 = svg3.append("g")
.attr("transform", `translate(${marginBar3.left})`)
.call(yAxis3)
// remove baseline from the axis
.call(g => g.select(".domain").remove()).append('text')
.attr('fill', 'black')
.attr('text-anchor', 'start')
.attr('dominant-baseline', 'hanging')
.attr('font-weight', 'bold')
.text('Month Name');
let barsGroup = svg3.append("g");

//update view based on leftVisualiztion selection
function update(data) {
//Group the data by Month Name and calculate the sum of Total Dispatched Trips for each month
const monthlyTotalDispatchedTrips = d3.rollup(data,
group => d3.sum(group, d => +d['Total Dispatched Trips']),
d => d['Month Name']
);
console.log(data)

// // Update the domain of the x scale based on the maximum total dispatched trips
x3.domain([0, d3.max(Array.from(monthlyTotalDispatchedTrips.values()))]);

// Update x axis
xAxisGroup3
.call(xAxis3);

//Draw bars
barsGroup.selectAll("rect")
.data(Array.from(monthlyTotalDispatchedTrips.entries()))
.join("rect")
.attr("fill", ([month, count]) => colorScale(month))
.attr("x", marginBar3.left)
.attr("y", ([month, count]) => y3(month))
.attr("width", ([month, count]) => x3(count) - marginBar3.left)
.attr("height", y3.bandwidth());
}
return Object.assign(svg3.node(), { update });
}

Insert cell
Insert cell
{
const vis_left = leftVisualization();
const vis_right = rightVisualization();

// update the visualization on the right when the visualization on the left
// selection changes
d3.select(vis_left).on('input', () => {
vis_right.update(vis_left.value);
});

// intial state of visualization on the right
vis_right.update(vis_left.value);

// use HTML to place the two charts next to each other
return html`<div style="display: flex">${vis_left}${vis_right}</div>`;
}
Insert cell
Insert cell
Insert cell
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