Published
Edited
Feb 25, 2019
1 fork
Importers
22 stars
Insert cell
Insert cell
Insert cell
Insert cell
arrow = require('apache-arrow@0.3.1')
Insert cell
Insert cell
Insert cell
Insert cell
crimes = loadData(dataUrl).then(buffer => arrow.Table.from(new Uint8Array(buffer)))
Insert cell
async function loadData(dataUrl){
const response = await fetch(dataUrl);
return await response.arrayBuffer();
}
Insert cell
Insert cell
rowCount = crimes.count()
Insert cell
Insert cell
fields = crimes.schema.fields.map(f => f.name)
Insert cell
fieldTypes = crimes.schema.fields.map(f => f.type)
Insert cell
fieldTypeNames = crimes.schema.fields.map(f => f.type.toString())
Insert cell
Insert cell
firstRow = crimes.get(0).toString() // 1st row data
Insert cell
lastRow = crimes.get(rowCount-1).toString() // last row data
Insert cell
randomRow = crimes.get(Math.random() * rowCount)
Insert cell
Insert cell
toJSON = crimes.get(0).toJSON()
Insert cell
toArray = crimes.get(rowCount-1).toArray()
Insert cell
Insert cell
every10KRow = range(crimes, 0, rowCount, 10000)
Insert cell
Insert cell
function range(data, start, end, step) {
const slice = [];
const rowCount = data.count();
for (let i=start; i<end && i <rowCount; i+= step) {
slice.push(data.get(i).toArray());
}
return slice;
}
Insert cell
Insert cell
md`${getMarkdown(every10KRow, fields)}`
Insert cell
function getMarkdown (dataFrame, fields, dateFields = []) {
let markdown = `${fields.join(' | ')}\n --- | --- | ---`; // header row
let i=0;
for (let row of dataFrame) {
markdown += '\n ';
let td = '';
let k = 0;
for (let cell of row) {
if ( Array.isArray(cell) ) {
td = '[' + cell.map((value) => value == null ? 'null' : value).join(', ') + ']';
} else if (fields[k] === 'Date' || dateFields.indexOf(fields[k]) >= 0) {
td = toDate(cell).toLocaleString(); // convert Apache arrow Timestamp to Date and format
} else {
td = cell.toString();
}
markdown += ` ${td} |`;
k++;
}
}
return markdown;
}
Insert cell
Insert cell
function toDate(timestamp) {
// Appache Arrow Timestamp is a 64-bit int of milliseconds since the epoch,
// represented as two 32-bit ints in JS to preserve precision.
// The fist number is the "low" int and the second number is the "high" int.
return new Date((timestamp[1] * Math.pow(2, 32) + timestamp[0])/1000);
}
Insert cell
Insert cell
latitude = columnStats('Latitude')
Insert cell
longitude = columnStats('Longitude')
Insert cell
Insert cell
Insert cell
function columnStats(columnName) {
const column = crimes.getColumn(columnName);
let max = column.get(0);
let min = max;
for (let value of column) {
if (value > max) {
max = value;
}
else if (value < min) {
min = value;
}
}
return {min, max, range: max-min};
}
Insert cell
function dateStats(columnName) {
const column = crimes.getColumn(columnName);
let max = toDate(column.get(0));
let min = max;
for (let value of column) {
const date = toDate(value);
if (date > max) {
max = date;
}
else if (date < min) {
min = date;
}
}
return {min, max, range: max-min};
}
Insert cell
Insert cell
timestampVector = crimes.getColumn('Date')
Insert cell
Insert cell
timestamps = {
const dates = []
let i = 0;
for (const timestamp of timestampVector.asEpochMilliseconds()) {
dates.push(new Date(timestamp));
if (++i >= 10) {
break;
}
}
return dates;
}
Insert cell
Insert cell
millisPerHour = 1*60*60*1000
Insert cell
startDate = new Date(dates.max - 2*millisPerHour)
Insert cell
endDate = new Date(dates.max - 1*millisPerHour)
Insert cell
crimesIn1Hour = filterByDate(startDate, endDate)
Insert cell
Insert cell
function filterByDate(startDate, endDate) {
let lat, long, date, results = [];
const dateFilter = arrow.predicate.custom(i => {
const date = toDate(crimes.getColumn('Date').get(i));
return date >= startDate && date <= endDate;
}, b => 1);
crimes.filter(dateFilter)
.scan((index) => {
results.push({
'lat': lat(index),
'long': long(index),
'date': toDate(date(index))
});
}, (batch) => {
lat = arrow.predicate.col('Latitude').bind(batch);
long = arrow.predicate.col('Longitude').bind(batch);
date = arrow.predicate.col('Date').bind(batch);
}
);
return results;
}
Insert cell
Insert cell
millisPerDay = 24*millisPerHour
Insert cell
days = Math.floor(dates.range / millisPerDay)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
crimesInDays = filterByDate(
daySliderToDate(startDaySlider),
daySliderToDate(endDaySlider)
)
Insert cell
function daySliderToDate(daySlider) {
return new Date(dates.min.getTime() + daySlider*millisPerDay);
}
Insert cell
Insert cell
mapWidth = width/2 // half of window width
Insert cell
mapHeight = width * .6
Insert cell
function scaleX(long) {
return (long - longitude.min) * (mapWidth/longitude.range);
}
Insert cell
function scaleY(lat) {
return mapHeight - (lat - latitude.min) * (mapHeight/latitude.range);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mappedCrimes = {
const canvas = DOM.canvas(mapWidth, mapHeight);
const context = canvas.getContext('2d');
context.fillStyle = 'steelblue'; //'#333333';
let lat, long, x, y, count = 0;
const startDate = daySliderToDate(startMapDaySlider);
const endDate = daySliderToDate(endMapDaySlider);
const dateFilter = arrow.predicate.custom(i => {
const date = toDate(crimes.getColumn('Date').get(i));
return date >= startDate && date <= endDate;
}, b => 1);
crimes.filter(dateFilter)
.scan((index) => {
x = scaleX(long(index));
y = scaleY(lat(index));
context.fillRect(x, y, 1, 1); // rectWidth, rectHeight);
count++;
}, (batch) => {
lat = arrow.predicate.col('Latitude').bind(batch);
long = arrow.predicate.col('Longitude').bind(batch);
}
);
return {canvas, count};
}
Insert cell
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