Public
Edited
Jun 8, 2023
1 star
Insert cell
Insert cell
Insert cell
addTooltips(
Plot.plot({
width: 1080,
height: 750,
projection: "albers",
color: {
scheme: "blues"
},
facet: {
data: allGamesWithLatLongFiltered,
y: "Division",
x: "rank",
label: null
},
marks: [
Plot.arrow(allGamesWithLatLongFiltered, {
x2: "Long",
y2: "Lat",
x1: "prevLong",
y1: "prevLat",
stroke: "#00b894",

strokeWidth: 1.25,
headAngle: 30,
bend: true,
title: (d) => `${d.team}`
}),
Plot.geo(statemesh, { clip: "frame", strokeOpacity: 0.5 }),
Plot.geo(nation, { strokeOpacity: 0.7, clip: "frame" })
]
})
)
Insert cell
Insert cell
allGames = database
.sort((a, b) => a.game_id - b.game_id)
.filter((_, index) => (index + 1) % 3 === 0) // keep one row per game played this season.
Insert cell
aq.from(allGames).view()
Insert cell
aq
.from(allGames)
.select(
"game_id",
"team_abbreviation_home",
"team_abbreviation_away",
"game_date",
"pts_home",
"pts_away"
)
.view()
Insert cell
Insert cell
[1, 2, 3].slice(-1)[0]
Insert cell
teamNames = aq
.from(allGames)
.select(
"game_id",
"game_date",
"team_abbreviation_home",
"team_abbreviation_away"
)
.fold(["team_abbreviation_home", "team_abbreviation_away"], {
as: ["location", "team"]
})
Insert cell
teamNames.view()
Insert cell
teamNames.objects()[0].location.split("_").slice(-1)[0]
Insert cell
points.view()
Insert cell
teamNames.join(points, (a, b) => {
console.log(a.location, b.location);
return op.equal(
op.split(a.location, "_").slice(-1),
op.split(b.location, "_").slice(-1)
);
})
Insert cell
console.log("here")
Insert cell
op.equal(
op.split("team_abbr_home", "_").slice(-1),
op.split("pts_home", "_").slice(-1)
)
Insert cell
aq
.from(allGames)
.select(
"game_id",
"team_abbreviation_home",
"team_abbreviation_away",
"game_date",
"wl_home",
"wl_away",
"pts_home",
"pts_away"
)
.fold(["wl_home", "wl_away"], {
as: ["wl_type", "outcome"]
})
.fold(["team_abbreviation_home", "team_abbreviation_away"], {
as: ["team_type", "team"]
})
.fold(["pts_home", "pts_away"], {
as: ["pts_type", "pts"]
})
.filter(
(d) =>
(d.wl_type == "wl_away" &&
d.team_type == "team_abbreviation_away" &&
d.pts_type === "pts_away") ||
(d.wl_type == "wl_home" &&
d.team_type == "team_abbreviation_home" &&
d.pts_type === "pts_home")
)
.derive({
location: (d) =>
d.team_type === "team_abbreviation_home" ? "home" : "away"
})
.relocate(["team", "location"], { after: "game_id" })
.select(aq.not("wl_type", "team_type", "pts_type"))
.view(20)
Insert cell
op.split()
Insert cell
aq
.from(allGames)
.select(
"game_id",
"team_abbreviation_home",
"team_abbreviation_away",
"game_date",
"wl_home"
)
.fold(["team_abbreviation_home", "team_abbreviation_away"], {
as: ["type", "team"]
})
.derive({
outcome: (d) =>
d.wl_home === "W" && d.type === "team_abbreviation_home" ? "W" : "L"
})
.view(20)
Insert cell
aq
.from(allGames)
.select(
"game_id",
"team_abbreviation_home",
"team_abbreviation_away",
"game_date"
)
.view()
Insert cell
aq
.from(allGames)
.select(
"game_id",
"team_abbreviation_home",
"team_abbreviation_away",
"pts_home",
"pts_away",
"game_date"
)
.fold(
[
"team_abbreviation_home",
"team_abbreviation_away",
"pts_home",
"pts_away"
],
{
as: ["type", "team"]
}
)
.view()
Insert cell
allGamesWithLatLong = appendPreviousLatLong(
rankWithinGroup(
aq
.from(joinLists(tidyGames(allGames), coords, "team_name_home", "Team"))
.join(aq.from(rankWithinGroup(divisions, ["Division"], "Division")), [
"team",
"Team"
])
.groupby("team")
.orderby("team", "game_id")
.objects(),
["team", "game_id"],
"team",
"gameCount"
)
)
Insert cell
function concatenateStringsInOrder(str1, str2) {
// Concatenate the two strings with a dash in between
var concatenated = str1 + "-" + str2;

// Split the concatenated string into an array of substrings
var substrings = concatenated.split("-");

// Sort the array of substrings alphabetically
var sortedSubstrings = substrings.sort();

// Join the sorted substrings back into a string with a dash
var sortedString = sortedSubstrings.join("-");

// Return the sorted string
return sortedString;
}
Insert cell
function groupTravel(data) {
const groups = Array.from(d3.group(data, (d) => d.team));

const trips = new Map();

for (let i = 0; i < groups.length; i++) {
const [team, games] = groups[i];

for (let j = 1; j < games.length; j++) {
const { Team_1: prevHomeTeam } = games[j - 1];
const { Team_1: currHomeTeam } = games[j];
if (currHomeTeam !== prevHomeTeam) {
const key = concatenateStringsInOrder(currHomeTeam, prevHomeTeam);
if (trips.has(key)) {
trips.set(key, trips.get(key) + 1);
} else {
trips.set(key, 1);
}
}
}
}
return Array.from(trips.entries()).map(([key, value]) => {
const [team1, team2] = key.split("-");
const { Lat: Lat1, Long: Long1 } = findLatLong(team1);
const { Lat: Lat2, Long: Long2 } = findLatLong(team2);
return { key, value, Lat1, Long1, Lat2, Long2 };
});
}
Insert cell
Insert cell
Inputs.table(groupTravel(allGamesWithLatLong))
Insert cell
Array.from(d3.group(allGamesWithLatLong, (d) => d.team))
Insert cell
allGamesWithLatLongFiltered = allGamesWithLatLong.filter((d) => d.gameCount <= gameNum)
Insert cell
mergeConsecutiveSameType(
rankWithinGroup(
aq
.from(joinLists(tidyGames(allGames), coords, "team_name_home", "Team"))
.join(aq.from(rankWithinGroup(divisions, ["Division"], "Division")), [
"team",
"Team"
])
.groupby("team")
.orderby("team", "game_id")
.objects(),
["team", "game_id"],
"team",
"gameCount"
)
)
Insert cell
distances = Array.from(
d3.rollup(
allGamesWithLatLong,
(d) => d.reduce((prev, curr) => prev + curr.distance, 0),
(d) => d.team
)
).map(([team, distance]) => {
return { team, totalDistance: distance };
})
Insert cell
function findLatLong(teamName) {
for (let i = 0; i < coords.length; i++) {
if (coords[i].Team === teamName) {
const { Lat, Long } = coords[i];
return { Lat, Long };
}
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
appendPreviousLatLong = function (mergedWithCoords) {
return mergedWithCoords.map((obj, index) => {
if (index === 0) {
return {
...obj,
prevLat: null,
prevLong: null,
distance: null
};
} else {
const prevObj = mergedWithCoords[index - 1];
const { Lat, Long, team } = obj;
const { Lat: prevLat, Long: prevLong } = prevObj;
const distance = calculateDistance(Lat, Long, prevLat, prevLong);

return { ...obj, prevLat, prevLong, distance };
}
});
}
Insert cell
convertLatLong = function (data) {
return data.map((d) => {
return {
...d,
Lat: parseFloat(d.Lat),
Long: -1 * parseFloat(d.Long)
};
});
}
Insert cell
function joinLists(list1, list2, key1, key2) {
return list1.map((obj1) => {
const obj2 = list2.find((obj2) => obj2[key2] === obj1[key1]);
const { game_date, team, type, game_id, site } = obj1;
return {
game_id,
team,
type,
site,
game_date,
...obj2
};
});
}
Insert cell
function mergeConsecutiveSameType(arr) {
const result = [];
let current = [];

for (let i = 0; i < arr.length; i++) {
if (current.length === 0 || current[0].site === arr[i].site) {
current.push(arr[i]);
} else {
result.push({ type: arr[i - 1].site, values: current });
current = [arr[i]];
}
}

if (current.length > 0) {
result.push({ type: arr[arr.length - 1].site, values: current });
}

return result;
}
Insert cell
tidyGames = function (games) {
// Tidied table
var tidiedTable = [];

// Iterate over each row in the input table
for (var i = 0; i < games.length; i++) {
var row = games[i];
const { team_name_away, team_name_home } = row;
// Create a new row for the home team
var homeRow = { team: team_name_home, type: "home" };
tidiedTable.push({ ...row, ...homeRow, site: team_name_home });

// Create a new row for the away team
var awayRow = { team: team_name_away, type: "away" };
tidiedTable.push({ ...row, ...awayRow, site: team_name_home });
}
return tidiedTable;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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