def global_recall(Theta_true, Theta_est, eps=1e-6): """Compute precision where true positives are the number of correctly estimated edges and the denominator (true positives + false positives) is the amount of detected edges. Parameters ---------- Theta_true : 3D ndarray, shape (timesteps, n_vertices, n_vertices) Theta_est : 3D ndarray, shape (timesteps, n_vertices, n_vertices) eps : float threshold to consider precision entry as edge Returns ------- float precision """ assert len(Theta_est) == len(Theta_true) n = len(Theta_est) tps, exps = 0, 0 for i in range(n): est_edges = set(get_edges(Theta_est[i], eps)) gt_edges = set(get_edges(Theta_true[i], eps)) n_joint = len(est_edges.intersection(gt_edges)) tps, exps = tps + n_joint, exps + len(gt_edges) return tps / exps if exps > 0 else 1
def recall(Theta_true, Theta_est, eps=1e-6, per_ts=False): """Compute the recall of graph recovery given true and estimated precision matrices. It is possible to average the recall score over all timesteps or get a list of recall scores for each timestep. For a global recall score, i.e. weighted average of recall by the amount of detected and true edges, see `global_recall`. Parameters ---------- Theta_true : 3D ndarray, shape (timesteps, n_vertices, n_vertices) Theta_est : 3D ndarray, shape (timesteps, n_vertices, n_vertices) eps : float per_ts : bool whether to compute average or per timestep recall Returns ------- ndarray or float recall list or single precision value """ assert len(Theta_est) == len(Theta_true) n = len(Theta_est) recall = [] if per_ts else 0 for i in range(n): est_edges = set(get_edges(Theta_est[i], eps)) gt_edges = set(get_edges(Theta_true[i], eps)) n_joint = len(est_edges.intersection(gt_edges)) latest = n_joint / len(gt_edges) if len(gt_edges) > 0 else 1 recall += [latest] if per_ts else latest return np.array(recall) if per_ts else recall / n
def global_f_score(Theta_true, Theta_est, beta=1, eps=1e-6): """In line with `global_precision` and `global_recall`, compute the global f score given true and estimated graphical structures. The f score has the only parameter beta. Parameters ---------- Theta_true : 3D ndarray, shape (timesteps, n_vertices, n_vertices) Theta_est : 3D ndarray, shape (timesteps, n_vertices, n_vertices) beta : float (default 1) beta value of the F score to be computed eps : float per_ts : bool whether to compute average or per timestep recall Returns ------- float f-beta score """ assert Theta_est.shape == Theta_true.shape d = Theta_true.shape[1] n = len(Theta_est) tps = fps = fns = tns = 0 for i in range(n): est_edges = set(get_edges(Theta_est[i], eps)) gt_edges = set(get_edges(Theta_true[i], eps)) n_joint = len(est_edges.intersection(gt_edges)) tps += n_joint fps += len(est_edges) - n_joint fns += len(gt_edges) - n_joint tns += d**2 - d - tps - fps - fns nom = (1 + beta**2) * tps denom = nom + beta**2 * fns + fps with np.errstate(divide='ignore', invalid='ignore'): f = np.nan_to_num(np.true_divide(nom, denom)) return f
def nxGraph(self): """Associates a networkX graph object with the corresponding precision matrix. Returns ------- nxGraph: nx graph object """ nxGraph = nx.Graph() p = self.Theta.shape[0] nxGraph.add_nodes_from(range(1, p + 1)) edges = np.array(get_edges(self.Theta, self.eps)) + 1 nxGraph.add_weighted_edges_from([(i, j, self.Theta[i-1, j-1]) for i, j in edges]) return nxGraph
def n_estimated_edges(Theta_est, eps=1e-6, per_ts=True): """Sums up the number of edges in each individual precision graph and by default sums the number for each graph over the whole timeseries. per_ts allows to change this so only a scalar is returned Parameters ---------- Theta_est : 3D ndarray, shape (timesteps, n_vertices, n_vertices) eps : float per_ts : bool whether to return array or sum the amount of edges up Returns ------- list for edges or int for global sum """ n_edges = [len(get_edges(G, eps)) for G in Theta_est] return n_edges if per_ts else sum(n_edges)
def changepoint_density(Theta_est, eps=1e-6, per_ts=True): """Sums up the number of edges changed either per timestep or globally. A changed edge exists at t if the weight of edge changes between timestep t-1 and t where the prior graph is equal to the first, so no changepoint is possible at t=1. Parameters ---------- Theta_est : 3D ndarray, shape (timesteps, n_vertices, n_vertices) eps : float per_ts : bool whether to return array or sum the amount of edges up Returns ------- list for changepoints or int for global sum """ cps = [len(get_edges(Theta_est[t-1] - Theta_est[t], eps)) for t in range(1, len(Theta_est))] cps = [0] + cps return cps if per_ts else sum(cps)
def n_edges(self): return len(get_edges(self.Theta, self.eps))
def test_active_edges(self): n_verts, n_edges = 5, 3 ER = ErdosRenyiPrecisionGraph(n_verts, n_edges) adj_list = get_edges(ER.Theta, eps=1e-7) self.assertEqual(len(adj_list), n_edges)