Sentiment Analysis Pipeline for Hospital Reviews in Yogyakarta



Overview
This project builds an end-to-end sentiment analysis pipeline for hospital reviews in the Special Region of Yogyakarta (DIY). Hospitals need to understand patient perception, and Google Maps reviews are one of the richest public sources of it. The pipeline scrapes those reviews, cleans the Indonesian text, classifies each review as Positive, Neutral, or Negative with a fine-tuned IndoBERT model, and visualizes the results in an interactive dashboard so hospital management can make data-driven decisions.
The scope covers all of DIY (Yogyakarta city, Sleman, Bantul, Gunung Kidul, and Kulon Progo), reviews from 2024–2026, and 255 discovered hospitals.
Tech Stack & Rationale
Layer | Technology | Why |
|---|---|---|
Data collection | Google Maps Places API + Selenium | Places API discovers hospitals; Selenium scrapes reviews through the UI, avoiding bot detection |
Text preprocessing | Sastrawi (stemming), NLTK (tokenization), custom slang dictionary | Indonesian-specific stemming and informal→formal normalization |
Model | IndoBERT (mdhugol/indonesia-bert-sentiment-classification) via Hugging Face Transformers | A BERT model pre-trained on Indonesian, fine-tuned for 3-class sentiment |
Inference | PyTorch, batch processing, 512-token max sequence | Efficient batch inference over thousands of reviews |
Backend | FastAPI, SQLAlchemy, pandas | Serves the dashboard with typed, fast endpoints over PostgreSQL |
Database | PostgreSQL (Docker) | Stores raw, cleaned, and labeled reviews plus derived analytics |
Frontend | Next.js 15, React 19, Recharts, SWR | Interactive charts and tables with client-side data fetching |
Orchestration | Docker & Docker Compose | Reproducible backend + database stack |
Architecture
The pipeline runs in five stages, from data collection to evaluation:
- Scraping — hospitals are discovered through Google Maps Places API text search (8 queries across regencies, 3 pages each, 50 km radius → 255 hospitals), then reviews are collected with Selenium (sorted by newest, scrolled until reviews predate 2024). Resume-on-failure and incremental saving keep long runs recoverable.
- Preprocessing — each review is cleaned (URLs, mentions, special characters removed), tokenized with NLTK, normalized against a slang dictionary (gak → tidak), stripped of stopwords (keeping negation words), and stemmed with Sastrawi.
- Classification — the cleaned text is fed to mdhugol/indonesia-bert-sentiment-classification (IndoBERT), producing a Positive/Neutral/Negative label with a confidence score.
- Storage — raw, cleaned, and labeled reviews are stored in PostgreSQL (tables: rumah_sakit, ulasan, ulasan_bersih, ulasan_labeled, data_uji_manual), with derived analytics (sentiment_drivers, ngrams, anomalies).
- Visualization — a Next.js dashboard (overview, ranking, comparison, drivers, per-hospital pages) renders sentiment distribution, word clouds, aspect bars, gap analysis, and monthly trends from the FastAPI backend.
Folder Structure
hospital-sentiment-jogja/
├── app/
│ ├── backend/ # FastAPI dashboard API
│ │ ├── app/routers/ # overview, hospitals, keywords, trends, analytics, comparison
│ │ ├── app/services/data_service.py
│ │ └── scripts/ # prepare_data.py, seed_db.py
│ ├── frontend/ # Next.js 15 dashboard (Recharts + SWR)
│ │ └── src/app/ # overview, ranking, comparison, drivers, hospital/[name]
│ ├── docker/initdb/01_schema.sql
│ ├── docker-compose.yml
│ └── docs/ # documentation.md, flow_penelitian.md
└── research/
├── analysis/ # scraping + preprocessing + modeling + evaluation
│ ├── src/ # scraper.py, preprocessing.py, model.py, evaluation.py, visualization.py
│ ├── notebooks/analisis_sentimen.ipynb
│ ├── data/raw/ # daftar_rs_jogja.csv, ulasan_rs_jogja.csv
│ └── data/processed/ # ulasan_bersih.csv, ulasan_labeled.csv
└── publication/ # articles (PDF + markdown), JNTETI paperImpact & Results
The IndoBERT model was evaluated on a 150-sample manually labeled test set:
Class | Precision | Recall | F1 | Support |
|---|---|---|---|---|
Negatif | 0.84 | 0.76 | 0.80 | 55 |
Netral | 0.24 | 0.80 | 0.37 | 15 |
Positif | 0.98 | 0.61 | 0.75 | 80 |
Macro avg | 0.69 | 0.73 | 0.64 | 150 |
Overall accuracy was 68.67% with a 64.10% macro F1. The weakest class is Neutral (precision 0.24), a common challenge in 3-class sentiment where the middle class is subjective and under-represented.
- Collected and labeled Google Maps reviews across 255 Yogyakarta hospitals (2024–2026), making hospital-level sentiment insight actionable for management.
- The pipeline and dashboard were documented as a research flow and written up as a JNTETI journal paper.
My Contributions
Built as a master's research project (single author):
- Wrote the Google Maps scraper (Places API discovery + Selenium review collection) with resume and incremental-save handling.
- Built the Indonesian text preprocessing pipeline (cleaning, tokenization, slang normalization, stopword removal, Sastrawi stemming).
- Integrated the IndoBERT sentiment classifier with batched inference and confidence scoring.
- Designed the PostgreSQL schema and built the FastAPI backend with the analytics endpoints.
- Built the Next.js dashboard (sentiment overview, ranking, comparison, drivers, per-hospital pages).
- Authored the research flow documentation and the JNTETI publication.
References
[1] A. S. Shina, "hospital-sentiment-jogja — Sentiment Analysis Pipeline for Hospital Reviews in Yogyakarta," GitHub repository, 2026. [Online]. Available: https://github.com/ave-shina/hospital-sentiment-jogja
[2] J. Devlin, M.-W. Chang, K. Lee, and K. Toutanova, "BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding," in Proc. NAACL-HLT, 2019.
[3] F. Koto, A. Rahimi, J. H. Lau, and T. Baldwin, "IndoLEM and IndoBERT: A Benchmark Dataset and Pre-trained Language Model for Indonesian NLP," in Proc. COLING, 2020.
[4] Hugging Face, "mdhugol/indonesia-bert-sentiment-classification," huggingface.co, 2026. [Online]. Available: https://huggingface.co/mdhugol/indonesia-bert-sentiment-classification
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!