UNPKG

5.15 kBJavaScriptView Raw
1'use strict';
2
3
4/*
5 trees - a module for trees in gramene
6
7 */
8var TreeModel = require('tree-model');
9var FlatToNested = require('flat-to-nested');
10var _ = require('lodash');
11var treemodelExtensions = require('./treemodelExtensions');
12
13module.exports = {
14 tree: function (taxonomy) {
15 function cleanUpProperties(taxonomy) {
16 return taxonomy.map(function (taxon) {
17 if (!taxon.is_a) {
18 if (taxon._id !== 1) {
19 throw new Error('unrooted node!');
20 }
21 }
22 if (taxon.hasOwnProperty('property_value')) {
23 taxon.rank = taxon.property_value.replace(/has_rank NCBITaxon:/,'');
24 }
25 return {
26 id: taxon._id,
27 parent: taxon.is_a ? taxon.is_a[0] : undefined,
28 rank: taxon.rank,
29 name: taxon.name,
30 synonyms: taxon.synonym || [],
31 geneCount: taxon.num_genes
32 };
33 });
34 }
35
36 function createTree(nestedTaxa) {
37 function childNodeNameLexComparator(a, b) {
38 return a.name > b.name ? 1 : -1;
39 }
40 function childNodeGeneCountComparator(a, b) {
41 return a.geneCount < b.geneCount ? 1 : -1;
42 }
43
44 return new TreeModel({modelComparatorFn: childNodeGeneCountComparator}).parse(nestedTaxa);
45 }
46
47 function compressTreePaths(tree) {
48 tree.all(function (node) {
49 return !node.isRoot() && node.children.length === 1;
50 }).forEach(function (node) {
51 var parent = node.parent
52 , child = node.children[0];
53
54 parent.addChild(child);
55 node.drop();
56
57 // maintain the link from the compressed node to the parent
58 // (this is deleted in call to node.drop())
59 node.parent = parent;
60 node.compressed = true;
61
62 // Add compressed nodes to child and not parent
63 if (!child.compressedNodes) {
64 child.compressedNodes = [];
65 }
66 child.compressedNodes.push(node);
67 if (node.compressedNodes) {
68 child.compressedNodes = node.compressedNodes.concat(child.compressedNodes);
69 delete node.compressedNodes;
70 }
71 });
72 }
73
74 //function decorateTree(tree) {
75 // tree.lca = function lowestCommonAncestor(nodes) {
76 // var parentNodesInCommon = _.chain(nodes)
77 // .map(function (node) {
78 // return node.getPath();
79 // })
80 // .reduce(function (acc, nextPath) {
81 // return _.intersection(acc, nextPath)
82 // })
83 // .value();
84 // return parentNodesInCommon.pop();
85 // };
86 //
87 // tree.pathBetween = function pathBetweenNodes(from, to) {
88 //
89 // var lca, fromPath, toPath, fromLcaIdx, toLcaIdx, pathBetween;
90 //
91 // // find the lowest commen ancestor
92 // lca = tree.lca([from, to]);
93 //
94 // // get the full path from -> root, and reverse it
95 // fromPath = _(from.getPath().reverse());
96 //
97 // // get the full path to -> root
98 // toPath = _(to.getPath());
99 //
100 // // find the index of lca in fromPath and toPath
101 // fromLcaIdx = fromPath.findIndex(lca);
102 // toLcaIdx = toPath.findIndex(lca);
103 //
104 // // slice and combine the arrays to get the path between
105 // pathBetween = fromPath.slice(0, fromLcaIdx).concat(toPath.slice(toLcaIdx).value());
106 //
107 // return pathBetween.value();
108 // };
109 //}
110 //
111 //function addPrototypeDecorations(tree) {
112 // var prototree = Object.getPrototypeOf(tree);
113 //
114 // prototree.depth = function calculateEffectiveNodeDepth(includeCompressedNodes) {
115 // var path = this.getPath()
116 // , depth = path.length - 1
117 // , compressedDepth;
118 //
119 // if(includeCompressedNodes) {
120 // compressedDepth = _.reduce(path, function (acc, n) {
121 // return acc + (n.compressedNodes ? n.compressedNodes.length : 0);
122 // }, 0);
123 // depth += compressedDepth;
124 // }
125 //
126 // return depth;
127 // };
128 //
129 // prototree.pathTo = function(to) {
130 // return tree.pathBetween(this, to);
131 // };
132 //
133 // prototree.leafNodes = function findAllLeafNodes() {
134 // return this.all(function (node) { return !node.hasChildren(); });
135 // };
136 //
137 // prototree.lcaWith = function(otherNodes) {
138 // var nodes = _.clone(otherNodes);
139 // nodes.push(this);
140 // return tree.lca(nodes);
141 // }
142 //}
143 //
144 //function indexTree(tree, attrs) {
145 // tree.indices = _.chain(attrs)
146 // .map(function (attr) {
147 // var result = {_attr: attr};
148 // tree.walk(function (node) {
149 // result[node.model[attr]] = node;
150 // });
151 // return result;
152 // })
153 // .indexBy('_attr')
154 // .value();
155 //}
156
157 var taxa
158 , nestedTaxa
159 , tree;
160
161 taxa = cleanUpProperties(taxonomy);
162 nestedTaxa = new FlatToNested().convert(taxa);
163 tree = createTree(nestedTaxa);
164 treemodelExtensions.indexTree(tree, ['id', 'name']);
165 compressTreePaths(tree);
166 treemodelExtensions.decorateTree(tree);
167 treemodelExtensions.addPrototypeDecorations(tree);
168
169 return tree;
170 }
171};
\No newline at end of file