customer_experience_analyzer / src /streamlit_app.py
cchaimin's picture
Update src/streamlit_app.py
513fd6e verified
import streamlit as st
import pandas as pd
from pathlib import Path
from collections import Counter
import re
st.set_page_config(page_title="Customer Experience Analyzer", layout="wide")
# Load data
DATA_PATH = Path(__file__).parent / "reviews.csv"
df = pd.read_csv(DATA_PATH)
# Title
st.title("Customer Experience Analyzer")
st.write("Analyze customer sentiment from restaurant reviews and identify where customer experience can improve.")
# Sidebar
st.sidebar.header("Filters")
selected_sentiment = st.sidebar.multiselect(
"Select sentiment",
options=df["sentiment"].unique(),
default=df["sentiment"].unique()
)
search_term = st.sidebar.text_input("Search reviews by keyword")
filtered_df = df[df["sentiment"].isin(selected_sentiment)]
if search_term:
filtered_df = filtered_df[
filtered_df["review_text"].str.contains(search_term, case=False, na=False)
]
# KPIs
total_reviews = len(filtered_df)
positive_rate = (filtered_df["sentiment"] == "positive").mean() * 100 if total_reviews > 0 else 0
negative_rate = (filtered_df["sentiment"] == "negative").mean() * 100 if total_reviews > 0 else 0
st.subheader("Overview")
col1, col2, col3 = st.columns(3)
col1.metric("Total Reviews", total_reviews)
col2.metric("Positive %", f"{positive_rate:.1f}%")
col3.metric("Negative %", f"{negative_rate:.1f}%")
# Chart
st.subheader("Sentiment Breakdown")
if total_reviews > 0:
st.bar_chart(filtered_df["sentiment"].value_counts())
else:
st.warning("No reviews match the selected filters.")
# Key complaint drivers
st.subheader("Key Complaint Drivers")
negative_reviews = filtered_df[filtered_df["sentiment"] == "negative"]["review_text"]
if len(negative_reviews) > 0:
all_text = " ".join(negative_reviews.astype(str)).lower()
words = re.findall(r"\b[a-z]+\b", all_text)
stopwords = {
"the", "and", "was", "were", "is", "it", "to", "of", "for", "in",
"a", "an", "this", "that", "with", "on", "at", "my", "our", "we",
"i", "had", "but", "very", "so", "not", "be", "been", "are", "as",
"they", "them", "you", "he", "she", "his", "her", "their", "there",
"here", "have", "has", "from", "too", "all", "will", "would", "back",
"one", "get", "got", "go", "went", "also", "even", "still", "really",
"just", "make", "made", "time", "day", "thing", "things", "place",
"again", "ever", "only", "your", "good", "like", "food"
}
complaint_keywords = {
"slow", "rude", "cold", "dirty", "bad", "worst", "terrible", "awful",
"expensive", "overpriced", "late", "poor", "disappointing", "unfriendly",
"noisy", "dry", "burnt", "bland", "tasteless", "small", "waiting",
"minutes", "service", "staff", "manager", "order", "orders", "table",
"wrong", "delay", "delayed", "undercooked", "overcooked", "stale"
}
filtered_words = [
word for word in words
if word not in stopwords and word in complaint_keywords
]
if filtered_words:
word_counts = Counter(filtered_words).most_common(10)
word_df = pd.DataFrame(word_counts, columns=["Word", "Count"])
st.bar_chart(word_df.set_index("Word"))
else:
st.info("No complaint keywords found in the current selection.")
else:
st.info("No negative reviews available.")
# Example reviews
st.subheader("Example Customer Reviews")
col4, col5 = st.columns(2)
positive_examples = filtered_df[filtered_df["sentiment"] == "positive"]
negative_examples = filtered_df[filtered_df["sentiment"] == "negative"]
with col4:
st.markdown("### Positive Review Example")
if len(positive_examples) > 0:
st.success(positive_examples.iloc[0]["review_text"])
else:
st.info("No positive review found for this selection.")
with col5:
st.markdown("### Negative Review Example")
if len(negative_examples) > 0:
st.error(negative_examples.iloc[0]["review_text"])
else:
st.info("No negative review found for this selection.")
# Key insights
st.subheader("Key Insights")
if total_reviews > 0:
if positive_rate > negative_rate:
st.write("Customer sentiment is mostly positive in the selected reviews.")
elif negative_rate > positive_rate:
st.write("Customer sentiment is mostly negative in the selected reviews.")
else:
st.write("Customer sentiment is evenly split between positive and negative.")
st.write(
f"""
- Out of **{total_reviews}** filtered reviews, **{positive_rate:.1f}%** are positive and **{negative_rate:.1f}%** are negative.
- This helps management quickly assess overall customer satisfaction.
- Searching by keyword can help identify specific issues in customer feedback.
"""
)
else:
st.write("No insights available because no reviews match the selected filters.")
# Recommendations
st.subheader("Manager Recommendations")
if total_reviews > 0:
if negative_rate > 60:
st.warning("Customer dissatisfaction is high. Management should urgently review repeated complaints and investigate operational issues.")
elif negative_rate > 40:
st.info("Customer sentiment is mixed. Management should identify recurring negative themes and improve consistency.")
else:
st.success("Customer sentiment is mostly positive. Management should preserve strengths and continue monitoring feedback.")
else:
st.write("No recommendation available.")
# Reviews table
st.subheader("Filtered Reviews Table")
if total_reviews > 0:
st.dataframe(
filtered_df[["review_text", "sentiment"]].reset_index(drop=True),
use_container_width=True
)
else:
st.write("No reviews to display.")
# Assistant
st.subheader("Ask the Assistant")
question = st.text_input("Ask a question about the reviews")
if question:
q = question.lower()
if "positive" in q:
st.write("Positive reviews suggest that customers were satisfied with their restaurant experience.")
elif "negative" in q:
st.write("Negative reviews suggest that customers experienced problems that may affect satisfaction and loyalty.")
elif "complaint" in q or "complaints" in q:
if len(negative_reviews) > 0 and filtered_words:
top_word = word_counts[0][0]
st.write(f"A common complaint-related word in the selected reviews is **{top_word}**.")
else:
st.write("There are no complaint words available in the current selection.")
elif "improve" in q or "improvement" in q:
st.write("Management should focus on recurring negative feedback and investigate the causes behind poor customer experiences.")
else:
st.write("This dashboard helps management understand customer sentiment, complaint patterns, and possible improvement areas.")