Spaces:
Sleeping
Sleeping
File size: 6,400 Bytes
01ad901 |
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 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
import spacy
import pandas as pd
import streamlit as st
from preprocessing import preprocess_reviews
from aspects_extraction import extract_aspects
from clustering import cluster_aspect_terms
import plotly.express as px
import matplotlib.pyplot as plt
defaultCsv = {
'Serco USB Hub + Sound Card': 'reviews.csv',
'Honey': 'reviews_honey.csv',
}
st.set_page_config(
page_title="Actionble Insights From Reviews",
layout="wide",
)
@st.cache
def load_reviews(uploaded_file=None, default_file=None):
if default_file is not None:
reviews = pd.read_csv(default_file)
if uploaded_file is not None:
reviews = pd.read_csv(uploaded_file)
reviews = validate_reviews_dataframe(reviews)
return preprocess_reviews(reviews)
def validate_reviews_dataframe(r):
if 'title' not in r.columns:
raise ValueError("column title is required")
if 'review' not in r.columns:
raise ValueError("column review is required")
if 'rating' not in r.columns:
raise ValueError("column rating is required")
if r['title'].dtype != 'O':
raise ValueError("column title must be string")
if r['review'].dtype != 'O':
raise ValueError("column review must be string")
if r['rating'].dtype != 'float64':
raise ValueError("column rating must be float")
r = r.dropna()
if ((r['rating'] < 0) & (r['rating'] > 5)).any():
raise ValueError("values in column rating must be between 0 and 5")
return r
@st.cache(allow_output_mutation=True, suppress_st_warning=True)
def load_model():
return spacy.load("en_core_web_lg")
@st.cache(allow_output_mutation=True, suppress_st_warning=True)
def get_aspects(reviews):
nlp = load_model()
return extract_aspects(nlp, reviews)
@st.cache(allow_output_mutation=True, suppress_st_warning=True)
def cluster_aspects(aspects):
nlp = load_model()
replacements = cluster_aspect_terms(nlp, aspects)
aspects['aspect'] = aspects['aspect'].map(replacements)
return aspects
def get_aspects_with_ratings(aspects, reviews):
aspect_with_ratings = pd.merge(aspects,
reviews[['rating']],
left_on='review_id',
right_index=True)
aspect_with_ratings['review_sentiment'] = pd.cut(aspect_with_ratings['rating'],
bins=[0, 3, 4, 5],
right=True,
labels=['Negative', 'Neutral', 'Positive']
)
return aspect_with_ratings
def get_aspect_treemap(aspects):
treemap = px.treemap(aspects.groupby(['aspect', 'opinion']).size().reset_index(),
path=[px.Constant('Aspects'), 'aspect', 'opinion'],
values=0,
)
treemap.update_layout(margin = dict(t=0, l=0, r=0, b=0))
return treemap
def plot_pain_points(aspect_with_ratings):
pain_points = (aspect_with_ratings
.query('review_sentiment == "Negative"')
.groupby('aspect')
.size()
.sort_values(ascending=False)[:10]
)
fig = px.bar(pain_points)
fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))
fig.update_traces(marker_color='red', showlegend=False)
return fig
def plot_gain_points(aspect_with_ratings):
gain_points = (aspect_with_ratings
.query('review_sentiment == "Positive"')
.groupby('aspect')
.size()
.sort_values(ascending=False)[:10]
)
fig = px.bar(gain_points)
fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))
fig.update_traces(marker_color='green', showlegend=False)
return fig
def plot_sentiment_by_aspect(aspect_with_ratings, top=15):
pivot = pd.crosstab(
index=aspect_with_ratings['aspect'],
columns=aspect_with_ratings['review_sentiment'],
margins=True,
).sort_values(by='All', ascending=False).iloc[1:, :-1]
fig = px.bar(pivot[:top], barmode='group', color_discrete_map={
'Positive': 'green',
'Negative': 'red',
'Neutral': 'blue',
})
fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))
return fig
st.write("## Actionble Insights From Reviews")
st.write("""
Key to building a successfull product is understanding what users want and what users don't want.
This insight can be useful in serveral ways.
1. Designing product that users actually want.
2. Fixing defects in product or addressing users pain points.
3. Staying ahead of the competition.
There are millions of reviews that people leave on sites like amazon, tripadvisor etc.
To gain insights from this data, you could either read all the reviews one by one or
let machine analyze these reviews and find main topics that user care about.
""")
st.write("## Extracting Aspect Opinion Pairs")
st.write("""
Let's say the customer wrote, `The material of the shirt is not soft`.
Here `material` is the `aspect` of shirt and `not soft` is the users `opinion`
about this aspect. The analyzer finds aspect opinion pairs from the reviews.
""")
st.write("### Customer Reviews")
st.write("""
Dataframe containing reviews of the customer. Title, review, and rating columns are required
""")
st.sidebar.title("Select Reviews File")
default_file = st.sidebar.selectbox(
"Choose Sample File",
defaultCsv.keys(),
)
if default_file is not None:
default_file = defaultCsv[default_file]
st.sidebar.write("<div style='text-align:center'>or</div>", unsafe_allow_html=True)
uploaded_file = st.sidebar.file_uploader(
'Choose a CSV File',
type='csv',
)
st.sidebar.write("CSV with title(string), review(string) and ratings(float 0-5) columns")
try:
reviews = load_reviews(uploaded_file, default_file)
st.write(reviews)
aspects = get_aspects(reviews)
aspects = cluster_aspects(aspects)
aspects_with_ratings = get_aspects_with_ratings(aspects, reviews)
st.write("### Extracted Aspect Opinion Pairs")
st.write("""
Treemap of aspect opinion pairs extracted from reviews, treemap
is sized according to number of reviews.
""")
st.plotly_chart(get_aspect_treemap(aspects), use_container_width=True)
st.write("### Pain Points And Gain Points")
col1, col2 = st.columns(2)
with col1:
st.write('Top Pain Points (by number of -ve reviews)')
st.plotly_chart(plot_pain_points(aspects_with_ratings), use_container_width=True)
with col2:
st.write('Top Gain Points (by number of +ve reviews)')
st.plotly_chart(plot_gain_points(aspects_with_ratings), use_container_width=True)
st.write("### Sentiment for each aspect")
st.write('(0-3 Negative) (4 Neutral) (5 Positive)')
st.plotly_chart(plot_sentiment_by_aspect(aspects_with_ratings), use_container_width=True)
except ValueError as e:
st.error(e) |