Published
Edited
May 25, 2021
Insert cell
# O*Net Occupation-Skills Matrix
## Visualization of Occupations and Skills by Groupings

2021-05-22 started this.

Insert cell
// cell: Main Display of skillsForOccup with occup index on x-axis and for y-axis show abilities
chart = {
/*
Input is skillsForOccup which has obj for each occup with occupId, seq info, and arays im and lv with
number for each skill.
5/24 addition of sequence info to permit sort in d3.selection by maj grp, random or onto group
** this did not work, not sure why not, BUT: myData is an entry PER POINT, not PER Occup
** we really need to sort occupations first in skillsForOccup and then create myData
*/
const myData = []
let numOccup = 0
let numPts = 0
let cntByImLv = Array(9).fill(0) // count of occup-skill by im-lv
console.log("DISPLAY MODE " + obscfg.occupSortSeq + " NUM skill-occup " + Object.entries(skillsForOccup).length)
// Go through skillsForOccup in the sort sequence set in obscfg.occupSortSeq
console.log("**** skills for occup len " + Object.entries(skillsForOccup).length)
let occupIndex = 0 // not reliable!
for (const [key, val] of Object.entries(skillsForOccup)) {
occupIndex += 1
let myx = occupIndex // start at 1
if (occupIndex < 2000) { // 997 total so over that is all
// console.log("myx " + myx + " ", valueObj.im)
// get all abilities and levels for this occup
numOccup += 1
for (let j = 0; j < 35; j++) {
if ( val.im[j] === -1 && val.lv[j] === -1) {
// do nothing, skip
} else {
numPts += 1
// what if either is not assigned (-1)? skip it even if only one
if (val.im[j] >= 0 && val.lv[j] >= 0) {
const imKeyVal = val.im[j]
const lvKeyVal = val.lv[j]
// const myColorIdx = imKeyVal * 3 + lvKeyVal // 0, 0 = 0; 1,2 = 5 rightmost of second row, looks ok
let myColorIdx
if (lvKeyVal === 0) myColorIdx = 2
if (lvKeyVal === 1) myColorIdx = 5
if (lvKeyVal === 2) myColorIdx = 8
cntByImLv[myColorIdx] += 1
const myDataRow = {
occupId: val.occupId,
seqMaj: val.seqMaj,
seqRnd: val.seqRnd,
seqOnt: val.seqOnt,
x: myx * 4, // not myx * 4 -- how did that work? << PROBLEM: Needs to be AFTER sort!!
y: 10 + j * 10,
col: imlvArColors[myColorIdx]
}
// myData.push({ x: myx * 4, y: 10 + (10 * j), col: imlvArColors[myColorIdx] }) // abil indexes going up and y also (vert loc going down)
myData.push(myDataRow)
}
}
}
}
}
console.log("DISPLAY MATRIX DONE: " + numOccup + " pts: " + numPts)
console.log("cnt by im lv : ", cntByImLv)
d3.select("svg").remove()
// let mySvg = d3.select("#svgMain").append("svg")
/* let mySvg = d3.select(DOM.svg(600, 400)) // cannot do: "body").append("svg") try also d3.create("svg")...
// .attr("width", "600")
// .attr("height", "400")
// .attr("x", "0") // see if this will align left margin; it did NOT
.append("g") */
const height = 400
/* let mySvg = d3.select(DOM.svg(width, height))
.append("g") */
// try create:
let mySvg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
mySvg.append("g")
let priorOccupId = ""
let xAxisCol = 0
mySvg.selectAll("dot")
.data(myData.sort((a, b) => {
/* why no-go: if (obscfg.occupSortSeq === "ont") return d3.ascending(a.SeqOnt, b.SeqOnt) */
if (obscfg.occupSortSeq === "maj") return a.seqMaj - b.seqMaj
if (obscfg.occupSortSeq === "rnd") return a.seqRnd - b.seqRnd
if (obscfg.occupSortSeq === "ont") return a.seqOnt- b.seqOnt
})
)
.enter().append("circle")
// 5/25: .attr("cx", d => d.x)
.attr("cx", (d, i) => {
if (d.occupId != priorOccupId) {
xAxisCol += 1
priorOccupId = d.occupId
// console.log("id = " + d.occupId)
} else {
// no action
}
return xAxisCol * 3
})
.attr("cy", d => d.y)
.attr("r", 2)
.attr("fill", (d) => {
return (d.col)
})
return mySvg.node()
}
Insert cell
// cell: convert data_OccupSkil table to an object of objects with occup id as key and for each skill:
// { skill idx: 1 to 35, col: color code index based on level-importance eg hilow }
/* 5/23/21: Need better analysis (and debugging):
// well: Make more dynamic - store the actual data values for IM and LV to allow easily changing thresholds!
these element names are im and lv
well, why not this: Have ability array of 35 entries one per ability, showing IM and LV values*100 as integers
put thresholds in obscfg! avgIM, hiIM, avgLV, hiLV
5/25 working on test file of only 7 records - need to filter data_OccupSkill for only those occup in data_occup(TST)
*/
skillsForOccup = {
let skoccup = {} // dict with occup id as key
let numRows = 0
let numSelected = 0
let numOccupIds = 0
console.log("START ** ***")
data_OccupSkill.forEach((row, i) => {
const dataValue = parseFloat(row['Data Value'])
numRows += 1
const occupId = row["SOC_Code"]
if (obsSOC2Idx.hasOwnProperty(occupId) && i < 100000) { // earlier elim if value only 2: if (dataValue > 2.0 && i < 600) {
numSelected += 1

// convert element id to skill list index
const skIdx = skillsIndex[skId]
let myRow // will be either new object or retrieved from skoccup
// AHAH: this is an array not a dict: if (!skoccup.hasOwnProperty(occupId)) {
// Fix with table lookup - or with separate dict of what values have been filled?
// or make a dict like it ought to be!!!
// 5/22/21 converting to dict with occcup id as key
if (!skoccup.hasOwnProperty(occupId)) {
// create the entry object and insert it
// 5/24 retrieve seq info from data_Occup[j] were j is the index from dictOccupIdx for SOC_Code
const occupIdx = obsSOC2Idx[occupId]
// const myOccup = data_Occup[occupIdx]
const myOccup = data_OccupTST[occupIdx]
myRow = {
occupId,
seqMaj: myOccup.SeqMajGrp,
seqRnd: myOccup.SeqRandom,
seqOnt: myOccup.SeqOntoGrp,
im: Array(35).fill(-1),
lv: Array(35).fill(-1)
}
skoccup[occupId] = myRow
numOccupIds += 1
} else {
myRow = skoccup[occupId]
}
// add current row ability index and value of IM or LV; converted to 0, 1 or 2 based on threshold
// whether addedd or existing, myRow should point into the array

let keyVal = 0 // default to low; 1 = abv, 2 = hi

if (row["Scale ID"] === 'IM') {
if (dataValue >= obscfg.imAbv) keyVal = 1
if (dataValue > obscfg.imHi) keyVal = 2
myRow.im[skIdx] = keyVal
// console.log("abl " + skIdx + " imval " + dataValue + " " + keyVal)
} else {
if (dataValue >= obscfg.lvAbv) keyVal = 1
if (dataValue > obscfg.lvHi) keyVal = 2
myRow.lv[skIdx] = keyVal
// console.log("abl " + skIdx + " lvval " + value + " " + keyVal)
}
// console.log("MyRow ", myRow)
}
})
console.log("In skills: Num sk-oc " + numRows + " sel: " + numSelected + " occup " + numOccupIds)
// analyze skill-occups: Number with 0 for all abils; Counts by keyvalue
let imCnts = [0, 0, 0]
let lvCnts = [0, 0, 0]
let numAllZeroIm = 0
let numAllZeroLv = 0
let numskoc = 0 // diag remove
for (const [key, val] of Object.entries(skoccup)) {
numskoc += 1
// if (numskoc < 5) console.log("im vals ", val.im)
let imAllZero = true
let lvAllZero = true
for (let j = 0; j < 35; j++) {
if (val.im[j] > -1) {
if (val.im[j] < 0 || val.im[j] > 2) console.log("Error at im " + key + " " + j + " " + val.im[j])
if (val.im[j] > 0) imAllZero = false
imCnts[val.im[j]] += 1
}
if (val.lv[j] > -1) {
if (val.lv[j] < 0 || val.lv[j] > 2) console.log("Error at lv " + key + " " + j)
if (val.lv[j] > 0) lvAllZero = false
lvCnts[val.lv[j]] += 1
}
}
if (imAllZero) numAllZeroIm += 1
if (lvAllZero) numAllZeroLv += 1
}
console.log(" cnts im ", imCnts)
console.log(" cnts lv ", lvCnts)
console.log(" Num all zero IM ", numAllZeroIm)
console.log(" Num all zero LV ", numAllZeroLv)
return skoccup
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// cell: create a dictionary of SOC Id's pointing to the occupation index
obsSOC2Idx = {
let result = {}
data_OccupTST.forEach((occup, i) => { result[occup["SOC_Code"]] = i }) // simply have presence for SOC_Code in dict
return result
}
Insert cell
Insert cell
// cell: Read in file - Occupations list of 997 SOC Occupations
data_OccupTST = d3.csvParse(await FileAttachment("SOC_OccupCSVTST3.csv").text())
Insert cell
Insert cell
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