Extracting Insights From Negative Reviews

Imagine you manage customer operations across hundreds of regional locations. You receive over 139,000 negative customer reviews. Reading every single review manually is humanly impossible.

Standard sentiment analysis won't cut it either—telling an executive that "customers are unhappy" provides zero actionable direction. Operations leadership needs to know specifically what is breaking: Is it check-in delays, broken heating units, unhelpful staff, or billing errors?

To solve this, we set out to build an end-to-end Natural Language Processing (NLP) pipeline capable of ingesting raw, unstructured customer feedback, isolating distinct operational flaws, and converting them into structured, executive-ready intelligence.

Step 1: Preprocessing & Sentence Explosion

Before running advanced topic models, we had to address a fundamental truth of customer feedback: A single review rarely contains just one complaint.

For instance, a customer might write:

"The front desk staff was extremely rude, the room was freezing, and the shower leaked all over the floor."

If treated as a single document, topic modeling algorithms get confused by overlapping themes. To capture granular operational failure points, we established a sentence-level data processing pipeline:

  1. Filtering the Signal: We isolated a target frame of 139,156 2-star negative reviews to focus strictly on moderate-to-severe operational failures.
  2. Sentence Explosion: Using sentence tokenization, we shattered those 139,156 reviews into 644,336 individual sentences. Every sentence became an independent observation representing a single, isolated claim.
import pandas as pd
import nltk
nltk.download('punkt')

def explode_reviews_to_sentences(df, text_column='review_text'):
    """
    Splits multi-sentence customer reviews into individual rows, 
    preserving review IDs and metadata for downstream analysis.
    """
    # Tokenize text into sentences
    df['sentences'] = df[text_column].apply(lambda x: nltk.sent_tokenize(str(x)))
    
    # Explode the dataframe so each sentence gets its own row
    exploded_df = df.explode('sentences').reset_index(drop=True)
    exploded_df = exploded_df.rename(columns={'sentences': 'sentence_text'})
    
    # Filter out extremely short fragments/noise
    exploded_df = exploded_df[exploded_df['sentence_text'].str.len() > 10]
    
    return exploded_df

# Raw dataset: ~139,156 reviews -> Exploded dataset: 644,336 sentences
# exploded_reviews_df = explode_reviews_to_sentences(raw_reviews_df)

Step 2: Unsupervised Topic Extraction with BERTopic

With our dataset expanded into 644,336 atomic sentence claims, we needed a scalable way to group similar complaints without manually reading or tagging them.

We leveraged BERTopic, a topic modeling technique that combines transformer embeddings with UMAP (User-Definable Manifold Approximation and Projection) and HDBSCAN (Hierarchical Density-Based Spatial Clustering of Applications with Noise).

We serialized and trained our dedicated model—D804_PA_Model_NegativeReviewTopics_trained—which successfully mapped our dataset into 321 distinct operational topics.

from bertopic import BERTopic

def extract_operational_topics(sentences, model_path):
    """
    Loads a pre-trained BERTopic model and assigns 
    leaf topics to every exploded complaint sentence.
    """
    # Load serialized BERTopic model
    topic_model = BERTopic.load(model_path)
    
    # Predict topics and probabilities for exploded sentences
    topics, probs = topic_model.transform(sentences)
    
    return topics, probs

# Load model and assign topics across the 644k dataset
# topics, _ = extract_operational_topics(exploded_reviews_df['sentence_text'].tolist(), "D804_PA_Model_NegativeReviewTopics_trained")
# exploded_reviews_df['leaf_topic_id'] = topics

Step 3: From Machine Clusters to Business Value: Corporate Taxonomy Mapping

While having 321 granular topic clusters is incredible for deep-dive diagnostics, it presents a challenge for high-level decision-makers. An executive VP doesn't have time to review 321 individual leaf IDs (e.g., Topic 42: frozen pipes vs. Topic 108: broken radiator).

