Spaces:
Sleeping
Sleeping
File size: 6,819 Bytes
12de14f 8b61600 7069e30 8b61600 a56f27f 76dbdba e85b463 a56f27f 8b61600 a56f27f 8b61600 a56f27f 8b61600 df8b0dc 8b61600 a56f27f 8b61600 513fd6e c2d5a1a 7069e30 513fd6e 7069e30 c2d5a1a 513fd6e c2d5a1a 7069e30 513fd6e 7069e30 c2d5a1a 7069e30 a56f27f 8b61600 a56f27f e85b463 a56f27f e85b463 a56f27f 8b61600 a56f27f 7069e30 a56f27f 7069e30 a56f27f 7069e30 8b61600 a56f27f 8b61600 a56f27f 8b61600 a56f27f 8b61600 12de14f 8b61600 df8b0dc a56f27f df8b0dc a56f27f df8b0dc a56f27f 7069e30 a56f27f 8b61600 7069e30 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | 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.") |