xerubin commited on
Commit
1f9bcbb
1 Parent(s): f5b4a41

commit inicial

Browse files
Files changed (7) hide show
  1. app.py +67 -0
  2. as_bert_df.py +126 -0
  3. assets/favicon.ico +0 -0
  4. assets/flags.jpg +0 -0
  5. assets/header.jpg +0 -0
  6. page1.py +150 -0
  7. page2_.py +149 -0
app.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sat Dec 23 13:22:00 2023
4
+ @author: Essio Rubin C.
5
+ """
6
+ from PIL import Image
7
+ import streamlit as st
8
+ from st_pages import Page, show_pages, add_page_title
9
+
10
+ # icon
11
+ im = Image.open("assets/favicon.ico")
12
+ st.set_page_config(
13
+ page_title="Analisis de sentimiento de reseñas de Hoteles",
14
+ page_icon=im,
15
+ layout="wide",
16
+ )
17
+
18
+ # img header
19
+ img = Image.open("assets/header.jpg")
20
+ st.image(img, width=1000)
21
+
22
+ # show links to pages
23
+ show_pages(
24
+ [
25
+ Page("app.py", "Presentación", "🏠"),
26
+ Page("page1.py", "Análisis de reseñas uno a uno", ":blue_book:"),
27
+ Page("page2_.py", "Análisis de reseñas en lotes", ":books:"),
28
+ ]
29
+ )
30
+
31
+ # show text
32
+ css_text_1 = '''
33
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
34
+
35
+ <p style="font-family: Verdana; font-size: 15px; font-weight: 400; color: #494848;">
36
+ <span style="font-family: Georgia, serif; font-size: 20px; font-weight: 400; color: blue; font-style: italic; ">IA Hotel Reviews</span> es una aplicación que utiliza Inteligencia Artificial para realizar un análisis inteligente de las reseñas que dejan tus clientes. Ayuda a identificar el sentimiento positivo o negativo que hay detrás de estos comentarios. Obtendrás un análisis certero de la opiniones que vierten tus clientes y con ello puedes tomar mejores decisiones sobre tu personal, las instalaciones y otros aspectos de tu hotel con el fin de ganar más clientes e incrementar tus ganancias.
37
+ </p>
38
+ <p style="font-family: Verdana; font-size: 3px;">&nbsp;</p>
39
+ <p style="font-family: Verdana; font-size: 15px; font-weight: 300; ; color: #494848;">
40
+ Este programa es multidioma ya que Las reseñas pueden estar escritas en idioma español, holandés, italiano, alemán, francés o inglés.
41
+ </p>
42
+
43
+ '''
44
+ st.write(css_text_1, unsafe_allow_html=True)
45
+
46
+ # show image
47
+ img = Image.open("assets/flags.jpg")
48
+ st.image(img, width=350)
49
+
50
+ css_text_2 = '''
51
+ <p style="font-family: Verdana; font-size: 3px;">&nbsp;</p>
52
+ <p style="font-family: Verdana; font-size: 15px; font-weight: 400; color: #494848;">
53
+ Te ofrecemos dos opciones de análisis:
54
+ </p>
55
+
56
+ <i class="fa-solid fa-gear"></i>
57
+ &nbsp;&nbsp;
58
+ <a style="font-family: Verdana; font-size: 17px; font-weight: 200; text-decoration: none; ; color: #6c88f4;" href="http://localhost:8501/An%C3%A1lisis%20de%20rese%C3%B1as%20uno%20a%20uno">Análisis de reseñas uno a uno</a>
59
+
60
+ <i class="fa-solid fa-gear"></i>
61
+ &nbsp;&nbsp;
62
+ <a style="font-family: Verdana; font-size: 17px; font-weight: 200; text-decoration: none; ; color: #6c88f4;" href="http://localhost:8501/An%C3%A1lisis%20de%20rese%C3%B1as%20en%20lotes">Análisis de reseñas en lotes</a>
63
+ '''
64
+
65
+ st.write(css_text_2, unsafe_allow_html=True)
66
+
67
+
as_bert_df.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sun Dec 3 09:26:33 2023
4
+
5
+ @author: ideaUser
6
+ Modelo (huggingface)
7
+ https://huggingface.co/nlptown/bert-base-multilingual-uncased-sentiment
8
+
9
+ """
10
+
11
+ #%%
12
+
13
+ import torch
14
+ from transformers import BertTokenizer, BertForSequenceClassification
15
+ from transformers import pipeline
16
+ import pandas as pd
17
+
18
+ #%%
19
+
20
+
21
+ # Download pretrained BERT from Hugging Face for sentiment analisis
22
+ model_name = "nlptown/bert-base-multilingual-uncased-sentiment"
23
+ tokenizer = BertTokenizer.from_pretrained(model_name)
24
+ model = BertForSequenceClassification.from_pretrained(model_name)
25
+
26
+ # function predict sentiment using BERT pretrained
27
+ def get_review_sentiment(text):
28
+ classifier = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
29
+ results = classifier(text)
30
+ return int(results[0]["label"][0])
31
+
32
+
33
+ #%%
34
+ # function for batch predict stars
35
+ def batch_predict_sentiment_stars(data):
36
+ sentiment = []
37
+ for index, row in data.iterrows():
38
+ sentiment.append(get_review_sentiment(row["review"]))
39
+ data_result = pd.DataFrame(
40
+ {
41
+ "review": list(data["review"]),
42
+ "predict": sentiment
43
+ })
44
+ return data_result
45
+
46
+
47
+ #%%
48
+ # function for batch predict sentiment label [NEGATIVE, NEUTRAL, POSITIVE]
49
+ def batch_predict_sentiment_3_label(data):
50
+ sentiment = []
51
+ for index, row in data.iterrows():
52
+ stars = get_review_sentiment(row["review"])
53
+ label = ""
54
+ if stars < 3:
55
+ label = "negative"
56
+ elif stars == 3:
57
+ label = "neutral"
58
+ elif stars > 3 & stars <= 5:
59
+ label = "positive"
60
+ sentiment.append(label)
61
+ data_result = pd.DataFrame(
62
+ {
63
+ "review": list(data["review"]),
64
+ "label": list(data["label"]),
65
+ "predict": sentiment
66
+ })
67
+ return data_result
68
+
69
+ #%%
70
+ # function for batch predict sentiment label [NEGATIVE, POSITIVE]
71
+ def batch_predict_sentiment_2_label(data):
72
+ total_rows = data.shape[0]
73
+ sentiment = []
74
+ i = 1
75
+ for index, row in data.iterrows():
76
+ stars = get_review_sentiment(row["review"])
77
+ label = ""
78
+ if stars < 3:
79
+ label = "negative"
80
+ elif stars >= 3 & stars <= 5:
81
+ label = "positive"
82
+ sentiment.append(label)
83
+ if i % 100 == 0:
84
+ print(i, " / " , total_rows )
85
+ i += 1
86
+
87
+ # return a dataframe
88
+ data_result = pd.DataFrame(
89
+ {
90
+ "review": list(data["review"]),
91
+ "label": list(data["label"]),
92
+ "predict": sentiment
93
+ })
94
+ return data_result
95
+
96
+
97
+ #%%
98
+ if __name__ == "__main__":
99
+
100
+ hotel_reviews = ["El fin de semana mi pareja y yo hicimos una reserva en este hotel, con el fin de descansar y desconectar, fue sólo una noche y menos mal. Nos llevaron a un ala bastante apartada del hotel porque nos dijeron que era mejor para descansar ya que la parte de fuera era muy “jaleosa”. Nos pareció bien porque era justo lo que buscábamos, y cuál fue nuestra sorpresa? Desde las 6 de la mañana con ruidos, primero lo que suponemos que eran unos tacones en la habitación de arriba (de eso no tiene culpa el hotel, obviamente) y después sobre las 7 o poco más, las limpiadoras moviendo muebles y arrastrando sofás o lo que fuera. Habíamos cogido sólo alojamiento para descansar, pensando en no tener que madrugar como habitualmente, pero fue IMPOSIBLE por los ruidos constantes. Por destacar algo…",
101
+ "El hotel en general está bien, las habtiaciones son espaciosas y el personal es muy amable (sobretodo el encargado del roof-top y la piscina) y la zona de la piscina es curiosa. Pero tiene dos grandes fallos: El primero es que el wifi no llegaba bien a la habitación ya que se cortaba continuamente. El segundo fallo es que no se les ocurre otra cosa que poner un edredón nórdico en vez de una sábana fina en pleno agosto en Sevilla.",
102
+ "El hotel es moderno, amplio y limpio, pero no hemos podido disfrutar de la experiencia porque con tanto ruido no hemos podido descansar. Además la piscina estaba llena de gente y no la hemos podido usar. Los empleados muy amables y la ubicación perfecta."
103
+ ]
104
+
105
+ data_df = pd.DataFrame(
106
+ {
107
+ "review": hotel_reviews,
108
+ "label":['positive','negative','positive']
109
+ }
110
+ )
111
+ # save dataframe
112
+ data_df.to_csv('data/sa_data.csv', index=False)
113
+
114
+ #%%
115
+ # pruebas
116
+ # read csv
117
+ data_df = pd.read_csv('data/sa_data.csv')
118
+ #result = batch_predict_sentiment_stars(data_df)
119
+ #result = batch_predict_sentiment_3_label(data_df)
120
+ result = batch_predict_sentiment_2_label(data_df)
121
+ print("Columns:\n", result.dtypes)
122
+ print("\n(rows, cols): ", result.shape)
123
+ print("\nData:\n",result)
124
+
125
+ #%%
126
+
assets/favicon.ico ADDED
assets/flags.jpg ADDED
assets/header.jpg ADDED
page1.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sat Dec 23 13:22:00 2023
4
+ @author: Essio Rubin C.
5
+ """
6
+ from PIL import Image
7
+ import streamlit as st
8
+ from st_pages import Page, show_pages, add_page_title
9
+ import time
10
+ import pandas as pd
11
+ import matplotlib.pyplot as plt
12
+ import numpy as np
13
+ from as_bert_df import batch_predict_sentiment_stars
14
+
15
+ c1 = st.container()
16
+ c2 = st.container()
17
+
18
+ data_df = pd.DataFrame(
19
+ {
20
+ "review": ["",], "predict":[0,]
21
+ }
22
+ )
23
+
24
+ # function to show base page
25
+ def show_base_page():
26
+ with c1:
27
+ # the title and icon to the current page
28
+ add_page_title()
29
+
30
+ # show text
31
+ text_css = """
32
+ <p style="font-family: Verdana; font-size: 15px; font-weight: 400; ; color: #494848;">
33
+ Una reseña debe ser un texto que tenga una o varias oraciones que incluyan opiniones referidas a servicios que brinda
34
+ un Hotel.
35
+ La reseña puede estar escrita en idioma español, holandés, italiano, alemán, francés o inglés.
36
+ </p>
37
+
38
+ """
39
+ st.write(text_css, unsafe_allow_html=True)
40
+
41
+ # show image
42
+ img = Image.open("assets/flags.jpg")
43
+ st.image(img, width=200)
44
+
45
+
46
+ # function to show data editor
47
+ def show_data_editor(data):
48
+ with c1:
49
+ # dataframe editor
50
+ global st_data
51
+ global placeholder
52
+ placeholder = st.empty()
53
+
54
+ #st_data = st.data_editor(
55
+ st_data = placeholder.data_editor(
56
+ data,
57
+ column_config={
58
+ "review": st.column_config.TextColumn(
59
+ "review",
60
+ help="Ingrese la reseña.",
61
+ width="large",
62
+ required=True,
63
+ max_chars=500,
64
+ validate="[a-zA-Záéíóúñ.,]+$"
65
+ ),
66
+ "predict": st.column_config.NumberColumn(
67
+ "predict",
68
+ help="Sentimiento expresado en cantidad de estrellas.",
69
+ width="small",
70
+ required=False,
71
+ default=0,
72
+ min_value=0,
73
+ max_value=5,
74
+ format="%d ⭐",
75
+ )
76
+
77
+ },
78
+ hide_index=True,
79
+ num_rows="dynamic",
80
+ height = 260,
81
+ width = 900,
82
+ )
83
+
84
+ def show_button():
85
+ global placeholder2
86
+ placeholder2 = st.empty()
87
+ with c1:
88
+ # show buttons
89
+ global button_1
90
+ button_1 = placeholder2.button(" :gear: Predecir", type="primary", key='but_1')
91
+
92
+
93
+ # define action funtion for button
94
+ def predecir():
95
+ #st.session_state.disabled = True
96
+ data_df = st_data
97
+ if data_df.shape[0] > 0:
98
+ st.write(data_df.shape[0])
99
+ # progress bar
100
+ progress_text = "Procesando. Espere."
101
+ my_bar = st.progress(0, text=progress_text)
102
+ for percent_complete in range(100):
103
+ time.sleep(0.01)
104
+ my_bar.progress(percent_complete + 1, text=progress_text)
105
+ time.sleep(1)
106
+ my_bar.empty()
107
+ result = batch_predict_sentiment_stars(data_df)
108
+ placeholder.empty()
109
+ placeholder2.empty()
110
+ show_data_editor(result)
111
+ show_chart(result)
112
+ else:
113
+ st.error("Debe ingresar una reseña.")
114
+
115
+
116
+ def show_chart(data):
117
+ with c2:
118
+ fig, ax = plt.subplots()
119
+ fig.set_figwidth(5)
120
+ fig.set_figheight(3)
121
+ # title
122
+ ax.set_title("Histograma de Frecuencias", fontsize = 8)
123
+
124
+ # axis
125
+ ax.set_xlim([0, 5])
126
+
127
+ # x label
128
+ ax.set_xlabel('Sentimiento (Número de estrellas)', fontsize = 6)
129
+
130
+ ax.set_ylabel('Frecuencia', fontsize = 6)
131
+
132
+ # Crear un histograma
133
+ ax.hist(data['predict'], bins=20, color ="green")
134
+
135
+ # Mostrar el gráfico en Streamlit
136
+ st.pyplot(fig)
137
+
138
+ #------------------------------------------
139
+ # main flow
140
+ #------------------------------------------
141
+ show_base_page()
142
+ show_data_editor(data_df)
143
+ show_button()
144
+ if button_1:
145
+ predecir()
146
+
147
+
148
+
149
+
150
+
page2_.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sat Dec 23 13:22:00 2023
4
+ @author: Essio Rubin C.
5
+ """
6
+ from PIL import Image
7
+ import streamlit as st
8
+ from st_pages import add_page_title
9
+ import time
10
+ import pandas as pd
11
+ import matplotlib.pyplot as plt
12
+ from as_bert_df import batch_predict_sentiment_stars
13
+
14
+ c1 = st.container()
15
+ c2 = st.container()
16
+
17
+ # function to show base page
18
+ def show_base_page():
19
+ with c1:
20
+ # show title and icon to the current page
21
+ add_page_title()
22
+
23
+ # show text
24
+ text_css = """
25
+ <p style="font-family: Verdana; font-size: 15px; font-weight: 400; ; color: #494848;">
26
+ El archivo de reseñas debe ser un archivo de texto que contenga una reseña por cada línea.
27
+ La reseña puede estar escrita en idioma español, holandés, italiano, alemán, francés o inglés.
28
+ </p>
29
+ <p>&nbsp;</p>
30
+ """
31
+ st.write(text_css, unsafe_allow_html=True)
32
+
33
+ # show image
34
+ img = Image.open("assets/flags.jpg")
35
+ st.image(img, width=200)
36
+
37
+ # placeholder for hide widget
38
+ global placeholder, placeholder2
39
+ placeholder = st.empty()
40
+ placeholder2 = st.empty()
41
+
42
+ # file upload
43
+ global uploaded_file
44
+ uploaded_file = placeholder.file_uploader("Cargar archivo de reseñas ...", type=['txt', 'csv'], key='uploader')
45
+
46
+ global button_1
47
+ button_1 = placeholder2.button(" :gear: Predecir", type="primary", key='but_1')
48
+
49
+
50
+
51
+ # define action funtion for button
52
+ def predecir():
53
+ if uploaded_file is not None:
54
+ global data_df
55
+ # progress bar
56
+ progress_text = "Procesando. Espere."
57
+ my_bar = st.progress(0, text=progress_text)
58
+
59
+ for percent_complete in range(100):
60
+ time.sleep(0.01)
61
+ my_bar.progress(percent_complete + 1, text=progress_text)
62
+ time.sleep(1)
63
+ my_bar.empty()
64
+
65
+ data_df = pd.read_csv(uploaded_file, sep=";")
66
+ if data_df.shape[0] > 0:
67
+ # formatear columnas
68
+ if data_df.shape[1] < 2:
69
+ data_df['review'] = 0
70
+ data_df.columns = ["review","predict"]
71
+
72
+ result = batch_predict_sentiment_stars(data_df)
73
+
74
+ # mostrar resulyados
75
+ show_data_editor(result)
76
+ show_chart(result)
77
+ placeholder.empty()
78
+ placeholder2.empty()
79
+ else:
80
+ st.error("Archivo se encuentra vacío.")
81
+ else:
82
+ st.error("Debe subir un archivo de texto")
83
+
84
+ def show_data_editor(data_df):
85
+ with c2:
86
+ st.data_editor(
87
+ data_df,
88
+ column_config={
89
+ "review": st.column_config.TextColumn(
90
+ "review",
91
+ help="Ingrese la reseña.",
92
+ width="large",
93
+ required=True,
94
+ max_chars=500,
95
+ validate="[a-zA-Z]+$"
96
+ ),
97
+ "predict": st.column_config.NumberColumn(
98
+ "predict",
99
+ help="Sentimiento expresado en cantidad de estrellas.",
100
+ width="small",
101
+ required=False,
102
+ default=0,
103
+ min_value=0,
104
+ max_value=5,
105
+ format="%d ⭐",
106
+ )
107
+ },
108
+ hide_index=True,
109
+ num_rows="dynamic",
110
+ height = 260,
111
+ width = 900,
112
+ )
113
+
114
+
115
+
116
+ def show_chart(data):
117
+ fig, ax = plt.subplots()
118
+ fig.set_figwidth(5)
119
+ fig.set_figheight(3)
120
+ # title
121
+ ax.set_title("Histograma de Frecuencias", fontsize = 8)
122
+
123
+ # axis
124
+ ax.set_xlim([0, 5])
125
+
126
+ # x label
127
+ ax.set_xlabel('Sentimiento (Número de estrellas)', fontsize = 6)
128
+
129
+ ax.set_ylabel('Frecuencia', fontsize = 6)
130
+
131
+ # Crear un histograma
132
+ ax.hist(data['predict'], bins=20, color ="green")
133
+
134
+ # Mostrar el gráfico en Streamlit
135
+ st.pyplot(fig)
136
+
137
+ #------------------------------------------
138
+ # main flow
139
+ #------------------------------------------
140
+ show_base_page()
141
+
142
+ if button_1:
143
+ predecir()
144
+
145
+
146
+
147
+
148
+
149
+