To bridge the gap between machine learning outputs and business strategy, we mapped the 321 leaf topics directly into a Corporate Executive Taxonomy.

# Sample Taxonomy Mapping Dictionary
EXECUTIVE_TAXONOMY = {
    "Lodging & Campground Infrastructure": [12, 42, 108, 115, 204],
    "Customer Service & Staff Conduct": [5, 19, 87, 143, 290],
    "Reservation & Billing Systems": [2, 18, 54, 99, 310],
    "Facility Cleanliness & Maintenance": [8, 33, 76, 150, 222]
}

def map_leaf_to_taxonomy(topic_id, taxonomy_map):
    """
    Maps raw BERTopic leaf IDs to high-level Corporate Executive Taxonomy branches.
    """
    for department, topic_ids in taxonomy_map.items():
        if topic_id in topic_ids:
            return department
    return "Other / Uncategorized"

# Apply high-level taxonomy mapping
# exploded_reviews_df['executive_department'] = exploded_reviews_df['leaf_topic_id'].apply(
#     lambda x: map_leaf_to_taxonomy(x, EXECUTIVE_TAXONOMY)
# )

This translation layer ensured that every individual sentence could roll up into macro operational categories like "Lodging & Campground Infrastructure" or "Customer Service & Staff Conduct" while retaining its micro-level topic assignment for root-cause analysis.

Step 4: Interactive Dashboarding & Spatiotemporal Metadata

Data is only as valuable as its accessibility. By merging our categorized sentences back with original review metadata, we added two critical analytical dimensions:

  • Temporal Context: Aggregated into YearQuarter markers to observe seasonal spikes or multi-year trends.
  • Geographical Context: Regional mapping (Region) grouping individual locations into West, Midwest, South, and Northeast territories.

Using Plotly, we constructed dynamic visual interfaces to explore the operational health of the organization:

  1. Relative Bar Charts: Highlighting the "Top 15 Operational Complaint Departments" across different regions.
  2. Time-Series Line Charts: Tracking the "Top 5 Operational Complaints Over Time" to identify emerging service failures before they escalate.
import plotly.express as px

def generate_complaint_trend_chart(df):
    """
    Generates a Plotly line chart tracking top operational complaints over time.
    """
    # Group by Quarter and Department
    trend_data = df.groupby(['YearQuarter', 'executive_department']).size().reset_index(name='complaint_count')
    
    fig = px.line(
        trend_data, 
        x='YearQuarter', 
        y='complaint_count', 
        color='executive_department',
        title='Top Operational Complaints Over Time (Quarterly)',
        labels={'complaint_count': 'Number of Complaints', 'YearQuarter': 'Quarter'},
        template='plotly_white'
    )
    return fig

# fig = generate_complaint_trend_chart(exploded_reviews_df)
# fig.show()

The Outcome: Turning Noise into Navigable Intelligence

By restructuring 139,156 raw reviews into 644,336 categorizable sentences, we successfully turned unstructured text into a fully queryable database of operational friction. Leadership could now hover over any region, drill down into a specific quarter, and immediately see what percentage of complaints stemmed from infrastructure vs. staff conduct.

However, identifying where complaints are happening is only half the battle. Executives still need concise, natural-language summaries explaining why those complaints occur without reading thousands of raw quotes.

In Part 2, we’ll explore how we took these 321 BERTopic clusters, validated an A100 cloud GPU environment, used an 8-bit Qwen2.5-32B-Instruct model to generate synthetic training data, and distilled that knowledge into lightweight student models (Qwen2.5-0.5B and FLAN-T5-Base) for real-time edge deployment.

👾 Test the live application: View on Hugging Face

📝 Author's Note & Original Files

This blog series is adapted from an operational data engineering project focusing on enterprise text analytics. This is an AI summary until I rewrite it and remove this note. It breaks down how topic extraction and deep learning models can be applied to real-world customer experience data.