Public
Edited
Nov 23
1 fork
1 star
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
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
## Office staff salaries
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
rank_choices = [
{ rank: "Professor", id: 1 },
{ rank: "Associate Professor", id: 2 },
{ rank: "Assistant Professor", id: 3 },
{ rank: "Instructor", id: 4 },
{ rank: "All", id: 7 }
]
Insert cell
function make_salary_comparison_bar_plot(cohort, rank, cost_adjust_flag) {
let ids = cohorts.filter((o) => o.cohort == cohort).map((o) => o.UNITID);
let filtered_data = salary_data.filter((o) => ids.indexOf(o.UnitID) > -1);
let tidy_data = get_single_value(rank.rank.toLowerCase());
let avg_data = tidy_data.map(avg);

let plot =
width < 900
? Plot.plot({
y: {
label: null,
domain: d3
.sort(avg_data, (d) =>
cost_adjust_flag ? -d.cost_adjusted_value : -d.value
)
.map((d) => d.UnitID),
tickFormat: (d) => get_initials(d)
},
width: width,
// height: 0.4 * width,
// marginLeft: 70,
marks: [
Plot.barX(avg_data, {
y: "UnitID",
x: cost_adjust_flag ? "cost_adjusted_value" : "value",
fill: (d) => (d.UnitID == "199111" ? "#003DA5" : "lightblue"),
title: "UnitID"
}),
Plot.ruleX([0])
]
})
: Plot.plot({
x: {
label: null,
domain: d3
.sort(avg_data, (d) =>
cost_adjust_flag ? -d.cost_adjusted_value : -d.value
)
.map((d) => d.UnitID),
tickFormat: (d) => get_initials(d)
},
width: width,
height: 0.4 * width,
marginLeft: 70,
marks: [
Plot.barY(avg_data, {
x: "UnitID",
y: cost_adjust_flag ? "cost_adjusted_value" : "value",
fill: (d) => (d.UnitID == "199111" ? "#003DA5" : "lightblue"),
title: "UnitID"
}),
Plot.ruleY([0])
]
});

let nodes = d3
.select(plot)
.selectAll("rect")
.on("pointerenter", function () {
d3.select(this).attr("stroke-width", "2px").attr("stroke", "black");
})
.on("pointerleave", function () {
d3.select(this).attr("stroke", null);
})
.nodes()
.forEach(function (bar) {
let title = d3.select(bar).select("title").text();
tippy(bar, {
content: `${get_name(title)}: $${parseInt(get_salary(title))} ${
cost_adjust_flag ? "(adjusted)" : ""
}`,
theme: "light"
});
});
d3.select(plot).selectAll("rect").select("title").remove();

return plot;

function get_single_value(column_start) {
let columns = Object.keys(filtered_data[0]).filter(
(s) => s.slice(0, column_start.length) == column_start
);
let tidy_data = filtered_data.map(function (d) {
let row = columns.map((key) => ({
year: parseInt(key.match(find_year_re)[0].slice(-4)),
value: parseInt(d[key]),
cost_adjusted_value: cost_adjust(parseInt(d[key]), d.UnitID),
UnitID: d.UnitID
}));
row = d3.sort(row, (o) => o.year);
return row;
});
return tidy_data;
}

function get_salary(uid) {
let row = avg_data.filter((o) => o.UnitID == uid)[0];
if (cost_adjust_flag) {
return Math.round(row.cost_adjusted_value);
} else {
return row.value;
}
}
}
Insert cell
function get_closest(time, value, data) {
let results = [];

data.forEach(function (school_data, i) {
let these_dates = school_data.map((o) => parseInt(o.year));
let these_values = school_data.map((o) => o.value);
try {
let idx = d3.bisect(these_dates, time);
let t1 = these_dates[idx - 1];
let t2 = these_dates[idx];
let p1 = these_values[idx - 1];
let p2 = these_values[idx];
let p = p1 + ((p2 - p1) * (time - t1)) / (t2 - t1);
results.push({
time: time,
value: p,
err: Math.abs(p - value),
id: school_data[0].UnitID
});
} catch {
("pass");
}
});
return results.sort((o1, o2) => o1.err - o2.err)[0];
}
Insert cell
function get_single_value(column_start, data) {
let columns = Object.keys(data[0]).filter(
(s) => s.slice(0, column_start.length) == column_start
);
let td = data.map(function (d) {
let row = columns.map((key) => ({
year: parseInt(key.match(find_year_re)[0].slice(-4)),
value: d[key],
UnitID: d.UnitID
}));
row = d3.sort(row, (o) => o.year);
return row;
});
return td;
}
Insert cell
function get_name(uid) {
let row = cohorts.filter((o) => o.UNITID == uid)[0];
return row.INSTNM;
}
Insert cell
function avg(a) {
// return a.slice(-3);
let this_avg = Math.round(d3.mean(a.slice(-1).map((r) => r.value)));

return {
value: this_avg,
UnitID: a[0].UnitID,
cost_adjusted_value: Math.round(cost_adjust(this_avg, a[0].UnitID))
};
}
Insert cell
// tidy_data = {
// let ids = cohorts.filter((o) => o.cohort == cohort).map((o) => o.UNITID);
// let filtered_data = salary_data.filter((o) => ids.indexOf(o.UnitID) > -1);
// return get_single_value(rank.rank.toLowerCase());

