Tu equipo vende tres productos y tiene unos cuantos miles de reseñas de cada uno. La media de estrellas apenas se mueve de un mes a otro, pero soporte dice que las quejas sobre uno de los productos se han duplicado. Leer todas las reseñas no es una opción, así que quieres un script que marque cada reseña como positiva, neutral o negativa y muestre dónde cambia el ánimo. El primer tutorial que encuentras usa VADER, el segundo un modelo transformer, y los dos dan respuestas distintas para la misma frase.
Esta guía aplica ambos métodos al mismo pequeño conjunto de reseñas de productos. Veremos cómo funciona cada uno, el código completo y probado (una pasada con léxico usando NLTK, una pasada con modelo usando el pipeline de Hugging Face y un informe que agrupa los resultados por producto y mes), hasta qué punto coincidió cada método con las estrellas y qué reseñas los engañaron. Todos los scripts se ejecutaron el 28 de septiembre de 2026 con Python 3.13, nltk 3.10.3, pandas 3.0.6, transformers 5.17.0 y la versión para CPU de torch 2.14.
¿Qué es el análisis de sentimiento?
El análisis de sentimiento, también llamado minería de opiniones, consiste en decidir qué actitud expresa un texto. Para reseñas de productos, el resultado suele ser una de tres etiquetas (positiva, neutral, negativa) o una puntuación en estrellas, a menudo con un nivel de confianza. Las herramientas a nivel de aspecto van más allá y separan "gran sonido, batería floja" en dos opiniones; eso requiere otro tipo de modelo y queda fuera de esta guía.
Como los métodos de Minería de datos: técnicas, usos y cómo obtener los datos, convierte texto no estructurado en una columna que puedes contar y representar en un gráfico. El valor está en la tendencia, no en una puntuación aislada: por ejemplo, que la proporción de reseñas negativas de un producto suba después de un cambio de proveedor.
¿Cómo funciona el análisis de sentimiento en Python?
Hay dos familias de herramientas, y funcionan de maneras muy distintas.
Basadas en léxico (VADER). VADER, siglas de Valence Aware Dictionary and sEntiment Reasoner, es una lista de unas 7.500 palabras, emoticonos y expresiones coloquiales en inglés que evaluadores humanos puntuaron de muy negativo a muy positivo, más un conjunto de reglas. Las reglas suben la puntuación con !!! y con MAYÚSCULAS, dan más peso a las palabras que siguen a "but" que a las anteriores e invierten la puntuación cuando una negación como "not" o "never" aparece hasta tres palabras antes de una palabra con carga emocional. El README de vaderSentiment describe estas reglas y los umbrales. NLTK incluye el mismo analizador como nltk.sentiment.vader, documentado en la guía de sentimiento de NLTK.
Basadas en modelos (transformers). Un transformer como BERT se entrenó primero con una gran cantidad de texto y después se ajustó (fine-tuning) con ejemplos etiquetados según su sentimiento. Convierte la frase entera en números y lee cada palabra a la luz de las demás, de modo que "not bad" y "killer" se pueden juzgar por el contexto y no por un valor fijo. Hugging Face aloja miles de modelos de este tipo, y su función pipeline carga uno con una sola línea.
En ambos casos la puntuación sigue cinco pasos:
- Cargar el texto. Una fila por reseña, con el producto, la fecha y, si la tienes, la puntuación en estrellas.
- Dividir o tokenizar. VADER trabaja con palabras y signos de puntuación. Un transformer usa su propio tokenizador, que corta las palabras en fragmentos más pequeños y se detiene en una longitud máxima (512 tokens en los modelos basados en BERT).
- Puntuar. VADER suma los valores de las palabras, aplica sus reglas y comprime el total en un valor
compoundentre -1 y +1. Un modelo devuelve una probabilidad para cada etiqueta. - Asignar una etiqueta. Con VADER eliges umbrales. Con un modelo tomas la etiqueta de mayor probabilidad y guardas la puntuación como confianza.
- Agregar. Cuenta las etiquetas por producto, semana o mes, y pon los resultados junto a las cifras que ya sigues.
VADER frente a un modelo transformer
| VADER (NLTK) | Transformer (Hugging Face) | |
|---|---|---|
| Cómo decide | Lista de palabras puntuadas más reglas | Red neuronal ajustada con texto etiquetado |
| Tamaño de instalación | nltk más un léxico de 90 KB | transformers, torch (unos 500 MB en la versión para CPU) y un modelo (670 MB el de abajo) |
| Velocidad en nuestra prueba (CPU de portátil, reseñas cortas) | Unas 14.000 reseñas por segundo | Unas 90 reseñas por segundo, más con GPU |
| Idiomas | Inglés | Depende del modelo; el de abajo cubre seis |
| Contexto y negación | Por reglas, ventana de tres palabras | Aprendidos a partir de ejemplos |
| Salida | Proporciones neg / neu / pos y una puntuación compound | Etiqueta y probabilidad, o puntuación en estrellas |
| Explicar un resultado | Fácil: se buscan las palabras | Difícil: hacen falta herramientas extra |
| Nuestra prueba de 24 reseñas | 14 de 24 coincidieron con las estrellas (58 %) | 19 de 24 coincidieron (79 %) |
La última fila sale de un conjunto de datos diminuto y escrito a mano. Muestra el tipo de errores que comete cada método, no cómo funcionarán con tus datos.
Cómo elegir un modelo de Hugging Face para reseñas de productos
Muchos modelos de sentimiento del hub de Hugging Face se entrenaron con tuits o con críticas de cine. Para reseñas de productos importan tres cosas:
- Datos de entrenamiento. Un modelo entrenado con tuits conoce la jerga; uno entrenado con reseñas de productos conoce el vocabulario de producto.
- Etiquetas. Tres etiquetas, cinco etiquetas o estrellas. Tradúcelas a tus propias etiquetas en un único sitio del código.
- Licencia. Algunos modelos populares tienen una licencia no comercial. Lee la ficha del modelo.
Probamos tres modelos multilingües pequeños con las mismas 24 reseñas y usamos nlptown/bert-base-multilingual-uncased-sentiment en el código de abajo. Se ajustó con reseñas de productos en inglés, neerlandés, alemán, francés, español e italiano, predice de una a cinco estrellas y tiene licencia MIT. Su ficha indica un 67 % de acierto exacto en estrellas y un 95 % con una estrella de margen en inglés. El turco no está entre sus idiomas, así que una reseña en turco recibe una conjetura, no una respuesta aprendida.
Los otros dos fueron lxyuan/distilbert-base-multilingual-cased-sentiments-student (tres etiquetas, Apache 2.0), que coincidió con nuestras estrellas en 13 de 24 reseñas, y tabularisai/multilingual-sentiment-analysis (cinco etiquetas, 16 de 24), que tiene licencia CC BY-NC 4.0 y, por tanto, no es libre para uso comercial.
Los datos de prueba: 24 reseñas de productos
No usamos reseñas obtenidas por scraping. El archivo de abajo contiene 24 reseñas que escribimos nosotros para tres productos inventados, cada una con fecha y puntuación en estrellas, para poder comparar ambos métodos con la puntuación. Incluimos los casos que dan problemas con datos reales: sarcasmo, jerga, negación, sentimientos mezclados y una reseña en alemán, otra en español y otra en turco. Guárdalo como reviews.csv:
review_id,product,date,rating,lang,text
1,kettle,2026-07-02,5,en,"Boils a full litre in under three minutes and the lid never drips. Love it."
2,kettle,2026-07-05,4,en,"Solid kettle, a bit loud but it does the job."
3,kettle,2026-07-11,1,en,"Stopped working after two weeks. Support never answered my emails."
4,kettle,2026-07-19,2,en,"The handle gets hot. Not great, not terrible."
5,kettle,2026-08-03,5,en,"Best purchase this year!!! Fast, quiet and it looks good on the counter :)"
6,kettle,2026-08-14,1,en,"Oh great, another kettle that leaks all over the counter. Just what I needed."
7,kettle,2026-08-21,3,en,"It is a kettle. It boils water."
8,kettle,2026-08-30,4,de,"Kocht schnell und ist leise. Gutes Preis-Leistungs-Verhältnis."
9,headphones,2026-07-03,5,en,"The noise cancelling is superb and the battery lasts all week."
10,headphones,2026-07-09,2,en,"Sound is fine but the ear cushions started peeling after a month."
11,headphones,2026-07-15,4,en,"Comfortable for long flights. The app is clunky though."
12,headphones,2026-07-28,1,en,"Right ear cut out on day three. Returned them."
13,headphones,2026-08-06,5,es,"Suenan de maravilla y la batería dura muchísimo."
14,headphones,2026-08-12,3,en,"Bass is heavy. Some people will like it, I am not sure I do."
15,headphones,2026-08-24,2,en,"I wanted to love these, but the Bluetooth keeps dropping."
16,headphones,2026-08-29,4,en,"Not bad at all for the price."
17,backpack,2026-07-01,5,en,"Survived a rainy week in the mountains and everything inside stayed dry."
18,backpack,2026-07-13,4,en,"Lots of pockets. The laptop sleeve is a little tight for a 16 inch model."
19,backpack,2026-07-22,1,en,"The zipper broke on the first trip. Cheap material."
20,backpack,2026-08-02,3,tr,"Fiyatına göre idare eder ama askılar biraz ince."
21,backpack,2026-08-09,5,en,"Killer design, this bag is sick!"
22,backpack,2026-08-17,2,en,"Looks nice in the photos, feels flimsy in real life."
23,backpack,2026-08-26,4,en,"Carries my camera gear without any back pain."
24,backpack,2026-08-31,1,en,"Terrible stitching. The strap tore off after a week."Las reseñas reales necesitan una limpieza antes de puntuarlas: duplicados, restos de HTML en el texto, caracteres rotos. Cómo limpiar datos de scraping con pandas en Python cubre ese paso.
Paso 1: puntuar las reseñas con VADER
Instala las dos bibliotecas en un entorno virtual:
python -m venv .venv
.venv\Scripts\activate # macOS/Linux: source .venv/bin/activate
pip install nltk pandasEl script puntúa cada frase por separado y promedia los resultados. Lo añadimos después de la primera ejecución: puntuada como una sola cadena, la reseña 1 ("…the lid never drips. Love it.") dio -0,52, porque la ventana de negación de tres palabras de VADER atravesó el punto e invirtió "love". Puntuada frase a frase, dio +0,32.
import re
import nltk
import pandas as pd
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download("vader_lexicon", quiet=True) # léxico de 90 KB, se descarga una vez
analyzer = SentimentIntensityAnalyzer()
SENTENCE_END = re.compile(r"(?<=[.!?])\s+")
def vader_compound(text: str) -> float:
"""Puntúa cada frase por separado y promedia las puntuaciones compound."""
sentences = [s for s in SENTENCE_END.split(text) if s.strip()]
scores = [analyzer.polarity_scores(s)["compound"] for s in sentences]
return round(sum(scores) / len(scores), 4)
def vader_label(compound: float) -> str:
# umbrales recomendados por los autores de VADER
if compound >= 0.05:
return "positive"
if compound <= -0.05:
return "negative"
return "neutral"
df = pd.read_csv("reviews.csv", parse_dates=["date"])
df["compound"] = df["text"].apply(vader_compound)
df["vader"] = df["compound"].apply(vader_label)
print(df[["review_id", "lang", "compound", "vader"]].to_string(index=False))
df.to_csv("reviews_vader.csv", index=False)Parte de la salida:
review_id lang compound vader
1 en 0.3185 positive
3 en 0.0878 positive
6 en 0.3125 positive
8 de 0.0000 neutral
17 en 0.4588 positive
21 en -0.8356 negativeLa reseña 3 es una queja de una estrella, pero "support" es una palabra positiva en el léxico y nada negativo la compensa. La reseña 6 es sarcasmo ("Oh great … Just what I needed") y VADER toma "great" al pie de la letra. La reseña en alemán puntúa exactamente cero, porque ninguna de sus palabras está en el léxico inglés. La reseña 21 usa "killer" y "sick" como elogios; VADER solo conoce ambas palabras en su sentido negativo.
Paso 2: puntuar las mismas reseñas con un modelo de Hugging Face
La pasada con modelo necesita transformers y un backend de deep learning. Si no tienes GPU, instala primero la versión de torch para CPU; es mucho más pequeña que la versión por defecto con CUDA:
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install transformersLa primera ejecución descarga el modelo (unos 670 MB) en la caché de Hugging Face. Define la variable de entorno HF_HOME si quieres esa caché en otro disco. El nombre de tarea sentiment-analysis es un alias de text-classification; batch_size, truncation y device se describen en la documentación de pipelines de Transformers.
import pandas as pd
from transformers import pipeline
MODEL = "nlptown/bert-base-multilingual-uncased-sentiment"
classifier = pipeline(
"sentiment-analysis", # alias de "text-classification"
model=MODEL,
device="cpu", # device=0 para la primera GPU
)
df = pd.read_csv("reviews_vader.csv", parse_dates=["date"])
results = classifier(
df["text"].tolist(),
batch_size=8,
truncation=True, # las reseñas de más de 512 tokens se recortan, no se rechazan
)
def stars_to_label(stars: int) -> str:
if stars >= 4:
return "positive"
if stars == 3:
return "neutral"
return "negative"
df["hf_stars"] = [int(r["label"][0]) for r in results] # "4 stars" -> 4
df["hf_score"] = [round(r["score"], 3) for r in results]
df["hf"] = df["hf_stars"].apply(stars_to_label)
print(df[["review_id", "rating", "hf_stars", "hf_score", "vader", "hf"]].to_string(index=False))
df.to_csv("reviews_scored.csv", index=False)Con el modelo ya en la caché, el script tardó unos siete segundos en una CPU de portátil, carga incluida. Parte de la salida:
review_id rating hf_stars hf_score vader hf
3 1 1 0.736 positive negative
6 1 1 0.230 positive negative
8 4 5 0.526 neutral positive
17 5 1 0.562 positive negative
20 3 3 0.622 neutral neutral
21 5 1 0.816 negative negativeEl modelo detectó la queja de la reseña 3, el sarcasmo de la reseña 6 (con una confianza baja, de 0,23) y la reseña en alemán. Falló con la reseña 17, la mochila que "survived a rainy week", y leyó la jerga de la reseña 21 como una estrella con confianza alta. La reseña 20 está en turco, un idioma con el que el modelo no se entrenó; sus tres estrellas coincidieron, pero no nos fiaríamos de eso.
Paso 3: agregar por producto y mes
El informe compara ambos métodos con las estrellas, calcula una puntuación de sentimiento neto (proporción de positivas menos proporción de negativas) por producto y mes, y escribe en un archivo las reseñas en las que los métodos no coinciden para que las lea una persona.
import pandas as pd
df = pd.read_csv("reviews_scored.csv", parse_dates=["date"])
def stars_to_label(rating: int) -> str:
if rating >= 4:
return "positive"
if rating == 3:
return "neutral"
return "negative"
df["truth"] = df["rating"].apply(stars_to_label)
for method in ("vader", "hf"):
hit = (df[method] == df["truth"]).mean()
print(f"{method}: {hit:.0%} agree with the star rating")
off_by_one = ((df["hf_stars"] - df["rating"]).abs() <= 1).mean()
print(f"model stars within one star of the rating: {off_by_one:.0%}")
# Sentimiento neto: proporción de positivas menos proporción de negativas, de -1 a +1
NET = {"positive": 1, "neutral": 0, "negative": -1}
df["net"] = df["hf"].map(NET)
df["month"] = df["date"].dt.to_period("M")
by_product = (
df.groupby("product")
.agg(reviews=("review_id", "count"),
net=("net", "mean"),
negative_share=("hf", lambda s: (s == "negative").mean()),
avg_rating=("rating", "mean"))
.round(2)
.sort_values("net")
)
print(by_product.to_string())
by_month = df.pivot_table(index="month", columns="product", values="net", aggfunc="mean").round(2)
print(by_month.to_string())
# Las filas en las que los dos métodos no coinciden pasan a revisión humana
disagree = df[df["vader"] != df["hf"]]
print(f"{len(disagree)} of {len(df)} reviews need a human look")
disagree[["review_id", "rating", "vader", "hf", "text"]].to_csv("review_queue.csv", index=False)vader: 58% agree with the star rating
hf: 79% agree with the star rating
model stars within one star of the rating: 92%
reviews net negative_share avg_rating
product
backpack 8 -0.25 0.50 3.12
headphones 8 0.12 0.25 3.25
kettle 8 0.12 0.38 3.12
product backpack headphones kettle
month
2026-07 -0.33 -0.25 0.00
2026-08 -0.20 0.50 0.25
11 of 24 reviews need a human lookLa mochila y el hervidor comparten una media de 3,12 estrellas, pero la mitad de las reseñas de la mochila se leen como negativas frente al 38 % del hervidor, una diferencia que la media de estrellas oculta. Con ocho reseñas por producto, una sola reseña mueve el sentimiento neto en 0,25, así que fija un número mínimo por grupo antes de representarlo en un gráfico. Cómo guardar las filas puntuadas para la siguiente ejecución se explica en Cómo guardar datos de scraping en CSV, JSON y SQLite.
¿De dónde salen las reseñas?
Al código le da igual de dónde venga el texto, pero la fuente decide qué puedes hacer con él:
- Tu propia tienda, la consola de la tienda de aplicaciones o tu herramienta de soporte. Es la fuente más limpia, y trae la puntuación en estrellas que necesitas para comprobar el modelo.
- Plataformas de reseñas y marketplaces. Muchos ofrecen una API oficial o una exportación para vendedores. Úsala antes de plantearte el scraping.
- Páginas públicas. Respeta robots.txt y los términos de servicio, mantén un ritmo de peticiones bajo y deja fuera los nombres de los autores y los enlaces a sus perfiles. ¿El web scraping es legal? Una visión general y Cómo tratar datos personales (PII) en datos de scraping explican las reglas.
Cuando un marketplace muestra reseñas o precios distintos según el país, un Proxies residenciales con segmentación por país te permite ver la página que ve un comprador local.
Casos de uso
- Investigación de productos y categorías: comparar cómo hablan los compradores de tus productos y de los de un competidor, como parte de la investigación de mercado.
- Seguimiento de e-commerce: vigilar el sentimiento de las reseñas junto a los datos de precio y stock en varios marketplaces; consulta proxies para e-commerce y Cómo hacer seguimiento de precios de la competencia.
- Protección de marca: detectar una racha repentina de reseñas negativas que mencionan falsificaciones o vendedores no autorizados; consulta protección de marca.
- Análisis de inversiones: el sentimiento de reseñas y redes sociales es una de las fuentes de los conjuntos de datos alternativos.
- Priorización en soporte: pasar primero a una persona las reseñas de una estrella y las de baja confianza.
Errores comunes y trampas
- Puntuar un párrafo entero de una vez con VADER. Las reglas de negación y de "but" pueden cruzar frases, como mostró la reseña 1. Puntúa frase a frase y combina los resultados.
- Usar VADER con otros idiomas. Las palabras desconocidas puntúan cero, así que las reseñas en alemán y en español salen neutrales. Usa un modelo multilingüe, o agrupa las reseñas por idioma y elige un modelo para cada uno.
- Confiar el sarcasmo y la jerga a cualquiera de los dos métodos. Ambos leen "Killer design, this bag is sick!" como una reseña de una estrella.
- Ignorar el dominio. "Impredecible" es bueno para la trama de una película y malo para los frenos de un coche. Revisa una muestra de tu propia categoría.
- Tomar la coincidencia por verdad. La reseña 21 no apareció en la cola de discrepancias porque los dos métodos se equivocaron de la misma forma. Mantén una pequeña muestra etiquetada a mano y mide contra ella.
- Descartar la confianza. Una etiqueta con una puntuación de 0,23, como la de la reseña 6, es apenas mejor que una conjetura. Pasa las puntuaciones bajas a una persona.
- Olvidar el recorte. Sin
truncation=True, una reseña más larga que el límite del modelo lanza un error y detiene el lote. - Representar grupos diminutos. Un sentimiento neto calculado con cinco reseñas oscila muchísimo. Fija un tamaño mínimo de grupo.
- Codificación de texto rota. Las reseñas con
éen lugar deéconfunden a ambos métodos; consulta Codificación en Python: tildes, ñ y UnicodeDecodeError.
Guía de decisión
| Necesidad | Recomendación |
|---|---|
| Un primer vistazo rápido a reseñas en inglés | VADER, puntuando frase a frase |
| Reseñas en varios idiomas | Un modelo multilingüe cuya ficha incluya tus idiomas |
| Una puntuación en estrellas en lugar de tres etiquetas | Un modelo entrenado con estrellas, como el de nlptown |
| Sin GPU y con millones de filas | VADER, o un modelo pequeño con procesamiento por lotes y una comprobación sobre una muestra |
| Uso comercial | Revisa la licencia del modelo; descarta los modelos no comerciales |
| Cifras que vas a presentar a la dirección | Etiqueta a mano entre 200 y 500 de tus reseñas y mide antes la precisión |
| Textos con mucho sarcasmo y jerga | Un modelo entrenado con texto de redes sociales, más revisión humana de las puntuaciones bajas |
Preguntas frecuentes
¿Sigue valiendo la pena usar VADER?
Sí, como punto de partida para textos cortos en inglés cuando necesitas velocidad y resultados que puedas explicar palabra por palabra. Cuando importan el contexto u otros idiomas, un modelo transformer lo hizo claramente mejor en nuestra prueba.
¿Qué significan los umbrales de la puntuación compound de VADER?
La puntuación compound va de -1 a +1. Los autores de VADER proponen positivo a partir de 0,05, negativo a partir de -0,05 hacia abajo y neutral entre ambos valores. Puedes mover los umbrales, pero ajústalos con ejemplos etiquetados, no a ojo.
¿Necesito una GPU para el pipeline de Hugging Face?
No. La versión de torch para CPU puntuó nuestras 24 reseñas en segundos. Para cientos de miles de reseñas, una GPU ahorra horas; pasa device=0 para usarla.
¿Qué modelo sirve para reseñas en turco?
El modelo de nlptown que usamos aquí no se entrenó con turco. Busca en el hub de Hugging Face modelos cuya ficha incluya el turco y datos de productos o reseñas, y prueba los mejores candidatos con una muestra etiquetada en turco.
¿Cómo mido la precisión con mis propios datos?
Etiqueta tú mismo una muestra aleatoria de unos cientos de reseñas y compara. Las estrellas sirven como etiquetas aproximadas, como en esta guía, pero una reseña de tres estrellas a menudo se lee como negativa.
¿Puedo aplicar el análisis de sentimiento a reseñas obtenidas por scraping?
La cuestión legal no está en la puntuación, sino en la recogida. Prefiere una API oficial, revisa los términos del sitio y su robots.txt, y deja fuera los datos personales.
En resumen
El análisis de sentimiento en Python se reduce a dos opciones. VADER de NLTK es pequeño, rápido y fácil de explicar, pero malinterpreta el sarcasmo, la jerga, el vocabulario de cada dominio y cualquier idioma que no sea el inglés. Un modelo de Hugging Face lee el contexto y maneja varios idiomas a cambio de una descarga grande y más cómputo; en nuestras 24 reseñas coincidió con las estrellas en 19, frente a 14 de VADER. Elijas el que elijas, puntúa siempre de la misma forma (por frase o por reseña), conserva la confianza, pasa las discrepancias a una persona y mide contra tu propia muestra etiquetada. Si tus reseñas vienen de páginas públicas de varios países, consulta los proxies de Proxynet para recogerlas a un ritmo respetuoso.




