Public
Edited
Sep 25, 2023
Insert cell
Insert cell
Object.entries(column_averages).filter(([col, avg]) => !isNaN(avg)).map(([column, avg]) => column + ": " + avg).join("\n")
Insert cell
Insert cell
clean = it => ("" + it).trim().toLowerCase();
Insert cell
sum = nums => nums.reduce((acc, n) => acc + n, 0)
Insert cell
mean = nums => {
const result = sum(nums) / nums.length;
if (isNaN(result)) console.log("NaN sum of", { nums });
return result;
}
Insert cell
clean_values = values => values.filter(value => !["undefined", "null", "don't know", "unsure", ""].includes(("" + value).trim().toLowerCase()))
Insert cell
mean([1, 1, 4])
Insert cell
clean(" Good")
Insert cell
xlsx = require("xlsx")
Insert cell
ab2023 = FileAttachment("2023RawData.xls").arrayBuffer()
Insert cell
xls2023 = xlsx.readFileSync(ab2023)
Insert cell
arr2023 = xlsx.utils.sheet_to_json(xls2023.Sheets.Sheet1, {})
Insert cell
arr2023_cleaned = arr2023.slice(1) // drop second row which is just column descriptions
Insert cell
// convert clean values to numbers
arr2023_with_numbers = arr2023_cleaned.map(row => Object.fromEntries(Object.entries(row).map(([column, value]) => {
if (["Very Bad", "Very Unsafe", "Very Dissatisfied"].includes(value)) value = 1;
else if (["Bad", "Unsafe", "Somewhat Dissatisfied"].includes(value)) value = 2;
else if (["Neutral"].includes(value)) value = 3;
else if (["Good", "Safe", "Satisfied", "Somewhat Satisfied"].includes(value)) value = 4;
else if (["Very Good", "Very Safe", "Very Satisfied"].includes(value)) value = 5;
else if (["Don't Know", "undefined", ""].includes(value)) value = null;
return [column, value]
})));
Insert cell
column_names = Object.keys(arr2023_cleaned[0])
Insert cell
// transpose rows into columns
column_values = Object.fromEntries(column_names.map(column => [column, arr2023_with_numbers.map(row => row[column])]))
Insert cell
cleaned_column_values = Object.fromEntries(Object.entries(column_values).map(([column, values]) => [column, clean_values(values)]))
Insert cell
cleaned_column_counts = Object.fromEntries(Object.entries(cleaned_column_values).map(([column, values]) => [column, count(values)]))
Insert cell
column_averages = Object.fromEntries(Object.entries(cleaned_column_values).map(([column, values]) => [column, mean(values)]))
Insert cell
columns = Object.keys(arr2023_cleaned[0])
Insert cell
scales = ({
very_bad_to_very_good: ["Very Bad", "Bad", "Neutral", "Good", "Very Good"],
very_unsafe_to_very_safe: ["Very Unsafe", "Unsafe", "Neutral", "Safe", "Very Safe"],
very_dissasi: ["Very Dissatisfied", "Somewhat Dissatisfied", "Neutral", "Satisfied", "Very Satisfied"]
})
Insert cell
lowered_scales = Object.fromEntries(Object.entries(scales).map(([name, values]) => [name, values.map(clean)]))
Insert cell
apply_scale = (scale, value) => scale.map(clean).indexOf(clean(value)) + 1
Insert cell
apply_scale(scales.good_bad, "bad")
Insert cell
findMatchingScales = (values) => {
try {
const uniques = Array.from(new Set(values)).sort();
const filtered = uniques.filter(value => !should_skip(value));
const lowered = filtered.map(value => value.toLowerCase());
// try if match each scale
const matches = [];
for (let name in lowered_scales) {
if (filtered.every(v => lowered_scales[name].includes(v.toLowerCase()))) {
matches.push(name);
}
}
return matches;
} catch (error) {
return { values };
}
}
Insert cell
findMatchingScales(["Safe"])
Insert cell
findOneOrNoneMatchingScale = values => {
const matches = findMatchingScales(values);
if (matches.length >= 2) throw new Error("too many matching scales");
if (matches.length === 1) return matches[0];
}
Insert cell
prepareScale = (values) => {
const matches = findMatchingScales(values);
if (matches.length === 0) throw new Error("unable to find matching scale");
if (matches.length > 1) throw new Error("found more than one matching scale");
const scale_name = matches[0];
const ordered_values = lowered_scales[scale_name];
return value => {
const i = ordered_values.indexOf(value);
if (i === -1) throw new Error("unable to find corresponding numerical value for " + value);
return i + 1;
};
}
Insert cell
should_skip = (value) => ["undefined", "null", "don't know", "unsure"].includes(("" + value).toLowerCase())
Insert cell
Insert cell
col_scales = Object.fromEntries(columns.map(c => ([c, findOneOrNoneMatchingScale(arr2023_cleaned.map(r => r[c]))])))
Insert cell
csv = arr2023_cleaned.map(row => {
return Object.fromEntries(Object.entries(row).map(([c, v]) => {
if (col_scales[c]) v = apply_scale(lowered_scales[col_scales[c]], v) + " - " + v
return [c, v];
}));
});
Insert cell
hist2023 = Object.fromEntries(columns.map(col => [col, count(arr2023_cleaned.map(row => row[col]))]))
Insert cell
hist2023_vectorized = Object.fromEntries(Object.entries(hist2023).map(([col, counts]) => {
const vectorized = {};
for (let value in counts) {
if (!should_skip(value)) {
vectorized
}
}
return vectorized;
}))
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