NextGenC commited on
Commit
64b5d29
·
verified ·
1 Parent(s): 96f3b39

Upload 27 files

Browse files
2025-04-15_Introduction_to_Artificial_Intelligence.pdf ADDED
Binary file (53.3 kB). View file
 
analysis_concept_frequencies.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:31cb3e93a0c0c1eb3f65ba695a75b03bdd2b67f80fefd9b7810497ec50100d42
3
+ size 3618
analysis_concept_similarities.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0cf17175e4b6a3fac9648e20938ce6c90e4d90dbdc1c71186846a17eff77b45a
3
+ size 4846
analysis_network_results.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:98e8ca5669f2a601c184388dda9b34d870ef5a35e69336a4bc23adff7ffc14c2
3
+ size 5022
bestTest.png ADDED
concept_embeddings.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aa1f484881118bfd68ca830e070e85c0e380a1be6bbef1c637ea17303bc4d167
3
+ size 17716
concept_network.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:acc729f59905a3394915ccb0d731ba2bc1a25b2a16f7cd524cb1cdfa451420db
3
+ size 2336
concept_network_visualization.html ADDED
@@ -0,0 +1,374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html>
2
+ <head>
3
+ <meta charset="utf-8">
4
+
5
+ <script>function neighbourhoodHighlight(params) {
6
+ // console.log("in nieghbourhoodhighlight");
7
+ allNodes = nodes.get({ returnType: "Object" });
8
+ // originalNodes = JSON.parse(JSON.stringify(allNodes));
9
+ // if something is selected:
10
+ if (params.nodes.length > 0) {
11
+ highlightActive = true;
12
+ var i, j;
13
+ var selectedNode = params.nodes[0];
14
+ var degrees = 2;
15
+
16
+ // mark all nodes as hard to read.
17
+ for (let nodeId in allNodes) {
18
+ // nodeColors[nodeId] = allNodes[nodeId].color;
19
+ allNodes[nodeId].color = "rgba(200,200,200,0.5)";
20
+ if (allNodes[nodeId].hiddenLabel === undefined) {
21
+ allNodes[nodeId].hiddenLabel = allNodes[nodeId].label;
22
+ allNodes[nodeId].label = undefined;
23
+ }
24
+ }
25
+ var connectedNodes = network.getConnectedNodes(selectedNode);
26
+ var allConnectedNodes = [];
27
+
28
+ // get the second degree nodes
29
+ for (i = 1; i < degrees; i++) {
30
+ for (j = 0; j < connectedNodes.length; j++) {
31
+ allConnectedNodes = allConnectedNodes.concat(
32
+ network.getConnectedNodes(connectedNodes[j])
33
+ );
34
+ }
35
+ }
36
+
37
+ // all second degree nodes get a different color and their label back
38
+ for (i = 0; i < allConnectedNodes.length; i++) {
39
+ // allNodes[allConnectedNodes[i]].color = "pink";
40
+ allNodes[allConnectedNodes[i]].color = "rgba(150,150,150,0.75)";
41
+ if (allNodes[allConnectedNodes[i]].hiddenLabel !== undefined) {
42
+ allNodes[allConnectedNodes[i]].label =
43
+ allNodes[allConnectedNodes[i]].hiddenLabel;
44
+ allNodes[allConnectedNodes[i]].hiddenLabel = undefined;
45
+ }
46
+ }
47
+
48
+ // all first degree nodes get their own color and their label back
49
+ for (i = 0; i < connectedNodes.length; i++) {
50
+ // allNodes[connectedNodes[i]].color = undefined;
51
+ allNodes[connectedNodes[i]].color = nodeColors[connectedNodes[i]];
52
+ if (allNodes[connectedNodes[i]].hiddenLabel !== undefined) {
53
+ allNodes[connectedNodes[i]].label =
54
+ allNodes[connectedNodes[i]].hiddenLabel;
55
+ allNodes[connectedNodes[i]].hiddenLabel = undefined;
56
+ }
57
+ }
58
+
59
+ // the main node gets its own color and its label back.
60
+ // allNodes[selectedNode].color = undefined;
61
+ allNodes[selectedNode].color = nodeColors[selectedNode];
62
+ if (allNodes[selectedNode].hiddenLabel !== undefined) {
63
+ allNodes[selectedNode].label = allNodes[selectedNode].hiddenLabel;
64
+ allNodes[selectedNode].hiddenLabel = undefined;
65
+ }
66
+ } else if (highlightActive === true) {
67
+ // console.log("highlightActive was true");
68
+ // reset all nodes
69
+ for (let nodeId in allNodes) {
70
+ // allNodes[nodeId].color = "purple";
71
+ allNodes[nodeId].color = nodeColors[nodeId];
72
+ // delete allNodes[nodeId].color;
73
+ if (allNodes[nodeId].hiddenLabel !== undefined) {
74
+ allNodes[nodeId].label = allNodes[nodeId].hiddenLabel;
75
+ allNodes[nodeId].hiddenLabel = undefined;
76
+ }
77
+ }
78
+ highlightActive = false;
79
+ }
80
+
81
+ // transform the object into an array
82
+ var updateArray = [];
83
+ if (params.nodes.length > 0) {
84
+ for (let nodeId in allNodes) {
85
+ if (allNodes.hasOwnProperty(nodeId)) {
86
+ // console.log(allNodes[nodeId]);
87
+ updateArray.push(allNodes[nodeId]);
88
+ }
89
+ }
90
+ nodes.update(updateArray);
91
+ } else {
92
+ // console.log("Nothing was selected");
93
+ for (let nodeId in allNodes) {
94
+ if (allNodes.hasOwnProperty(nodeId)) {
95
+ // console.log(allNodes[nodeId]);
96
+ // allNodes[nodeId].color = {};
97
+ updateArray.push(allNodes[nodeId]);
98
+ }
99
+ }
100
+ nodes.update(updateArray);
101
+ }
102
+ }
103
+
104
+ function filterHighlight(params) {
105
+ allNodes = nodes.get({ returnType: "Object" });
106
+ // if something is selected:
107
+ if (params.nodes.length > 0) {
108
+ filterActive = true;
109
+ let selectedNodes = params.nodes;
110
+
111
+ // hiding all nodes and saving the label
112
+ for (let nodeId in allNodes) {
113
+ allNodes[nodeId].hidden = true;
114
+ if (allNodes[nodeId].savedLabel === undefined) {
115
+ allNodes[nodeId].savedLabel = allNodes[nodeId].label;
116
+ allNodes[nodeId].label = undefined;
117
+ }
118
+ }
119
+
120
+ for (let i=0; i < selectedNodes.length; i++) {
121
+ allNodes[selectedNodes[i]].hidden = false;
122
+ if (allNodes[selectedNodes[i]].savedLabel !== undefined) {
123
+ allNodes[selectedNodes[i]].label = allNodes[selectedNodes[i]].savedLabel;
124
+ allNodes[selectedNodes[i]].savedLabel = undefined;
125
+ }
126
+ }
127
+
128
+ } else if (filterActive === true) {
129
+ // reset all nodes
130
+ for (let nodeId in allNodes) {
131
+ allNodes[nodeId].hidden = false;
132
+ if (allNodes[nodeId].savedLabel !== undefined) {
133
+ allNodes[nodeId].label = allNodes[nodeId].savedLabel;
134
+ allNodes[nodeId].savedLabel = undefined;
135
+ }
136
+ }
137
+ filterActive = false;
138
+ }
139
+
140
+ // transform the object into an array
141
+ var updateArray = [];
142
+ if (params.nodes.length > 0) {
143
+ for (let nodeId in allNodes) {
144
+ if (allNodes.hasOwnProperty(nodeId)) {
145
+ updateArray.push(allNodes[nodeId]);
146
+ }
147
+ }
148
+ nodes.update(updateArray);
149
+ } else {
150
+ for (let nodeId in allNodes) {
151
+ if (allNodes.hasOwnProperty(nodeId)) {
152
+ updateArray.push(allNodes[nodeId]);
153
+ }
154
+ }
155
+ nodes.update(updateArray);
156
+ }
157
+ }
158
+
159
+ function selectNode(nodes) {
160
+ network.selectNodes(nodes);
161
+ neighbourhoodHighlight({ nodes: nodes });
162
+ return nodes;
163
+ }
164
+
165
+ function selectNodes(nodes) {
166
+ network.selectNodes(nodes);
167
+ filterHighlight({nodes: nodes});
168
+ return nodes;
169
+ }
170
+
171
+ function highlightFilter(filter) {
172
+ let selectedNodes = []
173
+ let selectedProp = filter['property']
174
+ if (filter['item'] === 'node') {
175
+ let allNodes = nodes.get({ returnType: "Object" });
176
+ for (let nodeId in allNodes) {
177
+ if (allNodes[nodeId][selectedProp] && filter['value'].includes((allNodes[nodeId][selectedProp]).toString())) {
178
+ selectedNodes.push(nodeId)
179
+ }
180
+ }
181
+ }
182
+ else if (filter['item'] === 'edge'){
183
+ let allEdges = edges.get({returnType: 'object'});
184
+ // check if the selected property exists for selected edge and select the nodes connected to the edge
185
+ for (let edge in allEdges) {
186
+ if (allEdges[edge][selectedProp] && filter['value'].includes((allEdges[edge][selectedProp]).toString())) {
187
+ selectedNodes.push(allEdges[edge]['from'])
188
+ selectedNodes.push(allEdges[edge]['to'])
189
+ }
190
+ }
191
+ }
192
+ selectNodes(selectedNodes)
193
+ }</script>
194
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/vis-network/9.1.2/dist/dist/vis-network.min.css" integrity="sha512-WgxfT5LWjfszlPHXRmBWHkV2eceiWTOBvrKCNbdgDYTHrT2AeLCGbF4sZlZw3UMN3WtL0tGUoIAKsu8mllg/XA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
195
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/vis-network/9.1.2/dist/vis-network.min.js" integrity="sha512-LnvoEWDFrqGHlHmDD2101OrLcbsfkrzoSpvtSQtxK3RMnRV0eOkhhBN2dXHKRrUU8p2DGRTk35n4O8nWSVe1mQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+ <center>
205
+ <h1>ChronoSense Konsept A�� (Metriklerle)</h1>
206
+ </center>
207
+
208
+ <!-- <link rel="stylesheet" href="../node_modules/vis/dist/vis.min.css" type="text/css" />
209
+ <script type="text/javascript" src="../node_modules/vis/dist/vis.js"> </script>-->
210
+ <link
211
+ href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
212
+ rel="stylesheet"
213
+ integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6"
214
+ crossorigin="anonymous"
215
+ />
216
+ <script
217
+ src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
218
+ integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf"
219
+ crossorigin="anonymous"
220
+ ></script>
221
+
222
+
223
+ <center>
224
+ <h1>ChronoSense Konsept A�� (Metriklerle)</h1>
225
+ </center>
226
+ <style type="text/css">
227
+
228
+ #mynetwork {
229
+ width: 100%;
230
+ height: 800px;
231
+ background-color: #ffffff;
232
+ border: 1px solid lightgray;
233
+ position: relative;
234
+ float: left;
235
+ }
236
+
237
+
238
+
239
+
240
+ #config {
241
+ float: left;
242
+ width: 400px;
243
+ height: 600px;
244
+ }
245
+
246
+
247
+
248
+ </style>
249
+ </head>
250
+
251
+
252
+ <body>
253
+ <div class="card" style="width: 100%">
254
+
255
+
256
+ <div id="mynetwork" class="card-body"></div>
257
+ </div>
258
+
259
+
260
+
261
+ <div id="config"></div>
262
+
263
+
264
+ <script type="text/javascript">
265
+
266
+ // initialize global variables.
267
+ var edges;
268
+ var nodes;
269
+ var allNodes;
270
+ var allEdges;
271
+ var nodeColors;
272
+ var originalNodes;
273
+ var network;
274
+ var container;
275
+ var options, data;
276
+ var filter = {
277
+ item : '',
278
+ property : '',
279
+ value : []
280
+ };
281
+
282
+
283
+
284
+
285
+
286
+ // This method is responsible for drawing the graph, returns the drawn network
287
+ function drawGraph() {
288
+ var container = document.getElementById('mynetwork');
289
+
290
+
291
+
292
+ // parsing and collecting nodes and edges from the python
293
+ nodes = new vis.DataSet([{"color": "#ff7f0e", "id": "b8566bb8-f043-45d0-8442-c8f3e729a626", "label": "ai", "shape": "dot", "size": 40.0, "title": "ID: b8566bb8-f043-45d0-8442-c8f3e729a626\u003cbr\u003eName: ai\u003cbr\u003edegree_centrality: 0.300\u003cbr\u003ecommunity_id: 1"}, {"color": "#2ca02c", "id": "acdb0052-9fb5-4a61-8ce3-4fa9188ccd68", "label": "unsupervised learning: finding", "shape": "dot", "size": 40.0, "title": "ID: acdb0052-9fb5-4a61-8ce3-4fa9188ccd68\u003cbr\u003eName: unsupervised learning: finding\u003cbr\u003edegree_centrality: 0.300\u003cbr\u003ecommunity_id: 2"}, {"color": "#2ca02c", "id": "c9a071e5-358b-460f-897d-5a0d68b4dc91", "label": "reinforcement learning", "shape": "dot", "size": 40.0, "title": "ID: c9a071e5-358b-460f-897d-5a0d68b4dc91\u003cbr\u003eName: reinforcement learning\u003cbr\u003edegree_centrality: 0.300\u003cbr\u003ecommunity_id: 2"}, {"color": "#d62728", "id": "8bcb0007-453a-45a8-b0f5-ccb49fc963be", "label": "deep learning", "shape": "dot", "size": 10, "title": "ID: 8bcb0007-453a-45a8-b0f5-ccb49fc963be\u003cbr\u003eName: deep learning\u003cbr\u003edegree_centrality: 0.000\u003cbr\u003ecommunity_id: 3"}, {"color": "#1f77b4", "id": "544a779d-f9b6-4720-bfdf-80a26574d819", "label": "nlp", "shape": "dot", "size": 20.0, "title": "ID: 544a779d-f9b6-4720-bfdf-80a26574d819\u003cbr\u003eName: nlp\u003cbr\u003edegree_centrality: 0.100\u003cbr\u003ecommunity_id: 0"}, {"color": "#ff7f0e", "id": "1b3a4eb6-a80f-4098-b98e-2ca50ecbdbc6", "label": "chatbots", "shape": "dot", "size": 30.0, "title": "ID: 1b3a4eb6-a80f-4098-b98e-2ca50ecbdbc6\u003cbr\u003eName: chatbots\u003cbr\u003edegree_centrality: 0.200\u003cbr\u003ecommunity_id: 1"}, {"color": "#2ca02c", "id": "ffec4610-96c3-4a0f-a592-573143619a30", "label": "supervised learning", "shape": "dot", "size": 40.0, "title": "ID: ffec4610-96c3-4a0f-a592-573143619a30\u003cbr\u003eName: supervised learning\u003cbr\u003edegree_centrality: 0.300\u003cbr\u003ecommunity_id: 2"}, {"color": "#2ca02c", "id": "c7b69b48-9fea-45de-868d-27f935a7b2b7", "label": "labeled data unsupervised learning", "shape": "dot", "size": 40.0, "title": "ID: c7b69b48-9fea-45de-868d-27f935a7b2b7\u003cbr\u003eName: labeled data unsupervised learning\u003cbr\u003edegree_centrality: 0.300\u003cbr\u003ecommunity_id: 2"}, {"color": "#1f77b4", "id": "18f1cc03-9cfc-40c8-aa86-279a700a7f58", "label": "this approach", "shape": "dot", "size": 20.0, "title": "ID: 18f1cc03-9cfc-40c8-aa86-279a700a7f58\u003cbr\u003eName: this approach\u003cbr\u003edegree_centrality: 0.100\u003cbr\u003ecommunity_id: 0"}, {"color": "#ff7f0e", "id": "78b888f4-c0bf-492e-b514-3da1f628797d", "label": "gpt-4", "shape": "dot", "size": 30.0, "title": "ID: 78b888f4-c0bf-492e-b514-3da1f628797d\u003cbr\u003eName: gpt-4\u003cbr\u003edegree_centrality: 0.200\u003cbr\u003ecommunity_id: 1"}, {"color": "#ff7f0e", "id": "903e5742-9937-42c1-917d-ea7ff7ac449e", "label": "these models", "shape": "dot", "size": 20.0, "title": "ID: 903e5742-9937-42c1-917d-ea7ff7ac449e\u003cbr\u003eName: these models\u003cbr\u003edegree_centrality: 0.100\u003cbr\u003ecommunity_id: 1"}]);
294
+ edges = new vis.DataSet([{"color": "#9370DB", "from": "b8566bb8-f043-45d0-8442-c8f3e729a626", "title": "Type: combined\u003cbr\u003eRelation: RELATED_TO\u003cbr\u003eSimilarity: 0.648", "to": "1b3a4eb6-a80f-4098-b98e-2ca50ecbdbc6", "value": 0.647527813911438}, {"color": "#9370DB", "from": "b8566bb8-f043-45d0-8442-c8f3e729a626", "title": "Type: combined\u003cbr\u003eRelation: RELATED_TO\u003cbr\u003eSimilarity: 0.648", "to": "78b888f4-c0bf-492e-b514-3da1f628797d", "value": 0.647527813911438}, {"color": "#4682B4", "from": "b8566bb8-f043-45d0-8442-c8f3e729a626", "title": "Type: similarity\u003cbr\u003eSimilarity: 0.627", "to": "903e5742-9937-42c1-917d-ea7ff7ac449e", "value": 0.6268218755722046}, {"color": "#FF6347", "from": "acdb0052-9fb5-4a61-8ce3-4fa9188ccd68", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "c9a071e5-358b-460f-897d-5a0d68b4dc91", "value": 0.8}, {"color": "#FF6347", "from": "acdb0052-9fb5-4a61-8ce3-4fa9188ccd68", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "ffec4610-96c3-4a0f-a592-573143619a30", "value": 0.8}, {"color": "#FF6347", "from": "acdb0052-9fb5-4a61-8ce3-4fa9188ccd68", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "c7b69b48-9fea-45de-868d-27f935a7b2b7", "value": 0.8}, {"color": "#FF6347", "from": "c9a071e5-358b-460f-897d-5a0d68b4dc91", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "ffec4610-96c3-4a0f-a592-573143619a30", "value": 0.8}, {"color": "#FF6347", "from": "c9a071e5-358b-460f-897d-5a0d68b4dc91", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "c7b69b48-9fea-45de-868d-27f935a7b2b7", "value": 0.8}, {"color": "#FF6347", "from": "544a779d-f9b6-4720-bfdf-80a26574d819", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "18f1cc03-9cfc-40c8-aa86-279a700a7f58", "value": 0.8}, {"color": "#FF6347", "from": "1b3a4eb6-a80f-4098-b98e-2ca50ecbdbc6", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "78b888f4-c0bf-492e-b514-3da1f628797d", "value": 0.8}, {"color": "#FF6347", "from": "ffec4610-96c3-4a0f-a592-573143619a30", "title": "Type: extracted\u003cbr\u003eRelation: RELATED_TO", "to": "c7b69b48-9fea-45de-868d-27f935a7b2b7", "value": 0.8}]);
295
+
296
+ nodeColors = {};
297
+ allNodes = nodes.get({ returnType: "Object" });
298
+ for (nodeId in allNodes) {
299
+ nodeColors[nodeId] = allNodes[nodeId].color;
300
+ }
301
+ allEdges = edges.get({ returnType: "Object" });
302
+ // adding nodes and edges to the graph
303
+ data = {nodes: nodes, edges: edges};
304
+
305
+ var options = {
306
+ "configure": {
307
+ "enabled": true,
308
+ "filter": [
309
+ "physics",
310
+ "nodes",
311
+ "edges"
312
+ ]
313
+ },
314
+ "edges": {
315
+ "color": {
316
+ "inherit": true
317
+ },
318
+ "smooth": {
319
+ "enabled": true,
320
+ "type": "dynamic"
321
+ }
322
+ },
323
+ "interaction": {
324
+ "dragNodes": true,
325
+ "hideEdgesOnDrag": false,
326
+ "hideNodesOnDrag": false
327
+ },
328
+ "physics": {
329
+ "barnesHut": {
330
+ "avoidOverlap": 0,
331
+ "centralGravity": 0.1,
332
+ "damping": 0.09,
333
+ "gravitationalConstant": -8000,
334
+ "springConstant": 0.005,
335
+ "springLength": 150
336
+ },
337
+ "enabled": true,
338
+ "stabilization": {
339
+ "enabled": true,
340
+ "fit": true,
341
+ "iterations": 1000,
342
+ "onlyDynamicEdges": false,
343
+ "updateInterval": 50
344
+ }
345
+ }
346
+ };
347
+
348
+
349
+
350
+
351
+
352
+ // if this network requires displaying the configure window,
353
+ // put it in its div
354
+ options.configure["container"] = document.getElementById("config");
355
+
356
+
357
+ network = new vis.Network(container, data, options);
358
+
359
+
360
+
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+ return network;
369
+
370
+ }
371
+ drawGraph();
372
+ </script>
373
+ </body>
374
+ </html>
concept_similarities.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d17d91d0e64c82d91352c7d178c5f0bf6d19719c3c190036fad8395f7652fa72
3
+ size 3421
concepts.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c8e21aae40aeb1ca4155d06f5df51e5d63be4aecb619ad8d15f7fbca58e9a7e6
3
+ size 3200
documents.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:158b86abf339fb8862e128d62c1e64e4dc4536a3937eaf85e44e839eb12448f4
3
+ size 3921
extractor.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/extraction/extractor.py (AttributeError DÜZELTİLMİŞ TAM KOD)
2
+
3
+ import spacy
4
+ from pathlib import Path
5
+ import logging
6
+ import itertools
7
+ import re
8
+ import string
9
+
10
+ # Yerel modüllerimizi içe aktaralım
11
+ from src.data_management import storage
12
+ from src.data_management import loaders # extract_text_from_pdf için
13
+
14
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
15
+
16
+ # --- spaCy Model Yükleme ---
17
+ nlp = None
18
+ STOP_WORDS = set()
19
+ try:
20
+ nlp = spacy.load("en_core_web_lg")
21
+ logging.info("spaCy 'en_core_web_lg' modeli başarıyla yüklendi.")
22
+ STOP_WORDS = nlp.Defaults.stop_words
23
+ except OSError:
24
+ logging.error("spaCy 'en_core_web_lg' modeli bulunamadı. Lütfen indirin: python -m spacy download en_core_web_lg")
25
+
26
+ # --- Konsept Belirleme Kriterleri (Aynı kaldı) ---
27
+ TRUSTED_ENTITY_LABELS = {"PRODUCT", "ORG", "WORK_OF_ART"}
28
+ OTHER_ENTITY_LABELS = {"PERSON", "EVENT", "LAW", "NORP", "FAC", "GPE", "LOC"}
29
+ NOUN_CHUNK_PATTERNS = re.compile(r".*\b(learning|network|model|algorithm|system|technique|approach|agent|layer|architecture|transformer|attention)\b$", re.IGNORECASE)
30
+ MIN_CONCEPT_WORDS = 1
31
+ MAX_CONCEPT_WORDS = 6
32
+ AI_KEYWORDS = {"artificial intelligence", "machine learning", "deep learning",
33
+ "neural network", "reinforcement learning", "transformer", "llm",
34
+ "large language model", "computer vision", "natural language processing",
35
+ "algorithm", "model", "gpt", "bert", "agent", "attention", "supervised",
36
+ "unsupervised", "classification", "regression", "clustering"}
37
+ # --- İlişki Çıkarımı için Fiiller ve Desenler ---
38
+ RELATION_VERBS = {
39
+ "use": "USES", "utilize": "USES", "apply": "USES", "employ": "USES",
40
+ "improve": "IMPROVES", "enhance": "IMPROVES", "extend": "IMPROVES", "outperform": "IMPROVES",
41
+ "base on": "BASED_ON", "rely on": "BASED_ON",
42
+ "compare": "COMPARES_TO", "relate": "RELATED_TO", "associate": "RELATED_TO", "link": "RELATED_TO",
43
+ "propose": "PROPOSES", "introduce": "PROPOSES", "develop": "PROPOSES",
44
+ }
45
+
46
+ def normalize_and_validate_concept(text: str, is_entity: bool = False, entity_label: str = "") -> str | None:
47
+ """ Verilen metni temizler, doğrular... """
48
+ cleaned_text = text.strip()
49
+ word_count = len(cleaned_text.split())
50
+ if not (MIN_CONCEPT_WORDS <= word_count <= MAX_CONCEPT_WORDS): return None
51
+ if cleaned_text and all(word.lower() in STOP_WORDS for word in re.findall(r'\b\w+\b', cleaned_text)): return None
52
+ if cleaned_text.isdigit() or all(c in string.punctuation for c in cleaned_text): return None
53
+ generic_phrases = {"this approach", "these models", "this technique", "this system",
54
+ "the model", "the algorithm", "the method", "the approach",
55
+ "the system", "the technique", "our model", "our approach"}
56
+ if cleaned_text.lower() in generic_phrases: return None
57
+ return cleaned_text
58
+
59
+ def find_verb_relation(token1: spacy.tokens.Token, token2: spacy.tokens.Token) -> tuple[str, str] | None:
60
+ """ İki token arasındaki dependency path'e bakarak fiil ilişkisi bulur. """
61
+ common_ancestor = None
62
+ ancestors1 = list(token1.ancestors)
63
+ ancestors2 = list(token2.ancestors)
64
+ for t in reversed(ancestors1):
65
+ if t in ancestors2:
66
+ common_ancestor = t
67
+ break
68
+ if not common_ancestor: return None
69
+
70
+ verb1 = None; head = token1
71
+ while head != common_ancestor:
72
+ if head.pos_ == "VERB": verb1 = head; break
73
+ head = head.head
74
+ verb2 = None; head = token2
75
+ while head != common_ancestor:
76
+ if head.pos_ == "VERB": verb2 = head; break
77
+ head = head.head
78
+
79
+ verb_token = None
80
+ if common_ancestor.pos_ == "VERB": verb_token = common_ancestor
81
+ elif verb1 and verb1 == verb2: verb_token = verb1
82
+ # elif verb1: verb_token = verb1 # Tek taraflı fiilleri şimdilik yoksayalım
83
+ # elif verb2: verb_token = verb2
84
+ elif common_ancestor.head.pos_ == "VERB": verb_token = common_ancestor.head
85
+
86
+ if verb_token:
87
+ verb_lemma = verb_token.lemma_
88
+ # *** HATA DÜZELTME: Bu satırı geçici olarak kaldırıyoruz/yorum yapıyoruz ***
89
+ # if verb_token.is_aux or verb_token.is_stop:
90
+ # return None
91
+ # **********************************************************************
92
+ for verb, rel_type in RELATION_VERBS.items():
93
+ if verb_lemma == verb or verb_lemma in verb.split():
94
+ logging.debug(f"Fiil ilişkisi bulundu: {token1.text}... {verb_lemma} ({rel_type}) ...{token2.text}")
95
+ return rel_type, verb_lemma
96
+ return None
97
+
98
+ def extract_entities_and_relations(text: str, doc_id: str):
99
+ """ Metinden konseptleri, mention'ları ve İYİLEŞTİRİLMİŞ ilişkileri çıkarır. """
100
+ if not nlp: raise RuntimeError("spaCy modeli yüklenemedi.")
101
+ spacy_doc = nlp(text)
102
+ potential_concepts = {}; mentions_in_doc = []; valid_mentions = {}
103
+ processed_spans = set(); added_relations = set()
104
+
105
+ # 1. Adayları Bul
106
+ candidates = []
107
+ for ent in spacy_doc.ents:
108
+ if ent.label_ in TRUSTED_ENTITY_LABELS or ent.label_ in OTHER_ENTITY_LABELS:
109
+ candidates.append({"span": ent, "is_entity": True, "label": ent.label_})
110
+ for chunk in spacy_doc.noun_chunks:
111
+ is_covered = any(ent_data["span"].start_char <= chunk.start_char and ent_data["span"].end_char >= chunk.end_char
112
+ for ent_data in candidates if ent_data["is_entity"])
113
+ if not is_covered:
114
+ candidates.append({"span": chunk, "is_entity": False, "label": ""})
115
+
116
+ # 2. Adayları Filtrele, Normalleştir ve Kaydet
117
+ for data in candidates:
118
+ span = data["span"];
119
+ if span in processed_spans: continue
120
+ validated_text = normalize_and_validate_concept(span.text, data["is_entity"], data["label"])
121
+ if not validated_text: processed_spans.add(span); continue
122
+ concept_lemma = span.lemma_.lower().strip() if span.lemma_ else validated_text.lower()
123
+ is_concept = False
124
+ if data["is_entity"] and data["label"] in TRUSTED_ENTITY_LABELS: is_concept = True
125
+ elif NOUN_CHUNK_PATTERNS.match(validated_text): is_concept = True
126
+ elif any(keyword in concept_lemma.split() or keyword in validated_text.lower().split() for keyword in AI_KEYWORDS): is_concept = True
127
+ elif validated_text.isupper() and len(validated_text) > 1 and len(validated_text) < 6: is_concept = True
128
+
129
+ if is_concept:
130
+ concept_id = storage.add_concept(validated_text)
131
+ if concept_id:
132
+ mention_id = storage.add_mention(
133
+ doc_id=doc_id, concept_id=concept_id,
134
+ context=span.sent.text, start=span.start_char, end=span.end_char
135
+ )
136
+ if mention_id:
137
+ mention_data = {
138
+ "mention_id": mention_id, "concept_id": concept_id,
139
+ "start_char": span.start_char, "end_char": span.end_char,
140
+ "sentence": span.sent, "root_token": span.root
141
+ }
142
+ mentions_in_doc.append(mention_data); valid_mentions[mention_id] = mention_data
143
+ processed_spans.add(span)
144
+
145
+ # 3. İlişkileri Çıkar
146
+ for sentence in spacy_doc.sents:
147
+ mentions_in_sentence = [m for m in mentions_in_doc if m["sentence"] == sentence]
148
+ if len(mentions_in_sentence) >= 2:
149
+ for m1_data, m2_data in itertools.combinations(mentions_in_sentence, 2):
150
+ c1_id = m1_data["concept_id"]; c2_id = m2_data["concept_id"]
151
+ if c1_id == c2_id: continue
152
+ rel_pair = tuple(sorted((c1_id, c2_id)))
153
+ if rel_pair in added_relations: continue
154
+ relation_found = False
155
+ relation_info = find_verb_relation(m1_data["root_token"], m2_data["root_token"])
156
+ if relation_info:
157
+ rel_type, verb = relation_info
158
+ storage.add_relationship(
159
+ source_concept_id=c1_id, target_concept_id=c2_id, rel_type=rel_type,
160
+ mention_id=m1_data["mention_id"], doc_id=doc_id, sentence=sentence.text
161
+ )
162
+ relation_found = True; added_relations.add(rel_pair)
163
+ if not relation_found:
164
+ storage.add_relationship(
165
+ source_concept_id=c1_id, target_concept_id=c2_id, rel_type="RELATED_TO",
166
+ mention_id=m1_data["mention_id"], doc_id=doc_id, sentence=sentence.text
167
+ )
168
+ added_relations.add(rel_pair)
169
+
170
+ def process_documents_for_extraction():
171
+ """ Dokümanları işler ve durumu günceller... (Öncekiyle aynı) """
172
+ if not nlp: raise RuntimeError("spaCy modeli yüklenemedi.")
173
+ logging.info("Gelişmiş bilgi çıkarımı için dokümanlar işleniyor...")
174
+ documents_df = storage.load_dataframe('documents', storage.DOC_COLUMNS)
175
+ docs_to_process = documents_df[documents_df['status'] == 'added']
176
+ if docs_to_process.empty:
177
+ logging.info("Durumu 'added' olan ve işlenecek doküman bulunamadı.")
178
+ return
179
+ processed_count = 0; failed_count = 0
180
+ for index, doc_row in docs_to_process.iterrows():
181
+ doc_id = doc_row['doc_id']; filepath = Path(doc_row['filepath'])
182
+ logging.info(f"İşleniyor: {filepath.name} (ID: {doc_id})")
183
+ text = loaders.extract_text_from_pdf(filepath)
184
+ if text:
185
+ try:
186
+ extract_entities_and_relations(text, doc_id)
187
+ storage.update_document_status(doc_id, 'processed_v3') # Yeni versiyon durumu
188
+ processed_count += 1
189
+ except Exception as e:
190
+ logging.exception(f"'{filepath.name}' işlenirken BEKLENMEYEN HATA oluştu: {e}")
191
+ storage.update_document_status(doc_id, 'extraction_failed_v3')
192
+ failed_count += 1
193
+ else:
194
+ logging.warning(f"Metin çıkarılamadı: {filepath.name}")
195
+ storage.update_document_status(doc_id, 'text_extraction_failed')
196
+ failed_count += 1
197
+ logging.info(f"Gelişmiş bilgi çıkarımı tamamlandı. Başarılı: {processed_count}, Başarısız: {failed_count}")
loaders.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import PyPDF2 # PDF dosyalarını okumak için
2
+ from pathlib import Path
3
+ from datetime import datetime
4
+ import logging
5
+ import re # Tarih ayrıştırma için Regular Expressions
6
+
7
+ # Mevcut modüldeki storage fonksiyonlarını içe aktar (aynı klasörde olduğu için .)
8
+ from .storage import add_document, load_dataframe, save_dataframe, DOC_COLUMNS
9
+
10
+ # Ham veri klasörünün yolu
11
+ RAW_DATA_PATH = Path("data/raw")
12
+
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
14
+
15
+ def extract_text_from_pdf(pdf_path: Path) -> str | None:
16
+ """
17
+ Verilen PDF dosyasının metin içeriğini çıkarır.
18
+
19
+ Args:
20
+ pdf_path (Path): PDF dosyasının yolu.
21
+
22
+ Returns:
23
+ str | None: Çıkarılan metin veya hata durumunda None.
24
+ """
25
+ try:
26
+ with open(pdf_path, 'rb') as file:
27
+ reader = PyPDF2.PdfReader(file)
28
+ text = ""
29
+ for page in reader.pages:
30
+ page_text = page.extract_text()
31
+ if page_text:
32
+ text += page_text + "\n" # Sayfalar arasına yeni satır ekle
33
+ logging.info(f"Metin çıkarıldı: {pdf_path.name}")
34
+ return text
35
+ except Exception as e:
36
+ logging.error(f"PDF metni çıkarılırken hata ({pdf_path.name}): {e}")
37
+ # Şifreli PDF'ler veya bozuk dosyalar PyPDF2 tarafından hata verebilir
38
+ if "password" in str(e).lower():
39
+ logging.warning(f"Dosya şifreli olabilir: {pdf_path.name}")
40
+ return None
41
+
42
+ def parse_date_from_filename(filename: str) -> datetime | None:
43
+ """
44
+ Dosya adından YYYY-MM-DD veya YYYYMMDD formatında tarih ayrıştırmaya çalışır.
45
+
46
+ Args:
47
+ filename (str): Dosya adı.
48
+
49
+ Returns:
50
+ datetime | None: Bulunan tarih veya None.
51
+ """
52
+ # Örnek: 2023-10-26_paper.pdf, 20231026-paper.pdf, 2023_10_26 paper.pdf
53
+ patterns = [
54
+ r"(\d{4}-\d{2}-\d{2})", # YYYY-MM-DD
55
+ r"(\d{4}_\d{2}_\d{2})", # YYYY_MM_DD
56
+ r"(\d{8})" # YYYYMMDD
57
+ ]
58
+ for pattern in patterns:
59
+ match = re.search(pattern, filename)
60
+ if match:
61
+ date_str = match.group(1).replace("_", "-") # Alt çizgiyi tireye çevir
62
+ try:
63
+ # Sadece tarih kısmını al, saat bilgisi ekleme
64
+ return datetime.strptime(date_str, '%Y-%m-%d').date()
65
+ except ValueError:
66
+ continue # Geçersiz tarih formatı varsa diğer deseni dene
67
+ logging.warning(f"Dosya adından geçerli tarih ayrıştırılamadı: {filename}")
68
+ return None
69
+
70
+ def process_raw_documents():
71
+ """
72
+ 'data/raw/' klasöründeki tüm PDF dosyalarını işler,
73
+ tarihlerini ayrıştırır ve sisteme ekler (eğer zaten ekli değillerse).
74
+ """
75
+ if not RAW_DATA_PATH.exists():
76
+ logging.error(f"Ham veri klasörü bulunamadı: {RAW_DATA_PATH}")
77
+ return
78
+
79
+ logging.info(f"'{RAW_DATA_PATH}' klasöründeki PDF dosyaları işleniyor...")
80
+ processed_count = 0
81
+ added_count = 0
82
+
83
+ # Tüm PDF dosyalarını bul
84
+ pdf_files = list(RAW_DATA_PATH.glob('*.pdf'))
85
+
86
+ if not pdf_files:
87
+ logging.warning(f"'{RAW_DATA_PATH}' klasöründe işlenecek PDF dosyası bulunamadı.")
88
+ return
89
+
90
+ for pdf_path in pdf_files:
91
+ processed_count += 1
92
+ filename = pdf_path.name
93
+ filepath_str = str(pdf_path.resolve()) # Tam dosya yolunu al
94
+
95
+ # Dosya adından tarihi ayrıştır
96
+ publication_date = parse_date_from_filename(filename)
97
+
98
+ if publication_date:
99
+ # Dokümanı sisteme ekle (storage modülünü kullanarak)
100
+ # add_document, zaten varsa None yerine mevcut ID'yi döndürecek şekilde güncellendi
101
+ doc_id = add_document(filepath_str, publication_date)
102
+ if doc_id:
103
+ # Eğer yeni eklendiyse (veya mevcut ID döndüyse), sayacı artırabiliriz
104
+ # Şimdilik sadece eklenip eklenmediğini kontrol etmek yeterli
105
+ # Gerçek ekleme 'add_document' içinde loglanıyor
106
+ pass # Şimdilik ek bir işlem yapmıyoruz
107
+
108
+ else:
109
+ logging.warning(f"'{filename}' için yayın tarihi bulunamadı, doküman eklenemedi.")
110
+
111
+ logging.info(f"Toplam {processed_count} PDF dosyası tarandı.")
112
+ # Gerçekte kaç tane yeni eklendiği bilgisini storage loglarından takip edebiliriz.
113
+
114
+ # --- Metin Çıkarma ve Kaydetme (Sonraki Fazlar İçin Hazırlık) ---
115
+ # İleride bu fonksiyonu çağırıp metinleri ayrı dosyalara kaydedebiliriz
116
+ # ve documents_df'i güncelleyebiliriz.
117
+ #
118
+ # def extract_and_save_text(doc_id: str, pdf_path: Path):
119
+ # text = extract_text_from_pdf(pdf_path)
120
+ # if text:
121
+ # # Metni kaydet (örn: data/processed_data/text/{doc_id}.txt)
122
+ # text_path = DATA_PATH / "text" / f"{doc_id}.txt"
123
+ # text_path.parent.mkdir(parents=True, exist_ok=True)
124
+ # try:
125
+ # with open(text_path, 'w', encoding='utf-8') as f:
126
+ # f.write(text)
127
+ # logging.info(f"Metin '{text_path}' olarak kaydedildi.")
128
+ # # documents_df'i güncelle (status='text_extracted', processed_text_path=str(text_path))
129
+ # docs_df = load_dataframe('documents', DOC_COLUMNS)
130
+ # doc_index = docs_df[docs_df['doc_id'] == doc_id].index
131
+ # if not doc_index.empty:
132
+ # docs_df.loc[doc_index, 'status'] = 'text_extracted'
133
+ # docs_df.loc[doc_index, 'processed_text_path'] = str(text_path)
134
+ # save_dataframe(docs_df, 'documents')
135
+ # except Exception as e:
136
+ # logging.error(f"Metin kaydedilirken hata ({doc_id}): {e}")
mentions.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:49749194f77092f6c3b9e6eacd4ef3a3c34f9d5d1f9c766a51123bdc57885c24
3
+ size 9877
network_analysis.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/analysis/network_analysis.py
2
+
3
+ import networkx as nx
4
+ import pandas as pd
5
+ import logging
6
+
7
+ # Topluluk tespiti için Louvain metodu (önce 'pip install python-louvain community' yapılmalı)
8
+ try:
9
+ import community.community_louvain as community_louvain
10
+ community_lib_available = True
11
+ except ImportError:
12
+ logging.warning("'community' (python-louvain) kütüphanesi bulunamadı. Topluluk tespiti yapılamayacak. Kurulum için: pip install python-louvain community")
13
+ community_lib_available = False
14
+
15
+ # Yerel modüller
16
+ from src.data_management import storage
17
+
18
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
19
+
20
+ def calculate_centrality(graph: nx.Graph) -> dict:
21
+ """
22
+ Graf üzerindeki düğümler için merkeziyet metriklerini hesaplar.
23
+
24
+ Args:
25
+ graph (nx.Graph): Analiz edilecek NetworkX grafı.
26
+
27
+ Returns:
28
+ dict: {node_id: {'degree': float, 'betweenness': float, 'eigenvector': float (veya None)}}
29
+ formatında metrikleri içeren sözlük.
30
+ """
31
+ metrics = {}
32
+ if not graph or graph.number_of_nodes() == 0:
33
+ return metrics
34
+
35
+ try:
36
+ degree_centrality = nx.degree_centrality(graph)
37
+ except Exception as e:
38
+ logging.error(f"Degree Centrality hesaplanırken hata: {e}")
39
+ degree_centrality = {}
40
+
41
+ try:
42
+ betweenness_centrality = nx.betweenness_centrality(graph)
43
+ except Exception as e:
44
+ logging.error(f"Betweenness Centrality hesaplanırken hata: {e}")
45
+ betweenness_centrality = {}
46
+
47
+ try:
48
+ # Eigenvector centrality bağlantısız (disconnected) graflarda veya bazı durumlarda hata verebilir
49
+ # max_iter artırılabilir veya hata yakalanabilir
50
+ eigenvector_centrality = nx.eigenvector_centrality(graph, max_iter=500, tol=1e-06)
51
+ except Exception as e:
52
+ logging.warning(f"Eigenvector Centrality hesaplanırken hata (graf bağlantısız olabilir): {e}")
53
+ eigenvector_centrality = {} # Hata durumunda boş bırak
54
+
55
+ # Metrikleri birleştir
56
+ for node in graph.nodes():
57
+ metrics[node] = {
58
+ 'degree_centrality': degree_centrality.get(node, 0.0),
59
+ 'betweenness_centrality': betweenness_centrality.get(node, 0.0),
60
+ 'eigenvector_centrality': eigenvector_centrality.get(node, None) # Hata durumunda None olabilir
61
+ }
62
+ logging.info("Merkeziyet metrikleri hesaplandı.")
63
+ return metrics
64
+
65
+ def detect_communities(graph: nx.Graph) -> dict | None:
66
+ """
67
+ Louvain algoritması kullanarak graf üzerindeki toplulukları tespit eder.
68
+
69
+ Args:
70
+ graph (nx.Graph): Analiz edilecek NetworkX grafı.
71
+
72
+ Returns:
73
+ dict | None: {node_id: community_id} formatında bölümleme sözlüğü veya hata/kütüphane yoksa None.
74
+ """
75
+ if not community_lib_available:
76
+ return None # Kütüphane yoksa hesaplama yapma
77
+ if not graph or graph.number_of_nodes() == 0:
78
+ return None # Boş graf
79
+
80
+ # Louvain metodu yönlendirilmemiş graflarda daha iyi çalışır.
81
+ # Eğer graf yönlü ise, yönlendirilmemişe çevir (veya uyarı ver).
82
+ # Bizim grafımız zaten yönlendirilmemiş (nx.Graph).
83
+ # Ağırlıklı kenarları kullanabilir (varsayılan weight='weight')
84
+ try:
85
+ partition = community_louvain.best_partition(graph, weight='weight') # Kenar ağırlıklarını dikkate al
86
+ num_communities = len(set(partition.values()))
87
+ logging.info(f"Louvain ile topluluk tespiti tamamlandı. {num_communities} topluluk bulundu.")
88
+ return partition
89
+ except Exception as e:
90
+ logging.exception(f"Topluluk tespiti sırasında hata oluştu: {e}")
91
+ return None
92
+
93
+
94
+ def get_network_analysis_results(graph: nx.Graph) -> pd.DataFrame | None:
95
+ """
96
+ Merkeziyet ve topluluk analizlerini yapar ve sonuçları bir DataFrame'de birleştirir.
97
+
98
+ Args:
99
+ graph (nx.Graph): Analiz edilecek NetworkX grafı.
100
+
101
+ Returns:
102
+ pd.DataFrame | None: 'concept_id', 'name', 'degree_centrality', 'betweenness_centrality',
103
+ 'eigenvector_centrality', 'community_id' sütunlarını içeren DataFrame
104
+ veya hata durumunda None.
105
+ """
106
+ if not graph or graph.number_of_nodes() == 0:
107
+ logging.warning("Analiz için boş veya geçersiz graf sağlandı.")
108
+ return None
109
+
110
+ logging.info("Ağ analizi metrikleri hesaplanıyor...")
111
+ centrality_metrics = calculate_centrality(graph)
112
+ community_partition = detect_communities(graph)
113
+
114
+ # Sonuçları bir DataFrame'e dönüştür
115
+ analysis_data = []
116
+ concepts_df = storage.load_dataframe('concepts', storage.CONCEPT_COLUMNS) # İsimler için yükle
117
+
118
+ for node_id, metrics in centrality_metrics.items():
119
+ node_data = {
120
+ 'concept_id': node_id,
121
+ 'name': graph.nodes[node_id].get('name', 'N/A'), # Graf düğümünden al
122
+ 'degree_centrality': metrics.get('degree_centrality'),
123
+ 'betweenness_centrality': metrics.get('betweenness_centrality'),
124
+ 'eigenvector_centrality': metrics.get('eigenvector_centrality'),
125
+ 'community_id': community_partition.get(node_id, -1) if community_partition else -1 # Topluluk yoksa -1
126
+ }
127
+ analysis_data.append(node_data)
128
+
129
+ if not analysis_data:
130
+ logging.warning("Ağ analizi sonucu veri üretilemedi.")
131
+ return None
132
+
133
+ analysis_df = pd.DataFrame(analysis_data)
134
+
135
+ # Eğer graf düğümlerinde isim yoksa, concepts_df'ten almayı dene (yedek)
136
+ if 'N/A' in analysis_df['name'].values and concepts_df is not None:
137
+ analysis_df = analysis_df.drop(columns=['name']) # Eski 'name' sütununu sil
138
+ analysis_df = pd.merge(analysis_df, concepts_df[['concept_id', 'name']], on='concept_id', how='left')
139
+ # Sütun sırasını ayarla
140
+ cols = ['concept_id', 'name'] + [col for col in analysis_df.columns if col not in ['concept_id', 'name']]
141
+ analysis_df = analysis_df[cols]
142
+
143
+
144
+ logging.info("Ağ analizi sonuçları DataFrame'e dönüştürüldü.")
145
+ return analysis_df
146
+
147
+
148
+ def save_network_analysis(analysis_df: pd.DataFrame):
149
+ """ Ağ analizi sonuçlarını Parquet dosyasına kaydeder. """
150
+ if analysis_df is not None and not analysis_df.empty:
151
+ storage.save_dataframe(analysis_df, storage.NETWORK_ANALYSIS_FILENAME)
152
+ logging.info(f"Ağ analizi sonuçları '{storage.NETWORK_ANALYSIS_FILENAME}.parquet' olarak kaydedildi.")
153
+ else:
154
+ logging.warning("Kaydedilecek ağ analizi sonucu bulunamadı.")
network_builder.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/analysis/network_builder.py (DÜZELTİLMİŞ TAM KOD)
2
+
3
+ import networkx as nx
4
+ import pandas as pd
5
+ import logging
6
+
7
+ # Yerel modüller
8
+ from src.data_management import storage
9
+
10
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
11
+
12
+ # Grafı kaydetmek için dosya adı
13
+ GRAPH_FILENAME = "concept_network"
14
+ # Benzerlik dosyasının adı (Doğrudan burada tanımlayalım veya similarity'den import edelim)
15
+ # storage modülünde değil!
16
+ SIMILARITY_FILENAME = "concept_similarities"
17
+
18
+ def build_concept_network(similarity_threshold: float = 0.60,
19
+ include_similarity_edges: bool = True,
20
+ include_extracted_edges: bool = True) -> nx.Graph | None:
21
+ """
22
+ Konseptler, çıkarılmış ilişkiler ve anlamsal benzerliklerden bir NetworkX grafı oluşturur.
23
+
24
+ Args:
25
+ similarity_threshold (float): Grafiğe eklenecek minimum anlamsal benzerlik skoru.
26
+ include_similarity_edges (bool): Benzerlik kenarlarını dahil et.
27
+ include_extracted_edges (bool): Metinden çıkarılan ilişki kenarlarını dahil et.
28
+
29
+ Returns:
30
+ nx.Graph | None: Oluşturulan NetworkX grafı veya hata durumunda None.
31
+ """
32
+ logging.info("Konsept ağı oluşturuluyor...")
33
+ if not include_similarity_edges and not include_extracted_edges:
34
+ logging.warning("Hem benzerlik hem de çıkarılmış ilişki kenarları devre dışı bırakıldı.")
35
+
36
+ # Temel verileri yükle
37
+ concepts_df = storage.load_dataframe('concepts', storage.CONCEPT_COLUMNS)
38
+ relationships_df = storage.load_dataframe('relationships', storage.RELATIONSHIP_COLUMNS)
39
+ # *** DÜZELTME: SIMILARITY_FILENAME doğrudan kullanılıyor ***
40
+ similarity_df = storage.load_dataframe(SIMILARITY_FILENAME, ['concept_id_1', 'concept_id_2', 'similarity'])
41
+
42
+ if concepts_df is None or concepts_df.empty:
43
+ logging.error("Ağ oluşturmak için konsept verisi bulunamadı.")
44
+ return None
45
+
46
+ G = nx.Graph()
47
+
48
+ # 1. Adım: Konseptleri Düğüm Olarak Ekle
49
+ node_count = 0
50
+ valid_concept_ids = set() # Grafiğe eklenen geçerli ID'leri takip et
51
+ for index, row in concepts_df.iterrows():
52
+ concept_id = row['concept_id']
53
+ concept_name = row['name']
54
+ if pd.notna(concept_id) and pd.notna(concept_name):
55
+ G.add_node(concept_id, name=concept_name)
56
+ valid_concept_ids.add(concept_id)
57
+ node_count += 1
58
+ else:
59
+ logging.warning(f"Geçersiz konsept verisi atlandı: ID={concept_id}, Name={concept_name}")
60
+ logging.info(f"{node_count} konsept düğüm olarak eklendi.")
61
+
62
+ edge_count_extracted = 0
63
+ edge_count_similarity = 0
64
+ updated_edge_count = 0
65
+
66
+ # 2. Adım: Çıkarılmış İlişkileri Kenar Olarak Ekle
67
+ if include_extracted_edges and relationships_df is not None and not relationships_df.empty:
68
+ logging.info("Çıkarılmış ilişkiler kenar olarak ekleniyor...")
69
+ for index, row in relationships_df.iterrows():
70
+ source_id = row['source_concept_id']
71
+ target_id = row['target_concept_id']
72
+ rel_type = row['type'] or 'RELATED_TO'
73
+
74
+ # Düğümlerin grafide olduğundan ve geçerli olduğundan emin ol
75
+ if source_id in valid_concept_ids and target_id in valid_concept_ids:
76
+ if G.has_edge(source_id, target_id):
77
+ G.edges[source_id, target_id]['relation_type'] = rel_type
78
+ G.edges[source_id, target_id]['type'] = 'extracted'
79
+ else:
80
+ G.add_edge(source_id, target_id, type='extracted', relation_type=rel_type, weight=0.8)
81
+ edge_count_extracted += 1
82
+ else:
83
+ logging.warning(f"İlişki için düğüm(ler) bulunamadı veya geçersiz: {source_id} -> {target_id}")
84
+ logging.info(f"{edge_count_extracted} çıkarılmış ilişki kenarı eklendi.")
85
+
86
+ # 3. Adım: Anlamsal Benzerlikleri Kenar Olarak Ekle
87
+ if include_similarity_edges and similarity_df is not None and not similarity_df.empty:
88
+ logging.info(f"Anlamsal benzerlikler (Eşik > {similarity_threshold:.2f}) kenar olarak ekleniyor...")
89
+ filtered_similarity = similarity_df[(similarity_df['similarity'] >= similarity_threshold) & (similarity_df['similarity'] < 1.0)]
90
+ logging.info(f"{len(similarity_df)} benzerlik çiftinden {len(filtered_similarity)} tanesi eşik değerinin üzerinde (ve < 1.0).")
91
+
92
+ for index, row in filtered_similarity.iterrows():
93
+ id1 = row['concept_id_1']
94
+ id2 = row['concept_id_2']
95
+ similarity = row['similarity']
96
+
97
+ if id1 in valid_concept_ids and id2 in valid_concept_ids:
98
+ if G.has_edge(id1, id2):
99
+ G.edges[id1, id2]['similarity'] = similarity
100
+ if 'weight' not in G.edges[id1, id2] or similarity > G.edges[id1, id2].get('weight', 0):
101
+ G.edges[id1, id2]['weight'] = similarity
102
+ # Eğer extracted ilişki varsa, tipi 'combined' yapabiliriz?
103
+ G.edges[id1, id2]['type'] = 'combined' if G.edges[id1, id2].get('type') == 'extracted' else G.edges[id1, id2].get('type', 'similarity') # Önceliği koru veya birleştir
104
+ updated_edge_count += 1
105
+ else:
106
+ G.add_edge(id1, id2, type='similarity', weight=similarity)
107
+ edge_count_similarity += 1
108
+ else:
109
+ logging.warning(f"Benzerlik için düğüm(ler) bulunamadı veya geçersiz: {id1} <-> {id2}")
110
+ logging.info(f"{edge_count_similarity} yeni benzerlik kenarı eklendi, {updated_edge_count} mevcut kenara benzerlik/tip bilgisi eklendi.")
111
+
112
+ total_edges = G.number_of_edges()
113
+ logging.info(f"Konsept ağı oluşturuldu. Düğüm sayısı: {G.number_of_nodes()}, Kenar sayısı: {total_edges}.")
114
+
115
+ # 4. Adım: Grafı Kaydet
116
+ storage.save_network(G, GRAPH_FILENAME)
117
+
118
+ return G
plotting.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/visualization/plotting.py (Ağ Metrikleri ile Görselleştirme Güncellendi)
2
+
3
+ import networkx as nx
4
+ from pyvis.network import Network
5
+ import logging
6
+ from pathlib import Path
7
+ import pandas as pd
8
+ import random # Renk paleti için
9
+
10
+ # Yerel modüller
11
+ from src.data_management import storage
12
+
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
14
+
15
+ # Görselleştirme dosyalarının kaydedileceği yer
16
+ OUTPUT_DIR = Path("output/graphs")
17
+ DEFAULT_GRAPH_FILENAME = "concept_network"
18
+ # Analiz sonuçları dosyasının adı (storage'dan da alınabilirdi)
19
+ DEFAULT_ANALYSIS_FILENAME = storage.NETWORK_ANALYSIS_FILENAME
20
+
21
+
22
+ # Basit bir renk paleti (daha fazla renk eklenebilir veya matplotlib colormap kullanılabilir)
23
+ # Viridis, tab10, Set3 gibi paletler iyi çalışır
24
+ # Örnek: import matplotlib.cm as cm; colors = [cm.tab10(i) for i in range(10)]
25
+ DEFAULT_COLORS = [
26
+ "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
27
+ "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"
28
+ ]
29
+
30
+ def get_color_for_community(community_id, colors=DEFAULT_COLORS):
31
+ """ Verilen community ID için paletten bir renk döndürür. """
32
+ if community_id < 0 or community_id is None or pd.isna(community_id): # Topluluk yoksa veya geçersizse
33
+ return "#CCCCCC" # Gri
34
+ return colors[int(community_id) % len(colors)] # Modulo ile renk tekrarı
35
+
36
+ def scale_value(value, min_val=0, max_val=1, new_min=10, new_max=50):
37
+ """ Bir değeri belirli bir aralığa ölçekler (örn: merkeziyet -> düğüm boyutu). """
38
+ if max_val == min_val or value is None or pd.isna(value): # Bölme hatasını veya None değerini engelle
39
+ return new_min # Veya ortalama bir değer?
40
+ # Ölçekleme: (value - min) / (max - min) * (new_max - new_min) + new_min
41
+ scaled = ((value - min_val) / (max_val - min_val)) * (new_max - new_min) + new_min
42
+ return max(new_min, min(scaled, new_max)) # Sonuçların min/max arasında kalmasını sağla
43
+
44
+
45
+ def visualize_network(graph: nx.Graph | None = None,
46
+ graph_filename: str = DEFAULT_GRAPH_FILENAME,
47
+ analysis_filename: str = DEFAULT_ANALYSIS_FILENAME,
48
+ output_filename: str = "concept_network_visualization.html",
49
+ show_buttons: bool = True,
50
+ physics_solver: str = 'barnesHut',
51
+ size_metric: str = 'degree_centrality', # Boyut için kullanılacak metrik
52
+ color_metric: str = 'community_id', # Renk için kullanılacak metrik
53
+ height: str = "800px",
54
+ width: str = "100%"
55
+ ) -> str | None:
56
+ """
57
+ Ağ grafını Pyvis ile görselleştirir. Düğüm boyutu ve rengi için ağ
58
+ analizi metriklerini kullanır.
59
+ """
60
+ if graph is None:
61
+ logging.info(f"Graf sağlanmadı, '{graph_filename}.pkl' dosyasından yükleniyor...")
62
+ graph = storage.load_network(graph_filename)
63
+
64
+ if graph is None or not isinstance(graph, nx.Graph) or graph.number_of_nodes() == 0:
65
+ logging.error("Görselleştirilecek geçerli veya boş olmayan bir graf bulunamadı.")
66
+ return None
67
+
68
+ # Ağ analizi sonuçlarını yükle
69
+ logging.info(f"Ağ analizi sonuçları '{analysis_filename}.parquet' dosyasından yükleniyor...")
70
+ analysis_df = storage.load_dataframe(analysis_filename, []) # Sütunları bilmediğimiz için boş liste
71
+ metrics_dict = {}
72
+ min_size_val, max_size_val = 0, 1 # Boyut ölçekleme için min/max
73
+
74
+ if analysis_df is not None and not analysis_df.empty and 'concept_id' in analysis_df.columns:
75
+ # Eksik metrik sütunlarını kontrol et ve ekle (NaN ile)
76
+ required_metrics = [size_metric, color_metric]
77
+ for metric in required_metrics:
78
+ if metric not in analysis_df.columns:
79
+ logging.warning(f"Analiz sonuçlarında '{metric}' sütunu bulunamadı. Varsayılan değerler kullanılacak.")
80
+ analysis_df[metric] = None
81
+
82
+ # Boyut metriği için min/max değerleri bul (NaN olmayanlardan)
83
+ if size_metric in analysis_df.columns and analysis_df[size_metric].notna().any():
84
+ min_size_val = analysis_df[size_metric].min()
85
+ max_size_val = analysis_df[size_metric].max()
86
+
87
+ # Kolay erişim için sözlüğe çevir
88
+ metrics_dict = analysis_df.set_index('concept_id').to_dict('index')
89
+ logging.info("Ağ analizi metrikleri yüklendi.")
90
+ else:
91
+ logging.warning("Ağ analizi sonuçları yüklenemedi veya boş. Varsayılan düğüm boyutları/renkleri kullanılacak.")
92
+
93
+
94
+ logging.info(f"'{output_filename}' için Pyvis ağı oluşturuluyor...")
95
+ net = Network(notebook=False, height=height, width=width, heading='ChronoSense Konsept Ağı (Metriklerle)', cdn_resources='remote')
96
+ net.barnes_hut(gravity=-8000, central_gravity=0.1, spring_length=150, spring_strength=0.005, damping=0.09)
97
+
98
+ # Düğümleri (Nodes) Pyvis'e ekle (Boyut ve Renk ile)
99
+ for node, attrs in graph.nodes(data=True):
100
+ node_label = attrs.get('name', str(node))
101
+ node_metrics = metrics_dict.get(node, {}) # Bu düğüm için metrikleri al, yoksa boş dict
102
+
103
+ # Boyutu hesapla
104
+ size_val = node_metrics.get(size_metric)
105
+ node_size = scale_value(size_val, min_size_val, max_size_val, new_min=10, new_max=40) # 10-40 arası boyut
106
+
107
+ # Rengi hesapla
108
+ color_val = node_metrics.get(color_metric)
109
+ node_color = get_color_for_community(color_val)
110
+
111
+ # Başlığı (Title) güncelle (metrikleri ekle)
112
+ node_title = f"ID: {node}<br>Name: {attrs.get('name', 'N/A')}"
113
+ node_title += f"<br>{size_metric}: {size_val:.3f}" if pd.notna(size_val) else ""
114
+ node_title += f"<br>{color_metric}: {int(color_val)}" if pd.notna(color_val) else ""
115
+
116
+ net.add_node(node, label=node_label, title=node_title, size=node_size, color=node_color)
117
+
118
+ # Kenarları (Edges) Pyvis'e ekle (Öncekiyle aynı, sadece renk/kalınlık ayarları biraz daha belirgin)
119
+ for source, target, attrs in graph.edges(data=True):
120
+ edge_title = f"Type: {attrs.get('type', 'N/A')}"
121
+ edge_value = 0.5 ; edge_color = "#DDDDDD" # Daha soluk varsayılan
122
+
123
+ edge_type = attrs.get('type')
124
+ weight = attrs.get('weight', 0)
125
+
126
+ if edge_type == 'extracted':
127
+ edge_title += f"<br>Relation: {attrs.get('relation_type', 'N/A')}"
128
+ edge_value = max(0.6, weight) # extracted ilişkiler biraz daha belirgin olsun
129
+ edge_color = "#FF6347" # Koyu turuncu/kırmızımsı
130
+ elif edge_type == 'similarity':
131
+ sim_score = attrs.get('similarity', weight)
132
+ edge_title += f"<br>Similarity: {sim_score:.3f}"
133
+ edge_value = sim_score # Benzerlikle orantılı
134
+ edge_color = "#4682B4" # Çelik mavisi
135
+ elif edge_type == 'combined':
136
+ edge_title += f"<br>Relation: {attrs.get('relation_type', 'N/A')}"
137
+ sim_score = attrs.get('similarity', weight)
138
+ edge_title += f"<br>Similarity: {sim_score:.3f}"
139
+ edge_value = max(0.6, sim_score) # Combined da belirgin olsun
140
+ edge_color = "#9370DB" # Orta mor
141
+
142
+ net.add_edge(source, target, title=edge_title, value=max(0.1, edge_value), color=edge_color)
143
+
144
+ if show_buttons:
145
+ net.show_buttons(filter_=['physics', 'nodes', 'edges'])
146
+
147
+ try:
148
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
149
+ output_path = OUTPUT_DIR / output_filename
150
+ net.save_graph(str(output_path))
151
+ logging.info(f"Ağ görselleştirmesi başarıyla '{output_path}' olarak kaydedildi.")
152
+ return str(output_path)
153
+ except Exception as e:
154
+ logging.exception(f"Ağ görselleştirmesi kaydedilirken hata oluştu: {e}")
155
+ return None
relationships.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a41341ec01d3c46cd036ac155cc8ea50d773221f34ab578f067bc21d7581f5fe
3
+ size 10289
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ pip install pandas numpy spacy scikit-learn networkx matplotlib plotly pyvis streamlit PyPDF2 sentence-transformers pytest pyarrow
2
+ python -m spacy download en_core_web_lg
reset_status.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # reset_status.py
2
+ import pandas as pd
3
+ # storage modülünü doğru import etmek için src'yi sys.path'e ekleyebilir veya PYTHONPATH ayarlayabiliriz.
4
+ # En kolayı çalıştırmadan önce PYTHONPATH ayarlamak veya geçici olarak sys.path'e eklemek.
5
+ import sys
6
+ from pathlib import Path
7
+ sys.path.insert(0, str(Path(__file__).parent))
8
+
9
+ from src.data_management.storage import load_dataframe, save_dataframe, DOC_COLUMNS
10
+
11
+ print("Doküman durumları 'added' olarak sıfırlanıyor...")
12
+ df = load_dataframe('documents', DOC_COLUMNS)
13
+
14
+ if not df.empty:
15
+ # Sadece işlenmiş veya hata almış olanları sıfırla
16
+ reset_mask = df['status'].str.startswith('processed', na=False) | df['status'].str.contains('failed', na=False)
17
+ if reset_mask.any():
18
+ df.loc[reset_mask, 'status'] = 'added'
19
+ save_dataframe(df, 'documents')
20
+ print(f"{reset_mask.sum()} dokümanın durumu 'added' olarak sıfırlandı.")
21
+ else:
22
+ print("Durumu sıfırlanacak doküman bulunamadı ('processed' veya 'failed' durumunda olan).")
23
+ else:
24
+ print("Doküman DataFrame'i bulunamadı veya boş.")
run_analysis.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # run_analysis.py (Ağ Analizi Metrikleri Eklendi)
2
+ import time
3
+ import pandas as pd
4
+ import sys
5
+ from pathlib import Path
6
+ import networkx as nx
7
+ import webbrowser
8
+ import logging
9
+
10
+ # src klasöründeki modüllere erişim için
11
+ sys.path.insert(0, str(Path(__file__).parent))
12
+
13
+ from src.analysis.temporal import calculate_concept_frequencies
14
+ from src.analysis.similarity import calculate_concept_embeddings, calculate_similarity_matrix
15
+ from src.analysis.network_builder import build_concept_network
16
+ # YENİ importlar:
17
+ from src.analysis.network_analysis import get_network_analysis_results, save_network_analysis
18
+ from src.visualization.plotting import visualize_network
19
+ from src.data_management.storage import load_dataframe, save_dataframe, CONCEPT_COLUMNS, FREQUENCY_FILENAME, SIMILARITY_FILENAME, NETWORK_ANALYSIS_FILENAME # YENİ: NETWORK_ANALYSIS_FILENAME
20
+
21
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
22
+ pd.set_option('display.max_rows', 100)
23
+ pd.set_option('display.max_columns', 10)
24
+ pd.set_option('display.width', 1000)
25
+
26
+ if __name__ == "__main__":
27
+ print(">>> Analizler Çalıştırılıyor (Frekans + Benzerlik + Ağ + Metrikler + Görselleştirme) <<<")
28
+ overall_start_time = time.time()
29
+ concepts_df = None
30
+ frequency_results_df = None
31
+ similarity_results_df = None
32
+ concept_network = None # Grafı saklamak için
33
+ network_analysis_df = None # Analiz sonuçlarını saklamak için
34
+
35
+ # --- 1. Frekans Analizi ---
36
+ print("\n--- 1. Frekans Hesaplaması ---"); start_time = time.time()
37
+ # ... (önceki kodla aynı, sadece print süresi değişebilir) ...
38
+ frequency_df = calculate_concept_frequencies(time_period='YS')
39
+ if frequency_df is not None:
40
+ concepts_df = load_dataframe('concepts', CONCEPT_COLUMNS)
41
+ if not frequency_df.empty:
42
+ print(f"Toplam {len(frequency_df)} frekans kaydı hesaplandı.")
43
+ if concepts_df is not None and not concepts_df.empty:
44
+ frequency_results_df = pd.merge(frequency_df, concepts_df[['concept_id', 'name']], on='concept_id', how='left')
45
+ frequency_results_df = frequency_results_df[['concept_id', 'name', 'time_period_start', 'frequency']]
46
+ frequency_results_df.sort_values(by=['name', 'time_period_start'], inplace=True)
47
+ print("\n--- Konsept Frekansları (Yıllık) ---"); print(frequency_results_df.to_string())
48
+ save_dataframe(frequency_results_df, FREQUENCY_FILENAME)
49
+ else: print("\nKonsept isimleri yüklenemedi..."); print(frequency_df.to_string())
50
+ else: print("Frekans hesaplandı ancak sonuç boş."); save_dataframe(pd.DataFrame(columns=['concept_id', 'name', 'time_period_start', 'frequency']), FREQUENCY_FILENAME)
51
+ else: print("Frekans hesaplaması sırasında bir hata oluştu.")
52
+ print(f"--- Frekans Hesaplaması Tamamlandı. Süre: {time.time() - start_time:.2f} saniye ---")
53
+
54
+ # --- 2. Anlamsal Benzerlik Analizi ---
55
+ print("\n--- 2. Anlamsal Benzerlik Hesaplaması ---"); start_time = time.time()
56
+ # ... (önceki kodla aynı, sadece print süresi değişebilir) ...
57
+ try:
58
+ concept_embeddings = calculate_concept_embeddings(force_recalculate=False)
59
+ if concept_embeddings:
60
+ similarity_df = calculate_similarity_matrix(concept_embeddings, force_recalculate=False)
61
+ if similarity_df is not None and not similarity_df.empty:
62
+ print(f"Toplam {len(similarity_df)} konsept çifti için benzerlik hesaplandı/yüklendi.")
63
+ if concepts_df is None or concepts_df.empty: concepts_df = load_dataframe('concepts', CONCEPT_COLUMNS)
64
+ if concepts_df is not None and not concepts_df.empty:
65
+ sim_results = pd.merge(similarity_df, concepts_df[['concept_id', 'name']], left_on='concept_id_1', right_on='concept_id', how='left').rename(columns={'name': 'name_1'}).drop(columns=['concept_id'])
66
+ sim_results = pd.merge(sim_results, concepts_df[['concept_id', 'name']], left_on='concept_id_2', right_on='concept_id', how='left').rename(columns={'name': 'name_2'}).drop(columns=['concept_id'])
67
+ sim_results = sim_results[['concept_id_1', 'name_1', 'concept_id_2', 'name_2', 'similarity']]
68
+ sim_results.sort_values(by='similarity', ascending=False, inplace=True)
69
+ similarity_results_df = sim_results
70
+ print("\n--- En Benzer Konsept Çiftleri (Top 20) ---"); print(similarity_results_df.head(20).to_string(index=False))
71
+ save_dataframe(similarity_results_df, SIMILARITY_FILENAME)
72
+ else: print("\nKonsept isimleri yüklenemedi..."); print(similarity_df.sort_values(by='similarity', ascending=False).head(20).to_string(index=False))
73
+ elif similarity_df is not None: print("Benzerlik hesaplandı ancak sonuç boş."); save_dataframe(pd.DataFrame(columns=['concept_id_1', 'name_1', 'concept_id_2', 'name_2', 'similarity']), SIMILARITY_FILENAME)
74
+ except Exception as e: logging.exception("Benzerlik hesaplama sırasında beklenmedik hata oluştu.")
75
+ print(f"--- Benzerlik Hesaplaması Tamamlandı. Süre: {time.time() - start_time:.2f} saniye ---")
76
+
77
+ # --- 3. Ağ Oluşturma ---
78
+ print("\n--- 3. Konsept Ağı Oluşturma ---"); start_time = time.time()
79
+ # GÜNCELLEME: Ağ nesnesini değişkende tut
80
+ concept_network = build_concept_network(similarity_threshold=0.60)
81
+ if concept_network is not None:
82
+ print("\n--- Oluşturulan Ağ Bilgileri ---")
83
+ print(f"Düğüm Sayısı (Konseptler): {concept_network.number_of_nodes()}")
84
+ print(f"Kenar Sayısı (İlişkiler/Benzerlikler): {concept_network.number_of_edges()}")
85
+ print(f"Ağ başarıyla oluşturuldu ve kaydedildi.")
86
+ else:
87
+ print("Konsept ağı oluşturulamadı.")
88
+ print(f"--- Ağ Oluşturma Tamamlandı. Süre: {time.time() - start_time:.2f} saniye ---")
89
+
90
+
91
+ # --- YENİ: 4. Ağ Analizi (Metrik Hesaplama) ---
92
+ print("\n--- 4. Ağ Analizi Metrikleri ---"); start_time = time.time()
93
+ if concept_network is not None and concept_network.number_of_nodes() > 0:
94
+ network_analysis_df = get_network_analysis_results(concept_network)
95
+ if network_analysis_df is not None and not network_analysis_df.empty:
96
+ # Sonuçları kaydet
97
+ save_network_analysis(network_analysis_df)
98
+ print("Ağ metrikleri hesaplandı ve kaydedildi.")
99
+ # En yüksek derece merkeziyetine sahip ilk 10 konsepti göster
100
+ print("\n--- En Merkezi Konseptler (Degree Centrality Top 10) ---")
101
+ print(network_analysis_df.sort_values(by='degree_centrality', ascending=False).head(10).to_string(index=False))
102
+ else:
103
+ print("Ağ metrikleri hesaplanamadı veya sonuç boş.")
104
+ else:
105
+ print("Ağ analizi yapmak için geçerli bir ağ bulunamadı.")
106
+ print(f"--- Ağ Analizi Tamamlandı. Süre: {time.time() - start_time:.2f} saniye ---")
107
+
108
+
109
+ # --- YENİ SIRA: 5. Ağ Görselleştirme ---
110
+ print("\n--- 5. Ağ Görselleştirmesi Oluşturma ---"); start_time = time.time()
111
+ visualization_path = None
112
+ if concept_network is not None:
113
+ # GÜNCELLEME: Analiz sonuçlarını da görselleştirmeye gönderebiliriz (ileride plotting.py'ı güncelleyince)
114
+ # Şimdilik sadece grafı gönderiyoruz.
115
+ visualization_path = visualize_network(graph=concept_network, output_filename="concept_network_visualization.html")
116
+ if visualization_path:
117
+ print(f"\nBaşarılı! İnteraktif ağ görselleştirmesi oluşturuldu:\n-> {visualization_path}")
118
+ print("\nBu HTML dosyasını web tarayıcınızda açarak ağı inceleyebilirsiniz.")
119
+ else: print("Ağ görselleştirmesi oluşturulurken bir sorun oluştu.")
120
+ else: print("Ağ oluşturulamadığı için görselleştirme yapılamıyor.")
121
+ print(f"--- Ağ Görselleştirme Tamamlandı. Süre: {time.time() - start_time:.2f} saniye ---")
122
+
123
+
124
+ overall_end_time = time.time()
125
+ print(f"\n<<< Tüm İşlemler Tamamlandı. Toplam Süre: {overall_end_time - overall_start_time:.2f} saniye >>>")
run_extractor.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ # src klasöründeki modüllerimize erişmek için
3
+ from src.extraction.extractor import process_documents_for_extraction
4
+
5
+ if __name__ == "__main__":
6
+ print(">>> Bilgi çıkarıcı çalıştırılıyor...")
7
+ print("Not: Bu işlem dokümanların uzunluğuna ve sayısına göre biraz zaman alabilir.")
8
+ start_time = time.time()
9
+
10
+ # Ana çıkarım fonksiyonumuzu çağırıyoruz
11
+ process_documents_for_extraction()
12
+
13
+ end_time = time.time()
14
+ print(f"<<< Bilgi çıkarıcı tamamlandı. Süre: {end_time - start_time:.2f} saniye.")
15
+ print(f"Kontrol edilmesi gereken dosyalar: data/processed_data/ klasöründeki concepts.parquet, mentions.parquet, relationships.parquet ve güncellenmiş documents.parquet")
run_loader.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ # src klasöründeki modüllerimize erişmek için
3
+ from src.data_management.loaders import process_raw_documents
4
+
5
+ if __name__ == "__main__":
6
+ print(">>> Veri yükleyici çalıştırılıyor...")
7
+ start_time = time.time()
8
+
9
+ # Ana işlem fonksiyonumuzu çağırıyoruz
10
+ process_raw_documents()
11
+
12
+ end_time = time.time()
13
+ print(f"<<< Veri yükleyici tamamlandı. Süre: {end_time - start_time:.2f} saniye.")
14
+ print(f"Kontrol edilmesi gereken dosya: data/processed_data/documents.parquet")
similarity.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/analysis/similarity.py
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ from sentence_transformers import SentenceTransformer
6
+ from sklearn.metrics.pairwise import cosine_similarity
7
+ import logging
8
+ from pathlib import Path
9
+
10
+ # Yerel modüller
11
+ from src.data_management import storage
12
+
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
14
+
15
+ # Benzerlik matrisini kaydetmek için dosya adı
16
+ SIMILARITY_FILENAME = "concept_similarities"
17
+ EMBEDDINGS_FILENAME = "concept_embeddings" # Vektörleri de kaydedebiliriz
18
+
19
+ def calculate_concept_embeddings(model_name: str = 'all-MiniLM-L6-v2', force_recalculate: bool = False) -> dict[str, np.ndarray] | None:
20
+ """
21
+ Her konsept için ortalama embedding vektörünü hesaplar.
22
+ Mention'ların context_snippet'lerini kullanır.
23
+ Hesaplanmış embedding'leri yüklemeye çalışır, yoksa hesaplar.
24
+
25
+ Args:
26
+ model_name (str): Kullanılacak Sentence Transformer modeli.
27
+ force_recalculate (bool): Daha önce hesaplanmış olsa bile yeniden hesaplamaya zorla.
28
+
29
+ Returns:
30
+ dict[str, np.ndarray] | None: Concept ID -> Ortalama Embedding Vektörü sözlüğü veya hata durumunda None.
31
+ """
32
+ embeddings_filepath = storage.DATA_PATH / f"{EMBEDDINGS_FILENAME}.pkl" # Pickle ile saklayalım
33
+
34
+ if not force_recalculate and embeddings_filepath.exists():
35
+ try:
36
+ embeddings = pd.read_pickle(embeddings_filepath)
37
+ logging.info(f"Önceden hesaplanmış embedding'ler '{embeddings_filepath}' dosyasından yüklendi.")
38
+ # Dosyadan yüklenen bir sözlük olmalı
39
+ if isinstance(embeddings, dict):
40
+ return embeddings
41
+ else:
42
+ logging.warning("Yüklenen embedding dosyası beklenen formatta (dict) değil. Yeniden hesaplanacak.")
43
+ except Exception as e:
44
+ logging.error(f"Embedding'ler yüklenirken hata: {e}. Yeniden hesaplanacak.")
45
+
46
+ logging.info("Konsept embedding'leri hesaplanıyor...")
47
+ mentions_df = storage.load_dataframe('mentions', storage.MENTION_COLUMNS)
48
+
49
+ if mentions_df is None or mentions_df.empty:
50
+ logging.warning("Hesaplama için mention verisi bulunamadı.")
51
+ return None
52
+
53
+ # Geçerli context snippet'i olan mention'ları al
54
+ mentions_df.dropna(subset=['context_snippet', 'concept_id'], inplace=True)
55
+ if mentions_df.empty:
56
+ logging.warning("Geçerli context snippet bulunamadı.")
57
+ return None
58
+
59
+ # Modeli yükle (ilk seferde internetten indirilebilir)
60
+ try:
61
+ model = SentenceTransformer(model_name)
62
+ logging.info(f"Sentence Transformer modeli '{model_name}' yüklendi.")
63
+ except Exception as e:
64
+ logging.exception(f"Sentence Transformer modeli '{model_name}' yüklenirken hata: {e}")
65
+ return None
66
+
67
+ # Konseptlere göre grupla
68
+ grouped_mentions = mentions_df.groupby('concept_id')['context_snippet'].apply(list)
69
+
70
+ concept_embeddings = {}
71
+ logging.info(f"{len(grouped_mentions)} konsept için embedding hesaplanacak...")
72
+
73
+ # Her konsept için embedding'leri hesapla ve ortalamasını al
74
+ for concept_id, snippets in grouped_mentions.items():
75
+ if not snippets: continue # Boş snippet listesi varsa atla
76
+ try:
77
+ # Tüm snippet'ların embedding'lerini tek seferde hesapla (daha verimli)
78
+ embeddings = model.encode(snippets, show_progress_bar=False) # İlerleme çubuğunu kapat
79
+ # Ortalama embedding'i hesapla
80
+ avg_embedding = np.mean(embeddings, axis=0)
81
+ concept_embeddings[concept_id] = avg_embedding
82
+ except Exception as e:
83
+ logging.error(f"Concept ID {concept_id} için embedding hesaplanırken hata: {e}")
84
+ continue # Bu konsepti atla
85
+
86
+ # Hesaplanan embedding'leri kaydet
87
+ try:
88
+ storage.DATA_PATH.mkdir(parents=True, exist_ok=True)
89
+ pd.to_pickle(concept_embeddings, embeddings_filepath)
90
+ logging.info(f"Hesaplanan embedding'ler '{embeddings_filepath}' dosyasına kaydedildi.")
91
+ except Exception as e:
92
+ logging.error(f"Embedding'ler kaydedilirken hata: {e}")
93
+
94
+
95
+ logging.info(f"{len(concept_embeddings)} konsept için ortalama embedding hesaplandı.")
96
+ return concept_embeddings
97
+
98
+
99
+ def calculate_similarity_matrix(concept_embeddings: dict, force_recalculate: bool = False) -> pd.DataFrame | None:
100
+ """
101
+ Verilen embedding vektörleri arasındaki kosinüs benzerliğini hesaplar.
102
+ Hesaplanmış benzerlikleri yüklemeye çalışır, yoksa hesaplar.
103
+
104
+ Args:
105
+ concept_embeddings (dict[str, np.ndarray]): Concept ID -> Embedding Vektörü sözlüğü.
106
+ force_recalculate (bool): Daha önce hesaplanmış olsa bile yeniden hesaplamaya zorla.
107
+
108
+ Returns:
109
+ pd.DataFrame | None: 'concept_id_1', 'concept_id_2', 'similarity' sütunlarını
110
+ içeren DataFrame veya hata durumunda None.
111
+ """
112
+ similarity_filepath = storage.DATA_PATH / f"{SIMILARITY_FILENAME}.parquet"
113
+
114
+ if not force_recalculate and similarity_filepath.exists():
115
+ try:
116
+ similarity_df = storage.load_dataframe(SIMILARITY_FILENAME, ['concept_id_1', 'concept_id_2', 'similarity'])
117
+ logging.info(f"Önceden hesaplanmış benzerlik matrisi '{similarity_filepath}' dosyasından yüklendi.")
118
+ if similarity_df is not None and not similarity_df.empty:
119
+ return similarity_df
120
+ else:
121
+ logging.warning("Yüklenen benzerlik dosyası boş veya hatalı. Yeniden hesaplanacak.")
122
+ except Exception as e:
123
+ logging.error(f"Benzerlik matrisi yüklenirken hata: {e}. Yeniden hesaplanacak.")
124
+
125
+
126
+ if not concept_embeddings:
127
+ logging.error("Benzerlik hesaplamak için embedding verisi bulunamadı.")
128
+ return None
129
+
130
+ logging.info("Konseptler arası benzerlik matrisi hesaplanıyor...")
131
+
132
+ # Sözlükten sıralı liste ve matris oluştur
133
+ concept_ids = list(concept_embeddings.keys())
134
+ embedding_matrix = np.array(list(concept_embeddings.values()))
135
+
136
+ # Boyut kontrolü
137
+ if embedding_matrix.ndim != 2 or embedding_matrix.shape[0] != len(concept_ids):
138
+ logging.error(f"Embedding matrisinin boyutları ({embedding_matrix.shape}) beklenenden farklı.")
139
+ return None
140
+
141
+ # Kosinüs benzerliğini hesapla
142
+ try:
143
+ similarity_matrix = cosine_similarity(embedding_matrix)
144
+ except Exception as e:
145
+ logging.exception(f"Kosinüs benzerliği hesaplanırken hata: {e}")
146
+ return None
147
+
148
+ # Matrisi DataFrame'e dönüştür (uzun format)
149
+ similarity_data = []
150
+ num_concepts = len(concept_ids)
151
+ for i in range(num_concepts):
152
+ for j in range(i + 1, num_concepts): # Sadece üçgenin üstünü al (j > i) ve kendini (i=j) atla
153
+ similarity_data.append({
154
+ 'concept_id_1': concept_ids[i],
155
+ 'concept_id_2': concept_ids[j],
156
+ 'similarity': similarity_matrix[i, j]
157
+ })
158
+
159
+ similarity_df = pd.DataFrame(similarity_data)
160
+
161
+ if similarity_df.empty:
162
+ logging.warning("Hesaplama sonucu benzerlik verisi üretilemedi.")
163
+ # Boş DataFrame kaydetmeyelim, None döndürelim
164
+ return None
165
+
166
+ # Hesaplanan benzerlikleri kaydet
167
+ storage.save_dataframe(similarity_df, SIMILARITY_FILENAME)
168
+
169
+ logging.info(f"Benzerlik matrisi hesaplandı ve kaydedildi. {len(similarity_df)} çift.")
170
+ return similarity_df
storage.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/data_management/storage.py (TÜM SABİTLERİ İÇEREN DOĞRU TAM KOD)
2
+
3
+ import pandas as pd
4
+ from pathlib import Path
5
+ import logging
6
+ import uuid
7
+ from datetime import datetime
8
+ import networkx as nx
9
+ import pickle
10
+ import string
11
+
12
+ # Temel veri klasörünün yolu
13
+ DATA_PATH = Path("data/processed_data")
14
+ # NetworkX graf dosyalarının yolu
15
+ NETWORK_PATH = Path("output/networks")
16
+
17
+ # --- TÜM GEREKLİ SABİT TANIMLARI ---
18
+ FREQUENCY_FILENAME = "analysis_concept_frequencies"
19
+ SIMILARITY_FILENAME = "analysis_concept_similarities"
20
+ NETWORK_ANALYSIS_FILENAME = "analysis_network_results"
21
+ GRAPH_FILENAME = "concept_network"
22
+ EMBEDDINGS_FILENAME = "concept_embeddings"
23
+ # ------------------------------------
24
+
25
+ # DataFrame sütun isimleri
26
+ DOC_COLUMNS = ['doc_id', 'filepath', 'publication_date', 'status', 'processed_text_path']
27
+ CONCEPT_COLUMNS = ['concept_id', 'name', 'aliases']
28
+ MENTION_COLUMNS = ['mention_id', 'doc_id', 'concept_id', 'context_snippet', 'start_char', 'end_char']
29
+ RELATIONSHIP_COLUMNS = ['relationship_id', 'source_concept_id', 'target_concept_id', 'type', 'mention_id', 'doc_id', 'sentence']
30
+ NETWORK_ANALYSIS_COLUMNS = ['concept_id', 'name', 'degree_centrality', 'betweenness_centrality', 'eigenvector_centrality', 'community_id']
31
+
32
+ # Logging ayarları
33
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
34
+
35
+ # --- DataFrame Yükleme/Kaydetme (Değişiklik yok) ---
36
+ def load_dataframe(filename: str, columns: list) -> pd.DataFrame:
37
+ filepath = DATA_PATH / f"{filename}.parquet"
38
+ if filepath.exists():
39
+ try:
40
+ df = pd.read_parquet(filepath)
41
+ logging.info(f"'{filepath}' başarıyla yüklendi.")
42
+ if columns: # Check columns only if a list is provided
43
+ for col in columns:
44
+ if col not in df.columns:
45
+ logging.warning(f"'{filepath}' dosyasında '{col}' sütunu eksik. Ekleniyor...")
46
+ df[col] = None
47
+ return df
48
+ except Exception as e:
49
+ logging.error(f"'{filepath}' yüklenirken hata oluştu: {e}")
50
+ return pd.DataFrame(columns=columns if columns else None)
51
+ else:
52
+ logging.info(f"'{filepath}' bulunamadı. Boş DataFrame oluşturuluyor.")
53
+ return pd.DataFrame(columns=columns if columns else None)
54
+
55
+ def save_dataframe(df: pd.DataFrame, filename: str):
56
+ DATA_PATH.mkdir(parents=True, exist_ok=True)
57
+ filepath = DATA_PATH / f"{filename}.parquet"
58
+ try:
59
+ for col in df.select_dtypes(include=['object']).columns:
60
+ if df[col].map(type).isin([list, dict, datetime, pd.Timestamp]).any(): continue
61
+ df[col] = df[col].where(pd.notnull(df[col]), None)
62
+ try: df[col] = df[col].astype(pd.StringDtype())
63
+ except TypeError: logging.debug(f"Sütun '{col}' StringDtype'a çevrilemedi, orijinal tip korunuyor.")
64
+ df.to_parquet(filepath, index=False)
65
+ logging.info(f"DataFrame başarıyla '{filepath}' olarak kaydedildi.")
66
+ except Exception as e:
67
+ logging.error(f"DataFrame '{filepath}' olarak kaydedilirken hata oluştu: {e}")
68
+
69
+ # --- Doküman Yönetimi (Değişiklik yok) ---
70
+ def add_document(filepath_str: str, publication_date) -> str | None:
71
+ documents_df = load_dataframe('documents', DOC_COLUMNS)
72
+ filepath_str = str(Path(filepath_str).resolve())
73
+ existing_doc = documents_df[documents_df['filepath'] == filepath_str]
74
+ if not existing_doc.empty:
75
+ existing_doc_id = existing_doc['doc_id'].iloc[0]
76
+ logging.warning(f"Doküman zaten kayıtlı: {filepath_str} (ID: {existing_doc_id})")
77
+ return str(existing_doc_id)
78
+ new_doc_id = str(uuid.uuid4())
79
+ try: pub_date_obj = pd.to_datetime(publication_date).date()
80
+ except ValueError: logging.error(f"Geçersiz tarih formatı: {publication_date}. None olarak kaydedilecek."); pub_date_obj = None
81
+ new_document_data = {'doc_id': new_doc_id, 'filepath': filepath_str, 'publication_date': pub_date_obj, 'status': 'added', 'processed_text_path': None}
82
+ new_row_df = pd.DataFrame([new_document_data])
83
+ if pub_date_obj is not None: new_row_df['publication_date'] = pd.to_datetime(new_row_df['publication_date']); dtype_dict = {'publication_date': 'datetime64[s]'}
84
+ else: dtype_dict = {}
85
+ documents_df = pd.concat([documents_df, new_row_df], ignore_index=True)
86
+ for col, dtype in dtype_dict.items():
87
+ try: documents_df[col] = documents_df[col].astype(dtype)
88
+ except TypeError: logging.warning(f"Sütun '{col}' tipi '{dtype}' olarak ayarlanamadı.")
89
+ save_dataframe(documents_df, 'documents')
90
+ logging.info(f"Yeni doküman eklendi: {filepath_str} (ID: {new_doc_id})")
91
+ return new_doc_id
92
+
93
+ def update_document_status(doc_id: str, new_status: str, text_path: str | None = None):
94
+ docs_df = load_dataframe('documents', DOC_COLUMNS)
95
+ doc_index = docs_df[docs_df['doc_id'] == doc_id].index
96
+ if not doc_index.empty:
97
+ idx = doc_index[0]
98
+ docs_df.loc[idx, 'status'] = new_status
99
+ if text_path: docs_df.loc[idx, 'processed_text_path'] = text_path
100
+ save_dataframe(docs_df, 'documents')
101
+ logging.info(f"Doküman durumu güncellendi: ID {doc_id} -> {new_status}")
102
+ else: logging.warning(f"Durumu güncellenecek doküman bulunamadı: ID {doc_id}")
103
+
104
+ # --- Konsept, Mention, İlişki Yönetimi (Değişiklik yok) ---
105
+ def add_concept(raw_name: str) -> str | None:
106
+ concepts_df = load_dataframe('concepts', CONCEPT_COLUMNS)
107
+ name = raw_name.lower().strip().strip(string.punctuation + string.whitespace)
108
+ if name.endswith("'s"): name = name[:-2].strip()
109
+ name = ' '.join(name.split())
110
+ if not name or len(name) < 2: return None
111
+ existing_concept = concepts_df[concepts_df['name'] == name]
112
+ if not existing_concept.empty: return str(existing_concept['concept_id'].iloc[0])
113
+ new_concept_id = str(uuid.uuid4()); new_concept_data = {'concept_id': new_concept_id, 'name': name, 'aliases': [raw_name]}
114
+ new_row_df = pd.DataFrame([new_concept_data]); concepts_df = pd.concat([concepts_df, new_row_df], ignore_index=True)
115
+ concepts_df['aliases'] = concepts_df['aliases'].astype('object')
116
+ save_dataframe(concepts_df, 'concepts')
117
+ logging.info(f"Yeni konsept eklendi: '{name}' (Orijinal: '{raw_name}', ID: {new_concept_id})")
118
+ return new_concept_id
119
+
120
+ def add_mention(doc_id: str, concept_id: str, context: str, start: int, end: int) -> str | None:
121
+ if concept_id is None: return None
122
+ mentions_df = load_dataframe('mentions', MENTION_COLUMNS); new_mention_id = str(uuid.uuid4())
123
+ new_mention_data = {'mention_id': new_mention_id, 'doc_id': doc_id, 'concept_id': concept_id, 'context_snippet': context[:500], 'start_char': start, 'end_char': end}
124
+ new_row_df = pd.DataFrame([new_mention_data]); mentions_df = pd.concat([mentions_df, new_row_df], ignore_index=True)
125
+ save_dataframe(mentions_df, 'mentions'); return new_mention_id
126
+
127
+ def add_relationship(source_concept_id: str, target_concept_id: str, rel_type: str, mention_id: str | None, doc_id: str, sentence: str) -> str | None:
128
+ if source_concept_id is None or target_concept_id is None: return None
129
+ relationships_df = load_dataframe('relationships', RELATIONSHIP_COLUMNS); new_relationship_id = str(uuid.uuid4())
130
+ new_relationship_data = {'relationship_id': new_relationship_id, 'source_concept_id': source_concept_id, 'target_concept_id': target_concept_id, 'type': rel_type, 'mention_id': mention_id, 'doc_id': doc_id, 'sentence': sentence[:500]}
131
+ new_row_df = pd.DataFrame([new_relationship_data]); relationships_df = pd.concat([relationships_df, new_row_df], ignore_index=True)
132
+ save_dataframe(relationships_df, 'relationships'); return new_relationship_id
133
+
134
+ # --- NetworkX Graf Yükleme/Kaydetme (Değişiklik yok) ---
135
+ def save_network(graph: nx.Graph, filename: str):
136
+ NETWORK_PATH.mkdir(parents=True, exist_ok=True); filepath = NETWORK_PATH / f"{filename}.pkl"
137
+ try:
138
+ with open(filepath, 'wb') as f: pickle.dump(graph, f)
139
+ logging.info(f"NetworkX grafı başarıyla '{filepath}' olarak kaydedildi.")
140
+ except Exception as e: logging.error(f"Graf '{filepath}' olarak kaydedilirken hata: {e}")
141
+
142
+ def load_network(filename: str) -> nx.Graph | None:
143
+ filepath = NETWORK_PATH / f"{filename}.pkl"
144
+ if filepath.exists():
145
+ try:
146
+ with open(filepath, 'rb') as f: graph = pickle.load(f)
147
+ logging.info(f"NetworkX grafı '{filepath}' başarıyla yüklendi.")
148
+ return graph
149
+ except Exception as e: logging.error(f"Graf '{filepath}' yüklenirken hata: {e}"); return nx.Graph()
150
+ else: logging.warning(f"Graf dosyası bulunamadı: '{filepath}'"); return nx.Graph()
temporal.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/analysis/temporal.py (Yarı ömür fonksiyonu eklendi)
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ from scipy.optimize import curve_fit
6
+ import logging
7
+ from pathlib import Path
8
+ from datetime import datetime
9
+
10
+ # Yerel modüllerimizi içe aktaralım
11
+ from src.data_management import storage
12
+
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
14
+
15
+ def calculate_concept_frequencies(time_period: str = 'Y') -> pd.DataFrame | None:
16
+ """
17
+ Konseptlerin zaman içindeki kullanım sıklıklarını hesaplar. (Önceki kodla aynı)
18
+ """
19
+ logging.info(f"Konsept frekansları '{time_period}' periyodu için hesaplanıyor...")
20
+ mentions_df = storage.load_dataframe('mentions', storage.MENTION_COLUMNS)
21
+ documents_df = storage.load_dataframe('documents', storage.DOC_COLUMNS)
22
+
23
+ if mentions_df is None or documents_df is None:
24
+ logging.error("Mention veya Document verisi yüklenemedi. Frekans hesaplanamıyor.")
25
+ return None
26
+ if mentions_df.empty:
27
+ logging.warning("Mention verisi boş. Frekans hesaplanamıyor.")
28
+ return pd.DataFrame(columns=['concept_id', 'time_period_start', 'frequency'])
29
+ if documents_df.empty:
30
+ logging.warning("Document verisi boş. Tarih bilgisi alınamıyor, frekans hesaplanamıyor.")
31
+ return pd.DataFrame(columns=['concept_id', 'time_period_start', 'frequency'])
32
+
33
+ docs_subset = documents_df[['doc_id', 'publication_date']].copy()
34
+ try:
35
+ docs_subset['publication_date'] = pd.to_datetime(docs_subset['publication_date'], errors='coerce')
36
+ except Exception as e:
37
+ logging.error(f"Dokümanlardaki 'publication_date' sütunu datetime'a çevrilemedi: {e}")
38
+ return None
39
+
40
+ original_doc_count = len(docs_subset)
41
+ docs_subset.dropna(subset=['publication_date'], inplace=True)
42
+ valid_date_count = len(docs_subset)
43
+ if original_doc_count > valid_date_count:
44
+ logging.warning(f"{original_doc_count - valid_date_count} dokümanın geçerli yayın tarihi yok, frekans hesaplamasına dahil edilmeyecek.")
45
+
46
+ if docs_subset.empty:
47
+ logging.warning("Geçerli yayın tarihine sahip doküman bulunamadı. Frekans hesaplanamıyor.")
48
+ return pd.DataFrame(columns=['concept_id', 'time_period_start', 'frequency'])
49
+
50
+ mentions_with_dates = pd.merge(mentions_df, docs_subset, on='doc_id', how='inner')
51
+
52
+ if mentions_with_dates.empty:
53
+ logging.warning("Mention'lar ile doküman tarihleri birleştirilemedi veya sonuç boş.")
54
+ return pd.DataFrame(columns=['concept_id', 'time_period_start', 'frequency'])
55
+
56
+ logging.info(f"{len(mentions_with_dates)} mention için tarih bilgisi bulundu.")
57
+
58
+ try:
59
+ frequency_df = mentions_with_dates.groupby(
60
+ ['concept_id', pd.Grouper(key='publication_date', freq=time_period)]
61
+ ).size().reset_index(name='frequency')
62
+ frequency_df.rename(columns={'publication_date': 'time_period_start'}, inplace=True)
63
+ logging.info(f"Frekans hesaplaması tamamlandı. {len(frequency_df)} satır sonuç üretildi.")
64
+ frequency_df.sort_values(by=['concept_id', 'time_period_start'], inplace=True)
65
+ return frequency_df
66
+ except Exception as e:
67
+ logging.exception(f"Frekans hesaplanırken hata oluştu: {e}")
68
+ return None
69
+
70
+ # --- YENİ: Yarı Ömür Hesaplama ---
71
+
72
+ def exponential_decay(t, A, decay_rate):
73
+ """Üstel bozulma fonksiyonu: A * exp(-decay_rate * t)."""
74
+ # Decay rate negatif olmamalı (bozunma varsayımı)
75
+ decay_rate = max(0, decay_rate) # Negatifse sıfır yap
76
+ return A * np.exp(-decay_rate * t)
77
+
78
+ def calculate_half_life(concept_id: str,
79
+ frequency_df: pd.DataFrame,
80
+ concept_name: str | None = None,
81
+ min_data_points: int = 4,
82
+ min_decay_rate: float = 1e-6) -> float | None:
83
+ """
84
+ Verilen konsept için frekans verisine üstel bozulma modeli uygulayarak
85
+ yarı ömrü (yıl olarak) hesaplar.
86
+
87
+ Args:
88
+ concept_id (str): Hesaplanacak konseptin ID'si.
89
+ frequency_df (pd.DataFrame): calculate_concept_frequencies'ten dönen DataFrame.
90
+ ('concept_id', 'time_period_start', 'frequency' sütunları olmalı).
91
+ concept_name (str | None): Loglama için konseptin adı (opsiyonel).
92
+ min_data_points (int): Yarı ömür hesaplamak için gereken minimum zaman noktası sayısı.
93
+ min_decay_rate (float): Kabul edilebilir minimum bozunma oranı (çok küçükse yarı ömür sonsuz kabul edilir).
94
+
95
+ Returns:
96
+ float | None: Hesaplanan yarı ömür (yıl olarak) veya hesaplanamazsa None.
97
+ np.inf dönebilir eğer bozunma oranı çok küçükse.
98
+ """
99
+ log_prefix = f"Yarı Ömür ({concept_name or concept_id}):"
100
+
101
+ if frequency_df is None or frequency_df.empty:
102
+ logging.warning(f"{log_prefix} Frekans verisi boş.")
103
+ return None
104
+
105
+ # Konsepte ait veriyi filtrele ve zamana göre sırala
106
+ concept_data = frequency_df[frequency_df['concept_id'] == concept_id].sort_values(by='time_period_start').copy()
107
+
108
+ # Yeterli veri noktası var mı?
109
+ if len(concept_data) < min_data_points:
110
+ logging.info(f"{log_prefix} Yeterli veri noktası yok ({len(concept_data)} < {min_data_points}). Hesaplama yapılamıyor.")
111
+ return None
112
+
113
+ # Zamanı sayısal değere çevir (ilk yıldan itibaren geçen yıl sayısı)
114
+ try:
115
+ # İlk zaman noktasını t=0 kabul et
116
+ start_date = concept_data['time_period_start'].min()
117
+ # Zaman farkını gün olarak hesapla ve yıla çevir
118
+ concept_data['time_elapsed_years'] = (concept_data['time_period_start'] - start_date).dt.days / 365.25
119
+ except Exception as e:
120
+ logging.error(f"{log_prefix} Zaman farkı hesaplanırken hata: {e}")
121
+ return None
122
+
123
+ time_values = concept_data['time_elapsed_years'].values
124
+ frequency_values = concept_data['frequency'].values
125
+
126
+ # Frekanslar artıyor mu veya sabit mi kontrol et (basit kontrol)
127
+ # Eğer son değer ilk değerden büyükse veya tüm değerler aynıysa, bozunma yok kabul et
128
+ if frequency_values[-1] > frequency_values[0] or np.all(frequency_values == frequency_values[0]):
129
+ logging.info(f"{log_prefix} Veride belirgin bir azalma gözlenmedi. Yarı ömür hesaplanamıyor.")
130
+ return None # Veya np.inf? Şimdilik None.
131
+
132
+ # Modeli uydurmak için başlangıç tahminleri
133
+ initial_A_guess = frequency_values[0] # İlk frekans değeri
134
+ initial_lambda_guess = 0.1 # Küçük pozitif bir bozunma oranı tahmini
135
+
136
+ try:
137
+ # curve_fit ile modeli verilere uydur
138
+ params, covariance = curve_fit(
139
+ exponential_decay,
140
+ time_values,
141
+ frequency_values,
142
+ p0=[initial_A_guess, initial_lambda_guess],
143
+ bounds=([0, 0], [np.inf, np.inf]) # Parametrelerin pozitif olmasını sağla
144
+ # maxfev artırılabilir eğer "Optimal parameters not found" hatası alınırsa
145
+ )
146
+
147
+ A_fit, decay_rate_fit = params
148
+
149
+ # Bozunma oranı anlamlı mı?
150
+ if decay_rate_fit < min_decay_rate:
151
+ logging.info(f"{log_prefix} Hesaplanan bozunma oranı ({decay_rate_fit:.4f}) çok düşük. Yarı ömür sonsuz kabul ediliyor.")
152
+ return np.inf # Sonsuz yarı ömür
153
+
154
+ # Yarı ömrü hesapla: ln(2) / decay_rate
155
+ half_life_years = np.log(2) / decay_rate_fit
156
+ logging.info(f"{log_prefix} Başarıyla hesaplandı. A={A_fit:.2f}, Bozunma Oranı={decay_rate_fit:.4f}, Yarı Ömür={half_life_years:.2f} yıl.")
157
+ return half_life_years
158
+
159
+ except RuntimeError as e:
160
+ logging.warning(f"{log_prefix} Üstel bozulma modeli uydurulamadı: {e}. Yarı ömür hesaplanamıyor.")
161
+ return None
162
+ except Exception as e:
163
+ logging.exception(f"{log_prefix} Yarı ömür hesaplanırken beklenmeyen hata: {e}")
164
+ return None
test1.png ADDED