copulas 0.10.1.dev0__py3-none-any.whl

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.

Potentially problematic release.


This version of copulas might be problematic. Click here for more details.

@@ -0,0 +1,693 @@
1
+ """Multivariate trees module."""
2
+
3
+ import logging
4
+ from enum import Enum
5
+
6
+ import numpy as np
7
+ import scipy
8
+
9
+ from copulas import EPSILON, get_qualified_name
10
+ from copulas.bivariate.base import Bivariate
11
+ from copulas.multivariate.base import Multivariate
12
+
13
+ LOGGER = logging.getLogger(__name__)
14
+
15
+
16
+ class TreeTypes(Enum):
17
+ """The available types of trees."""
18
+
19
+ CENTER = 0
20
+ DIRECT = 1
21
+ REGULAR = 2
22
+
23
+
24
+ class Tree(Multivariate):
25
+ """Helper class to instantiate a single tree in the vine model."""
26
+
27
+ tree_type = None
28
+ fitted = False
29
+
30
+ def fit(self, index, n_nodes, tau_matrix, previous_tree, edges=None):
31
+ """Fit this tree object.
32
+
33
+ Args:
34
+ index (int):
35
+ index of the tree.
36
+ n_nodes (int):
37
+ number of nodes in the tree.
38
+ tau_matrix (numpy.array):
39
+ kendall's tau matrix of the data, shape (n_nodes, n_nodes).
40
+ previous_tree (Tree):
41
+ tree object of previous level.
42
+ """
43
+ self.level = index + 1
44
+ self.n_nodes = n_nodes
45
+ self.tau_matrix = tau_matrix
46
+ self.previous_tree = previous_tree
47
+ self.edges = edges or []
48
+
49
+ if not self.edges:
50
+ if self.level == 1:
51
+ self.u_matrix = previous_tree
52
+ self._build_first_tree()
53
+
54
+ else:
55
+ self._build_kth_tree()
56
+
57
+ self.prepare_next_tree()
58
+
59
+ self.fitted = True
60
+
61
+ def _check_constraint(self, edge1, edge2):
62
+ """Check if two edges satisfy vine constraint.
63
+
64
+ Args:
65
+ edge1 (Edge):
66
+ edge object representing edge1
67
+ edge2 (Edge):
68
+ edge object representing edge2
69
+
70
+ Returns:
71
+ bool:
72
+ True if the two edges satisfy vine constraints
73
+ """
74
+ full_node = {edge1.L, edge1.R, edge2.L, edge2.R}
75
+ full_node.update(edge1.D)
76
+ full_node.update(edge2.D)
77
+ return len(full_node) == (self.level + 1)
78
+
79
+ def _get_constraints(self):
80
+ """Get neighboring edges for each edge in the edges."""
81
+ num_edges = len(self.edges)
82
+ for k in range(num_edges):
83
+ for i in range(num_edges):
84
+ # add to constraints if i shared an edge with k
85
+ if k != i and self.edges[k].is_adjacent(self.edges[i]):
86
+ self.edges[k].neighbors.append(i)
87
+
88
+ def _sort_tau_by_y(self, y):
89
+ """Sort tau matrix by dependece with variable y.
90
+
91
+ Args:
92
+ y (int):
93
+ index of variable of intrest
94
+
95
+ Returns:
96
+ numpy.ndarray:
97
+ sorted tau matrix.
98
+ """
99
+ # first column is the variable of interest
100
+ tau_y = self.tau_matrix[:, y]
101
+ tau_y[y] = np.NaN
102
+
103
+ temp = np.empty([self.n_nodes, 3])
104
+ temp[:, 0] = np.arange(self.n_nodes)
105
+ temp[:, 1] = tau_y
106
+ temp[:, 2] = abs(tau_y)
107
+ temp[np.isnan(temp)] = -10
108
+ sort_temp = temp[:, 2].argsort()[::-1]
109
+ tau_sorted = temp[sort_temp]
110
+
111
+ return tau_sorted
112
+
113
+ def get_tau_matrix(self):
114
+ """Get tau matrix for adjacent pairs.
115
+
116
+ Returns:
117
+ tau (numpy.ndarray):
118
+ tau matrix for the current tree
119
+ """
120
+ num_edges = len(self.edges)
121
+ tau = np.empty([num_edges, num_edges])
122
+
123
+ for i in range(num_edges):
124
+ edge = self.edges[i]
125
+ for j in edge.neighbors:
126
+ if self.level == 1:
127
+ left_u = self.u_matrix[:, edge.L]
128
+ right_u = self.u_matrix[:, edge.R]
129
+
130
+ else:
131
+ left_parent, right_parent = edge.parents
132
+ left_u, right_u = Edge.get_conditional_uni(left_parent, right_parent)
133
+
134
+ tau[i, j], pvalue = scipy.stats.kendalltau(left_u, right_u)
135
+
136
+ return tau
137
+
138
+ def get_adjacent_matrix(self):
139
+ """Get adjacency matrix.
140
+
141
+ Returns:
142
+ numpy.ndarray:
143
+ adjacency matrix
144
+ """
145
+ edges = self.edges
146
+ num_edges = len(edges) + 1
147
+ adj = np.zeros([num_edges, num_edges])
148
+
149
+ for k in range(num_edges - 1):
150
+ adj[edges[k].L, edges[k].R] = 1
151
+ adj[edges[k].R, edges[k].L] = 1
152
+
153
+ return adj
154
+
155
+ def prepare_next_tree(self):
156
+ """Prepare conditional U matrix for next tree."""
157
+ for edge in self.edges:
158
+ copula_theta = edge.theta
159
+
160
+ if self.level == 1:
161
+ left_u = self.u_matrix[:, edge.L]
162
+ right_u = self.u_matrix[:, edge.R]
163
+
164
+ else:
165
+ left_parent, right_parent = edge.parents
166
+ left_u, right_u = Edge.get_conditional_uni(left_parent, right_parent)
167
+
168
+ # compute conditional cdfs C(i|j) = dC(i,j)/duj and dC(i,j)/du
169
+ left_u = [x for x in left_u if x is not None]
170
+ right_u = [x for x in right_u if x is not None]
171
+ X_left_right = np.array([[x, y] for x, y in zip(left_u, right_u)])
172
+ X_right_left = np.array([[x, y] for x, y in zip(right_u, left_u)])
173
+
174
+ copula = Bivariate(copula_type=edge.name)
175
+ copula.theta = copula_theta
176
+ left_given_right = copula.partial_derivative(X_left_right)
177
+ right_given_left = copula.partial_derivative(X_right_left)
178
+
179
+ # correction of 0 or 1
180
+ left_given_right[left_given_right == 0] = EPSILON
181
+ right_given_left[right_given_left == 0] = EPSILON
182
+ left_given_right[left_given_right == 1] = 1 - EPSILON
183
+ right_given_left[right_given_left == 1] = 1 - EPSILON
184
+ edge.U = np.array([left_given_right, right_given_left])
185
+
186
+ def get_likelihood(self, uni_matrix):
187
+ """Compute likelihood of the tree given an U matrix.
188
+
189
+ Args:
190
+ uni_matrix (numpy.array):
191
+ univariate matrix to evaluate likelihood on.
192
+
193
+ Returns:
194
+ tuple[float, numpy.array]:
195
+ likelihood of the current tree, next level conditional univariate matrix
196
+ """
197
+ uni_dim = uni_matrix.shape[1]
198
+ num_edge = len(self.edges)
199
+ values = np.zeros([1, num_edge])
200
+ new_uni_matrix = np.empty([uni_dim, uni_dim])
201
+
202
+ for i in range(num_edge):
203
+ edge = self.edges[i]
204
+ value, left_u, right_u = edge.get_likelihood(uni_matrix)
205
+ new_uni_matrix[edge.L, edge.R] = left_u
206
+ new_uni_matrix[edge.R, edge.L] = right_u
207
+ values[0, i] = np.log(value)
208
+
209
+ return np.sum(values), new_uni_matrix
210
+
211
+ def __str__(self):
212
+ """Produce printable representation of the class."""
213
+ template = 'L:{} R:{} D:{} Copula:{} Theta:{}'
214
+ return '\n'.join([
215
+ template.format(edge.L, edge.R, edge.D, edge.name, edge.theta)
216
+ for edge in self.edges
217
+ ])
218
+
219
+ def _serialize_previous_tree(self):
220
+ if self.level == 1:
221
+ return self.previous_tree.tolist()
222
+
223
+ return None
224
+
225
+ @classmethod
226
+ def _deserialize_previous_tree(cls, tree_dict, previous):
227
+ if tree_dict['level'] == 1:
228
+ return np.array(tree_dict['previous_tree'])
229
+
230
+ return previous
231
+
232
+ def to_dict(self):
233
+ """Return a `dict` with the parameters to replicate this Tree.
234
+
235
+ Returns:
236
+ dict:
237
+ Parameters of this Tree.
238
+ """
239
+ fitted = self.fitted
240
+ result = {
241
+ 'tree_type': self.tree_type,
242
+ 'type': get_qualified_name(self),
243
+ 'fitted': fitted
244
+ }
245
+
246
+ if not fitted:
247
+ return result
248
+
249
+ result.update({
250
+ 'level': self.level,
251
+ 'n_nodes': self.n_nodes,
252
+ 'tau_matrix': self.tau_matrix.tolist(),
253
+ 'previous_tree': self._serialize_previous_tree(),
254
+ 'edges': [edge.to_dict() for edge in self.edges],
255
+ })
256
+
257
+ return result
258
+
259
+ @classmethod
260
+ def from_dict(cls, tree_dict, previous=None):
261
+ """Create a new instance from a parameters dictionary.
262
+
263
+ Args:
264
+ params (dict):
265
+ Parameters of the Tree, in the same format as the one
266
+ returned by the ``to_dict`` method.
267
+
268
+ Returns:
269
+ Tree:
270
+ Instance of the tree defined on the parameters.
271
+ """
272
+ instance = get_tree(tree_dict['tree_type'])
273
+
274
+ fitted = tree_dict['fitted']
275
+ instance.fitted = fitted
276
+ if fitted:
277
+ instance.level = tree_dict['level']
278
+ instance.n_nodes = tree_dict['n_nodes']
279
+ instance.tau_matrix = np.array(tree_dict['tau_matrix'])
280
+ instance.previous_tree = cls._deserialize_previous_tree(tree_dict, previous)
281
+ instance.edges = [Edge.from_dict(edge) for edge in tree_dict['edges']]
282
+
283
+ return instance
284
+
285
+
286
+ class CenterTree(Tree):
287
+ """Tree for a C-vine copula."""
288
+
289
+ tree_type = TreeTypes.CENTER
290
+
291
+ def _build_first_tree(self):
292
+ """Build first level tree."""
293
+ tau_sorted = self._sort_tau_by_y(0)
294
+ for itr in range(self.n_nodes - 1):
295
+ ind = int(tau_sorted[itr, 0])
296
+ copula = Bivariate.select_copula(self.u_matrix[:, (0, ind)])
297
+ name, theta = copula.copula_type, copula.theta
298
+
299
+ new_edge = Edge(itr, 0, ind, name, theta)
300
+ new_edge.tau = self.tau_matrix[0, ind]
301
+ self.edges.append(new_edge)
302
+
303
+ def _build_kth_tree(self):
304
+ """Build k-th level tree."""
305
+ anchor = self.get_anchor()
306
+ aux_sorted = self._sort_tau_by_y(anchor)
307
+ edges = self.previous_tree.edges
308
+
309
+ for itr in range(self.n_nodes - 1):
310
+ right = int(aux_sorted[itr, 0])
311
+ left_parent, right_parent = Edge.sort_edge([edges[anchor], edges[right]])
312
+ new_edge = Edge.get_child_edge(itr, left_parent, right_parent)
313
+ new_edge.tau = aux_sorted[itr, 1]
314
+ self.edges.append(new_edge)
315
+
316
+ def get_anchor(self):
317
+ """Find anchor variable with highest sum of dependence with the rest.
318
+
319
+ Returns:
320
+ int:
321
+ Anchor variable.
322
+ """
323
+ temp = np.empty([self.n_nodes, 2])
324
+ temp[:, 0] = np.arange(self.n_nodes, dtype=int)
325
+ temp[:, 1] = np.sum(abs(self.tau_matrix), 1)
326
+ anchor = int(temp[0, 0])
327
+ return anchor
328
+
329
+
330
+ class DirectTree(Tree):
331
+ """DirectTree class."""
332
+
333
+ tree_type = TreeTypes.DIRECT
334
+
335
+ def _build_first_tree(self):
336
+ # find the pair of maximum tau
337
+ tau_matrix = self.tau_matrix
338
+ tau_sorted = self._sort_tau_by_y(0)
339
+ left_ind = tau_sorted[0, 0]
340
+ right_ind = tau_sorted[1, 0]
341
+ T1 = np.array([left_ind, 0, right_ind]).astype(int)
342
+ tau_T1 = tau_sorted[:2, 1]
343
+
344
+ # replace tau matrix of the selected variables as a negative number
345
+ tau_matrix[:, [T1]] = -10
346
+ for k in range(2, self.n_nodes - 1):
347
+ left = np.argmax(tau_matrix[T1[0], :])
348
+ right = np.argmax(tau_matrix[T1[-1], :])
349
+ valL = np.max(tau_matrix[T1[0], :])
350
+ valR = np.max(tau_matrix[T1[-1], :])
351
+
352
+ if valL > valR:
353
+ # add nodes to the left
354
+ T1 = np.append(int(left), T1)
355
+ tau_T1 = np.append(valL, tau_T1)
356
+ tau_matrix[:, left] = -10
357
+
358
+ else:
359
+ # add node to the right
360
+ T1 = np.append(T1, int(right))
361
+ tau_T1 = np.append(tau_T1, valR)
362
+ tau_matrix[:, right] = -10
363
+
364
+ for k in range(self.n_nodes - 1):
365
+ copula = Bivariate.select_copula(self.u_matrix[:, (T1[k], T1[k + 1])])
366
+ name, theta = copula.copula_type, copula.theta
367
+
368
+ left, right = sorted([T1[k], T1[k + 1]])
369
+ new_edge = Edge(k, left, right, name, theta)
370
+ new_edge.tau = tau_T1[k]
371
+ self.edges.append(new_edge)
372
+
373
+ def _build_kth_tree(self):
374
+ edges = self.previous_tree.edges
375
+ for k in range(self.n_nodes - 1):
376
+ left_parent, right_parent = Edge.sort_edge([edges[k], edges[k + 1]])
377
+ new_edge = Edge.get_child_edge(k, left_parent, right_parent)
378
+ new_edge.tau = self.tau_matrix[k, k + 1]
379
+ self.edges.append(new_edge)
380
+
381
+
382
+ class RegularTree(Tree):
383
+ """RegularTree class."""
384
+
385
+ tree_type = TreeTypes.REGULAR
386
+
387
+ def _build_first_tree(self):
388
+ """Build the first tree with n-1 variable."""
389
+ # Prim's algorithm
390
+ neg_tau = -1.0 * abs(self.tau_matrix)
391
+ X = {0}
392
+
393
+ while len(X) != self.n_nodes:
394
+ adj_set = set()
395
+ for x in X:
396
+ for k in range(self.n_nodes):
397
+ if k not in X and k != x:
398
+ adj_set.add((x, k)) # noqa: PD005
399
+
400
+ # find edge with maximum
401
+ edge = sorted(adj_set, key=lambda e: neg_tau[e[0]][e[1]])[0]
402
+ copula = Bivariate.select_copula(self.u_matrix[:, (edge[0], edge[1])])
403
+ name, theta = copula.copula_type, copula.theta
404
+
405
+ left, right = sorted([edge[0], edge[1]])
406
+ new_edge = Edge(len(X) - 1, left, right, name, theta)
407
+ new_edge.tau = self.tau_matrix[edge[0], edge[1]]
408
+ self.edges.append(new_edge)
409
+ X.add(edge[1]) # noqa: PD005
410
+
411
+ def _build_kth_tree(self):
412
+ """Build tree for level k."""
413
+ neg_tau = -1.0 * abs(self.tau_matrix)
414
+ edges = self.previous_tree.edges
415
+ visited = {0}
416
+ unvisited = set(range(self.n_nodes))
417
+
418
+ while len(visited) != self.n_nodes:
419
+ adj_set = set()
420
+ for x in visited:
421
+ for k in range(self.n_nodes):
422
+ # check if (x,k) is a valid edge in the vine
423
+ if k not in visited and k != x and self._check_constraint(edges[x], edges[k]):
424
+ adj_set.add((x, k)) # noqa: PD005
425
+
426
+ # find edge with maximum tau
427
+ if len(adj_set) == 0:
428
+ visited.add(list(unvisited)[0]) # noqa: PD005
429
+ continue
430
+
431
+ pairs = sorted(adj_set, key=lambda e: neg_tau[e[0]][e[1]])[0]
432
+ left_parent, right_parent = Edge.sort_edge([edges[pairs[0]], edges[pairs[1]]])
433
+
434
+ new_edge = Edge.get_child_edge(len(visited) - 1, left_parent, right_parent)
435
+ new_edge.tau = self.tau_matrix[pairs[0], pairs[1]]
436
+ self.edges.append(new_edge)
437
+
438
+ visited.add(pairs[1]) # noqa: PD005
439
+ unvisited.remove(pairs[1])
440
+
441
+
442
+ def get_tree(tree_type):
443
+ """Get a Tree instance of the specified type.
444
+
445
+ Args:
446
+ tree_type (str or TreeTypes):
447
+ Type of tree of which to get an instance.
448
+
449
+ Returns:
450
+ Tree:
451
+ Instance of a Tree of the specified type.
452
+ """
453
+ if not isinstance(tree_type, TreeTypes):
454
+ if (isinstance(tree_type, str) and tree_type.upper() in TreeTypes.__members__):
455
+ tree_type = TreeTypes[tree_type.upper()]
456
+ else:
457
+ raise ValueError(f'Invalid tree type {tree_type}')
458
+
459
+ if tree_type == TreeTypes.CENTER:
460
+ return CenterTree()
461
+ if tree_type == TreeTypes.REGULAR:
462
+ return RegularTree()
463
+ if tree_type == TreeTypes.DIRECT:
464
+ return DirectTree()
465
+
466
+
467
+ class Edge(object):
468
+ """Represents an edge in the copula."""
469
+
470
+ def __init__(self, index, left, right, copula_name, copula_theta):
471
+ """Initialize an Edge object.
472
+
473
+ Args:
474
+ left (int):
475
+ left_node index (smaller)
476
+ right (int):
477
+ right_node index (larger)
478
+ copula_name (str):
479
+ name of the fitted copula class
480
+ copula_theta (float):
481
+ parameters of the fitted copula class
482
+ """
483
+ self.index = index
484
+ self.L = left
485
+ self.R = right
486
+ self.D = set() # dependence_set
487
+ self.parents = None
488
+ self.neighbors = []
489
+
490
+ self.name = copula_name
491
+ self.theta = copula_theta
492
+ self.tau = None
493
+ self.U = None
494
+ self.likelihood = None
495
+
496
+ @staticmethod
497
+ def _identify_eds_ing(first, second):
498
+ """Find nodes connecting adjacent edges.
499
+
500
+ Args:
501
+ first (Edge):
502
+ Edge object representing the first edge.
503
+ second (Edge):
504
+ Edge object representing the second edge.
505
+
506
+ Returns:
507
+ tuple[int, int, set[int]]:
508
+ The first two values represent left and right node
509
+ indicies of the new edge. The third value is the new dependence set.
510
+ """
511
+ A = {first.L, first.R}
512
+ A.update(first.D)
513
+
514
+ B = {second.L, second.R}
515
+ B.update(second.D)
516
+
517
+ depend_set = A & B
518
+ left, right = sorted(A ^ B)
519
+
520
+ return left, right, depend_set
521
+
522
+ def is_adjacent(self, another_edge):
523
+ """Check if two edges are adjacent.
524
+
525
+ Args:
526
+ another_edge (Edge):
527
+ edge object of another edge
528
+
529
+ Returns:
530
+ bool:
531
+ True if the two edges are adjacent.
532
+ """
533
+ return (
534
+ self.L == another_edge.L
535
+ or self.L == another_edge.R
536
+ or self.R == another_edge.L
537
+ or self.R == another_edge.R
538
+ )
539
+
540
+ @staticmethod
541
+ def sort_edge(edges):
542
+ """Sort iterable of edges first by left node indices then right.
543
+
544
+ Args:
545
+ edges (list[Edge]):
546
+ List of edges to be sorted.
547
+
548
+ Returns:
549
+ list[Edge]:
550
+ Sorted list by left and right node indices.
551
+ """
552
+ return sorted(edges, key=lambda x: (x.L, x.R))
553
+
554
+ @classmethod
555
+ def get_conditional_uni(cls, left_parent, right_parent):
556
+ """Identify pair univariate value from parents.
557
+
558
+ Args:
559
+ left_parent (Edge):
560
+ left parent
561
+ right_parent (Edge):
562
+ right parent
563
+
564
+ Returns:
565
+ tuple[np.ndarray, np.ndarray]:
566
+ left and right parents univariate.
567
+ """
568
+ left, right, _ = cls._identify_eds_ing(left_parent, right_parent)
569
+
570
+ left_u = left_parent.U[0] if left_parent.L == left else left_parent.U[1]
571
+ right_u = right_parent.U[0] if right_parent.L == right else right_parent.U[1]
572
+
573
+ return left_u, right_u
574
+
575
+ @classmethod
576
+ def get_child_edge(cls, index, left_parent, right_parent):
577
+ """Construct a child edge from two parent edges.
578
+
579
+ Args:
580
+ index (int):
581
+ Index of the new Edge.
582
+ left_parent (Edge):
583
+ Left parent
584
+ right_parent (Edge):
585
+ Right parent
586
+
587
+ Returns:
588
+ Edge:
589
+ The new child edge.
590
+ """
591
+ [ed1, ed2, depend_set] = cls._identify_eds_ing(left_parent, right_parent)
592
+ left_u, right_u = cls.get_conditional_uni(left_parent, right_parent)
593
+ X = np.array([[x, y] for x, y in zip(left_u, right_u)])
594
+ copula = Bivariate.select_copula(X)
595
+ name, theta = copula.copula_type, copula.theta
596
+ new_edge = Edge(index, ed1, ed2, name, theta)
597
+ new_edge.D = depend_set
598
+ new_edge.parents = [left_parent, right_parent]
599
+ return new_edge
600
+
601
+ def get_likelihood(self, uni_matrix):
602
+ """Compute likelihood given a U matrix.
603
+
604
+ Args:
605
+ uni_matrix (numpy.array):
606
+ Matrix to compute the likelihood.
607
+
608
+ Return:
609
+ tuple (np.ndarray, np.ndarray, np.array):
610
+ likelihood and conditional values.
611
+ """
612
+ if self.parents is None:
613
+ left_u = uni_matrix[:, self.L]
614
+ right_u = uni_matrix[:, self.R]
615
+
616
+ else:
617
+ left_ing = list(self.D - self.parents[0].D)[0]
618
+ right_ing = list(self.D - self.parents[1].D)[0]
619
+ left_u = uni_matrix[self.L, left_ing]
620
+ right_u = uni_matrix[self.R, right_ing]
621
+
622
+ copula = Bivariate(copula_type=self.name)
623
+ copula.theta = self.theta
624
+
625
+ X_left_right = np.array([[left_u, right_u]])
626
+ X_right_left = np.array([[right_u, left_u]])
627
+
628
+ value = np.sum(copula.probability_density(X_left_right))
629
+ left_given_right = copula.partial_derivative(X_left_right)
630
+ right_given_left = copula.partial_derivative(X_right_left)
631
+
632
+ return value, left_given_right, right_given_left
633
+
634
+ def to_dict(self):
635
+ """Return a `dict` with the parameters to replicate this Edge.
636
+
637
+ Returns:
638
+ dict:
639
+ Parameters of this Edge.
640
+ """
641
+ parents = None
642
+ if self.parents:
643
+ parents = [parent.to_dict() for parent in self.parents]
644
+
645
+ U = None
646
+ if self.U is not None:
647
+ U = self.U.tolist()
648
+
649
+ return {
650
+ 'index': self.index,
651
+ 'L': self.L,
652
+ 'R': self.R,
653
+ 'D': self.D,
654
+ 'parents': parents,
655
+ 'neighbors': self.neighbors,
656
+ 'name': self.name,
657
+ 'theta': self.theta,
658
+ 'tau': self.tau,
659
+ 'U': U,
660
+ 'likelihood': self.likelihood
661
+ }
662
+
663
+ @classmethod
664
+ def from_dict(cls, edge_dict):
665
+ """Create a new instance from a parameters dictionary.
666
+
667
+ Args:
668
+ params (dict):
669
+ Parameters of the Edge, in the same format as the one
670
+ returned by the ``to_dict`` method.
671
+
672
+ Returns:
673
+ Edge:
674
+ Instance of the edge defined on the parameters.
675
+ """
676
+ instance = cls(
677
+ edge_dict['index'], edge_dict['L'], edge_dict['R'],
678
+ edge_dict['name'], edge_dict['theta']
679
+ )
680
+ instance.U = np.array(edge_dict['U'])
681
+ parents = edge_dict['parents']
682
+
683
+ if parents:
684
+ instance.parents = []
685
+ for parent in parents:
686
+ edge = Edge.from_dict(parent)
687
+ instance.parents.append(edge)
688
+
689
+ regular_attributes = ['D', 'tau', 'likelihood', 'neighbors']
690
+ for key in regular_attributes:
691
+ setattr(instance, key, edge_dict[key])
692
+
693
+ return instance