Spaces:
Sleeping
Sleeping
| 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.") |