Skip to content

Instantly share code, notes, and snippets.

@ra100
Last active January 28, 2018 17:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ra100/fb983ceaa483822a2e8ae7a8d7f28871 to your computer and use it in GitHub Desktop.
Save ra100/fb983ceaa483822a2e8ae7a8d7f28871 to your computer and use it in GitHub Desktop.
Normalize relative scales across multiple evaluations.
const DEFAULT_MODIFIER = { multiplier: 1, offset: 0, error: Number.MAX_VALUE }
const users = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
const sourceData = [
{ A: 1, B: 0.8, C: 0.34, D: 0.33, E: 0 },
{ B: 1, A: 0.9, C: 0.85, F: 0.5, J: 0.1, G: 0 },
{ B: 1, H: 0.7, D: 0.5, J: 0.3, F: 0 },
{ C: 1, A: 0.9, B: 0.8, I: 0.6, E: 0.1, J: 0 }
]
const getAverage = arr =>
arr.reduce((acc, curr, index) => (acc * index + curr) / (index + 1), 0)
const modifySingle = (feedback, offset, multiplier) =>
Object.entries(feedback)
.map(([key, val]) => [key, (val + offset) * multiplier])
.reduce((acc, curr) => {
acc[curr[0]] = curr[1]
return acc
}, {})
const offsetAndMultiply = (source, offset, multiplier) =>
source.map(feedback => modifySingle(feedback, offset, multiplier))
const offsetSource = offsetAndMultiply(sourceData, 1, 1)
const getIntersection = (scaleA = {}, scaleB = {}) =>
Object.entries(scaleA)
.filter(([key]) => scaleB[key] !== undefined)
.reduce((acc, [key, value]) => {
acc[key] = { etalon: value, compare: scaleB[key], name: key }
return acc
}, {})
const getMultiplier = (offset, etalon, value) => etalon / (value + offset)
const getOptimalModifier = (etalon, index, sourceData) =>
Object.values(sourceData).map((data, i) => {
if (i === index) return { index: i, skip: true }
const intersection = getIntersection(etalon, data)
Object.entries(intersection).forEach(([key, value]) => {
intersection[key] = {
...value,
offset: value.etalon - value.compare
}
})
Object.values(intersection).forEach(val => {
const multipliers = Object.values(intersection)
.filter(({ name }) => name !== val.name)
.map(({ compare, etalon }) =>
getMultiplier(val.offset, etalon, compare)
)
const min = Math.min(...multipliers)
const max = Math.max(...multipliers)
const avg = (min + max) / 2
const difference = getAverage(multipliers.map(m => Math.abs(m - avg)))
intersection[val.name] = {
...val,
multipliers,
averageMultiplier: avg,
multiplierError: difference
}
})
const modifier = Object.values(intersection).reduce(
(
acc,
{ averageMultiplier: multiplier, multiplierError: error, offset }
) => {
if (error > acc.error) return acc
return { multiplier, offset, error }
},
{ ...DEFAULT_MODIFIER }
)
// console.log('Modifier', modifier)
return { index: i, modifier }
})
const test = getOptimalModifier(offsetSource[0], 0, offsetSource)
offsetSource.forEach((data, index) => {
getOptimalModifier(offsetSource[index], index, offsetSource).map(modifier => {
if (modifier.skip) return
offsetSource[modifier.index] = modifySingle(
offsetSource[modifier.index],
modifier.modifier.offset,
modifier.modifier.multiplier
)
})
})
const normalizedEvalutions = offsetAndMultiply(offsetSource, -1, 1)
console.log('Normalized Evaluations:\n', JSON.stringify(normalizedEvalutions))
const evaluation = {}
normalizedEvalutions.forEach(feedback => {
Object.entries(feedback).forEach(([name, value]) => {
evaluation[name] = [...(evaluation[name] || []), value]
})
})
// values for heatmap
console.log('Merged evaluations:\n', evaluation)
const result = Object.entries(evaluation)
.map(([name, value]) => ({
name,
average: getAverage(value)
}))
.sort(({ average: a }, { average: b }) => b - a)
.map(({ name, average }) => `${name}: ${average}`)
// user averages
console.log('Evaluations:\n', result.join('\n'))
const users = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
const sourceData = [
{A: 1, B: 0.8, C: 0.34, D: 0.33, E: 0},
{B: 1, A: 0.9, C: 0.85, F: 0.5, J: 0.1, G: 0},
{B: 1, H: 0.7, D: 0.5, J: 0.3, F: 0},
{C: 1, A: 0.9, B: 0.8, I: 0.6, E: 0.1, J: 0}
]
const getDistanceVector = (name, data) => {
const vector = {}
data.forEach(res => {
if (res[name] === undefined) return
const position = res[name]
Object.entries(res).forEach(([key, value]) => {
const distance = value - position
const [avg, weight] = vector[key] || [0, 0]
vector[key] = [((avg * weight) + distance) / (weight + 1), weight + 1]
})
})
return vector
}
const vectors = users.map(name => ({
name,
vector: getDistanceVector(name, sourceData)
}))
console.log('Source:', JSON.stringify(sourceData))
console.log('Vectors', JSON.stringify(vectors))
const scale = vectors.reduce((acc, curr) => {
const name = curr.name
if (!acc[name]) {
acc[name] = [0, 0]
}
console.log(name)
Object.entries(curr.vector).forEach(([distName, dist]) => {
if (distName === name) return
if (!acc[distName]) acc[distName] = dist
const currentDistPosition = acc[distName][0]
const currentDistance = acc[distName][0] - acc[name][0]
const avgPosition = currentDistPosition - currentDistance
console.log(`distance: ${name} ${distName}`, currentDistance, avgPosition)
})
return acc
}, {})
console.log(scale)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment