Public
Edited
Mar 16, 2023
Importers
Insert cell
Insert cell
function getCountsByOrientation(bins, orientation) {
const orientations = [];

bins.forEach((bin) => {
const time = bin.start_time;

orientations.push({
...bin[orientation],
orientation,
start_time: time,
total: _.sum(_.values(bin[orientation])),
});
});

return orientations;
}
Insert cell
function parse(rawFileText, options = {}) {
const raw_data_rows = rawFileText.split('\r\n');

const summary = Object.fromEntries(raw_data_rows.slice(1, 7).map((row) => {
const [key, value] = csv.parse(row)[0];
return [key.toLowerCase().replaceAll(' ', '_'), value];
}));
const [lat, lng] = summary.latitude_and_longitude.split(',');
const bicycles = raw_data_rows.slice(8);

const orientationIndexes = getOrientationIndexes(bicycles);
const header = bicycles[2].split(',').map((label) => label.toLowerCase().replaceAll(/[\s-]/g, '_'));

const bicycleCounts = bicycles
.slice(3)
.map((row) => parseBicycleCountRow(row, { header, orientationIndexes, ...options }));

const total = _.sum(_.map(bicycleCounts, "total"));

return { ...summary, lat, lng, total, bicycles: bicycleCounts };
}
Insert cell
function getOrientationIndexes(countRows) {
const entryIndexes = parseDirections(countRows[0]);
const directionIndexes = parseDirections(countRows[1]);
return entryIndexes.map(({ indexes, direction }, index) => ({ entry: direction, direction: directionIndexes[index].direction, indexes }));
}
Insert cell
function parseBicycleCountRow(row, { header, orientationIndexes, flatten = false }) {
// For each row, get the start time (first value) and count values
const [startTimeRaw, ...rowValues] = row.split(',');
const startTime = new Date(startTimeRaw);

// Match the count values to their labels (regular csv parsing)
const rowValuesWithLabels = rowValues.map((v, index) => {
const label = header[index + 1];

// Parse value as Number
return { entry: [label, Number(v)], index };
});

// Group count values by their orientation
const countsByOrientationGroups = _.groupBy(rowValuesWithLabels, ({ index }) => {
// Based on the value's index, find it's orientation
const { entry, direction } = orientationIndexes.find(({ indexes }) => indexes.includes(index));

// Use entry + direction as the groups label
return `${entry}_${direction}`.toLowerCase();
});

const orientationEntries = _.map(countsByOrientationGroups, (value, orientation) => {
const entries = _.map(value, 'entry');

// For each orientation, sum up it's total
const total = _.sum(entries.map(([key, count]) => count));
entries.push(['total', total]);

if (flatten) {
return _.map(entries, ([key, count]) => ([`${orientation}.${key}`, count]));
}
return [orientation, Object.fromEntries(entries)];
});

// Combine all counts for each orientation
const countsByOrientation = Object.fromEntries(
flatten ? orientationEntries.flat() : orientationEntries
);
// For every bin, sum up it's total across all orientations
const total = _.sum(orientationEntries.flat().filter(([key, value]) => key.endsWith('.total')).map(([key, value]) => value));

return {
start_time: startTime,
total,
...countsByOrientation,
};
}
Insert cell
function parseDirections(directions) {
return directions.split(',').slice(1).reduce((accumulator, direction, index) => {
let currentDirectionIndex = accumulator.length - 1;
let currentDirection = accumulator[currentDirectionIndex] || { direction, indexes: [] };
if (direction) {
currentDirection = { direction, indexes: [] };
currentDirectionIndex += 1;
}
currentDirection.indexes.push(index);
accumulator[currentDirectionIndex] = currentDirection;
return accumulator;
}, []);
}
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