JonnyBP commited on
Commit
4be4a27
·
1 Parent(s): f46289d

feat: add preprocessing, new version. #3

Browse files
Files changed (2) hide show
  1. .gitignore +0 -1
  2. notebooks/02_preprocessing_v2.ipynb +835 -0
.gitignore CHANGED
@@ -54,4 +54,3 @@ mlruns/
54
  mlartifacts/
55
 
56
  #jony
57
- 02_preprocessing_v2.ipynb
 
54
  mlartifacts/
55
 
56
  #jony
 
notebooks/02_preprocessing_v2.ipynb ADDED
@@ -0,0 +1,835 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# 🔧 Notebook 02 — Preprocesamiento de Texto\n",
8
+ "\n",
9
+ "### ¿Qué hace este notebook?\n",
10
+ "Construimos y validamos el pipeline de limpieza de texto **paso a paso**.\n",
11
+ "\n",
12
+ "### ¿Por qué se hace así?\n",
13
+ "El texto crudo de YouTube tiene ruido que engaña al modelo: URLs, menciones, caracteres raros (`\\xa0`), contracciones rotas (`don t`).\n",
14
+ "Antes de vectorizar necesitamos texto limpio y normalizado.\n",
15
+ "\n",
16
+ "### Herramientas\n",
17
+ "- **`re`** → expresiones regulares para limpiar ruido estructural\n",
18
+ "- **`NLTK`** → lista curada de 179 stopwords en inglés\n",
19
+ "- **`spaCy`** → lematización con modelo de lenguaje real `en_core_web_sm`\n",
20
+ "- **`MLflow`** → registrar qué configuración de preprocesamiento usamos\n",
21
+ "\n",
22
+ "### Output de este notebook\n",
23
+ "- Columna `clean_text` lista para vectorizar\n",
24
+ "- `data/processed/v2/comments_preprocessed.csv`\n",
25
+ "- Experimento registrado en MLflow: `Youtube_project_experiment`"
26
+ ]
27
+ },
28
+ {
29
+ "cell_type": "markdown",
30
+ "metadata": {},
31
+ "source": [
32
+ "## 0. Imports y configuración\n",
33
+ "\n",
34
+ "Cargamos todo desde el YAML. Ningún valor hardcodeado en el notebook."
35
+ ]
36
+ },
37
+ {
38
+ "cell_type": "code",
39
+ "execution_count": 1,
40
+ "metadata": {},
41
+ "outputs": [
42
+ {
43
+ "name": "stderr",
44
+ "output_type": "stream",
45
+ "text": [
46
+ "/home/under/miniconda3/envs/py310/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
47
+ " from .autonotebook import tqdm as notebook_tqdm\n"
48
+ ]
49
+ },
50
+ {
51
+ "name": "stdout",
52
+ "output_type": "stream",
53
+ "text": [
54
+ "PROJECT_ROOT : /mnt/c/Users/under/Documents/F5/3_Projects/Project_9_Equipo3/Project_YT\n",
55
+ "Python : 3.10.20\n"
56
+ ]
57
+ }
58
+ ],
59
+ "source": [
60
+ "import re\n",
61
+ "import sys\n",
62
+ "import yaml\n",
63
+ "import pandas as pd\n",
64
+ "import numpy as np\n",
65
+ "import matplotlib.pyplot as plt\n",
66
+ "import seaborn as sns\n",
67
+ "import mlflow\n",
68
+ "import nltk\n",
69
+ "import spacy\n",
70
+ "from nltk.corpus import stopwords\n",
71
+ "from pathlib import Path\n",
72
+ "import warnings\n",
73
+ "warnings.filterwarnings('ignore')\n",
74
+ "\n",
75
+ "# Ruta raiz — sube desde notebooks/ a la raiz del proyecto\n",
76
+ "PROJECT_ROOT = Path.cwd().parent\n",
77
+ "sys.path.insert(0, str(PROJECT_ROOT))\n",
78
+ "\n",
79
+ "plt.rcParams['figure.figsize'] = (12, 5)\n",
80
+ "plt.rcParams['axes.spines.top'] = False\n",
81
+ "plt.rcParams['axes.spines.right'] = False\n",
82
+ "\n",
83
+ "print(f'PROJECT_ROOT : {PROJECT_ROOT}')\n",
84
+ "print(f'Python : {sys.version.split()[0]}')"
85
+ ]
86
+ },
87
+ {
88
+ "cell_type": "code",
89
+ "execution_count": 2,
90
+ "metadata": {},
91
+ "outputs": [
92
+ {
93
+ "name": "stdout",
94
+ "output_type": "stream",
95
+ "text": [
96
+ "Configuracion de preprocesamiento:\n",
97
+ " lowercase: True\n",
98
+ " remove_urls: True\n",
99
+ " remove_mentions: True\n",
100
+ " remove_emojis: True\n",
101
+ " remove_special_chars: True\n",
102
+ " remove_stopwords: True\n",
103
+ " lemmatize: True\n",
104
+ " min_token_length: 2\n",
105
+ " language: en\n"
106
+ ]
107
+ }
108
+ ],
109
+ "source": [
110
+ "# Carga de configuracion desde YAML\n",
111
+ "CONFIG_PATH = PROJECT_ROOT / 'configs' / 'features.yaml'\n",
112
+ "\n",
113
+ "with open(CONFIG_PATH) as f:\n",
114
+ " config = yaml.safe_load(f)\n",
115
+ "\n",
116
+ "prep_cfg = config['preprocessing']\n",
117
+ "print('Configuracion de preprocesamiento:')\n",
118
+ "for k, v in prep_cfg.items():\n",
119
+ " print(f' {k}: {v}')"
120
+ ]
121
+ },
122
+ {
123
+ "cell_type": "code",
124
+ "execution_count": null,
125
+ "metadata": {},
126
+ "outputs": [
127
+ {
128
+ "name": "stdout",
129
+ "output_type": "stream",
130
+ "text": [
131
+ "spaCy : core_web_sm v3.8.0\n",
132
+ "NLTK : 198 stopwords en ingles\n"
133
+ ]
134
+ }
135
+ ],
136
+ "source": [
137
+ "# Descargar recursos NLTK\n",
138
+ "nltk.download('stopwords', quiet=True)\n",
139
+ "nltk.download('punkt', quiet=True)\n",
140
+ "\n",
141
+ "# Cargar modelo spaCy\n",
142
+ "# Si no lo tienes instalado: python -m spacy download en_core_web_sm\n",
143
+ "nlp = spacy.load('en_core_web_sm', disable=['parser', 'ner'])\n",
144
+ "\n",
145
+ "print(f\"spaCy : {nlp.meta['name']} v{nlp.meta['version']}\")\n",
146
+ "print(f'NLTK : {len(stopwords.words(\"english\"))} stopwords en ingles')"
147
+ ]
148
+ },
149
+ {
150
+ "cell_type": "markdown",
151
+ "metadata": {},
152
+ "source": [
153
+ "## 1. Carga de datos\n",
154
+ "\n",
155
+ "Las rutas vienen del YAML de pipeline, no se escriben a mano."
156
+ ]
157
+ },
158
+ {
159
+ "cell_type": "code",
160
+ "execution_count": 4,
161
+ "metadata": {},
162
+ "outputs": [
163
+ {
164
+ "name": "stdout",
165
+ "output_type": "stream",
166
+ "text": [
167
+ "Dataset : (1000, 15)\n",
168
+ "Texto : \"Text\"\n",
169
+ "Target : \"IsToxic\"\n",
170
+ "Sublabels: ['IsAbusive', 'IsProvocative', 'IsHatespeech', 'IsRacist', 'IsObscene']\n",
171
+ "\n",
172
+ "Muestra texto crudo:\n",
173
+ " -> \"You call yourself an anarchist but defend a cop shooting an unarmed civilian. I'm highly disappointe\"\n",
174
+ " -> 'My mother told me the same thing.\\xa0 God Bless this woman.'\n",
175
+ " -> 'Love it I same the saem thing Go Peggy! #stupid \\xa0\\nYa Killing ya selves more quicker than\\xa0 STLPD cou'\n"
176
+ ]
177
+ }
178
+ ],
179
+ "source": [
180
+ "PIPELINE_PATH = PROJECT_ROOT / 'configs' / 'pipeline.yaml'\n",
181
+ "\n",
182
+ "with open(PIPELINE_PATH) as f:\n",
183
+ " pipeline_cfg = yaml.safe_load(f)\n",
184
+ "\n",
185
+ "DATA_PATH = PROJECT_ROOT / pipeline_cfg['data']['raw_path']\n",
186
+ "TEXT_COL = pipeline_cfg['data']['text_column']\n",
187
+ "TARGET_BIN = pipeline_cfg['data']['target_binary']\n",
188
+ "SUBLABELS = pipeline_cfg['data']['target_multilabel']\n",
189
+ "\n",
190
+ "df = pd.read_csv(DATA_PATH)\n",
191
+ "print(f'Dataset : {df.shape}')\n",
192
+ "print(f'Texto : \"{TEXT_COL}\"')\n",
193
+ "print(f'Target : \"{TARGET_BIN}\"')\n",
194
+ "print(f'Sublabels: {SUBLABELS}')\n",
195
+ "print()\n",
196
+ "print('Muestra texto crudo:')\n",
197
+ "for t in df[TEXT_COL].sample(3, random_state=42):\n",
198
+ " print(f' -> {repr(t[:100])}')"
199
+ ]
200
+ },
201
+ {
202
+ "cell_type": "markdown",
203
+ "metadata": {},
204
+ "source": [
205
+ "## 2. Pipeline paso a paso\n",
206
+ "\n",
207
+ "Construimos cada función por separado para entender su efecto antes de encadenarlas.\n",
208
+ "\n",
209
+ "```\n",
210
+ "texto raw\n",
211
+ " → [1] lowercase\n",
212
+ " → [2] regex: URLs, @menciones, \\xa0, contracciones rotas, números\n",
213
+ " → [3] spaCy: tokenización + lematización\n",
214
+ " → [4] NLTK: filtrado stopwords + tokens cortos + puntuación\n",
215
+ " → texto limpio\n",
216
+ "```"
217
+ ]
218
+ },
219
+ {
220
+ "cell_type": "code",
221
+ "execution_count": null,
222
+ "metadata": {},
223
+ "outputs": [
224
+ {
225
+ "name": "stdout",
226
+ "output_type": "stream",
227
+ "text": [
228
+ "PASO 1 — Lowercase\n",
229
+ "-----------------------------------------------------------------\n",
230
+ " ANTES : Stephan, Thank you for the video. It takes all the available information into ac\n",
231
+ " DESPUES: stephan, thank you for the video. it takes all the available information into ac\n",
232
+ "\n",
233
+ " ANTES : Body cams should also air what the police run into day to day.\n",
234
+ "Not just when the\n",
235
+ " DESPUES: body cams should also air what the police run into day to day.\n",
236
+ "not just when the\n",
237
+ "\n",
238
+ " ANTES : This is a really sad story. Someone is killed for no reason, racial war increase\n",
239
+ " DESPUES: this is a really sad story. someone is killed for no reason, racial war increase\n",
240
+ "\n"
241
+ ]
242
+ }
243
+ ],
244
+ "source": [
245
+ "# ── PASO 1: Lowercase ──\n",
246
+ "# Por que: 'BLACK' y 'black' son la misma palabra.\n",
247
+ "# Sin esto el modelo aprende 'BLACK' y 'black' como features distintas.\n",
248
+ "\n",
249
+ "def to_lowercase(text: str) -> str:\n",
250
+ " \"\"\"Convierte el texto a minusculas.\"\"\"\n",
251
+ " return str(text).lower()\n",
252
+ "\n",
253
+ "# Validacion visual\n",
254
+ "print('PASO 1 — Lowercase')\n",
255
+ "print('-' * 65)\n",
256
+ "for ex in df[TEXT_COL].sample(3, random_state=1).tolist():\n",
257
+ " print(f' ANTES : {ex[:60]}')\n",
258
+ " print(f' DESPUES: {to_lowercase(ex)[:60]}')\n",
259
+ " print()"
260
+ ]
261
+ },
262
+ {
263
+ "cell_type": "code",
264
+ "execution_count": null,
265
+ "metadata": {},
266
+ "outputs": [
267
+ {
268
+ "name": "stdout",
269
+ "output_type": "stream",
270
+ "text": [
271
+ "PASO 2 — Limpieza Regex\n",
272
+ "-----------------------------------------------------------------\n",
273
+ " ANTES : 'Check this out http://youtube.com/watch?v=abc123 ok?'\n",
274
+ " DESPUES: 'Check this out ok?'\n",
275
+ "\n",
276
+ " ANTES : \"Hey @username you're so stupid\\xa0\\xa0 really\"\n",
277
+ " DESPUES: 'Hey youre so stupid really'\n",
278
+ "\n",
279
+ " ANTES : 'dont\\nyou see?\\n\\nThis is wrong!!!'\n",
280
+ " DESPUES: 'dont you see? This is wrong!!!'\n",
281
+ "\n",
282
+ " ANTES : 'He has 100 guns and 50 knives'\n",
283
+ " DESPUES: 'He has guns and knives'\n",
284
+ "\n"
285
+ ]
286
+ }
287
+ ],
288
+ "source": [
289
+ "# ── PASO 2: Limpieza con Regex ────────────────────────────────────────────\n",
290
+ "# Por que regex: hay ruido sistematico en comentarios de YouTube.\n",
291
+ "# El EDA mostro: \\xa0 embebidos, saltos de linea, URLs, @menciones.\n",
292
+ "# Orden importante: primero lo mas especifico, luego lo general.\n",
293
+ "\n",
294
+ "def clean_regex(text: str) -> str:\n",
295
+ " \"\"\"Limpieza con expresiones regulares.\"\"\"\n",
296
+ " # URLs completas\n",
297
+ " text = re.sub(r'http\\S+|www\\.\\S+', '', text)\n",
298
+ " # Menciones @usuario\n",
299
+ " text = re.sub(r'@\\w+', '', text)\n",
300
+ " # Saltos de linea y tabulaciones -> espacio\n",
301
+ " text = re.sub(r'[\\n\\t\\r]', ' ', text)\n",
302
+ " # Caracteres no-ASCII: \\xa0, emojis, caracteres especiales\n",
303
+ " text = re.sub(r'[^\\x00-\\x7F]+', ' ', text)\n",
304
+ " # Apostrofes: \"don't\" -> \"dont\", \"it's\" -> \"its\"\n",
305
+ " text = re.sub(r\"'\", '', text)\n",
306
+ " # Numeros solos sin contexto lexico util\n",
307
+ " text = re.sub(r'\\b\\d+\\b', '', text)\n",
308
+ " # Espacios multiples -> uno solo\n",
309
+ " text = re.sub(r'\\s+', ' ', text)\n",
310
+ " return text.strip()\n",
311
+ "\n",
312
+ "# Validacion con casos problematicos reales del dataset (ejemplo)\n",
313
+ "print('PASO 2 — Limpieza Regex')\n",
314
+ "print('-' * 65)\n",
315
+ "test_cases = [\n",
316
+ " 'Check this out http://youtube.com/watch?v=abc123 ok?',\n",
317
+ " 'Hey @username you\\'re so stupid\\xa0\\xa0 really',\n",
318
+ " 'dont\\nyou see?\\n\\nThis is wrong!!!',\n",
319
+ " 'He has 100 guns and 50 knives',\n",
320
+ "]\n",
321
+ "for tc in test_cases:\n",
322
+ " print(f' ANTES : {repr(tc)}')\n",
323
+ " print(f' DESPUES: {repr(clean_regex(tc))}')\n",
324
+ " print()"
325
+ ]
326
+ },
327
+ {
328
+ "cell_type": "code",
329
+ "execution_count": null,
330
+ "metadata": {},
331
+ "outputs": [
332
+ {
333
+ "name": "stdout",
334
+ "output_type": "stream",
335
+ "text": [
336
+ "PASO 3+4 — Lematizacion (spaCy) + Filtrado (NLTK)\n",
337
+ "-----------------------------------------------------------------\n",
338
+ " ANTES : black people are being killed by cops\n",
339
+ " DESPUES: black people kill cop\n",
340
+ "\n",
341
+ " ANTES : you are so stupid and racist thug\n",
342
+ " DESPUES: stupid racist thug\n",
343
+ "\n",
344
+ " ANTES : running faster than the police officers\n",
345
+ " DESPUES: run fast police officer\n",
346
+ "\n",
347
+ " ANTES : these cops are criminals and killers\n",
348
+ " DESPUES: cop criminal killer\n",
349
+ "\n"
350
+ ]
351
+ }
352
+ ],
353
+ "source": [
354
+ "# ── PASO 3 + 4: Lematizacion con spaCy + filtrado con NLTK ───────────────\n",
355
+ "#\n",
356
+ "# Por que spaCy para LEMATIZAR:\n",
357
+ "# Conoce gramatica inglesa real. // 'running' -> 'run' | 'cops' -> 'cop' | 'better' -> 'good'\n",
358
+ "\n",
359
+ "# Por que NLTK para STOPWORDS:\n",
360
+ "# Lista curada de 179 palabras funcionales (the, is, at, which...)\n",
361
+ "# Mas explicita y facil de personalizar que la lista interna de spaCy\n",
362
+ "#\n",
363
+ "# DECISION CRITICA del EDA:\n",
364
+ "# NO anadir 'black', 'white', 'police', 'cop' a stopwords.\n",
365
+ "# Aparecen en ambas clases pero con contexto DISTINTO.\n",
366
+ "# El modelo necesita verlas para discriminar por bigrams.\n",
367
+ "\n",
368
+ "STOP_WORDS = set(stopwords.words('english'))\n",
369
+ "\n",
370
+ "# Stopwords custom: palabras tematicas sin valor discriminante\n",
371
+ "CUSTOM_STOPWORDS = {'youtube', 'video', 'watch', 'like', 'comment', 'channel'}\n",
372
+ "STOP_WORDS = STOP_WORDS | CUSTOM_STOPWORDS\n",
373
+ "\n",
374
+ "MIN_TOKEN_LEN = prep_cfg.get('min_token_length', 2)\n",
375
+ "\n",
376
+ "def lemmatize_and_filter(text: str) -> str:\n",
377
+ " \"\"\"Lematiza con spaCy y filtra stopwords con NLTK.\"\"\"\n",
378
+ " doc = nlp(text)\n",
379
+ " tokens = [\n",
380
+ " token.lemma_\n",
381
+ " for token in doc\n",
382
+ " if not token.is_punct # sin puntuacion\n",
383
+ " and not token.is_space # sin espacios\n",
384
+ " and len(token.text) >= MIN_TOKEN_LEN # tokens de al menos 2 chars\n",
385
+ " and token.lemma_ not in STOP_WORDS # sin stopwords\n",
386
+ " ]\n",
387
+ " return ' '.join(tokens)\n",
388
+ "\n",
389
+ "# Validacion: ver exactamente que hace la lematizacion\n",
390
+ "print('PASO 3+4 — Lematizacion (spaCy) + Filtrado (NLTK)')\n",
391
+ "print('-' * 65)\n",
392
+ "test_texts = [\n",
393
+ " 'black people are being killed by cops',\n",
394
+ " 'you are so stupid and racist thug',\n",
395
+ " 'running faster than the police officers',\n",
396
+ " 'these cops are criminals and killers',\n",
397
+ "]\n",
398
+ "for tt in test_texts:\n",
399
+ " result = lemmatize_and_filter(tt)\n",
400
+ " print(f' ANTES : {tt}')\n",
401
+ " print(f' DESPUES: {result}')\n",
402
+ " print()"
403
+ ]
404
+ },
405
+ {
406
+ "cell_type": "code",
407
+ "execution_count": null,
408
+ "metadata": {},
409
+ "outputs": [
410
+ {
411
+ "name": "stdout",
412
+ "output_type": "stream",
413
+ "text": [
414
+ "PIPELINE COMPLETO — Ejemplos reales del dataset\n",
415
+ "======================================================================\n",
416
+ "[NO TOXICO ]\n",
417
+ " CRUDO : You call yourself an anarchist but defend a cop shooting an unarmed civilian. I'm highly disappointe\n",
418
+ " LIMPIO : call anarchist defend cop shoot unarmed civilian highly disappointed beginning refer square know blu\n",
419
+ "\n",
420
+ "[NO TOXICO ]\n",
421
+ " CRUDO : My mother told me the same thing.  God Bless this woman.\n",
422
+ " LIMPIO : mother tell I thing god bless woman\n",
423
+ "\n",
424
+ "[NO TOXICO ]\n",
425
+ " CRUDO : Love it I same the saem thing Go Peggy! #stupid  \n",
426
+ "Ya Killing ya selves more quicker than  STLPD cou\n",
427
+ " LIMPIO : love saem thing go peggy stupid ya kill ya self quick stlpd could ever wakeup\n",
428
+ "\n",
429
+ "[TOXICO ]\n",
430
+ " CRUDO : Next time they do that, line up some cars and start making burnout smoke to \"riot gas\" them. Non let\n",
431
+ " LIMPIO : next time line car start make burnout smoke riot gas non lethal eficient double check actualy non le\n",
432
+ "\n",
433
+ "[NO TOXICO ]\n",
434
+ " CRUDO : He was Robbing the Store and Being a Big Man .\n",
435
+ "If you Play with Fire you Will Get Burnt .?\n",
436
+ "The Polic\n",
437
+ " LIMPIO : rob store big man play fire get burn police job\n",
438
+ "\n",
439
+ "[NO TOXICO ]\n",
440
+ " CRUDO : The crazy thing is I thought offices never do anything I want of random people over\n",
441
+ " LIMPIO : crazy thing think office never anything want random people\n",
442
+ "\n"
443
+ ]
444
+ }
445
+ ],
446
+ "source": [
447
+ "# ── PIPELINE COMPLETO ──\n",
448
+ "\n",
449
+ "def preprocess_text(text: str) -> str:\n",
450
+ " \"\"\"\n",
451
+ " Pipeline completo de preprocesamiento NLP.\n",
452
+ "\n",
453
+ " Pasos:\n",
454
+ " 1. lowercase\n",
455
+ " 2. limpieza regex (URLs, menciones, chars especiales)\n",
456
+ " 3. lematizacion con spaCy\n",
457
+ " 4. filtrado stopwords con NLTK\n",
458
+ "\n",
459
+ " \"\"\"\n",
460
+ " text = to_lowercase(text)\n",
461
+ " text = clean_regex(text)\n",
462
+ " text = lemmatize_and_filter(text)\n",
463
+ " return text\n",
464
+ "\n",
465
+ "# Verificacion con ejemplos reales del dataset\n",
466
+ "print('PIPELINE COMPLETO — Ejemplos reales del dataset')\n",
467
+ "print('=' * 70)\n",
468
+ "for _, row in df.sample(6, random_state=42).iterrows():\n",
469
+ " label = 'TOXICO ' if row[TARGET_BIN] else 'NO TOXICO '\n",
470
+ " print(f'[{label}]')\n",
471
+ " print(f' CRUDO : {row[TEXT_COL][:100]}')\n",
472
+ " print(f' LIMPIO : {preprocess_text(row[TEXT_COL])[:100]}')\n",
473
+ " print()"
474
+ ]
475
+ },
476
+ {
477
+ "cell_type": "markdown",
478
+ "metadata": {},
479
+ "source": [
480
+ "## 3. Aplicar el pipeline al dataset completo\n",
481
+ "\n",
482
+ "Procesamos las 1000 filas y validamos que no haya pérdida de información crítica."
483
+ ]
484
+ },
485
+ {
486
+ "cell_type": "code",
487
+ "execution_count": 9,
488
+ "metadata": {},
489
+ "outputs": [
490
+ {
491
+ "name": "stdout",
492
+ "output_type": "stream",
493
+ "text": [
494
+ "Procesando dataset completo...\n",
495
+ "Completado: 1000 comentarios procesados\n"
496
+ ]
497
+ }
498
+ ],
499
+ "source": [
500
+ "print('Procesando dataset completo...')\n",
501
+ "df['clean_text'] = df[TEXT_COL].apply(preprocess_text)\n",
502
+ "print(f'Completado: {len(df)} comentarios procesados')"
503
+ ]
504
+ },
505
+ {
506
+ "cell_type": "code",
507
+ "execution_count": 10,
508
+ "metadata": {},
509
+ "outputs": [
510
+ {
511
+ "name": "stdout",
512
+ "output_type": "stream",
513
+ "text": [
514
+ " CRUDO LIMPIO REDUCCION\n",
515
+ "----------------------------------------------------\n",
516
+ " mean 33.8 16.6 50.7%\n",
517
+ " median 19.0 9.0 52.6%\n",
518
+ " min 1.0 1.0 0.0%\n",
519
+ " max 815.0 373.0 54.2%\n",
520
+ "\n",
521
+ "Comentarios vacios tras limpieza : 0\n"
522
+ ]
523
+ }
524
+ ],
525
+ "source": [
526
+ "# Estadisticas antes vs despues\n",
527
+ "df['tokens_raw'] = df[TEXT_COL].str.split().str.len()\n",
528
+ "df['tokens_clean'] = df['clean_text'].str.split().str.len()\n",
529
+ "\n",
530
+ "print(f\"{'':20} {'CRUDO':>8} {'LIMPIO':>8} {'REDUCCION':>11}\")\n",
531
+ "print('-' * 52)\n",
532
+ "for stat in ['mean', 'median', 'min', 'max']:\n",
533
+ " raw = getattr(df['tokens_raw'], stat)()\n",
534
+ " clean = getattr(df['tokens_clean'], stat)()\n",
535
+ " pct = (1 - clean / raw) * 100 if raw > 0 else 0\n",
536
+ " print(f' {stat:18} {raw:8.1f} {clean:8.1f} {pct:10.1f}%')\n",
537
+ "\n",
538
+ "print()\n",
539
+ "empty_after = (df['tokens_clean'] == 0).sum()\n",
540
+ "print(f'Comentarios vacios tras limpieza : {empty_after}')"
541
+ ]
542
+ },
543
+ {
544
+ "cell_type": "code",
545
+ "execution_count": null,
546
+ "metadata": {},
547
+ "outputs": [
548
+ {
549
+ "data": {
550
+ "image/png": "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VapUkaQLiq24LuZ5Oz4+3nz/xRdf6O2335bVatWYMWMKbd+mTRs1bdpULVu2VOXKlXX8+HFt377drM/OzlZkZKS6dOmiRYsWadeuXbrppps0aNAghYaG6rffftN3332nDz/8UGvXrlW1atUu6t6By0KZroAIwGcKW+ja4zm1WG/dunULLPp7+tWnTx+P2+02258uP73ZREZGhqdJkyYFNoM4evSop2HDhkX2mz+OC93oo0OHDkX2GR0d7bWQ9Jnxnsu4cePOGW9RCynnN3r06ALnV6pUydwYJP/9ZGdnexo3blygfe3atQuNPysry3P99dcXGWfbtm092dnZ53W/69ev9zrXbrcXWBR70KBBRV4rMDDQs2vXrrNeo7gbl4wfP94sz7/w9uzZs8+778jISE9cXFyBuG+88Ubz77qov78L/W8DAIDS5q/Pc4Up7sYl5/Mcca6+4+PjPWFhYQXi/vvf/272UdTz4q5du8wNNQp7jRo1ymw7efLkIttZLBbPV199ddbPKP9959/47vTLZrN53feFxHa27/tcLuZ5O/9mM6dfbdq0KfS5smbNmkVep379+p68vDyPx+Px/Pbbb4U+L+Z/nf47AK50TDcG/FxkZKTWrVun0aNHq06dOnI6nQoODtY111yjWbNmad68eWedQhAcHKyFCxcqNjZWkvT6669r3LhxioiI0Nq1azV58mQ1btxYgYGBCgoKUu3atXXLLbfo7bffVuvWrYsV8/3336/bb79dNWvWVEhIiGw2mypXrqzk5GR99dVXXtN9L9TEiRP16aefqnPnzipfvrzsdrtiY2N18803e03BPZdx48bpnnvuUUREhPkL6Zo1awqNzeFwaOnSpUpOTlZYWJjCwsJ02223ea2vkp/T6dTSpUs1depUNWrUSIGBgQoICFDDhg01ZcoULVmyRA6H47zibNmypde0jy5dungtVC2d+sV3wIABqlOnjsLDw2W1WhUVFaVevXrpyy+/POu0Gl8LDQ3Vl19+qR49eig4OFiRkZG677779OGHH55zaszF/rcBAEBZ4XmubFWrVk2rVq1SYmKiAgMDFRMTozFjxmjWrFnnPLdGjRr69ttvNXToUFWvXl12u11hYWFq27at5s+fr6lTp5ptu3btqnvvvVcNGjRQuXLlZLVaFRkZqU6dOunzzz9XmzZtzjvmESNG6KWXXlLNmjXlcDjUtGlTLVy40GsU6IXEdjEu5nn7rbfe0oABA1ShQgVFRESoX79++uSTTwptO3r0aPXs2VPx8fEKCgqS3W5XtWrVdN999+mLL74w1+SsWrWqvvvuO40cOVJ169ZVQECAQkNDVbduXfXv31//+9//zNGWwJXO8HjOYyVWAAAuIxcyzRwAAOBc9uzZYyaw2rVrV+SPuZeSCRMmaOLEiZKk2bNna+DAgb4NCMAlj5GEAAAAAAAAgJ8jSQgAAAAAAAD4OZKEAAAAAAAAgJ9jTUIAAAAAAADAzzGSEAAAAAAAAPBzJAkBAAAAAAAAP0eSUJLH49GxY8fEzGsAAIArA893AAAAF4YkoaTjx48rPDxcx48fL9PrZmZnKeLB2xXx4O3KzM7yqsvKytMt3Rbrlm6LlZWVV6ZxAQAAXO589XwHAABwuSJJCAAAAAAAAPg5koQAAAAAAACAnyNJCAAAAAAAAPg5koQAAAAAAACAnyNJCAAAAAAAAPg5m68DAADAH3k8HuXl5cnlcvk6FPghu90uq9Xq6zAAAMAlxOVyKTc319dhoBhK6tmOJKEP2a02jep8i/k+P6vVolvvqGm+BwBcOXJycpSSkqITJ074OhT4KcMwFBcXp5CQEF+HAgAAfMzj8Sg1NVVpaWm+DgUXISIiQjExMTIMo9h9GB6Px1OCMV2Wjh07pvDwcKWnpyssLMzX4QAArmBut1s7d+6U1WpVxYoV5XA4LuofcuBCeTweHTp0SCdOnFDt2rWv2BGFPN8BAHB+UlJSlJaWpqioKAUFBfFsepnxeDw6ceKEDh48qIiICFWqVKnYfTGSEACAMpSTkyO3260qVaooKCjI1+HAT1WsWFF79uxRbm7uFZskBAAA5+ZyucwEYfny5X0dDoopMDBQknTw4EFFRUUV+/mOJKEPud1u7TjwhySpTnRlWSyWfHUe/fF7hiSpcpUQWSxk8gHgSpL/f/OBssYIAQAAIMlcg5Afry9/p7/Di/kRmCShD53MzVHC1H9Ikv6Y9oaCnQFmXU6OSyPuXyNJ+u8HHRUQwFcFAAAAAABKHj8gXv5K4jtkGAMAAPCZ4cOHa+DAgb4OAwAAAPB7DE8DAOAScPKkS9lZ7lK9hjPAosDAC5t68NVXX+nJJ5/UunXr5PF4FB8fr+TkZA0fPlwOh6OUIgUAAADK3sqVK3XDDTfo6NGjioiI8HU4ZY4kIQAAl4DsLLe2bM4otUShM8Cihk1CLihJuHDhQvXt21eTJ0/WW2+9pQoVKuinn37S1KlTlZKSovj4eLNtbm6u7HZ7aYQOAACAMubOPC7Picwyu54RFCxLcOj5tT3HtNrx48drwoQJxYrj2muvVUpKisLDw4t1/uWOJCEAAJeI7Cy3Tp4s3dGE58vj8eiBBx7QqFGjNHz4cLO8bt26mjNnjvbs2SPDMPT666/rySef1PHjx7V+/XpVr17d65fX4cOHKy0tTXPmzJEkrV69WkOGDNHu3bvVqVMnlStXzuu6Gzdu1IMPPqht27YpNjZWjz/+uPr27VtGdw0AAABJ8pzIVNbXy+Q+kVHq17IEhSjg2o7SeSYJU1JSzPfz58/XuHHjtGPHDrMsJCSk2LE4HA7FxMQU+/zLHWsSAgCAAnbu3Kndu3efM0H3v//9Txs3btTu3bvP2efRo0d10003aejQoUpLS9Ndd92l//73v2Z9WlqaOnfurD59+ujQoUOaNWuW7r77bq1Zs+ai7wcAAAAXxn0iQ57M46X+utBEZExMjPkKDw+XYRjmcVRUlJ599lnFxcXJ6XSqSZMmWrx4saRTP4J37NhRSUlJ8ng8kqQjR44oLi5O48aNk3RqurFhGEpLSzOvt2bNGiUmJiooKEjlypVTUlKSjh49KknKzs7WAw88oKioKAUEBOi6667Thg0bSuDT9w2ShAAAoIBDhw5JkipXrnzWduPHj1dERISCgoLO2efChQsVGxure++9VzabTT169FD79u3N+k8//VQVK1bUsGHDZLfb1a5dO91xxx164403Lu5mAAAA4BdeeOEF/etf/9IzzzyjH374QUlJSbrpppu0c+dOGYahN954Qxs2bND06dMlSffdd58qV65sJgnPtHnzZnXo0EH169fX2rVr9dVXX6lHjx5yuVySpEceeUQffPCB3njjDX377beqVauWkpKSdOTIkTK755LEdGMfslttGta+h/k+P6vVopturma+BwCgLFWoUEGS9Mcff6hmzZpFtqtatep597l//36vdQwlKT4+XllZWZKkffv2qVq1al71NWrU0OrVq8/7GgAAAPBfzzzzjEaNGqU+ffpIkp5++mmtWLFCzz//vGbMmKHKlSvr3//+t/r376/U1FQtWrRI3333nWy2wtNj06ZNU4sWLTRz5kyz7Oqrr5YkZWZmatasWZozZ466dOkiSXr11Ve1dOlSvfbaaxo5cmQp323JI0noQw6bTZN73llond1uUf9Bdcs4IgAATrnqqqtUrVo1vfPOOxo7dmyR7SyWv37IOr3+y4kTJ8w1CVNSUhQYGChJio2N1W+//eZ1/t69exUVFSVJiouL0549e7zq9+zZo7i4uIu9HQAAAFzhjh07pv3796tNmzZe5W3atNH3339vHt9666366KOPNHXqVM2aNUu1a9cuss/Nmzfr1ltvLbRu165dys3N9bqe3W5Xy5Yt9eOPP17k3fgGQ9TKWGZejv7MzijwyszL8XVoAACYDMPQiy++qKlTp+rFF1/U4cOHJUk///yzBg0aVCDZJ50afVi1alW98cYbcrvdWrFihRYtWmTWd+vWTX/88YdeffVV5eXl6dNPP9UXX3xh1nft2lUHDx7UzJkzlZeXpy+//FJz585V//79S/+GgYvkzjwu16HUi3q5M4/7+jYAALjinThxQps2bZLVatXOnTvP2vb0j93+gpGEZeykK0drD/+mk65cuT0eHTmWrgCrXd2rNVSwzWG2c7s9+vPQSUlShYqBsljOvsU3AODy5wwovd/uitN39+7d9dlnn+mJJ57Q448/LunU9OJ+/fqpUqVKhZ7z+uuva/DgwXrqqafUrVs39enTR7m5uZKkyMhIffzxxxo6dKhGjBihG2+8UcnJyeaaLuXKldNnn32m4cOHa/To0YqNjdWsWbN03XXXFfOugbJzsbtAXujOjgAAwFtYWJhiY2O1Zs0atWvXzixfs2aNWrZsaR4//PDDslgs+uyzz9S1a1d169bNa53s/Bo1aqTly5dr4sSJBepq1qwph8OhNWvWmEvq5ObmasOGDRo+fHjJ3lwZIUnoAydducp05Sg7N1djXp0lSer4xEyvNjk5Lt3/t1NrMP33g44KCOCrAoArmTPAooZNQkr9GhfquuuuM3eEO9PpXeHy69Chg37++eci+0tMTNTWrVuLrG/ZsqW+/vrrC44TuBSc3gWyWOeWcCwAAPijkSNHavz48apZs6aaNGmi2bNna/PmzZo7d66kUxvlvf7661q7dq2aNWumkSNHasCAAfrhhx9Urly5Av2NHj1aDRs21P3336/77rtPDodDK1as0K233qoKFSpo8ODBGjlypCIjI1W1alVNmzZNJ06c0KBBg8r61ksEmScAAC4BgYFWBQZafR0GAAAAIOnUKPey+BHLElRyP5Q/8MADSk9P18MPP6yDBw+qfv36+t///qfatWvr0KFDGjRokCZMmKBmzZpJkiZOnKglS5bovvvu0/z58wv0d9VVV2nJkiUaM2aMWrZsqcDAQLVq1Up9+/aVJE2dOlVut1v9+vXT8ePH1aJFC33++eeFJhwvByQJAQAAAAAAYDKCgk8tg1GG1yuOgQMHauDAgeaxxWLR+PHjNX78+AJtK1asqNTUVK8yu92ujRs3mseJiYkFZsu0a9dOa9asKfT6AQEBmj59uqZPn16s+C81JAnLmMvlUU6OW9l5buXk/pWTz831KD0tV6f/FrOzXGbdyZNuBQSUdaQAAAAAAMAfWYJDWSfXD5EkLGNut5R2NE9HT+QoNy/XLD+enqf9ezKUnXUqcZib+1eSMCeLVWoAAAAAAABQekgS+oDL5TFfZplbys5y6+TJ00lCEoMAAAAAAAAoGxe+zSEAAAAAAACAKwojCX3IMCxqVL2BHFarrIZ3vtZiMVSnXkXZbIYsVsNHEQIAAAAAAMAfkCT0IZvVqvaN2yrc6ZTDZpfybS5utVrU+tqqCgy0yG5nwCcAAAAAAABKD9knAABw3q6++motXLiwVPqeO3eurr322lLpGwAAAMDZMZLQhzwej07mZMnmccnj8RSoy87Kk2QpUAcAuPJk5uXopCunVK8RaHUo2OY4r7aJiYnq1auXhg8f7lW+bdu2UojslOTkZCUnJ5da/wAAAACKRpLQh/JceXrls9mSpC4PvCDJ/lddnlvz5/0gSWqZkOiD6AAAZemkK0drD/+mk67cUuk/0GpXQvn4804SAgAAAPAvTDcGAOAScdKVq0xXTqm8Sir5WK1aNS1YsECSNGfOHDVp0kTjxo1ThQoVFBMTo/nz52vNmjVq0KCBwsPDNWjQILndp9bcXblypSIiIvTiiy+qUqVKiomJ0fjx480R86f7O+3AgQO67bbbVLFiRVWtWlVjx45VXl5eidwHAAAAUJYGDhyoXr16+TqMs2IkIQAAKLatW7fqb3/7m1JTU/XGG2/onnvuUVJSklatWqXs7Gw1bdpUCxYs0M033yxJOn78uL799lvt2rVLe/fu1Y033qgaNWpowIABBfq+4447FBMTo927d+vw4cPq2rWrgoODNWbMmLK+TQAAAL9SFkvh5Hchy+JIpxJub7zxhqZMmaJHH33ULF+wYIH+7//+76KWbZswYYIWLFigzZs3F7uPwrzwwguX/HJyJAkBAECxVaxYUQ888IAkqW/fvvr73/+uQYMGqXz58pKkdu3a6dtvvzWThG63W08//bSCgoJUt25dDR06VG+99VaBJOEff/yhL774QqmpqQoJCVFISIjGjh2rCRMmkCQEAAAoZaW9FE5+xV0WJyAgQE8//bTuvfdelStXrpSiKznh4eG+DuGcmG4MAACKLTo62nwfFBRUaFlGRoZ5HBAQoKioKPM4Pj5ef/zxR4F+9+3bp4CAAK++atSooX379pVo/AAAAChcaS6FUxLL4nTs2FExMTGaMmXKWdt98MEHuvrqq+V0OlWtWjX961//KrLtnDlzNHHiRH3//fcyDEOGYWjOnDmSpL1796pnz54KCQlRWFiYbrvtNh04cECS9NNPPykoKEjz5s0z+3r33XcVGBio7du3Syo43djtdmvatGmqVauWnE6nqlatqieffNKs37Jli9q3b6/AwECVL19e99xzj9dzdWkgSQgAAMpMVlaWDh48aB7v3btXlStXLtAuLi5OWVlZ5oOXJO3Zs0dxcXFlEicAAAAubVarVU899ZRefPHFIn9I3rRpk2677Tb16dNHW7Zs0YQJE/T444+bib8z3X777Xr44Yd19dVXKyUlRSkpKbr99tvldrvVs2dPHTlyRKtWrdLSpUv166+/6vbbb5ck1a1bV88884zuv/9+7d27V/v27dN9992np59+WvXr1y/0WqNHj9bUqVP1+OOPa/v27Zo3b575A3lmZqaSkpJUrlw5bdiwQe+9956WLVumoUOHXvwHdxZMNwYAAIXKy8tTVlaWeWwYxkX3abFYNHr0aL300kvau3evZsyYoQkTJhRoV7lyZd1www36xz/+oZdfflmHDx/Wk08+WejahQAAAPBP//d//6cmTZpo/Pjxeu211wrUP/vss+rQoYMef/xxSdJVV12l7du365///KcGDhxYoH1gYKBCQkJks9kUExNjli9dulRbtmzR7t27VaVKFUnSm2++qauvvlobNmzQNddco/vvv1+LFi3SnXfeKYfDoWuuuUbDhg0rNO7jx4/rhRde0EsvvWQ+39asWVPXXXedJGnevHnKysrSm2++qeDgYEnSSy+9pB49eujpp5/2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XC6XVq5cqSeeeEKTJk06r74BAAAAn083zi8iIkJXXXWVfvnlF8XExCgnJ0dpaWlebQ4cOGBOM4qJidGBAwcK1J+uK8ro0aOVnp5uvn7//feSvREAAK4gkZGR6tixY6Fr1ISEhGjZsmVavny5qlWrpvLly+uOO+4wR/R37NhRkydPVu/evVWpUiW53W41aNCg0OvUr19fY8eOVfv27VW+fHnNnz9fN91003nH+dBDD8lqtSo6OloNGjQw10EsSq9evfTss89qyJAhCg8P1zXXXKMtW7YU2b59+/a69957de2116pWrVpq2LChQkNDzfq//e1vuvPOO3X99derRo0aatq0qVd9//79NWzYMHXv3l0xMTH6/vvv9cknn5jrHT711FPq0qVLkddfvXq1AgMDlZSUpPT0dAUGBiowMNCsX7FiherUqaPQ0FA9+OCDmjlzpjp3vvB1KAEAAOCfDM/5zuEpAxkZGapataomTJigAQMGqGLFinr77bfNRcx37NihunXrau3atWrdurU+++wzde/eXSkpKeZC46+88opGjhypgwcPeq0DdDbHjh1TeHi40tPTS31h75SM43p323YdPZHltXHJ8gde0NGf7Tp50i3pjI1L/pOo6EoBpRoXAKBsZGVlaffu3apevboCAvjfdvhGaf4dTpkyRR9++KF++uknBQYG6tprr9XTTz+tOnXqmG0SExO1atUqr/Puvfdevfzyy+bx3r17NXjwYK1YsUIhISEaMGCApkyZcl6byEhl+3wnSa5DqTqxbIE8mcfP3bgQRnCogjr2krVi0T90AwAAlCafrkn4j3/8Qz169FB8fLz279+v8ePHy2q1qm/fvgoPD9egQYP00EMPKTIyUmFhYRo2bJgSEhLUunVrSVKnTp1Uv3599evXT9OmTVNqaqoee+wxDRky5LwThAAAACg5q1at0pAhQ3TNNdcoLy9PY8aMUadOnbR9+3av9RHvvvtur+nQQUFB5nuXy6Vu3bopJiZGX3/9tVJSUtS/f3/Z7XY99dRTZXo/AAAA/sKnScJ9+/apb9++Onz4sCpWrKjrrrtO69atU8WKFSVJzz33nCwWi3r37q3s7GwlJSVp5syZ5vlWq1ULFy7U4MGDlZCQoODgYA0YMOCyWX/HMAxVi64qu8Uqi+E989tiGKocFyar1ZDlkpoUDgAAULTFixd7Hc+ZM0dRUVHatGmT2rZta5YHBQUVuTzMkiVLtH37di1btkzR0dFq0qSJJk+erFGjRmnChAlyOByleg8AAAD+yKdJwnfeeees9QEBAZoxY4ZmzJhRZJv4+HgtWrSopEMrEzarTb0Suivc6ZTTZpfkNuusNos6JtVWYKBF9jMWrAcAALhcpKenSzq1tmV+c+fO1X//+1/FxMSoR48eevzxx83RhGvXrlXDhg0VHR1ttk9KStLgwYO1bds2NW3atMB1srOzvXaTzr/zNAAAAM7Np0lCAAAAXLncbreGDx+uNm3aeG1Yc8cddyg+Pl6xsbH64YcfNGrUKO3YsUMffvihJCk1NdUrQSjJPD69Kc6ZpkyZookTJ5bSnQAAAFz5SBICAACgVAwZMkRbt27VV1995VV+zz33mO8bNmyoSpUqqUOHDtq1a5dq1qxZrGuNHj1aDz30kHl87NgxValSpXiBAwAA+CGShD6Um5erf382W4aktkP+Jcn+V12uS+/O+0GS1LBJW686AACAS93QoUO1cOFCrV69WnFxcWdt26pVK0nSL7/8opo1ayomJkbffPONV5sDBw5IUpHrGDqdTjauAwAAuAhsieFjea485bryCq/Lcysvz11oHQAAwKXI4/Fo6NCh+uijj/TFF1+oevXq5zxn8+bNkqRKlSpJkhISErRlyxYdPHjQbLN06VKFhYWpfv36pRI3AACAv2MkIQAAAErMkCFDNG/ePH388ccKDQ011xAMDw9XYGCgdu3apXnz5qlr164qX768fvjhB40YMUJt27ZVo0aNJEmdOnVS/fr11a9fP02bNk2pqal67LHHNGTIEEYLAgAAlBJGEgIAgDLVpEkTzZkzR9KpHW6vvfZan8QxYcIE9erVyzwOCQnRli1bSuVaTz31lPr27VsqfV9qZs2apfT0dCUmJqpSpUrma/78+ZIkh8OhZcuWqVOnTqpbt64efvhh9e7dW5988onZh9Vq1cKFC2W1WpWQkKA777xT/fv316RJk3x1WwAAAFc8koQAAKCAxMREGYahZcuWeZX/85//lGEYGj58eIlcJzk5WV9//XWJ9HWxMjIy1LBhw1Lpe8yYMXr77bdLrL+lS5eqWbNmCg0NVf369bV48eIS6/tieTyeQl8DBw6UJFWpUkWrVq3S4cOHlZWVpZ07d2ratGkKCwvz6ic+Pl6LFi3SiRMndOjQIT3zzDOy2ZgEAwAAUFpIEgIAgELVqVNHs2fP9iqbPXu26tat66OIIEm//vqr/u///k+TJk1Senq6pk2bpt69e+vXX3/1dWgAAAC4jJEkBAAAherTp48+++wzpaenS5LWr18v6a+daE/btWuXevTooYoVKyo+Pl5PPPGE3O6/Nt566aWXVKVKFZUvX15jx471OnfOnDlq0qSJefzss8+qdu3aCg0NVc2aNfXSSy+ZdXv27JFhGHrrrbdUq1YtRUREaODAgcrNzZV0aiRgz549FRUVpfDwcLVt21bff//9ed+vYRjmBhoTJkxQ9+7dde+99yo8PFzVq1fXypUrtWDBAtWqVUvlypXzupfT9zFmzBiVL19eVatW1cyZM836M6c2//LLL0pKSlJkZKRq1qyp559//rzjXLx4sZo1a6bu3bvLYrGoe/fuatmypd58883z7gMAAAA4E0lCHzIMQ5XLxyq+YpwsMgrURceEqFJsqAy+JQC44mVmZxX5ysrNOe+2J3OKbnuhIiIi1LlzZ3Oa7Ouvv6677rrLq82JEyfUoUMHdejQQX/88Ye+/PJLvfPOO+YIxC+++EJjx47Vu+++q5SUFEnS1q1bi7xmfHy8vvjiCx07dkz/+c9/NHLkSK1Zs8arzWeffabvvvtO27dv1/LlyzV37lxJktvt1h133KHdu3frwIEDatq0qW677TZ5PJ4LvndJWrJkiZKSknTkyBH169dPd955pz7++GN9//33WrNmjf71r3/p22+/Ndtv3bpVhmEoJSVF8+fP16OPPqrVq1cX6DcvL0/du3dX48aNtX//fn300UeaNm2a5s2bZ7aJiIjQV199VWhcbre7wD253W798MMPxbpPAAAAQGJ3Y5+yWW269fpeCnc65bQ7JP016sJms6hztzoKDLTI4bD6LkgAQJmo/MiAIus61W+qd+991Dyu/dg9OpGTXWjbNrXq69Nh483jRhOH6nDmcUlS2gvzLziuu+66S4899pgGDBigDz74QFu3btWjj/4Vy6effqpy5cqZaxRWrVpVDz74oObNm6dBgwZp7ty5Sk5OVkJCgqRTI+ryjw48U+/evc33N9xwg5KSkrRy5Uq1adPGLB83bpxCQ0MVGhqqzp07a9OmTRo4cKDCwsJ0++23m+0mTpyo6dOna//+/apcufIF33vz5s118803Szo1qnLy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4NXH3r171a1bNwUFBSkqKkojR45UXl7eRccGAAAAAAAA+ItiJQlvvfVWLVmy5KIvHhcXp6lTp2rTpk3auHGj2rdvr549e2rbtm2SpBEjRuiTTz7Re++9p1WrVmn//v26+eabzfNdLpe6deumnJwcff3113rjjTc0Z84cpjsDAAAAAAAAF6BY041r1aqlxx9/XOvWrVPDhg1lt9u96h944IHz6qdHjx5ex08++aRmzZqldevWKS4uTq+99prmzZun9u3bS5Jmz56tevXqad26dWrdurWWLFmi7du3a9myZYqOjlaTJk00efJkjRo1ShMmTJDD4SjO7QEAAAAAAAB+pVhJwldeeUUhISFatWqVVq1a5VVnGMZ5Jwnzc7lceu+995SZmamEhARt2rRJubm56tixo9mmbt26qlq1qtauXavWrVtr7dq1atiwoaKjo802SUlJGjx4sLZt26amTZsW5/bKjNVqVZ92vRVid8hhtZ9RZ1G3m+rK6TRkdxRrwCcAAAAAAABwXoqVfdq9e3eRr19//fWC+tqyZYtCQkLkdDp133336aOPPlL9+vWVmpoqh8OhiIgIr/bR0dFKTU2VJKWmpnolCE/Xn64rSnZ2to4dO+b18gWLYVFMuWhVLh8jq8X7q7BYDFWoGKyo6BBZLEYRPQAAAFx6Vq9erR49eig2NlaGYWjBggVe9QMHDpRhGF6vzp07e7U5cuSIkpOTFRYWpoiICA0aNEgZGRlleBcAAAD+5aKGqOXk5GjHjh0XtVFInTp1tHnzZq1fv16DBw/WgAEDtH379osJ65ymTJmi8PBw81WlSpVSvR4AAIA/yczMVOPGjTVjxowi23Tu3FkpKSnm6+233/aqT05O1rZt27R06VItXLhQq1ev1j333FPaoQMAAPitYk03PnHihIYNG6Y33nhDkvTzzz+rRo0aGjZsmCpXrqxHH330vPtyOByqVauWJKl58+basGGDXnjhBd1+++3KyclRWlqa12jCAwcOKCYmRpIUExOjb775xqu/07sfn25TmNGjR+uhhx4yj48dO+aTRKHL7dJ3u35QgM2m9pVqKn/O1uVy68dtB2W3G2rSIqTMYwMAACiuLl26qEuXLmdt43Q6i3xe+/HHH7V48WJt2LBBLVq0kCS9+OKL6tq1q5555hnFxsaWeMwAAAD+rlgjCUePHq3vv/9eK1euVEBAgFnesWNHzZ8//6ICcrvdys7OVvPmzWW327V8+XKzbseOHdq7d68SEhIkSQkJCdqyZYsOHjxotlm6dKnCwsJUv379Iq/hdDoVFhbm9fIFt9utr7at1bLvv1Se23VGnUebNvyhdV/vkyvP45P4AAAASsvKlSsVFRWlOnXqaPDgwTp8+LBZt3btWkVERJgJQunUc6bFYtH69et9ES4AAMAVr1gjCRcsWKD58+erdevWMoy/1su7+uqrtWvXrvPuZ/To0erSpYuqVq2q48ePa968eVq5cqU+//xzhYeHa9CgQXrooYcUGRmpsLAwDRs2TAkJCWrdurUkqVOnTqpfv7769eunadOmKTU1VY899piGDBkip9NZnFsDAABAKevcubNuvvlmVa9eXbt27dKYMWPUpUsXrV27VlarVampqYqKivI6x2azKTIyssh1p7Ozs5WdnW0e+2rNaQAAgMtVsZKEhw4dKvDgJp1afyZ/0vBcDh48qP79+yslJUXh4eFq1KiRPv/8c914442SpOeee04Wi0W9e/dWdna2kpKSNHPmTPN8q9WqhQsXavDgwUpISFBwcLAGDBigSZMmFee2AAAAUAb69Oljvm/YsKEaNWqkmjVrauXKlerQoUOx+pwyZYomTpxYUiECAAD4nWIlCVu0aKFPP/1Uw4YNkyQzMfif//zHnAp8Pl577bWz1gcEBGjGjBlnXfQ6Pj5eixYtOu9rAgAA4NJSo0YNVahQQb/88os6dOigmJgYr+VkJCkvL09Hjhwpch3DS2XNaQAAgMtVsZKETz31lLp06aLt27crLy9PL7zwgrZv366vv/5aq1atKukYAQAAcAXbt2+fDh8+rEqVKkk6te50WlqaNm3apObNm0uSvvjiC7ndbrVq1arQPpxOJ8vNAAAAXIRibVxy3XXXafPmzcrLy1PDhg21ZMkSRUVFae3ateaDHAAAAPxTRkaGNm/erM2bN0uSdu/erc2bN2vv3r3KyMjQyJEjtW7dOu3Zs0fLly9Xz549VatWLSUlJUmS6tWrp86dO+vuu+/WN998ozVr1mjo0KHq06cPOxsDAACUkmKNJJSkmjVr6tVXXy3JWAAAAHAF2Lhxo2644Qbz+PQ04AEDBmjWrFn64Ycf9MYbbygtLU2xsbHq1KmTJk+e7DUScO7cuRo6dKg6dOhgrlE9ffr0Mr8XAAAAf1GsJOHevXvPWl+1atViBeNvrFarerfpqRCHXQ6r/Yw6i5K6XiWHw5DdUawBnwAAAD6RmJgoj8dTZP3nn39+zj4iIyM1b968kgwLAAAAZ1GsJGG1atXOuouxy+UqdkD+xGJYVKViZYU7nbJaLJLcf9VZDMVUClVgoEUWy/nvGA0AAAAAAABcqGIlCb/77juv49zcXH333Xd69tln9eSTT5ZIYAAAAAAAAADKRrGShI0bNy5Q1qJFC8XGxuqf//ynbr755osOzB+43C5t3bNdATabOlSqJemvEYNut0c//3RIdruhJi1CfBckAAAAAAAArngluthdnTp1tGHDhpLs8ormdru14ocv9dm3K5TrzvOqc7ncWr/2d321eq9ceUWv6QMAAAAAAABcrGKNJDx27JjXscfjUUpKiiZMmKDatWuXSGAAAAAAAAAAykaxkoQREREFNi7xeDyqUqWK3nnnnRIJDAAAAAAAAEDZKFaS8IsvvvBKElosFlWsWFG1atWSzVasLgEAAAAAAAD4SLEyeomJiSUcBgAAAAAAAABfKdbGJVOmTNHrr79eoPz111/X008/fdFBAQAAAAAAACg7xUoS/vvf/1bdunULlF999dV6+eWXLzooAAAAAAAAAGWnWNONU1NTValSpQLlFStWVEpKykUH5S+sFqt6tu6qILtddqv3V2G1WtThxlpyOA3Z7EYRPQAAAAAAAAAXr1hJwipVqmjNmjWqXr26V/maNWsUGxtbIoH5A4vFouox1RTudMpmsUpy56szFFc1XIGBFlmtxRrwCQAAAAAAAJyXYiUJ7777bg0fPly5ublq3769JGn58uV65JFH9PDDD5dogAAAAAAAAABKV7GShCNHjtThw4d1//33KycnR5IUEBCgUaNGafTo0SUa4JXM5Xbpp993KshuU4dKtST9Na3Y7fbo118Oy+6wqEmLEN8FCQAAAAAAgCtesZKEhmHo6aef1uOPP64ff/xRgYGBql27tpxOZ0nHd0Vzu91a+t0XkqQHWneTZDfrXC631nz5myTp1r7xvggPAAAAAAAAfuKiFrtLTU3VkSNHVLNmTTmdTnk8npKKCwAAAAAAAEAZKVaS8PDhw+rQoYOuuuoqde3a1dzReNCgQaxJCAAAAAAAAFxmipUkHDFihOx2u/bu3augoCCz/Pbbb9fixYtLLDgAAAAAAAAA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3/nOd6S8vDxgP6dPn5bx48eL2WyW+Ph4mTdvnrjd7g5uzY2ZOnWqpKamitFolB49esi4ceP8/0GJhH/7Q2mYJAz3OEyePFl69uwpRqNRevXqJZMnT5YTJ07414d7++v93//9n9xxxx1iMplk0KBB8sc//jFgfbifE0VENm7cKAAatUtEG/2gurpa5syZIykpKRIRESF9+/aVn//85+J0Ov1ltNAPtCCcx3ct1Rbn/nCh9TFxU5qKjd1ul29/+9vSo0cPMRgMkpqaKk8++WSjhHu4xobfJUJrLjZnz56V+++/X7p37y4mk0n69+8v8+fPlytXrgTsJxxjw+9foTUVGy33mZZSRETa9VJFIiIiIiIiIiIi6tI4JyEREREREREREZHGMUlIRERERERERESkcUwSEhERERERERERaRyThERERERERERERBrHJCEREREREREREZHGMUlIRERERERERESkcUwSEhERERERERERaRyThERERERERERERBrHJCERURdTUFAARVFw+fLlDj/2r371K9x5550dflwiIiKizsBx1zWnT5+GoigoLi4G0LGxURQFa9asaffjEFHTmCQkoi5nypQpUBQFS5YsCVi+Zs0aKIrSSbW6tXS1QScRERF1TRx33bxwHXfde++9KC8vR7du3dr9WOXl5Rg/fny7H4eImsYkIRF1SREREfjNb36DS5cudXZVWsTlcnV2FYiIiIhuCMddFIzRaERSUlKHJIuTkpJgMpna/ThE1DQmCYmoS8rKykJSUhIWL14cskywX21fffVVpKWl+V9PmTIFkyZNwksvvYTExETExMTghRdegMfjwfz589G9e3fcdtttWLZsWcB+zp07h+9///uIiYlB9+7dMXHiRJw+fbrRfl988UUkJydj4MCBAIBDhw7hn/7pn2A2mxEXF4dp06bBZrM12db169djwIABMJvN+OY3vxlwnHo7duzA2LFjYTab0bt3b8yePRu1tbVB97d8+XI8//zzOHDgABRFgaIoWL58OQDg7NmzmDhxIqxWK6Kjo/H9738f58+fD1m3kydPom/fvpg1axZEBE6nE8888wx69eqFyMhIjB49GgUFBQHHjomJwcaNGzF48GBYrVbk5OSgvLzcX6agoACjRo1CZGQkYmJiMGbMGJw5c6bJGBEREVH74bgrEMdd17a9/nbj+uOtXbsW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",
551
+ "text/plain": [
552
+ "<Figure size 1300x500 with 2 Axes>"
553
+ ]
554
+ },
555
+ "metadata": {},
556
+ "output_type": "display_data"
557
+ },
558
+ {
559
+ "name": "stdout",
560
+ "output_type": "stream",
561
+ "text": [
562
+ "Guardado en reports/07_tokens_comparativa.png\n"
563
+ ]
564
+ }
565
+ ],
566
+ "source": [
567
+ "# Visualizacion: distribucion tokens antes vs despues\n",
568
+ "fig, axes = plt.subplots(1, 2, figsize=(13, 5))\n",
569
+ "\n",
570
+ "# Comparativa global\n",
571
+ "axes[0].hist(df['tokens_raw'], bins=40, alpha=0.6, label='Crudo',\n",
572
+ " color='#7F77DD', edgecolor='white')\n",
573
+ "axes[0].hist(df['tokens_clean'], bins=40, alpha=0.6, label='Limpio',\n",
574
+ " color='#5DCAA5', edgecolor='white')\n",
575
+ "axes[0].axvline(df['tokens_raw'].median(), color='#534AB7',\n",
576
+ " linestyle='--', lw=1.5, label=f'Mediana crudo: {df[\"tokens_raw\"].median():.0f}')\n",
577
+ "axes[0].axvline(df['tokens_clean'].median(), color='#0F6E56',\n",
578
+ " linestyle='--', lw=1.5, label=f'Mediana limpio: {df[\"tokens_clean\"].median():.0f}')\n",
579
+ "axes[0].set_title('Tokens: crudo vs limpio', fontweight='bold')\n",
580
+ "axes[0].set_xlabel('Numero de tokens')\n",
581
+ "axes[0].set_ylabel('Frecuencia')\n",
582
+ "axes[0].legend(fontsize=9)\n",
583
+ "\n",
584
+ "# Por clase\n",
585
+ "for clase, color in [('Toxico', '#E8593C'), ('No toxico', '#5DCAA5')]:\n",
586
+ " mask = df[TARGET_BIN] == (clase == 'Toxico')\n",
587
+ " axes[1].hist(df.loc[mask, 'tokens_clean'], bins=30, alpha=0.6,\n",
588
+ " label=clase, color=color, edgecolor='white')\n",
589
+ "axes[1].set_title('Tokens limpios por clase', fontweight='bold')\n",
590
+ "axes[1].set_xlabel('Numero de tokens limpios')\n",
591
+ "axes[1].legend()\n",
592
+ "\n",
593
+ "plt.tight_layout()\n",
594
+ "plt.savefig(PROJECT_ROOT / 'reports' / 'v2' / '07_tokens_comparativa.png',\n",
595
+ " dpi=150, bbox_inches='tight')\n",
596
+ "plt.show()\n",
597
+ "print('💾 Guardado en reports/07_tokens_comparativa.png')"
598
+ ]
599
+ },
600
+ {
601
+ "cell_type": "code",
602
+ "execution_count": 12,
603
+ "metadata": {},
604
+ "outputs": [
605
+ {
606
+ "name": "stdout",
607
+ "output_type": "stream",
608
+ "text": [
609
+ "Comentarios con < 3 tokens tras limpieza: 100\n",
610
+ "\n",
611
+ " [NO TOXICO]\n",
612
+ " CRUDO : I agree with the protestor.\n",
613
+ " LIMPIO : \"agree protestor\"\n",
614
+ "\n",
615
+ " [TOXICO]\n",
616
+ " CRUDO : I think ,he kill him\n",
617
+ " LIMPIO : \"think kill\"\n",
618
+ "\n",
619
+ " [NO TOXICO]\n",
620
+ " CRUDO : 1 word: Provocateur........\n",
621
+ " LIMPIO : \"word provocateur\"\n",
622
+ "\n",
623
+ " [NO TOXICO]\n",
624
+ " CRUDO : I LIKE TURTLES!\n",
625
+ " LIMPIO : \"turtle\"\n",
626
+ "\n",
627
+ " [TOXICO]\n",
628
+ " CRUDO : CNN is such Bullcrap!\n",
629
+ " LIMPIO : \"cnn bullcrap\"\n",
630
+ "\n",
631
+ " [TOXICO]\n",
632
+ " CRUDO : You all are some dumb as people\n",
633
+ " LIMPIO : \"dumb people\"\n",
634
+ "\n"
635
+ ]
636
+ }
637
+ ],
638
+ "source": [
639
+ "# Casos problematicos: comentarios con menos de 3 tokens tras limpieza\n",
640
+ "# Son comentarios que casi desaparecen -> hay que revisarlos\n",
641
+ "short_clean = df[df['tokens_clean'] < 3]\n",
642
+ "print(f'Comentarios con < 3 tokens tras limpieza: {len(short_clean)}')\n",
643
+ "print()\n",
644
+ "for _, row in short_clean.head(6).iterrows():\n",
645
+ " label = 'TOXICO' if row[TARGET_BIN] else 'NO TOXICO'\n",
646
+ " print(f' [{label}]')\n",
647
+ " print(f' CRUDO : {row[TEXT_COL][:80]}')\n",
648
+ " print(f' LIMPIO : \"{row[\"clean_text\"]}\"')\n",
649
+ " print()"
650
+ ]
651
+ },
652
+ {
653
+ "cell_type": "markdown",
654
+ "metadata": {},
655
+ "source": [
656
+ "## 4. Registro en MLflow\n",
657
+ "\n",
658
+ "Guardamos exactamente qué configuración usamos.\n",
659
+ "Si mañana cambiamos `min_token_length` o las custom stopwords,\n",
660
+ "podremos comparar qué configuración produjo mejores métricas al modelar.\n",
661
+ "\n",
662
+ "> Para ver el dashboard: `mlflow ui` desde la raíz del proyecto.\n",
663
+ "\n",
664
+ "> mlflow ui --backend-store-uri file:./mlruns --port 5001 "
665
+ ]
666
+ },
667
+ {
668
+ "cell_type": "code",
669
+ "execution_count": 13,
670
+ "metadata": {},
671
+ "outputs": [
672
+ {
673
+ "name": "stderr",
674
+ "output_type": "stream",
675
+ "text": [
676
+ "2026/05/11 14:32:35 INFO mlflow.tracking.fluent: Experiment with name 'Youtube_project_experiment' does not exist. Creating a new experiment.\n"
677
+ ]
678
+ },
679
+ {
680
+ "name": "stdout",
681
+ "output_type": "stream",
682
+ "text": [
683
+ "MLflow run registrado\n",
684
+ " Run ID : d01f86f75bd749b8b6a14240af14ec0b\n",
685
+ " Experimento: Youtube_project_experiments\n",
686
+ " Ver UI : mlflow ui --backend-store-uri file:///mnt/c/Users/under/Documents/F5/3_Projects/Project_9_Equipo3/Project_YT/mlruns\n"
687
+ ]
688
+ }
689
+ ],
690
+ "source": [
691
+ "# ── Configuración MLflow ──\n",
692
+ "MLFLOW_DIR = PROJECT_ROOT / 'mlruns'\n",
693
+ "EXPERIMENT_NAME = 'Youtube_project_experiment'\n",
694
+ "\n",
695
+ "mlflow.set_tracking_uri(f\"file:{MLFLOW_DIR}\")\n",
696
+ "mlflow.set_experiment(EXPERIMENT_NAME)\n",
697
+ "\n",
698
+ "\n",
699
+ "with mlflow.start_run(run_name='preprocessing_v2'):\n",
700
+ "\n",
701
+ " # Params: que configuracion usamos\n",
702
+ " mlflow.log_param('spacy_model', 'en_core_web_sm')\n",
703
+ " mlflow.log_param('nltk_stopwords', 'english')\n",
704
+ " mlflow.log_param('custom_stopwords', list(CUSTOM_STOPWORDS))\n",
705
+ " mlflow.log_param('min_token_length', MIN_TOKEN_LEN)\n",
706
+ " mlflow.log_param('remove_urls', prep_cfg['remove_urls'])\n",
707
+ " mlflow.log_param('remove_mentions', prep_cfg['remove_mentions'])\n",
708
+ " mlflow.log_param('lemmatize', prep_cfg['lemmatize'])\n",
709
+ "\n",
710
+ " # Metrics: que produce este preprocesamiento\n",
711
+ " reduction = (1 - df['tokens_clean'].mean() / df['tokens_raw'].mean()) * 100\n",
712
+ " mlflow.log_metric('avg_tokens_raw', round(df['tokens_raw'].mean(), 2))\n",
713
+ " mlflow.log_metric('avg_tokens_clean', round(df['tokens_clean'].mean(), 2))\n",
714
+ " mlflow.log_metric('median_tokens_clean', float(df['tokens_clean'].median()))\n",
715
+ " mlflow.log_metric('token_reduction_pct', round(reduction, 2))\n",
716
+ " mlflow.log_metric('empty_after_clean', int(empty_after))\n",
717
+ " mlflow.log_metric('short_texts_lt3', len(short_clean))\n",
718
+ "\n",
719
+ " # Artefactos: archivos que produce\n",
720
+ " out_csv = PROJECT_ROOT / 'data' / 'processed' / 'v2' /'comments_preprocessed.csv'\n",
721
+ " df[['CommentId', TEXT_COL, 'clean_text', TARGET_BIN] + SUBLABELS].to_csv(\n",
722
+ " out_csv, index=False\n",
723
+ " )\n",
724
+ " mlflow.log_artifact(str(out_csv))\n",
725
+ " mlflow.log_artifact(str(PROJECT_ROOT / 'reports' / 'v2' / '07_tokens_comparativa.png'))\n",
726
+ " mlflow.log_artifact(str(CONFIG_PATH))\n",
727
+ "\n",
728
+ " run_id = mlflow.active_run().info.run_id\n",
729
+ " print(f'MLflow run registrado')\n",
730
+ " print(f' Run ID : {run_id}')\n",
731
+ " print(f' Experimento: Youtube_project_experiments')\n",
732
+ " print(f' Ver UI : mlflow ui --backend-store-uri file://{MLFLOW_DIR}')"
733
+ ]
734
+ },
735
+ {
736
+ "cell_type": "markdown",
737
+ "metadata": {},
738
+ "source": [
739
+ "## 5. Conclusiones y decisiones"
740
+ ]
741
+ },
742
+ {
743
+ "cell_type": "code",
744
+ "execution_count": 16,
745
+ "metadata": {},
746
+ "outputs": [
747
+ {
748
+ "name": "stdout",
749
+ "output_type": "stream",
750
+ "text": [
751
+ "\n",
752
+ "CONCLUSIONES — PREPROCESAMIENTO\n",
753
+ "=======================================================\n",
754
+ "Pipeline aplicado:\n",
755
+ " 1. lowercase\n",
756
+ " 2. regex: URLs, @menciones, \\xa0, apostrofes, numeros\n",
757
+ " 3. spaCy: lematizacion (en_core_web_sm)\n",
758
+ " 4. NLTK: stopwords english + custom\n",
759
+ "\n",
760
+ "Resultado:\n",
761
+ " Reduccion tokens : 50.7%\n",
762
+ " Tokens mediana : 9 (antes: 19)\n",
763
+ " Comentarios vacios: 0\n",
764
+ "\n",
765
+ "Decisiones clave:\n",
766
+ " NO quitados: black, white, police, cop\n",
767
+ " -> EDA mostro que aparecen en ambas clases con contexto distinto\n",
768
+ " -> el modelo necesita verlas para aprender por bigrams\n",
769
+ " SI quitados: youtube, video, watch, like, comment, channel\n",
770
+ " -> ruido tematico sin valor discriminante\n",
771
+ " spaCy para lemma, no NLTK Stemmer\n",
772
+ " -> stemmer corta letras, lemma entiende gramatica\n",
773
+ "\n",
774
+ "Archivo guardado:\n",
775
+ " data/processed/comments_preprocessed.csv\n",
776
+ "\n",
777
+ "\n"
778
+ ]
779
+ }
780
+ ],
781
+ "source": [
782
+ "reduction = (1 - df['tokens_clean'].mean() / df['tokens_raw'].mean()) * 100\n",
783
+ "\n",
784
+ "print(f\"\"\"\n",
785
+ "CONCLUSIONES — PREPROCESAMIENTO\n",
786
+ "{'='*55}\n",
787
+ "Pipeline aplicado:\n",
788
+ " 1. lowercase\n",
789
+ " 2. regex: URLs, @menciones, \\\\xa0, apostrofes, numeros\n",
790
+ " 3. spaCy: lematizacion (en_core_web_sm)\n",
791
+ " 4. NLTK: stopwords english + custom\n",
792
+ "\n",
793
+ "Resultado:\n",
794
+ " Reduccion tokens : {reduction:.1f}%\n",
795
+ " Tokens mediana : {df['tokens_clean'].median():.0f} (antes: {df['tokens_raw'].median():.0f})\n",
796
+ " Comentarios vacios: {empty_after}\n",
797
+ "\n",
798
+ "Decisiones clave:\n",
799
+ " NO quitados: black, white, police, cop\n",
800
+ " -> EDA mostro que aparecen en ambas clases con contexto distinto\n",
801
+ " -> el modelo necesita verlas para aprender por bigrams\n",
802
+ " SI quitados: youtube, video, watch, like, comment, channel\n",
803
+ " -> ruido tematico sin valor discriminante\n",
804
+ " spaCy para lemma, no NLTK Stemmer\n",
805
+ " -> stemmer corta letras, lemma entiende gramatica\n",
806
+ "\n",
807
+ "Archivo guardado:\n",
808
+ " data/processed/comments_preprocessed.csv\n",
809
+ "\n",
810
+ "\"\"\")"
811
+ ]
812
+ }
813
+ ],
814
+ "metadata": {
815
+ "kernelspec": {
816
+ "display_name": "py310",
817
+ "language": "python",
818
+ "name": "python3"
819
+ },
820
+ "language_info": {
821
+ "codemirror_mode": {
822
+ "name": "ipython",
823
+ "version": 3
824
+ },
825
+ "file_extension": ".py",
826
+ "mimetype": "text/x-python",
827
+ "name": "python",
828
+ "nbconvert_exporter": "python",
829
+ "pygments_lexer": "ipython3",
830
+ "version": "3.10.20"
831
+ }
832
+ },
833
+ "nbformat": 4,
834
+ "nbformat_minor": 4
835
+ }