// function get_single_value(column_start) {
// let columns = Object.keys(filtered_data[0]).filter(
// (s) => s.slice(0, column_start.length) == column_start
// );
// let tidy_data = filtered_data.map(function (d) {
// let row = columns.map((key) => ({
// year: parseInt(key.match(find_year_re)[0].slice(-4)),
// value: parseInt(d[key]),
// cost_adjusted_value: cost_adjust(parseInt(d[key]), d.UnitID),
// UnitID: d.UnitID
// }));
// row = d3.sort(row, (o) => o.year);
// return row;
// });
// return tidy_data;
// }
// }
Insert cell
find_year_re = /\([A-Z]{2,}20\d{2}/
Insert cell
function cost_adjust(value, id) {
let adjuster = cohorts.filter((o) => o.UNITID == id)[0].cost_index;
return (100 * value) / adjuster;
}
Insert cell
function get_initials(uid) {
let row = cohorts.filter((o) => o.UNITID == uid)[0];
return row.INSTNM.split(" ")
.map((s) => s[0])
.filter((s) => s.charCodeAt(0) < 97)
.join("");
}
Insert cell
d3.sort(cohorts.map((o) => +o.cost_index))
Insert cell
cohorts = FileAttachment("updated_cohort_data.csv").csv()
Insert cell
housing_data = FileAttachment("UNCSystemZillow@1.csv").csv()
Insert cell
salary_data = FileAttachment("public_peer_salaries.csv").csv()
Insert cell
compression_ratios = FileAttachment("compression_ratios.csv").csv({
typed: true
})
Insert cell
location_data = FileAttachment("cohort_data.csv").csv()
Insert cell
d3.sort(
office_data.map((o) => ({
name: o.InstitutionName,
salary: o.outlay2020 / o.cnt2020
})),
(o) => -o.salary
)
Insert cell
office_data = FileAttachment("nc_office_staff2.csv").csv()
Insert cell
tippy = require("tippy.js@6")
Insert cell
tippy_style = html`<link rel="stylesheet" href="${await require.resolve(
`tippy.js/themes/light.css`
)}">`
Insert cell
import { map, caption } from "@mcmcclur/unc-system-salary-map"
Insert cell
import { regression_chart } from "cf61f59a18c0635a"
Insert cell
data = d3.sort(
cohorts.filter((row) => row.cohort == "UNC System"),
(o) => -o.cost_index
)
Insert cell
<table>
<thead>
<tr>
<th>Name</th>
<th>CoL Index</th>
</tr>
</thead>
<tbody>
${d3.sort(data, row => -row.overall_col).map(row => htl.html`<tr>
<td>${row["INSTNM"]}</td>
<td>${row["cost_index"]}</td>
</tr>`)}
</tbody>
</table>
Insert cell
{
let ids = cohorts.filter((o) => o.cohort == cohort2).map((o) => o.UNITID);
let filtered_data = salary_data.filter((o) => ids.indexOf(o.UnitID) > -1);
let tidy_data = get_single_value("all", filtered_data).map((A) => A.slice(4));

tidy_data.forEach(function (A) {
let v0 = A[0].value;
A.forEach((o) => (o.value = o.value / v0));
});
return tidy_data;
}
Insert cell
Plot = require("https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6.1")
Insert cell
d3.sort(compression_ratios, (o) => o.ratio).slice(452)
Insert cell
compression_ratios
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