| 1 | """ |
|---|
| 2 | <name>Pubmed Network View</name> |
|---|
| 3 | <description></description> |
|---|
| 4 | <icon>icons/Network.png</icon> |
|---|
| 5 | <contact></contact> |
|---|
| 6 | <priority>6470</priority> |
|---|
| 7 | """ |
|---|
| 8 | |
|---|
| 9 | import Orange |
|---|
| 10 | import OWGUI |
|---|
| 11 | |
|---|
| 12 | from OWWidget import * |
|---|
| 13 | |
|---|
| 14 | class PubmedNetworkView(Orange.network.NxView): |
|---|
| 15 | """Network Inside View |
|---|
| 16 | |
|---|
| 17 | """ |
|---|
| 18 | |
|---|
| 19 | def __init__(self, parent): |
|---|
| 20 | Orange.network.NxView.__init__(self) |
|---|
| 21 | |
|---|
| 22 | self._nhops = 2 |
|---|
| 23 | self._edge_threshold = 0.5 |
|---|
| 24 | self._algorithm = 0 # 0 without clustering, 1 with clustering |
|---|
| 25 | self._n_max_neighbors = 10 |
|---|
| 26 | self._center_nodes = [] |
|---|
| 27 | self.parent = parent |
|---|
| 28 | self._hidden_nodes = [] |
|---|
| 29 | self._k_algorithm = 0.3 |
|---|
| 30 | self._delta_score = {} |
|---|
| 31 | |
|---|
| 32 | def init_network(self, graph): |
|---|
| 33 | self._network = graph |
|---|
| 34 | |
|---|
| 35 | if hasattr(self.parent, 'init_network'): |
|---|
| 36 | self.parent.init_network() |
|---|
| 37 | |
|---|
| 38 | if graph is None: |
|---|
| 39 | return None |
|---|
| 40 | |
|---|
| 41 | return graph |
|---|
| 42 | |
|---|
| 43 | |
|---|
| 44 | def update_network(self): |
|---|
| 45 | if self._center_nodes == []: |
|---|
| 46 | return |
|---|
| 47 | |
|---|
| 48 | subnet = Orange.network.Graph() |
|---|
| 49 | central_nodes, to_add = self._center_nodes[:], self._center_nodes[:] |
|---|
| 50 | for l in range(self._nhops): |
|---|
| 51 | for i in central_nodes: |
|---|
| 52 | neig = sorted([x for x in self._network.neighbors(i) if self._network.edge[i][x]['weight'] > self._edge_threshold], reverse=True) |
|---|
| 53 | if len(neig) > self._n_max_neighbors: |
|---|
| 54 | neig = neig[:self._n_max_neighbors] |
|---|
| 55 | to_add.extend(neig) |
|---|
| 56 | central_nodes = neig |
|---|
| 57 | to_add = list(set(to_add)) |
|---|
| 58 | subnet.add_nodes_from([(x, self._network.node[x]) for x in to_add]) |
|---|
| 59 | nodes = subnet.nodes() |
|---|
| 60 | while nodes: |
|---|
| 61 | i = nodes.pop() |
|---|
| 62 | subnet.node[i] = self._network.node[i] |
|---|
| 63 | neig = [x for x in self._network.neighbors(i) if x in nodes] |
|---|
| 64 | subnet.add_weighted_edges_from([(i, x, w) for x, w in zip(neig, [self._network.edge[i][y]['weight'] for y in neig])]) |
|---|
| 65 | |
|---|
| 66 | subnet.remove_nodes_from(self._hidden_nodes) |
|---|
| 67 | subnet = self._propagate(subnet) |
|---|
| 68 | |
|---|
| 69 | if self._nx_explorer is not None: |
|---|
| 70 | self._nx_explorer.change_graph(subnet) |
|---|
| 71 | |
|---|
| 72 | def set_nhops(self, nhops): |
|---|
| 73 | self._nhops = nhops |
|---|
| 74 | |
|---|
| 75 | def set_edge_threshold(self, edge_threshold): |
|---|
| 76 | self._edge_threshold = edge_threshold |
|---|
| 77 | |
|---|
| 78 | def set_algorithm(self, algorithm): |
|---|
| 79 | self._algorithm = algorithm |
|---|
| 80 | |
|---|
| 81 | def set_n_max_neighbors(self, n_max_neighbors): |
|---|
| 82 | self._n_max_neighbors = n_max_neighbors |
|---|
| 83 | |
|---|
| 84 | def set_center_nodes(self, c_nodes): |
|---|
| 85 | self._center_nodes = c_nodes |
|---|
| 86 | |
|---|
| 87 | def set_k(self, k): |
|---|
| 88 | self._k_algorithm = k |
|---|
| 89 | |
|---|
| 90 | def node_selection_changed(self): |
|---|
| 91 | pass |
|---|
| 92 | |
|---|
| 93 | def set_user_node_score(self, node_index, new_score): |
|---|
| 94 | self._delta_score[node_index] = new_score - self._network.node[node_index]['score'] |
|---|
| 95 | self._network.node[node_index]['score'] = new_score |
|---|
| 96 | self._network.node[node_index]['user_score'] = 1 |
|---|
| 97 | |
|---|
| 98 | def update_center_nodes(self, new_node): |
|---|
| 99 | self._center_nodes.append(new_node) |
|---|
| 100 | |
|---|
| 101 | def _get_neighbors(self): |
|---|
| 102 | #TO DELETE? |
|---|
| 103 | nodes = set([self._center_node]) |
|---|
| 104 | for n in range(self._nhops): |
|---|
| 105 | neighbors = set() |
|---|
| 106 | for node in nodes: |
|---|
| 107 | neighbors.update(self._network.neighbors(node)) |
|---|
| 108 | nodes.update(neighbors) |
|---|
| 109 | return nodes |
|---|
| 110 | |
|---|
| 111 | def _propagate(self, net): |
|---|
| 112 | central_nodes = [i for i in net.nodes() if net.node[i]['user_score'] == 1] |
|---|
| 113 | central_delta_score = [self._delta_score[id] for id in central_nodes] |
|---|
| 114 | cluster_centers = [net.node[x]['cluster'] for x in central_nodes if net.node[x]['cluster'] != ''] |
|---|
| 115 | updated = central_nodes[:] |
|---|
| 116 | L = 0 |
|---|
| 117 | while True: |
|---|
| 118 | to_update = [] |
|---|
| 119 | for i in central_nodes: |
|---|
| 120 | to_update.extend(net.neighbors(i)) |
|---|
| 121 | to_update = set(to_update) - set(updated) |
|---|
| 122 | if to_update: |
|---|
| 123 | for i in to_update: |
|---|
| 124 | if not net.node[i]['user_score']: |
|---|
| 125 | predec = [j for j in net.neighbors(i) if j in central_nodes] |
|---|
| 126 | predec_delta_score = [central_delta_score[central_nodes.index(x)] for x in predec] |
|---|
| 127 | self._k_algortihm = self.compute_k(i, cluster_centers, net) |
|---|
| 128 | if self._k_algortihm: #else the node is isolated and the score doesn't change |
|---|
| 129 | net.node[i]['score'] = _prop(predec, predec_delta_score, i, L, net) |
|---|
| 130 | net.node[i]['level'] = L |
|---|
| 131 | updated.extend(to_update) |
|---|
| 132 | central_nodes = list(to_update) |
|---|
| 133 | central_delta_score = [] |
|---|
| 134 | for id in central_nodes: |
|---|
| 135 | if self._delta_score.get(id, []): |
|---|
| 136 | central_delta_score.append(self._delta_score[id]) |
|---|
| 137 | else: |
|---|
| 138 | central_delta_score.append(net.node[id]['score'] - 0.5) |
|---|
| 139 | L += 1 |
|---|
| 140 | else: |
|---|
| 141 | return net |
|---|
| 142 | |
|---|
| 143 | def compute_k(self, id, centers, net): |
|---|
| 144 | import networkx as nx #VEDERE SE C'E' IN ORANGENET |
|---|
| 145 | if self._algorithm == 0: #no clustering |
|---|
| 146 | return self._algorithm |
|---|
| 147 | else: #clustering |
|---|
| 148 | paths = [] |
|---|
| 149 | for c in centers: |
|---|
| 150 | try: |
|---|
| 151 | paths.append(nx.algorithms.shortest_paths.generic.shortest_path_length(net, id, c)) |
|---|
| 152 | except nx.exception.NetworkXNoPath: |
|---|
| 153 | return None #isolated node |
|---|
| 154 | |
|---|
| 155 | return float(min(paths)) / 10 |
|---|
| 156 | |
|---|
| 157 | |
|---|
| 158 | def _prop(self, predec, predec_delta_score, node, L, net): |
|---|
| 159 | edges_weight = [net[i][node]['weight'] for i in predecessors] |
|---|
| 160 | x = [(delta * w + 0.5) / (1 + level * self._k_algorithm) for delta, w in zip(predec_delta_score, edges_weight)] |
|---|
| 161 | return max(x) |
|---|
| 162 | |
|---|
| 163 | |
|---|
| 164 | class OWPubmedView(OWWidget): |
|---|
| 165 | |
|---|
| 166 | settingsList = ['_nhops'] |
|---|
| 167 | |
|---|
| 168 | def __init__(self, parent=None, signalManager=None): |
|---|
| 169 | OWWidget.__init__(self, parent, signalManager, 'Pubmed Network View', wantMainArea=0) |
|---|
| 170 | |
|---|
| 171 | self.inputs = [] |
|---|
| 172 | self.outputs = [("Nx View", Orange.network.NxView)] |
|---|
| 173 | |
|---|
| 174 | self._nhops = 2 |
|---|
| 175 | self._edge_threshold = 0.5 |
|---|
| 176 | self._n_max_neighbors = 20 |
|---|
| 177 | self.selected_titles = [] |
|---|
| 178 | self.titles = [] |
|---|
| 179 | self.filter = '' |
|---|
| 180 | self.ids = [] |
|---|
| 181 | self._selected_nodes = [] |
|---|
| 182 | self._algorithm = 0 |
|---|
| 183 | self._k_algorithm = 0.3 |
|---|
| 184 | |
|---|
| 185 | self.loadSettings() |
|---|
| 186 | |
|---|
| 187 | box = OWGUI.widgetBox(self.controlArea, "Paper Selection", orientation="vertical") |
|---|
| 188 | OWGUI.lineEdit(box, self, "filter", callback=self.filter_list, callbackOnType=True) |
|---|
| 189 | self.list_titles = OWGUI.listBox(box, self, "selected_titles", "titles", selectionMode=QListWidget.MultiSelection, callback=self.update_view) |
|---|
| 190 | OWGUI.separator(self.controlArea) |
|---|
| 191 | box_pref = OWGUI.widgetBox(self.controlArea, "Preferences", orientation="vertical") |
|---|
| 192 | OWGUI.spin(box_pref, self, "_nhops", 1, 6, 1, label="Number of hops: ", callback=self.update_view) |
|---|
| 193 | OWGUI.spin(box_pref, self, "_n_max_neighbors", 1, 100, 1, label="Max number of neighbors: ", callback=self.update_view) |
|---|
| 194 | OWGUI.doubleSpin(box_pref, self, "_edge_threshold", 0, 1, step=0.01, label="Edge threshold: ", callback=self.update_view) |
|---|
| 195 | OWGUI.separator(self.controlArea) |
|---|
| 196 | box_alg = OWGUI.widgetBox(self.controlArea, "Interest Propagation Algorithm", orientation="vertical") |
|---|
| 197 | radio_box = OWGUI.radioButtonsInBox(box_alg, self, "_algorithm", [], callback=self.update_view) |
|---|
| 198 | OWGUI.appendRadioButton(radio_box, self, "_algorithm", "Without Clustering", callback=self.update_view) |
|---|
| 199 | OWGUI.doubleSpin(OWGUI.indentedBox(radio_box), self, "_k_algorithm", 0, 1, step=0.01, label="Parameter k: ", callback=self.update_view) |
|---|
| 200 | OWGUI.appendRadioButton(radio_box, self, "_algorithm", "With Clustering", callback=self.update_view) |
|---|
| 201 | |
|---|
| 202 | self.inside_view = PubmedNetworkView(self) |
|---|
| 203 | self.send("Nx View", self.inside_view) |
|---|
| 204 | |
|---|
| 205 | |
|---|
| 206 | def init_network(self): |
|---|
| 207 | if self.inside_view._network is None: |
|---|
| 208 | return |
|---|
| 209 | |
|---|
| 210 | self.titles = [self.inside_view._network.node[node]['title'] for node in self.inside_view._network.nodes()] |
|---|
| 211 | self.ids = self.inside_view._network.nodes() |
|---|
| 212 | |
|---|
| 213 | QObject.connect(self.inside_view._nx_explorer.networkCanvas, SIGNAL('point_rightclicked(Point*)'), self.node_menu_show) |
|---|
| 214 | |
|---|
| 215 | |
|---|
| 216 | def update_view(self): |
|---|
| 217 | self.inside_view.set_nhops(self._nhops) |
|---|
| 218 | self.inside_view.set_edge_threshold(self._edge_threshold) |
|---|
| 219 | self.inside_view.set_n_max_neighbors(self._n_max_neighbors) |
|---|
| 220 | self.inside_view.set_algorithm(self._algorithm) |
|---|
| 221 | self.inside_view.set_k(self._k_algorithm) |
|---|
| 222 | self._selected_nodes = [self.ids[row] for row in self.selected_titles] |
|---|
| 223 | self.inside_view.set_center_nodes(self._selected_nodes) |
|---|
| 224 | self.inside_view.update_network() |
|---|
| 225 | |
|---|
| 226 | |
|---|
| 227 | def filter_list(self): |
|---|
| 228 | """Given a query for similar titles sets titles and ids""" |
|---|
| 229 | |
|---|
| 230 | str_input = self.filter |
|---|
| 231 | str_input = str_input.strip(' .').lower().split(' ') |
|---|
| 232 | titles2 = [(n, str.lower(self.inside_view._network.node[n]['title'].encode('utf-8').strip(' .')).split(' ')) for n in self.inside_view._network.nodes()] # [(id,title)] |
|---|
| 233 | titles2 = sorted(titles2, key=lambda x: sum(i in str_input for i in x[1]), reverse=True) |
|---|
| 234 | self.titles = [self.inside_view._network.node[x[0]]['title'] for x in titles2] |
|---|
| 235 | self.ids = [x[0] for x in titles2] |
|---|
| 236 | |
|---|
| 237 | def node_menu_show(self, node): |
|---|
| 238 | menu = QMenu(self) |
|---|
| 239 | menu.addAction('Expand Node') |
|---|
| 240 | menu.addAction('Hide Node') |
|---|
| 241 | qstr = QString('Set Score') |
|---|
| 242 | submenu = menu.addMenu(qstr) |
|---|
| 243 | submenu.addAction('0') |
|---|
| 244 | submenu.addAction('0.2') |
|---|
| 245 | submenu.addAction('0.5') |
|---|
| 246 | submenu.addAction('0.7') |
|---|
| 247 | submenu.addAction('1') |
|---|
| 248 | self.connect(menu, SIGNAL("triggered(QAction*)"), lambda action, node=node: self.node_menu_triggered(action, node)) |
|---|
| 249 | menu.popup(QCursor.pos()) |
|---|
| 250 | |
|---|
| 251 | def node_menu_triggered(self, action, node): |
|---|
| 252 | # delete_node_and_neig --> ???? |
|---|
| 253 | if action.text() == 'Expand Node': |
|---|
| 254 | self.inside_view.update_center_nodes(node.index()) |
|---|
| 255 | |
|---|
| 256 | elif action.text() == 'Hide Node': |
|---|
| 257 | self.inside_view._hidden_nodes.append(node.index()) |
|---|
| 258 | |
|---|
| 259 | else: |
|---|
| 260 | self.inside_view.set_user_node_score(node.index(), float(action.text())) |
|---|
| 261 | |
|---|
| 262 | self.inside_view.update_network() #chiama propagate |
|---|
| 263 | |
|---|
| 264 | |
|---|
| 265 | |
|---|
| 266 | |
|---|