@datagrok-libraries/utils 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,272 @@
1
+ import * as umj from 'umap-js';
2
+ import {TSNE} from '@keckelt/tsne';
3
+
4
+ import {Options, DistanceMetric, Coordinates, Vector, Vectors, Matrix} from './type_declarations';
5
+ import {calcDistanceMatrix, transposeMatrix} from './operations';
6
+ import {SPEBase, PSPEBase} from './spe';
7
+ import {Measurer} from './string-measure';
8
+ //import {AlignedSequenceEncoder} from './sequence-encoder';
9
+
10
+ /**
11
+ * Abstract dimensionality reducer.
12
+ *
13
+ * @abstract
14
+ * @class Reducer
15
+ */
16
+ abstract class Reducer {
17
+ protected data: Vectors;
18
+
19
+ /**
20
+ * Creates an instance of Reducer.
21
+ * @param {Options} options Options to pass to the constructor.
22
+ * @memberof Reducer
23
+ */
24
+ constructor(options: Options) {
25
+ this.data = options.data;
26
+ }
27
+
28
+ /**
29
+ * Is to embed the data given into the two-dimensional space.
30
+ *
31
+ * @abstract
32
+ * @return {Coordinates} Cartesian coordinate of this embedding.
33
+ * @memberof Reducer
34
+ */
35
+ abstract transform(): Coordinates;
36
+ }
37
+
38
+ /**
39
+ * Implements t-SNE dimensionality reduction.
40
+ *
41
+ * @class TSNEReducer
42
+ * @extends {Reducer}
43
+ */
44
+ class TSNEReducer extends Reducer {
45
+ protected reducer: TSNE;
46
+ protected iterations: number;
47
+ protected distance: DistanceMetric;
48
+
49
+ /**
50
+ * Creates an instance of TSNEReducer.
51
+ * @param {Options} options Options to pass to the constructor.
52
+ * @memberof TSNEReducer
53
+ */
54
+ constructor(options: Options) {
55
+ super(options);
56
+ this.reducer = new TSNE(options);
57
+ this.iterations = options?.iterations ?? 100;
58
+ this.distance = options.distance;
59
+ }
60
+
61
+ /**
62
+ * Embeds the data given into the two-dimensional space using t-SNE method.
63
+ * @return {Coordinates} Cartesian coordinate of this embedding.
64
+ */
65
+ public transform(): Coordinates {
66
+ this.reducer.initDataDist(calcDistanceMatrix(this.data, this.distance));
67
+
68
+ for (let i = 0; i < this.iterations; ++i) {
69
+ this.reducer.step(); // every time you call this, solution gets better
70
+ }
71
+ return this.reducer.getSolution();
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Implements UMAP dimensionality reduction.
77
+ *
78
+ * @class UMAPReducer
79
+ * @extends {Reducer}
80
+ */
81
+ class UMAPReducer extends Reducer {
82
+ protected reducer: umj.UMAP;
83
+ /* protected encoder: AlignedSequenceEncoder;
84
+ protected distanceMatrix: Matrix;*/
85
+
86
+ /**
87
+ * Creates an instance of UMAPReducer.
88
+ * @param {Options} options Options to pass to the constructor.
89
+ * @memberof UMAPReducer
90
+ */
91
+ constructor(options: Options) {
92
+ super(options);
93
+ this.reducer = new umj.UMAP(options);
94
+ /* this.encoder = new AlignedSequenceEncoder();
95
+ this.distanceMatrix = calcDistanceMatrix(this.data, options.distanceFn);
96
+ this.distanceFn = options.distanceFn;*/
97
+ }
98
+
99
+ /* protected _encodedDistance(a: number[], b: number[]): number {
100
+ return this.distanceMatrix[a[a.length-1]][b[b.length-1]];
101
+ }
102
+
103
+ protected _decodedDistance(a: number[], b: number[]): number {
104
+ return this.encoder.decode(a);
105
+ }
106
+
107
+ protected encode(): number[][] {
108
+ let vectors: number[][] = [];
109
+
110
+ for (let i = 0; i < this.data.length; ++i) {
111
+ let encoded = this.encoder.encode(this.data[i]);
112
+ vectors.push(encoded);
113
+ }
114
+ return vectors;
115
+ }
116
+
117
+ protected _calcDistance() {
118
+ const nItems = this.data.length;
119
+ const vectors = this.encode();
120
+
121
+ }*/
122
+
123
+ /**
124
+ * Embeds the data given into the two-dimensional space using UMAP method.
125
+ * @return {Coordinates} Cartesian coordinate of this embedding.
126
+ */
127
+ public transform(): Coordinates {
128
+ const embedding = this.reducer.fit(this.data);
129
+
130
+ function arrayCast2Coordinates(data: number[][]): Coordinates {
131
+ return new Array(data.length).fill(0).map((_, i) => (Vector.from(data[i])));
132
+ }
133
+ return arrayCast2Coordinates(embedding);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Implements original SPE dimensionality reduction.
139
+ *
140
+ * @class SPEReducer
141
+ * @extends {Reducer}
142
+ */
143
+ class SPEReducer extends Reducer {
144
+ protected reducer: SPEBase;
145
+
146
+ /**
147
+ * Creates an instance of SPEReducer.
148
+ * @param {Options} options Options to pass to the constructor.
149
+ * @memberof SPEReducer
150
+ */
151
+ constructor(options: Options) {
152
+ super(options);
153
+ this.reducer = new SPEBase(options);
154
+ }
155
+
156
+ /**
157
+ * Embeds the data given into the two-dimensional space using the original SPE method.
158
+ * @return {Coordinates} Cartesian coordinate of this embedding.
159
+ */
160
+ public transform(): Coordinates {
161
+ return this.reducer.embed(this.data);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Implements modified SPE dimensionality reduction.
167
+ *
168
+ * @class PSPEReducer
169
+ * @extends {Reducer}
170
+ */
171
+ class PSPEReducer extends Reducer {
172
+ protected reducer: PSPEBase;
173
+
174
+ /**
175
+ * Creates an instance of PSPEReducer.
176
+ * @param {Options} options Options to pass to the constructor.
177
+ * @memberof PSPEReducer
178
+ */
179
+ constructor(options: Options) {
180
+ super(options);
181
+ this.reducer = new PSPEBase(options);
182
+ }
183
+
184
+ /**
185
+ * Embeds the data given into the two-dimensional space using the modified SPE method.
186
+ * @return {Coordinates} Cartesian coordinate of this embedding.
187
+ */
188
+ public transform(): Coordinates {
189
+ return this.reducer.embed(this.data);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Unified class implementing different dimensionality reduction methods.
195
+ *
196
+ * @export
197
+ * @class DimensionalityReducer
198
+ */
199
+ export class DimensionalityReducer {
200
+ private reducer: Reducer | undefined;
201
+ private methods: string[];
202
+ private measurer: Measurer;
203
+
204
+ /**
205
+ * Creates an instance of DimensionalityReducer.
206
+ * @param {any[]} data Vectors to embed.
207
+ * @param {string} method Embedding method to be applied
208
+ * @param {string} metric Distance metric to be computed between each of the vectors.
209
+ * @param {Options} [options] Options to pass to the implementing embedders.
210
+ * @memberof DimensionalityReducer
211
+ */
212
+ constructor(data: any[], method: string, metric: string, options?: Options) {
213
+ this.methods = ['UMAP', 'TSNE', 'SPE', 'PSPE'];
214
+
215
+ if (!this.availableMethods.includes(method)) {
216
+ throw new Error('The method "'+method+'" is not supported');
217
+ }
218
+
219
+ this.measurer = new Measurer(metric);
220
+ const measure = this.measurer.getMeasure()
221
+
222
+ if (method == 'UMAP') {
223
+ this.reducer = new UMAPReducer({
224
+ ...{data: data},
225
+ ...{distanceFn: measure},
226
+ ...{nEpochs: options?.cycles},
227
+ ...options
228
+ });
229
+ } else if (method == 'TSNE') {
230
+ this.reducer = new TSNEReducer({
231
+ ...{data: data},
232
+ ...{distance: measure},
233
+ ...{iterations: options?.cycles ?? undefined},
234
+ ...options,
235
+ });
236
+ } else if (method == 'SPE') {
237
+ this.reducer = new SPEReducer({...{data: data}, ...{distance: measure}, ...options});
238
+ } else {
239
+ this.reducer = new PSPEReducer({...{data: data}, ...{distance: measure}, ...options});
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Embeds the data given into the two-dimensional space using the chosen method.
245
+ *
246
+ * @param {boolean} transpose Whether to transform coordinates to have columns-first orientation.
247
+ * @throws {Error} If the embedding method was not found.
248
+ * @return {Coordinates} Cartesian coordinate of this embedding.
249
+ * @memberof DimensionalityReducer
250
+ */
251
+ public transform(transpose: boolean = false): Coordinates {
252
+ if (this.reducer == undefined) {
253
+ throw new Error('Reducer was not defined.');
254
+ }
255
+ let embedding = this.reducer.transform();
256
+
257
+ if (transpose) {
258
+ embedding = transposeMatrix(embedding);
259
+ }
260
+ return embedding;
261
+ }
262
+
263
+ /**
264
+ * Returns dimensionality reduction methods available.
265
+ *
266
+ * @readonly
267
+ * @memberof DimensionalityReducer
268
+ */
269
+ get availableMethods() {
270
+ return this.methods;
271
+ }
272
+ }
@@ -0,0 +1,116 @@
1
+ import { assert } from './operations';
2
+ /**
3
+ * Class to categorial encode/decode aligned amino acid residues sequence.
4
+ *
5
+ * @export
6
+ * @class AlignedSequenceEncoder
7
+ */
8
+ export class AlignedSequenceEncoder {
9
+ constructor() {
10
+ this.aa2num = {
11
+ '-': 0,
12
+ 'A': 1,
13
+ 'C': 2,
14
+ 'D': 3,
15
+ 'E': 4,
16
+ 'F': 5,
17
+ 'G': 6,
18
+ 'H': 7,
19
+ 'I': 8,
20
+ 'K': 9,
21
+ 'L': 10,
22
+ 'M': 11,
23
+ 'N': 12,
24
+ 'P': 13,
25
+ 'Q': 14,
26
+ 'R': 15,
27
+ 'S': 16,
28
+ 'T': 17,
29
+ 'V': 18,
30
+ 'W': 19,
31
+ 'Y': 20,
32
+ };
33
+ this.num2aa = {};
34
+ Object.entries(this.aa2num).forEach(([k, v]) => (this.num2aa[v] = k));
35
+ }
36
+ /**
37
+ * Truncate NH2 and -COOH terminals of the given sequence.
38
+ *
39
+ * @protected
40
+ * @param {string} seq The sequence provided.
41
+ * @return {string} Truncated sequence.
42
+ * @memberof AlignedSequenceEncoder
43
+ */
44
+ _truncateSequence(seq) {
45
+ let start = 0;
46
+ let end = seq.length;
47
+ const termina = ['NH2', 'COOH'];
48
+ if (seq.startsWith(termina[0])) {
49
+ const l = termina[0].length; // Cut only 'NH2' without following '-'.
50
+ assert(seq[l] == '-', `Wrong sequence format: ${termina[0]} without following '-' in '${seq}'.`);
51
+ start = l;
52
+ }
53
+ if (seq.endsWith(termina[1])) {
54
+ const l = termina[1].length + 1; // Cut both 'COOH' and precending '-'.
55
+ assert(seq[end - l] == '-', `Wrong sequence format: ${termina[1]} without '-' precending in '${seq}'.`);
56
+ end -= l;
57
+ }
58
+ return seq.substring(start, end);
59
+ }
60
+ /**
61
+ * Cuts auxiliary defises before a residue.
62
+ *
63
+ * @protected
64
+ * @param {string} seq The sequence to process.
65
+ * @return {string} Processed sequence.
66
+ * @memberof AlignedSequenceEncoder
67
+ */
68
+ _dropDefises(seq) {
69
+ return seq.replace(/(-)([^-]+)/g, '$2');
70
+ }
71
+ /**
72
+ * Performs truncation and cutting auxiliary defises.
73
+ *
74
+ * @param {string} sequence The sequence work under process.
75
+ * @return {string} Result of cleaning.
76
+ * @memberof AlignedSequenceEncoder
77
+ */
78
+ clean(sequence) {
79
+ return this._dropDefises(this._truncateSequence(sequence));
80
+ }
81
+ /**
82
+ * Categorial encode of the sequence provided.
83
+ *
84
+ * @param {string} sequence The sequence.
85
+ * @return {number[]} Encoded vector.
86
+ * @memberof AlignedSequenceEncoder
87
+ */
88
+ encode(sequence) {
89
+ const seq = this.clean(sequence);
90
+ const nItems = seq.length;
91
+ let values = new Array(nItems).fill(0);
92
+ for (let i = 0; i < nItems; ++i) {
93
+ let char = seq[i];
94
+ assert(char in this.aa2num, `Unknown char '${char}' found in sequence '${seq}'`);
95
+ values[i] = this.aa2num[char];
96
+ }
97
+ return values;
98
+ }
99
+ /**
100
+ * Decode the encoded vector into the sequence back.
101
+ *
102
+ * @param {number[]} value The vector encoded.
103
+ * @return {string} Decoded sequence.
104
+ * @memberof AlignedSequenceEncoder
105
+ */
106
+ decode(value) {
107
+ let s = '';
108
+ for (let i = 0; i < value.length; ++i) {
109
+ let code = value[i];
110
+ assert(code in this.num2aa, `Unknown code '${code}' found in vector '${value}'`);
111
+ s += this.num2aa[code];
112
+ }
113
+ return s;
114
+ }
115
+ }
116
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVuY2UtZW5jb2Rlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNlcXVlbmNlLWVuY29kZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLE1BQU0sRUFBQyxNQUFNLGNBQWMsQ0FBQztBQUVwQzs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxzQkFBc0I7SUFJL0I7UUFDSSxJQUFJLENBQUMsTUFBTSxHQUFHO1lBQ1YsR0FBRyxFQUFFLENBQUM7WUFDTixHQUFHLEVBQUUsQ0FBQztZQUNOLEdBQUcsRUFBRSxDQUFDO1lBQ04sR0FBRyxFQUFFLENBQUM7WUFDTixHQUFHLEVBQUUsQ0FBQztZQUNOLEdBQUcsRUFBRSxDQUFDO1lBQ04sR0FBRyxFQUFFLENBQUM7WUFDTixHQUFHLEVBQUUsQ0FBQztZQUNOLEdBQUcsRUFBRSxDQUFDO1lBQ04sR0FBRyxFQUFFLENBQUM7WUFDTixHQUFHLEVBQUUsRUFBRTtZQUNQLEdBQUcsRUFBRSxFQUFFO1lBQ1AsR0FBRyxFQUFFLEVBQUU7WUFDUCxHQUFHLEVBQUUsRUFBRTtZQUNQLEdBQUcsRUFBRSxFQUFFO1lBQ1AsR0FBRyxFQUFFLEVBQUU7WUFDUCxHQUFHLEVBQUUsRUFBRTtZQUNQLEdBQUcsRUFBRSxFQUFFO1lBQ1AsR0FBRyxFQUFFLEVBQUU7WUFDUCxHQUFHLEVBQUUsRUFBRTtZQUNQLEdBQUcsRUFBRSxFQUFFO1NBQ1YsQ0FBQztRQUNGLElBQUksQ0FBQyxNQUFNLEdBQUcsRUFBRSxDQUFDO1FBQ2pCLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRSxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNPLGlCQUFpQixDQUFDLEdB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LENBQUM7U0FDMUI7UUFDRCxPQUFPLENBQUMsQ0FBQztJQUNiLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7YXNzZXJ0fSBmcm9tICcuL29wZXJhdGlvbnMnO1xyXG5cclxuLyoqXHJcbiAqIENsYXNzIHRvIGNhdGVnb3JpYWwgZW5jb2RlL2RlY29kZSBhbGlnbmVkIGFtaW5vIGFjaWQgcmVzaWR1ZXMgc2VxdWVuY2UuXHJcbiAqXHJcbiAqIEBleHBvcnRcclxuICogQGNsYXNzIEFsaWduZWRTZXF1ZW5jZUVuY29kZXJcclxuICovXHJcbmV4cG9ydCBjbGFzcyBBbGlnbmVkU2VxdWVuY2VFbmNvZGVyIHtcclxuICAgIHByb3RlY3RlZCBhYTJudW06IHtbbmFtZTogc3RyaW5nXTogbnVtYmVyfTtcclxuICAgIHByb3RlY3RlZCBudW0yYWE6IHtbY29kZTogbnVtYmVyXTogc3RyaW5nfTtcclxuXHJcbiAgICBjb25zdHJ1Y3RvciAoKSB7XHJcbiAgICAgICAgdGhpcy5hYTJudW0gPSB7XHJcbiAgICAgICAgICAgICctJzogMCxcclxuICAgICAgICAgICAgJ0EnOiAxLFxyXG4gICAgICAgICAgICAnQyc6IDIsXHJcbiAgICAgICAgICAgICdEJzogMyxcclxuICAgICAgICAgICAgJ0UnOiA0LFxyXG4gICAgICAgICAgICAnRic6IDUsXHJcbiAgICAgICAgICAgICdHJzogNixcclxuICAgICAgICAgICAgJ0gnOiA3LFxyXG4gICAgICAgICAgICAnSSc6IDgsXHJcbiAgICAgICAgICAgICdLJzogOSxcclxuICAgICAgICAgICAgJ0wnOiAxMCxcclxuICAgICAgICAgICAgJ00nOiAxMSxcclxuICAgICAgICAgICAgJ04nOiAxMixcclxuICAgICAgICAgICAgJ1AnOiAxMyxcclxuICAgICAgICAgICAgJ1EnOiAxNCxcclxuICAgICAgICAgICAgJ1InOiAxNSxcclxuICAgICAgICAgICAgJ1MnOiAxNixcclxuICAgICAgICAgICAgJ1QnOiAxNyxcclxuICAgICAgICAgICAgJ1YnOiAxOCxcclxuICAgICAgICAgICAgJ1cnOiAxOSxcclxuICAgICAgICAgICAgJ1knOiAyMCxcclxuICAgICAgICB9O1xyXG4gICAgICAgIHRoaXMubnVtMmFhID0ge307XHJcbiAgICAgICAgT2JqZWN0LmVudHJpZXModGhpcy5hYTJudW0pLmZvckVhY2goKFtrLCB2XSkgPT4gKHRoaXMubnVtMmFhW3ZdID0gaykpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVHJ1bmNhdGUgTkgyIGFuZCAtQ09PSCB0ZXJtaW5hbHMgb2YgdGhlIGdpdmVuIHNlcXVlbmNlLlxyXG4gICAgICpcclxuICAgICAqIEBwcm90ZWN0ZWRcclxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBzZXEgVGhlIHNlcXVlbmNlIHByb3ZpZGVkLlxyXG4gICAgICogQHJldHVybiB7c3RyaW5nfSBUcnVuY2F0ZWQgc2VxdWVuY2UuXHJcbiAgICAgKiBAbWVtYmVyb2YgQWxpZ25lZFNlcXVlbmNlRW5jb2RlclxyXG4gICAgICovXHJcbiAgICBwcm90ZWN0ZWQgX3RydW5jYXRlU2VxdWVuY2Uoc2VxOiBzdHJpbmcpOiBzdHJpbmcge1xyXG4gICAgICAgIGxldCBzdGFydCA9IDA7XHJcbiAgICAgICAgbGV0IGVuZCA9IHNlcS5sZW5ndGg7XHJcbiAgICAgICAgY29uc3QgdGVybWluYSA9IFsnTkgyJywgJ0NPT0gnXTtcclxuICAgIFxyXG4gICAgICAgIGlmIChzZXEuc3RhcnRzV2l0aCh0ZXJtaW5hWzBdKSkge1xyXG4gICAgICAgICAgICBjb25zdCBsID0gdGVybWluYVswXS5sZW5ndGg7IC8vIEN1dCBvbmx5ICdOSDInIHdpdGhvdXQgZm9sbG93aW5nICctJy5cclxuICAgICAgICAgICAgYXNzZXJ0KHNlcVtsXSA9PSAnLScsIGBXcm9uZyBzZXF1ZW5jZSBmb3JtYXQ6ICR7dGVybWluYVswXX0gd2l0aG91dCBmb2xsb3dpbmcgJy0nIGluICcke3NlcX0nLmApXHJcbiAgICAgICAgICAgIHN0YXJ0ID0gbDtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKHNlcS5lbmRzV2l0aCh0ZXJtaW5hWzFdKSkge1xyXG4gICAgICAgICAgICBjb25zdCBsID0gdGVybWluYVsxXS5sZW5ndGgrMTsgLy8gQ3V0IGJvdGggJ0NPT0gnIGFuZCBwcmVjZW5kaW5nICctJy5cclxuICAgICAgICAgICAgYXNzZXJ0KHNlcVtlbmQtbF0gPT0gJy0nLCBgV3Jvbmcgc2VxdWVuY2UgZm9ybWF0OiAke3Rlcm1pbmFbMV19IHdpdGhvdXQgJy0nIHByZWNlbmRpbmcgaW4gJyR7c2VxfScuYClcclxuICAgICAgICAgICAgZW5kIC09IGw7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBzZXEuc3Vic3RyaW5nKHN0YXJ0LCBlbmQpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQ3V0cyBhdXhpbGlhcnkgZGVmaXNlcyBiZWZvcmUgYSByZXNpZHVlLlxyXG4gICAgICpcclxuICAgICAqIEBwcm90ZWN0ZWRcclxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBzZXEgVGhlIHNlcXVlbmNlIHRvIHByb2Nlc3MuXHJcbiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IFByb2Nlc3NlZCBzZXF1ZW5jZS5cclxuICAgICAqIEBtZW1iZXJvZiBBbGlnbmVkU2VxdWVuY2VFbmNvZGVyXHJcbiAgICAgKi9cclxuICAgIHByb3RlY3RlZCBfZHJvcERlZmlzZXMoc2VxOiBzdHJpbmcpOiBzdHJpbmcge1xyXG4gICAgICAgIHJldHVybiBzZXEucmVwbGFjZSgvKC0pKFteLV0rKS9nLCAnJDInKVxyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogUGVyZm9ybXMgdHJ1bmNhdGlvbiBhbmQgY3V0dGluZyBhdXhpbGlhcnkgZGVmaXNlcy5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gc2VxdWVuY2UgVGhlIHNlcXVlbmNlIHdvcmsgdW5kZXIgcHJvY2Vzcy5cclxuICAgICAqIEByZXR1cm4ge3N0cmluZ30gUmVzdWx0IG9mIGNsZWFuaW5nLlxyXG4gICAgICogQG1lbWJlcm9mIEFsaWduZWRTZXF1ZW5jZUVuY29kZXJcclxuICAgICAqL1xyXG4gICAgcHVibGljIGNsZWFuKHNlcXVlbmNlOiBzdHJpbmcpOiBzdHJpbmcge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9kcm9wRGVmaXNlcyh0aGlzLl90cnVuY2F0ZVNlcXVlbmNlKHNlcXVlbmNlKSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBDYXRlZ29yaWFsIGVuY29kZSBvZiB0aGUgc2VxdWVuY2UgcHJvdmlkZWQuXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHNlcXVlbmNlIFRoZSBzZXF1ZW5jZS5cclxuICAgICAqIEByZXR1cm4ge251bWJlcltdfSBFbmNvZGVkIHZlY3Rvci5cclxuICAgICAqIEBtZW1iZXJvZiBBbGlnbmVkU2VxdWVuY2VFbmNvZGVyXHJcbiAgICAgKi9cclxuICAgIHB1YmxpYyBlbmNvZGUoc2VxdWVuY2U6IHN0cmluZyk6IG51bWJlcltdIHtcclxuICAgICAgICBjb25zdCBzZXEgPSB0aGlzLmNsZWFuKHNlcXVlbmNlKTtcclxuICAgICAgICBjb25zdCBuSXRlbXMgPSBzZXEubGVuZ3RoO1xyXG4gICAgICAgIGxldCB2YWx1ZXMgPSBuZXcgQXJyYXkobkl0ZW1zKS5maWxsKDApO1xyXG4gICAgXHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuSXRlbXM7ICsraSkge1xyXG4gICAgICAgICAgICBsZXQgY2hhciA9IHNlcVtpXTtcclxuICAgIFxyXG4gICAgICAgICAgICBhc3NlcnQoY2hhciBpbiB0aGlzLmFhMm51bSwgYFVua25vd24gY2hhciAnJHtjaGFyfScgZm91bmQgaW4gc2VxdWVuY2UgJyR7c2VxfSdgKTtcclxuICAgIFxyXG4gICAgICAgICAgICB2YWx1ZXNbaV0gPSB0aGlzLmFhMm51bVtjaGFyXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHZhbHVlcztcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIERlY29kZSB0aGUgZW5jb2RlZCB2ZWN0b3IgaW50byB0aGUgc2VxdWVuY2UgYmFjay5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge251bWJlcltdfSB2YWx1ZSBUaGUgdmVjdG9yIGVuY29kZWQuXHJcbiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IERlY29kZWQgc2VxdWVuY2UuXHJcbiAgICAgKiBAbWVtYmVyb2YgQWxpZ25lZFNlcXVlbmNlRW5jb2RlclxyXG4gICAgICovXHJcbiAgICBwdWJsaWMgZGVjb2RlKHZhbHVlOiBudW1iZXJbXSk6IHN0cmluZyB7XHJcbiAgICAgICAgbGV0IHM6IHN0cmluZyA9ICcnO1xyXG4gICAgXHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZS5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgICAgICBsZXQgY29kZSA9IHZhbHVlW2ldO1xyXG5cclxuICAgICAgICAgICAgYXNzZXJ0KGNvZGUgaW4gdGhpcy5udW0yYWEsIGBVbmtub3duIGNvZGUgJyR7Y29kZX0nIGZvdW5kIGluIHZlY3RvciAnJHt2YWx1ZX0nYCk7XHJcblxyXG4gICAgICAgICAgICBzICs9IHRoaXMubnVtMmFhW2NvZGVdO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gcztcclxuICAgIH1cclxufVxyXG4iXX0=
@@ -0,0 +1,131 @@
1
+ import {assert} from './operations';
2
+
3
+ /**
4
+ * Class to categorial encode/decode aligned amino acid residues sequence.
5
+ *
6
+ * @export
7
+ * @class AlignedSequenceEncoder
8
+ */
9
+ export class AlignedSequenceEncoder {
10
+ protected aa2num: {[name: string]: number};
11
+ protected num2aa: {[code: number]: string};
12
+
13
+ constructor () {
14
+ this.aa2num = {
15
+ '-': 0,
16
+ 'A': 1,
17
+ 'C': 2,
18
+ 'D': 3,
19
+ 'E': 4,
20
+ 'F': 5,
21
+ 'G': 6,
22
+ 'H': 7,
23
+ 'I': 8,
24
+ 'K': 9,
25
+ 'L': 10,
26
+ 'M': 11,
27
+ 'N': 12,
28
+ 'P': 13,
29
+ 'Q': 14,
30
+ 'R': 15,
31
+ 'S': 16,
32
+ 'T': 17,
33
+ 'V': 18,
34
+ 'W': 19,
35
+ 'Y': 20,
36
+ };
37
+ this.num2aa = {};
38
+ Object.entries(this.aa2num).forEach(([k, v]) => (this.num2aa[v] = k));
39
+ }
40
+
41
+ /**
42
+ * Truncate NH2 and -COOH terminals of the given sequence.
43
+ *
44
+ * @protected
45
+ * @param {string} seq The sequence provided.
46
+ * @return {string} Truncated sequence.
47
+ * @memberof AlignedSequenceEncoder
48
+ */
49
+ protected _truncateSequence(seq: string): string {
50
+ let start = 0;
51
+ let end = seq.length;
52
+ const termina = ['NH2', 'COOH'];
53
+
54
+ if (seq.startsWith(termina[0])) {
55
+ const l = termina[0].length; // Cut only 'NH2' without following '-'.
56
+ assert(seq[l] == '-', `Wrong sequence format: ${termina[0]} without following '-' in '${seq}'.`)
57
+ start = l;
58
+ }
59
+ if (seq.endsWith(termina[1])) {
60
+ const l = termina[1].length+1; // Cut both 'COOH' and precending '-'.
61
+ assert(seq[end-l] == '-', `Wrong sequence format: ${termina[1]} without '-' precending in '${seq}'.`)
62
+ end -= l;
63
+ }
64
+ return seq.substring(start, end);
65
+ }
66
+
67
+ /**
68
+ * Cuts auxiliary defises before a residue.
69
+ *
70
+ * @protected
71
+ * @param {string} seq The sequence to process.
72
+ * @return {string} Processed sequence.
73
+ * @memberof AlignedSequenceEncoder
74
+ */
75
+ protected _dropDefises(seq: string): string {
76
+ return seq.replace(/(-)([^-]+)/g, '$2')
77
+ }
78
+
79
+ /**
80
+ * Performs truncation and cutting auxiliary defises.
81
+ *
82
+ * @param {string} sequence The sequence work under process.
83
+ * @return {string} Result of cleaning.
84
+ * @memberof AlignedSequenceEncoder
85
+ */
86
+ public clean(sequence: string): string {
87
+ return this._dropDefises(this._truncateSequence(sequence));
88
+ }
89
+
90
+ /**
91
+ * Categorial encode of the sequence provided.
92
+ *
93
+ * @param {string} sequence The sequence.
94
+ * @return {number[]} Encoded vector.
95
+ * @memberof AlignedSequenceEncoder
96
+ */
97
+ public encode(sequence: string): number[] {
98
+ const seq = this.clean(sequence);
99
+ const nItems = seq.length;
100
+ let values = new Array(nItems).fill(0);
101
+
102
+ for (let i = 0; i < nItems; ++i) {
103
+ let char = seq[i];
104
+
105
+ assert(char in this.aa2num, `Unknown char '${char}' found in sequence '${seq}'`);
106
+
107
+ values[i] = this.aa2num[char];
108
+ }
109
+ return values;
110
+ }
111
+
112
+ /**
113
+ * Decode the encoded vector into the sequence back.
114
+ *
115
+ * @param {number[]} value The vector encoded.
116
+ * @return {string} Decoded sequence.
117
+ * @memberof AlignedSequenceEncoder
118
+ */
119
+ public decode(value: number[]): string {
120
+ let s: string = '';
121
+
122
+ for (let i = 0; i < value.length; ++i) {
123
+ let code = value[i];
124
+
125
+ assert(code in this.num2aa, `Unknown code '${code}' found in vector '${value}'`);
126
+
127
+ s += this.num2aa[code];
128
+ }
129
+ return s;
130
+ }
131
+ }