Skip to content

Instantly share code, notes, and snippets.

@rasmushaglund
Last active March 8, 2020 00:56
Show Gist options
  • Save rasmushaglund/8fb753d9e37584bc5396ff187aa79a54 to your computer and use it in GitHub Desktop.
Save rasmushaglund/8fb753d9e37584bc5396ff187aa79a54 to your computer and use it in GitHub Desktop.
Calculate coefficient of relationship in javascript
// Used to calculate Coefficient of relationship
// https://en.wikipedia.org/wiki/Coefficient_of_relationship
// Limitations: Does not handle identical twin common ancestors
// Test examples based on http://www.genetic-genealogy.co.uk/Toc115570135.html
var init = function (persons) {
for (p in persons) {
persons[p].children = [];
}
// Add reversed relationship
for (p in persons) {
const o = persons[p];
if (o.mother) {
o.mother.children.push(o);
}
if (o.father) {
o.father.children.push(o);
}
}
}
// Find distinct paths from p to ancestors
var findPaths = function(_acc, p) {
if (!p) {
return [];
}
// Create a new object since javascript uses references as default
const acc = _acc.slice();
acc.push(p.name);
return [acc].concat(findPaths(acc, p.mother)).concat(findPaths(acc, p.father));
}
// Check if an array has duplicates
var hasDuplicates = function (arr) {
return new Set(arr).size !== arr.length;
}
// Find all paths through ancestors between a and b
var findAncestorPaths = function(a, b) {
var result = [];
var aPaths = findPaths([], a);
var bPaths = findPaths([], b);
if (aPaths.length > 1 && bPaths.length > 1) {
for (const i in aPaths) {
// Choose a node to check as common ancestor
const nodeToCheck = aPaths[i].slice(-1)[0];
for (const j in bPaths) {
// Find a path which shares the same end node (to test for common ancestor)
if (nodeToCheck == bPaths[j].slice(-1)[0]) {
const aPath = aPaths[i].slice(0,-1);
const bPath = bPaths[j].slice(0,-1);
console.log('Testing ' + aPaths[i] + ', ' + bPaths[j])
// Make sure the paths doesn't share any nodes except last/ancestor
if (!hasDuplicates(aPath.concat(bPath))) {
console.log('Found match: ' + aPaths[i] + ' - ' + bPaths[j]);
result.push(aPath.concat(bPaths[j]));
}
}
}
}
} else {
// In case one of the nodes is a root node
var nodeToCheck;
var paths;
if (aPaths.length == 0) {
nodeToCheck = a.name;
paths = bPaths;
} else {
nodeToCheck = b.name;
paths = aPaths;
}
for (i in paths) {
const path = paths[i];
if (nodeToCheck == path.slice(-1)[0]) {
console.log('Found match: ' + path + ', ' + nodeToCheck);
result.push(path);
}
}
}
return result;
}
var coefficientOfRelationship = function(a, b) {
console.log("\n");
var paths = findAncestorPaths(a, b);
var sum = 0;
console.log("\n");
for (const i in paths) {
const path = paths[i];
// Generations between a and b
const n = path.length - 1;
// Coefficients of inbreeding, default 1.0
const fa = a.f || 1.0;
const fo = b.f || 1.0;
const result = Math.pow(2, -n)*Math.pow((1+fa)/(1+fo), 1/2);
sum += result;
console.log("Calculating coefficient for n=" + n, "a=" + a.name, "b=" + b.name, "fa=" + fa, "fo=" + fo, "path=" + path, "result=" + result, "sum=" + sum);
}
return sum;
}
// = 0.0625
var testFigure8 = function() {
console.log('\n\n=== Running test figure 8 ===');
const a = {name: 'a', f: 1.0};
const b = {name: 'b', f: 1.0};
const c = {name: 'c', f: 1.0};
const d = {name: 'd', f: 1.0};
const e = {name: 'e', f: 1.0};
const f = {name: 'f', f: 1.0};
const g = {name: 'g', f: 1.0};
const h = {name: 'h', f: 1.0};
const i = {name: 'i', f: 1.0};
i.mother=g;
i.father=h;
g.mother=e;
g.father=f;
e.mother=c;
e.father=d;
c.mother=a;
c.father=b;
const persons = [a, b, c, d, e, f, g, h, i];
init(persons);
return coefficientOfRelationship(i, a);
}
// = 0.125
var testFigure9 = function() {
console.log('\n\n=== Running test figure 9 ===');
const a = {name: 'a', f: 1.0};
const b = {name: 'b', f: 1.0};
const c = {name: 'c', f: 1.0};
const d = {name: 'd', f: 1.0};
const e = {name: 'e', f: 1.0};
const f = {name: 'f', f: 1.0};
const g = {name: 'g', f: 1.0};
const h = {name: 'h', f: 1.0};
const i = {name: 'i', f: 1.0};
const j = {name: 'j', f: 1.0};
const k = {name: 'k', f: 1.0};
const l = {name: 'l', f: 1.0};
const m = {name: 'm', f: 1.0};
m.mother=k;
m.father=l;
k.mother=g;
k.father=h;
l.mother=i;
l.father=j;
h.mother=c;
h.father=d;
i.mother=e;
i.father=f;
d.mother=a;
d.father=b;
e.mother=a;
e.father=b;
const persons = [a, b, c, d, e, f, g, h, i, j, k, l, m];
init(persons);
return coefficientOfRelationship(m, a);
}
// = 0.125
var testFigure10 = function() {
console.log('\n\n=== Running test figure 10 ===');
const a = {name: 'a', f: 1.0};
const b = {name: 'b', f: 1.0};
const c = {name: 'c', f: 1.0};
const d = {name: 'd', f: 1.0};
const e = {name: 'e', f: 1.0};
const f = {name: 'f', f: 1.0};
const g = {name: 'g', f: 1.0};
const h = {name: 'h', f: 1.0};
g.mother=c;
g.father=d;
h.mother=e;
h.father=f;
d.mother=a;
d.father=b;
e.mother=a;
e.father=b;
const persons = [a, b, c, d, e, f, g, h];
init(persons);
return coefficientOfRelationship(g, h);
}
// = 0.5625
var testFigure11 = function() {
console.log('\n\n=== Running test figure 11 ===');
const a = {name: 'a', f: 1.0};
const b = {name: 'b', f: 1.0};
const c = {name: 'c', f: 1.0};
const d = {name: 'd', f: 1.0};
const e = {name: 'e', f: 1.0};
const f = {name: 'f', f: 1.0};
const g = {name: 'g', f: 1.0};
const h = {name: 'h', f: 1.0};
const i = {name: 'i', f: 1.0};
const j = {name: 'j', f: 1.0};
i.mother=g;
i.father=h;
j.mother=g;
j.father=h;
g.mother=c;
g.father=d;
h.mother=e;
h.father=f;
d.mother=a;
d.father=b;
e.mother=a;
e.father=b;
const persons = [a, b, c, d, e, f, g, h, i, j];
init(persons);
return coefficientOfRelationship(i, j);
}
// = 0.1875
var testFigure12 = function() {
console.log('\n\n=== Running test figure 12 ===');
const a = {name: 'a', f: 1.0};
const b = {name: 'b', f: 1.0};
const c = {name: 'c', f: 1.0};
const d = {name: 'd', f: 1.0};
const e = {name: 'e', f: 1.0};
const f = {name: 'f', f: 1.0};
const g = {name: 'g', f: 1.0};
const h = {name: 'h', f: 1.0};
const i = {name: 'i', f: 1.0};
const j = {name: 'j', f: 1.0};
const k = {name: 'k', f: 1.0};
const l = {name: 'l', f: 1.0};
const m = {name: 'm', f: 1.0};
m.mother=k;
m.father=l;
k.mother=g;
k.father=h;
l.mother=i;
l.father=j;
h.mother=c;
h.father=d;
i.mother=e;
i.father=f;
d.mother=a;
d.father=b;
e.mother=a;
e.father=b;
const persons = [a, b, c, d, e, f, g, h, i, j, k, l, m];
init(persons);
return coefficientOfRelationship(m, d);
}
console.log(testFigure8());
console.log(testFigure9());
console.log(testFigure10());
console.log(testFigure11());
console.log(testFigure12());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment