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.")