{
const Plotly = await require("https://cdn.plot.ly/plotly-latest.min.js");
const createDropdown = (name, options) => {
const select = document.createElement("select");
select.name = name;
options.forEach(option => {
const optionElement = document.createElement("option");
optionElement.value = option;
optionElement.text = option;
select.appendChild(optionElement);
});
select.onchange = updateDropdowns;
return select;
};
function updateDropdowns() {
updateHeatmap();
updateTimeSeries();
updateUserComparison();
}
const durationBinOptions = ["All", "0-10 min", "10-30 min", "30-60 min", "1-2 hours", "2-4 hours", "4+ hours"];
const memberCasualOptions = ["All", "member", "casual"];
const timePeriodOptions = ["Monthly", "Quarterly"];
const durationBinDropdown = createDropdown("Duration Bin", durationBinOptions);
const memberCasualDropdown = createDropdown("Member Type", memberCasualOptions);
const timePeriodDropdown = createDropdown("Time Period", timePeriodOptions);
const chartContainer = document.createElement("div");
chartContainer.style.width = "100%";
chartContainer.style.height = "600px";
// Function to filter data based on dropdown values
function filterData() {
const selectedDurationBin = durationBinDropdown.value;
const selectedMemberCasual = memberCasualDropdown.value;
const filteredData = bikeshare_data.filter(entry => {
let includeData = true;
// Filter by Duration Bin
if (selectedDurationBin !== "All") {
if (selectedDurationBin === "0-10 min" && entry.ride_duration > 10) includeData = false;
else if (selectedDurationBin === "10-30 min" && (entry.ride_duration <= 10 || entry.ride_duration > 30)) includeData = false;
else if (selectedDurationBin === "30-60 min" && (entry.ride_duration <= 30 || entry.ride_duration > 60)) includeData = false;
else if (selectedDurationBin === "1-2 hours" && (entry.ride_duration <= 60 || entry.ride_duration > 120)) includeData = false;
else if (selectedDurationBin === "2-4 hours" && (entry.ride_duration <= 120 || entry.ride_duration > 240)) includeData = false;
else if (selectedDurationBin === "4+ hours" && entry.ride_duration <= 240) includeData = false;
}
// Filter by Member Type
if (selectedMemberCasual !== "All" && entry.member_casual !== selectedMemberCasual) {
includeData = false;
}
return includeData;
});
return filteredData;
}
// Heatmap: Average Ride Duration by Hour of Day and Day of Week
function updateHeatmap() {
const dayOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const hours = Array.from({ length: 24 }, (_, i) => i);
const rideDurationMatrix = Array.from({ length: 7 }, () => Array(24).fill(0));
const rideCountMatrix = Array.from({ length: 7 }, () => Array(24).fill(0));
const filteredData = filterData();
filteredData.forEach(entry => {
const startTime = new Date(entry.started_at);
const day = startTime.getDay();
const hour = startTime.getHours();
rideDurationMatrix[day][hour] += entry.ride_duration;
rideCountMatrix[day][hour] += 1;
});
const avgRideDurationMatrix = rideDurationMatrix.map((row, i) => row.map((duration, j) => {
return rideCountMatrix[i][j] === 0 ? 0 : duration / rideCountMatrix[i][j];
}));
const trace = {
z: avgRideDurationMatrix,
x: hours,
y: dayOfWeek,
type: 'heatmap',
colorscale: 'Viridis',
colorbar: {
title: 'Average Ride Duration (min)'
}
};
const layout = {
title: 'Average Ride Duration by Hour of Day and Day of Week',
xaxis: { title: 'Hour of Day' },
yaxis: { title: 'Day of Week' },
};
Plotly.newPlot(chartContainer, [trace], layout);
}
// Time-Series Analysis: Ride Frequency Trends (Monthly or Quarterly)
function updateTimeSeries() {
const timePeriod = timePeriodDropdown.value;
const timeSeriesData = {};
const filteredData = filterData();
filteredData.forEach(entry => {
const startTime = new Date(entry.started_at);
const period = (timePeriod === "Monthly") ? startTime.getMonth() : Math.floor(startTime.getMonth() / 3);
timeSeriesData[period] = (timeSeriesData[period] || 0) + 1;
});
const periods = Object.keys(timeSeriesData).map(key => `Period ${parseInt(key) + 1}`);
const counts = Object.values(timeSeriesData);
const trace = {
x: periods,
y: counts,
type: 'bar',
};
const layout = {
title: `Ride Frequency: ${timePeriod} Trends`,
xaxis: { title: `Period (${timePeriod})` },
yaxis: { title: 'Ride Count' },
};
Plotly.newPlot(chartContainer, [trace], layout);
}
// User Classification Comparisons: Casual vs Member Users
function updateUserComparison() {
const filteredData = filterData();
const casualRiders = filteredData.filter(entry => entry.member_casual === 'casual');
const memberRiders = filteredData.filter(entry => entry.member_casual === 'member');
const casualRideDurations = casualRiders.map(entry => entry.ride_duration);
const memberRideDurations = memberRiders.map(entry => entry.ride_duration);
const trace1 = {
type: 'histogram',
x: casualRideDurations,
name: 'Casual Riders',
opacity: 0.75,
marker: { color: 'rgba(255, 99, 132, 0.6)' }
};
const trace2 = {
type: 'histogram',
x: memberRideDurations,
name: 'Member Riders',
opacity: 0.75,
marker: { color: 'rgba(54, 162, 235, 0.6)' }
};
const layout = {
title: 'Ride Duration Comparison: Casual vs Member Riders',
barmode: 'overlay',
xaxis: { title: 'Ride Duration (minutes)' },
yaxis: { title: 'Frequency' },
};
Plotly.newPlot(chartContainer, [trace1, trace2], layout);
}
// UI setup: append dropdowns and chart container to the document
const uiContainer = document.createElement("div");
uiContainer.appendChild(document.createTextNode("Select Filters: "));
uiContainer.appendChild(durationBinDropdown);
uiContainer.appendChild(memberCasualDropdown);
uiContainer.appendChild(timePeriodDropdown);
const container = document.createElement("div");
container.appendChild(uiContainer);
container.appendChild(chartContainer);
// Initial rendering
updateHeatmap();
updateTimeSeries();
updateUserComparison();
return container;
}