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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more