import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import random
import io

# 1. Copy of your exported bibliometric data
data = """document id	keyword id	frequency
1	549	1
8	23	1
8	289	1
8	701	1
10	712	1
11	23	1
11	340	1
11	452	1
11	527	1
13	23	1
13	710	1
15	289	1
15	376	1
15	487	1
17	11	1
17	328	1
18	340	1
19	23	1
20	452	1
20	487	1
21	23	1
21	397	1
22	328	1
22	712	1
23	328	1
23	701	1
24	89	1
24	705	1
26	23	1
26	89	1
26	452	1
27	441	1
28	259	1
28	452	1
29	340	1
29	452	1
30	89	1
30	701	1
32	107	1
32	259	1
33	328	1
34	23	1
34	89	1
34	441	1
34	712	1
35	89	1
35	701	1
36	527	1
37	89	1
37	202	1
37	452	1
38	312	1
38	712	1
39	89	1
39	107	1
39	375	1
39	714	1
40	312	1
42	397	1
42	527	1
43	241	1
43	441	1
44	202	1
44	502	1
44	710	1
45	23	1
45	487	1
45	502	1
46	107	1
46	452	1
47	23	1
48	289	1
49	89	1
49	241	1
50	23	1
50	328	1
50	549	1
51	11	1
52	107	1
52	202	1
52	312	1
52	340	1
52	452	1
52	549	1
54	202	1
54	376	1
55	11	1
55	330	1
55	712	1
55	714	1
56	701	1
57	340	1
58	527	1
58	705	1
59	340	1
60	23	1
60	376	1
60	701	1
61	23	1
61	452	1
61	487	1
61	705	1
64	89	1
64	701	1
65	89	1
65	397	1
65	705	1
65	712	1
67	452	1
68	89	1
69	89	1
69	312	1
70	89	1
70	328	1
70	397	1
71	107	1
72	202	1
72	375	1
73	452	1
73	549	1
73	712	1
74	289	1
74	375	1
74	452	1
75	23	1
75	312	1
75	452	1
75	712	1
77	375	1
77	397	1
77	452	1
78	397	1
81	11	1
81	23	1
82	124	1
82	701	1
82	714	1
83	107	1
83	241	1
83	330	1
83	376	1
85	376	1
85	452	1
85	549	1
85	701	1
85	710	1
86	328	1
86	330	1
87	23	1
87	549	1
87	701	1
88	89	1
88	701	1
89	107	1
89	330	1
90	375	1
92	23	1
94	23	1
95	452	1
95	487	1
95	712	1
96	23	1
96	312	1
96	452	1
97	312	1
97	397	1
98	107	1
98	330	1
98	397	1
98	710	1
100	527	1
101	328	1
101	712	1
103	23	1
103	107	1
103	328	1
103	397	1
105	23	1
105	312	1
105	330	1
105	397	1
105	441	1
105	701	1
107	23	1
107	549	1
108	328	1
108	340	1
108	549	1
108	705	1
109	487	1
109	712	1
112	89	1
112	124	1
112	549	1
112	701	1
114	23	1
114	452	1
114	701	1
115	23	1
115	397	1
117	124	1
117	312	1
117	527	1
117	701	1
118	23	1
118	89	1
118	549	1
119	23	1
119	241	1
120	312	1
120	452	1
122	89	1
122	714	1
123	23	1
123	124	1
124	89	1
124	259	1
124	441	1
126	441	1
126	452	1
127	375	1
127	397	1
128	11	1
128	452	1
128	549	1
129	376	1
129	441	1
130	89	1
130	549	1
131	330	1
131	712	1
133	714	1
134	441	1
135	89	1
135	375	1
135	452	1
136	487	1
136	701	1
136	712	1
137	11	1
137	23	1
137	712	1
138	289	1
138	375	1
141	452	1
141	487	1
141	527	1
141	701	1
142	312	1
142	375	1
142	712	1
143	107	1
143	202	1
143	241	1
143	452	1
144	11	1
144	452	1
144	712	1
145	289	1
145	375	1
146	441	1
147	23	1
148	11	1
148	23	1
148	107	1
148	397	1
149	89	1
149	452	1
149	527	1
150	23	1
150	487	1
150	712	1
152	23	1
152	452	1
152	710	1
152	714	1
153	259	1
153	375	1
153	714	1
156	452	1
156	705	1
158	23	1
158	710	1
160	124	1
160	289	1
160	375	1
161	124	1
161	376	1
162	89	1
162	202	1
166	701	1
167	23	1
167	124	1
167	328	1
167	452	1
168	397	1
168	452	1
168	710	1
169	107	1
170	89	1
170	289	1
170	452	1
171	202	1
171	289	1
171	712	1
172	23	1
173	89	1
173	452	1
173	502	1
173	705	1
174	89	1
174	124	1
174	705	1
175	202	1
175	397	1
175	452	1
176	202	1
176	397	1
176	452	1
176	502	1
177	701	1
179	259	1
179	441	1
179	701	1
180	312	1
181	312	1
182	124	1
182	452	1
182	527	1
183	312	1
183	452	1
183	705	1
184	259	1
184	289	1
184	375	1
184	527	1
186	502	1
188	107	1
188	397	1
188	452	1
189	712	1
191	397	1
191	701	1
191	712	1
193	376	1
193	527	1
194	312	1
195	340	1
196	705	1
197	23	1
197	259	1
197	701	1
198	23	1
198	259	1
198	397	1
198	452	1
198	527	1
198	701	1
199	89	1
199	107	1
199	397	1
199	452	1
201	712	1
204	340	1
204	452	1
205	375	1
207	289	1
209	441	1
209	701	1
210	89	1
210	397	1
210	527	1
210	710	1
211	107	1
211	701	1
211	712	1
212	23	1
212	710	1
213	23	1
213	714	1
214	375	1
214	452	1
216	107	1
216	502	1
220	712	1
221	289	1
221	328	1
225	23	1
226	23	1
227	289	1
228	312	1
229	107	1
229	376	1
230	107	1
230	202	1
230	241	1
230	441	1
230	502	1
231	23	1
232	452	1
233	124	1
233	259	1
233	312	1
233	376	1
233	701	1
233	712	1
234	328	1
234	712	1
236	89	1
237	124	1
237	710	1
238	375	1
238	452	1
239	23	1
245	23	1
245	330	1
245	397	1
245	452	1
248	241	1
249	241	1
249	259	1
249	330	1
249	452	1
252	23	1
252	289	1
252	712	1
254	11	1
254	289	1
257	289	1
258	89	1
258	328	1
258	397	1
259	107	1
259	375	1
260	375	1
261	11	1
261	312	1
261	375	1
261	452	1
261	527	1
261	710	1
262	89	1
263	312	1
264	289	1
264	375	1
264	397	1
264	549	1
265	11	1
265	23	1
265	328	1
265	397	1
268	23	1
268	714	1
270	712	1
271	23	1
271	397	1
273	11	1
273	502	1
274	712	1
279	397	1
281	397	1
281	527	1
281	701	1
282	452	1
283	375	1
283	397	1
283	714	1
284	23	1
284	241	1
284	487	1
284	701	1
288	312	1
289	89	1
289	241	1
289	289	1
289	712	1
290	23	1
292	397	1
292	701	1
295	23	1
297	23	1
297	328	1
297	330	1
297	397	1
299	11	1
302	89	1
304	23	1
305	452	1
307	89	1
307	710	1
309	397	1
309	452	1
310	289	1
311	23	1
311	107	1
311	124	1
311	330	1
311	376	1
313	259	1
313	502	1
313	701	1
313	714	1
314	23	1
314	701	1
314	712	1
315	340	1
318	701	1
319	259	1
319	710	1
320	712	1
324	397	1
325	23	1
325	397	1
325	452	1
325	712	1"""

