Public
Edited
Sep 27, 2023
Fork of Net flows
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
projections = {
let projection = [];
const last_observation = total_snapshot.find(
(d) =>
d.date.getTime() === d3.max(total_snapshot.map((d) => d.date.getTime()))
);
let previous_day = last_observation.date;
let previous_count = last_observation.count;
for (const _ of d3.range(90)) {
const next_day = new Date(previous_day.getTime());
next_day.setDate(previous_day.getDate() + 1);
const next_count =
previous_count +
scenario_arrivals -
(previous_count * 1) / scenario_average_stay;
projection.push({ date: next_day, count: next_count });
previous_day = next_day;
previous_count = next_count;
}
return projection;
}
Insert cell
average_arrivals = d3.mean(
staging_entrances.map(
(d) => d.count / ((d.end - d.start) / (1000 * 60 * 60 * 24))
)
)
Insert cell
hazard_rate = d3.mean(
shelter_exits.map(
(d) =>
-d.count /
types_snapshot.find(
(e) => (e.type === "Shelter") & (e.date.getTime() === d.start.getTime())
).count /
((d.end - d.start) / (1000 * 60 * 60 * 24))
)
)
Insert cell
average_lifetime = 1 / hazard_rate
Insert cell
half_life = average_lifetime * Math.log(2)
Insert cell
shelter_entrances
Insert cell
Plot.plot({
color: { legend: true },
marginLeft: 60,
x: {
type: "time",
domain: [
d3.min(total_snapshot.map((d) => d.date)),
d3.max(cumulative_arrivals.map((d) => d.date))
]
},
marks: [
Plot.ruleY([0]),
Plot.line(cumulative_arrivals, {
x: "date",
y: "count",
tip: true,
filter: (d) => d.date > new Date("2023-06-01"),
stroke: (d) => "total asylum seekers arrivals"
}),
Plot.lineY(
census.filter((d) => d["Housing Status"] === "Total Arrivals To Date"),
{ x: "Data Received Date", y: "of People" }
),
Plot.line(total_snapshot, {
x: "date",
y: "count",
tip: true,
filter: (d) => d.date > new Date("2023-06-01"),
stroke: (d) => "asylum seekers housed"
}),
Plot.line(cumulative_arrivals, {
x: "date",
y: "count",
tip: true,
filter: (d) => d.date > new Date("2023-06-01"),
stroke: (d) => "total asylum seekers arrivals"
})
]
})
Insert cell
Plot.plot({
marks: [
Plot.ruleY([0]),
Plot.lineY(
census.filter((d) => d["Housing Status"] === "Exited Shelter"),
{ x: "Data Received Date", y: "of People", tip: true }
)
]
})
Insert cell
total_snapshot = d3
.flatRollup(
housing_snapshots,
(v) => d3.sum(v, (d) => d.count),
(d) => d.date
)
.map(([date, count]) => ({ date, count }))
Insert cell
Plot.plot(
(() => {
const n = 3; // number of facet columns
const keys = Array.from(d3.union(housing_snapshots.map((d) => d.location)));
const index = new Map(keys.map((key, i) => [key, i]));
const fx = (key) => index.get(key) % n;
const fy = (key) => Math.floor(index.get(key) / n);
return {
fx: { axis: null },
fy: { axis: null },
marks: [
Plot.line(housing_snapshots, {
x: "date",
y: "count",
fx: (d) => fx(d.location),
fy: (d) => fy(d.location)
}),
Plot.text(keys, { fx, fy, frameAnchor: "top-left", dx: 6, dy: 6 }),
Plot.frame()
]
};
})()
)
Insert cell
Plot.plot(
(() => {
const n = 1; // number of facet columns
const keys = Array.from(
d3.union(designations_snapshot.map((d) => d.designation))
);
const index = new Map(keys.map((key, i) => [key, i]));
const fx = (key) => index.get(key) % n;
const fy = (key) => Math.floor(index.get(key) / n);
return {
fx: { axis: null },
fy: { axis: null },
marks: [
Plot.line(designations_snapshot, {
x: "date",
y: "count",
fx: (d) => fx(d.designation),
fy: (d) => fy(d.designation)
}),
Plot.text(keys, { fx, fy, frameAnchor: "top-left", dx: 6, dy: 6 }),
Plot.ruleY([0]),
Plot.frame()
]
};
})()
)
Insert cell
Plot.plot(
(() => {
const n = 1; // number of facet columns
const keys = Array.from(d3.union(types_snapshot.map((d) => d.type)));
const index = new Map(keys.map((key, i) => [key, i]));
const fx = (key) => index.get(key) % n;
const fy = (key) => Math.floor(index.get(key) / n);
return {
fx: { axis: null },
fy: { axis: null },
marks: [
Plot.line(types_snapshot, {
x: "date",
y: "count",
fx: (d) => fx(d.type),
fy: (d) => fy(d.type),
tip: true
}),
Plot.text(keys, { fx, fy, frameAnchor: "top-left", dx: 6, dy: 6 }),
Plot.frame()
]
};
})()
)
Insert cell
designations_snapshot = d3
.flatRollup(
housing_snapshots,
(v) => d3.sum(v, (d) => d.count),
(d) => d.designation,
(d) => d.date
)
.map(([designation, date, count]) => ({ designation, date, count }))
.filter((d) => d.designation)
Insert cell
types_snapshot = d3
.flatRollup(
housing_snapshots,
(v) => d3.sum(v, (d) => d.count),
(d) => d.type,
(d) => d.date
)
.map(([type, date, count]) => ({ type, date, count }))
Insert cell
housing_snapshots
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
raw_housing_snapshots = FileAttachment("All- Shared(7).csv").csv()
Insert cell
housing_snapshots = raw_housing_snapshots
.filter((d) => d["of People"])
.map((d) => ({
...housing_types.get(d["Housing Locations"]),
count: Number(d["of People"]),
date: parseDateTime(
d["Data Received Date"] === "9/22/2023 11:00pm"
? "9/22/2023 12:00am"
: d["Data Received Date"]
)
}))
Insert cell
date_handling_flag = raw_housing_snapshots.some(
(d) =>
parseDateTime(d["Data Received Date"]).getTime() ===
new Date("2023-09-22T23:00Z").getTime()
)
Insert cell
raw_housing_snapshots.map(
(d) => !hasTimeComponent(parseDateTime(d["Data Received Date"]))
)
Insert cell
function parseDateTime(dateTimeString) {
// Split the input string into date and time parts
const parts = dateTimeString.split(" ");

if (parts.length !== 2) {
throw new Error("Invalid datetime format");
}

const dateString = parts[0];
const timeString = parts[1];

// Split the date into day, month, and year
const dateParts = dateString.split("/");

if (dateParts.length !== 3) {
throw new Error("Invalid date format");
}

const month = parseInt(dateParts[0]) - 1; // Months are zero-based
const day = parseInt(dateParts[1]);
const year = parseInt(dateParts[2]);

// Split the time into hours and minutes
const timeParts = timeString.match(/(\d+):(\d+)([ap]m)/);

if (!timeParts) {
throw new Error("Invalid time format");
}

let hours = parseInt(timeParts[1]);
const minutes = parseInt(timeParts[2]);
const ampm = timeParts[3];

// Adjust hours for PM
if (ampm === "pm" && hours !== 12) {
hours += 12;
} else if (ampm === "am" && hours === 12) {
hours = 0;
}

// Create a new Date object
const date = new Date(year, month, day, hours, minutes);

return date;
}
Insert cell
raw_housing_types = FileAttachment("All- Shared(8)(1).csv").csv()
Insert cell
housing_types = new Map(
raw_housing_types.map((d) => [
d["ID"],
{
location: d["Housing Location"],
type: d["Housing Type"],
designation: d["Designation"],
ward: d["Ward"]
}
])
)
Insert cell
import { getCsvUrl } from "@observablehq/google-sheets-starter"
Insert cell
Plot.plot({
marks: [
Plot.ruleY([0]),
Plot.lineY(cumulative_arrivals, { x: "date", y: "count", tip: true })
]
})
Insert cell
cumulative_arrivals = raw_cumulative_arrivals.filter((item, index) => {
// If it's the first item, keep it
if (index === 0) {
return true;
}
for (let i = 0; i < index; i++) {
if (item.count < raw_cumulative_arrivals[i].count) {
return false;
}
}
return true;
})
Insert cell
raw_cumulative_arrivals = d3.csv(
getCsvUrl(
"https://docs.google.com/spreadsheets/d/1WzpREP7o7v3oPKXV19fZn2UL7pehjZ5ggYARt2XMhEQ/edit#gid=0"
),
d3.autoType
)
Insert cell
All- Shared(9).csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
shelter_exits = {
const exits = census
.filter((d) => d["Housing Status"] === "Exited Shelter")
.map((d) => ({ date: d["Data Received Date"], count: d["of People"] }))
.sort((a, b) => a.date - b.date);
return exits
.map((item, index) => {
if (index === 0) {
return null;
} else {
return {
start: exits[index - 1].date,
end: item.date,
count: -(item.count - exits[index - 1].count)
};
}
})
.filter((d) => d);
}
Insert cell
shelter_entrances = {
const shelter_totals = types_snapshot
.filter((d) => d.type === "Shelter")
.sort((a, b) => a.date - b.date);
return shelter_totals
.map((item, index) => {
if (index === 0) {
return null;
} else {
return {
start: shelter_totals[index - 1].date,
end: item.date,
count: item.count - shelter_totals[index - 1].count
};
}
})
.filter(
(d) =>
d && shelter_exits.find((e) => e.start.getTime() === d.start.getTime())
)
.map((d) => ({
...d,
count:
d.count -
shelter_exits.find((e) => e.start.getTime() === d.start.getTime()).count
}));
}
Insert cell
staging_entrances = {
const totals = types_snapshot
.filter((d) => d.type === "Staging Area")
.sort((a, b) => a.date - b.date);
return totals
.map((item, index) => {
if (index === 0) {
return null;
} else {
return {
start: totals[index - 1].date,
end: item.date,
count: item.count - totals[index - 1].count
};
}
})
.filter(
(d) =>
d &&
shelter_entrances.find((e) => e.start.getTime() === d.start.getTime())
)
.map((d) => ({
...d,
count:
d.count +
shelter_entrances.find((e) => e.start.getTime() === d.start.getTime())
.count
}));
}
Insert cell
function hasTimeComponent(date) {
// Check if hours, minutes, seconds, or milliseconds are non-zero
return (
date.getHours() !== 0 ||
date.getMinutes() !== 0 ||
date.getSeconds() !== 0 ||
date.getMilliseconds() !== 0
);
}
Insert cell
census
.filter((d) => d["Housing Status"] === "Exited Shelter")
.map((d) => ({ date: d["Data Received Date"], count: d["of People"] }))
.sort((a, b) => a.date - b.date)
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