df = pd.read_csv(io.StringIO(data), sep="\t")

# 2. Group keywords by document to map co-occurrences
doc_to_kw = df.groupby('document id')['keyword id'].apply(list).to_dict()

# 3. Building the Co-occurrence Graph
G = nx.Graph()
for doc_id, kws in doc_to_kw.items():
    for i in range(len(kws)):
        G.add_node(kws[i])
        for j in range(i + 1, len(kws)):
            if G.has_edge(kws[i], kws[j]):
                G[kws[i]][kws[j]]['weight'] += 1
            else:
                G.add_edge(kws[i], kws[j], weight=1)

# 4. Stability Simulation Function
def simular_robustez(grafo, estrategia="aleatoria"):
    G_temp = grafo.copy()
    num_inicial = G_temp.number_of_nodes()
    
    eje_x = [0.0]
    eje_y = [1.0]
    
    while G_temp.number_of_nodes() > 1:
        if estrategia == "ataque":
            # Remove the word with the highest current degree (Concept Hub)
            nodo_a_eliminar = max(G_temp.degree, key=lambda x: x[1])[0]
        else:
            # Remove a random keyword (normal obsolescence)
            nodo_a_eliminar = random.choice(list(G_temp.nodes()))
            
        G_temp.remove_node(nodo_a_eliminar)
        
        porcentaje_eliminado = ((num_inicial - G_temp.number_of_nodes()) / num_inicial) * 100
        componentes = sorted(nx.connected_components(G_temp), key=len, reverse=True)
        tamano_lcc = len(componentes[0]) / num_inicial if componentes else 0
        
        eje_x.append(porcentaje_eliminado)
        eje_y.append(tamano_lcc)
        
    return eje_x, eje_y

# 5. Run scenarios
x_rand, y_rand = simular_robustez(G, estrategia="aleatoria")
x_ataq, y_ataq = simular_robustez(G, estrategia="ataque")

# 6. Plot the conceptual resilience curve
plt.figure(figsize=(8, 5))
plt.plot(x_rand, y_rand, label="Random Obsolescence (Random Disuse)", color="#1f77b4", linewidth=2.5)
plt.plot(x_ataq, y_ataq, label="Targeted Attack (Fall of Paradigms)", color="#d62728", linewidth=2.5)
plt.xlabel("% of Keywords Removed from the Network", fontsize=11)
plt.ylabel("Integrity of the Largest Connected Component (LCC %)", fontsize=11)
# 7. Customize the title: "Keywords Plus©" OR "Author Keywords"
plt.title("Analysis of the Epistemological Resilience of Keywords Plus©", fontsize=12, fontweight='bold')
plt.legend()
plt.grid(True, linestyle=":", alpha=0.6)
plt.tight_layout()
plt.show()