File size: 14,084 Bytes
a86ebfe
074b087
4f600ec
 
123a2a5
64547bc
4c1a7b2
eb03325
4913bb4
edb53bb
8255c77
4c1a7b2
8255c77
a76f382
95dec4b
4913bb4
a86ebfe
336e489
a76f382
 
 
8255c77
a86ebfe
 
 
 
 
 
 
8255c77
 
bfaf0da
4913bb4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0e521b1
8255c77
e5f8353
 
 
 
 
 
 
8255c77
074b087
 
 
e7fc6e7
fc2c6f0
9e0c8f4
1b4e97e
9e0c8f4
1b4e97e
9e0c8f4
3b9d4b9
cb408d3
3b9d4b9
8255c77
1b4e97e
 
 
074b087
1b4e97e
a585bd7
1b4e97e
074b087
8255c77
a585bd7
457d41e
8255c77
bfaf0da
8255c77
bfaf0da
d0e7d06
 
bfaf0da
8255c77
bfaf0da
8255c77
 
d0e7d06
8255c77
d0e7d06
 
 
4913bb4
8255c77
4913bb4
 
 
8255c77
bfaf0da
4913bb4
 
 
8255c77
4913bb4
 
8255c77
4913bb4
 
 
 
8255c77
d0e7d06
8255c77
d0e7d06
cfaf805
7386027
8255c77
2ba6811
8255c77
70da8b4
 
 
ae3c7df
7386027
d8e3643
8255c77
4d7d94e
7386027
f9ed481
8255c77
7386027
bfaf0da
7386027
0e521b1
8255c77
3e92edb
8255c77
dc1887d
8255c77
dc1887d
 
8255c77
 
ba85806
8255c77
 
bb0de7f
8255c77
485e4eb
d5291cb
4f31ce9
dad8f09
 
8255c77
bfaf0da
6de011c
dad8f09
8255c77
6de011c
fe0b968
8255c77
a7ff4ae
8255c77
6de011c
7386027
 
6de011c
46ac277
8255c77
796c7fd
8255c77
46ac277
645e710
f99d3df
 
 
 
bdb23ce
f99d3df
 
 
 
 
a0c7133
 
8255c77
30abc5f
a0c7133
 
d76af9e
 
 
8255c77
9ee7fd1
a0c7133
38b5c86
e52085c
34030e5
e52085c
a0c7133
 
 
 
f701f7f
bdb23ce
46ac277
7386027
 
dad8f09
7386027
bb0de7f
7c434c5
bb0de7f
33f003a
eb4e7e0
7386027
 
4913bb4
89d7f96
 
 
 
 
 
 
33f003a
dfcadc4
 
89d7f96
b9decf4
8255c77
 
021bcd0
8255c77
366279c
8255c77
a882dd0
8255c77
a882dd0
ae3c7df
8255c77
a882dd0
 
 
8255c77
a882dd0
8255c77
a882dd0
8255c77
 
a882dd0
 
 
 
 
 
 
 
8255c77
 
a882dd0
 
366279c
a882dd0
366279c
a882dd0
366279c
8255c77
 
074b087
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
import tweepy as tw
import streamlit as st
import pandas as pd
import regex as re
import numpy as np
import pysentimiento
import geopy
import matplotlib.pyplot as plt
import langdetect

#Importar Nominatim para geocalización
from geopy.geocoders import Nominatim
#Importar pipeline para usar al modelo Transformer
from transformers import pipeline
#función detect es utilizada para detectar el idioma de un texto dado.
from langdetect import detect


model_checkpoint = "hackathon-pln-es/twitter_sexismo-finetuned-robertuito-exist2021" 
pipeline_nlp = pipeline("text-classification", model=model_checkpoint)

#Claves de acceso a la API de Twitter
consumer_key = "BjipwQslVG4vBdy4qK318KnoA"
consumer_secret = "3fzL70v9faklrPgvTi3zbofw9rwk92fgGdtAslFkFYt8kGmqBJ"
access_token = "1217853705086799872-Y5zEChpTeKccuLY3XJRXDPPZhNrlba"
access_token_secret = "pqQ5aFSJxzJ2xnI6yhVtNjQO36FOu8DBOH6DtUrPAU54J"
auth = tw.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tw.API(auth, wait_on_rate_limit=True)

#Función para limpiar datos
def limpieza_datos(tweet):
    # Eliminar emojis
    tweet = re.sub(r'[\U0001F600-\U0001F64F]', '', tweet)
    tweet = re.sub(r'[\U0001F300-\U0001F5FF]', '', tweet)
    tweet = re.sub(r'[\U0001F680-\U0001F6FF]', '', tweet)
    tweet = re.sub(r'[\U0001F1E0-\U0001F1FF]', '', tweet)
    # Eliminar arrobas
    tweet = re.sub(r'@\w+', '', tweet)
    # Eliminar URL
    tweet = re.sub(r'http\S+', '', tweet)
    # Eliminar hashtags
    tweet = re.sub(r'#\w+', '', tweet)
    # Eliminar caracteres especiales
    #tweet = re.sub(r'[^a-zA-Z0-9 \n\.]', '', tweet)
    tweet = re.sub(r'[^a-zA-Z0-9 \n\áéíóúÁÉÍÓÚñÑ.]', '', tweet)
    return tweet

#Función para cambiar el color de la palabra "Sexista"
def highlight_survived(s):
    return ['background-color: red']*len(s) if (s.Sexista == 1) else ['background-color: green']*len(s)

def color_survived(val):
    color = 'red' if val=='Sexista' else 'white'
    return f'background-color: {color}'

#Función para el ancho de la pagina web
st.set_page_config(layout="wide")
st.markdown('<style>body{background-color: Blue;}</style>',unsafe_allow_html=True)

#colT1,colT2 = st.columns([2,8])


st.markdown(""" <style> .font {
font-size:40px ; font-family: 'Cooper Black'; color: #301E67;} 
</style> """, unsafe_allow_html=True)

#st.markdown('<p class="font"; style="text-align: center;>Análisis de comentarios sexistas en linea</p>', unsafe_allow_html=True)
st.markdown('<p class="font" style="text-align: center;">Detectando el Sexismo en Linea: Un proyecto de Investigación</p>', unsafe_allow_html=True)

#Editar el tipo de letra
st.markdown(""" <style> .font1 {
font-size:28px ; font-family: 'Times New Roman'; color: #8d33ff;} 
</style> """, unsafe_allow_html=True)

st.markdown(""" <style> .font2 {
font-size:18px ; font-family: 'Times New Roman'; color: #5B8FB9;} 
</style> """, unsafe_allow_html=True)

#Parrafo
st.markdown('<p class="font2">Este proyecto consiste en una aplicación web que utiliza la biblioteca Tweepy de Python para descargar tweets de Twitter, permitiendo buscar Tweets por usuario y por localidad. Luego, utiliza modelos de lenguaje basados en Transformers para analizar los tweets y detectar comentarios y determinar si son "Sexistas" o "No Sexistas". El proyecto busca identificar y combatir el discurso sexista en línea para promover la igualdad de género y la inclusión.</p>',unsafe_allow_html=True)

#Función para extrer tweets de usuarios de Twitter, y analizarlos por el modelo Transformer
def tweets_usuario(usuario, cant_de_tweets):
  tabla = [] # crea una lista vacía llamada tabla
  if(cant_de_tweets > 0 and usuario != "" ):
      try:
          # Buscar la información del perfil de usuario
          user = api.get_user(screen_name=usuario)
          # utiliza la API de Twitter para buscar tweets en español de un usuario de Twitter
          tweets = api.user_timeline(screen_name = usuario,tweet_mode="extended", count= cant_de_tweets)
          result = [] # crea una lista vacía llamada result
          # itera a través de cada tweet en la lista de tweets encontrados
          for tweet in tweets:
              # si el tweet comienza con 'RT' o si el tweet está vacío, continúa con el siguiente tweet
              if (tweet.full_text.startswith('RT')):
                  continue
              else:
                  text = tweet.full_text
                  #Try Catch para detectar que sean del idioma español
                  try:
                      language = detect(text)
                      if language == 'es':
                          #Limpieza de cada tweet extraido
                          datos=limpieza_datos(text)
                          if datos == "":
                              continue
                          else:
                              #Predicción del modelo Transformer para cada tweet
                              prediction = pipeline_nlp(datos)
                              for predic in prediction:
                                   # Agrega la etiqueta de Tweets, Predicción y Probabilidad en una lista y la agrega a la lista de resultados
                                etiqueta = {'Tweets': datos, 'Prediccion': predic['label'], 'Probabilidad': predic['score']}
                                result.append(etiqueta)
                  except:
                      pass   
           # Crea un dataframe a partir de la lista de resultados
          df = pd.DataFrame(result)
           # Si el dataframe está vacío, muestra un mensaje de que no se encontraron tweets sexistas dentro de la localidad
          if df.empty:
              muestra= st.text("No hay tweets Sexistas a analizar")
              tabla.append(muestra)
           
          else:
               # ordena el dataframe por Predicción y Probabilidad en orden descendente
              df.sort_values(by=['Prediccion', 'Probabilidad'], ascending=[False, False], inplace=True)
              df['Prediccion'] = np.where(df['Prediccion'] == 'LABEL_1', 'Sexista', 'No Sexista')
              df['Probabilidad'] = df['Probabilidad'].apply(lambda x: round(x, 3))
              muestra = st.table(df.reset_index(drop=True).head(50).style.applymap(color_survived, subset=['Prediccion']))
              tabla.append(muestra)
      except Exception as e:
          #Try except para detectar si la cuenta es existente
          muestra = st.text(f"La cuenta {usuario} no existe.") 
          tabla.append(muestra) 
  else:
      #Condición que se solicita cuando no se llenaron los datos para la busqueda
      muestra= st.text("Ingrese los parametros correspondientes")
      tabla.append(muestra)      
  return tabla

#Función para buscar por localidad
def tweets_localidad(buscar_localidad):
    tabla = []  # crea una lista vacía llamada tabla
    try:
        #Crea un objeto Geolocalizador y busca la ubicación que se encuentra en el parámetro buscar_localidad
        geolocator = Nominatim(user_agent="nombre_del_usuario")
        location = geolocator.geocode(buscar_localidad)
        radius = "15km" #establece el radio de búsqueda en 15km
         # utiliza la API de Twitter para buscar tweets en español cerca de la ubicación encontrada
        tweets = api.search_tweets(q="",lang="es",geocode=f"{location.latitude},{location.longitude},{radius}", count = 100, tweet_mode="extended")
        result = [] # crea una lista vacía llamada result
        # itera a través de cada tweet en la lista de tweets encontrados
        for tweet in tweets:
            # si el tweet comienza con 'RT' o si el tweet está vacío, continúa con el siguiente tweet
            if (tweet.full_text.startswith('RT')):
                continue
            elif not tweet.full_text.strip():
                continue
            else:
                # Limpia los datos del tweet y realiza una predicción sobre si es sexista o no
                datos = limpieza_datos(tweet.full_text)
                prediction = pipeline_nlp(datos)
                for predic in prediction:
                    # Agrega la etiqueta de Tweets, Predicción y Probabilidad en una lista y la agrega a la lista de resultados
                    etiqueta = {'Tweets': datos,'Prediccion': predic['label'], 'Probabilidad': predic['score']}
                    result.append(etiqueta)
        # Crea un dataframe a partir de la lista de resultados
        df = pd.DataFrame(result)
        # Si el dataframe está vacío, muestra un mensaje de que no se encontraron tweets sexistas dentro de la localidad
        if df.empty:
            muestra=st.text("No se encontraron tweets sexistas dentro de la localidad")
            tabla.append(muestra)
        else:
            #tabla.append(muestra)
            # ordena el dataframe por Predicción y Probabilidad en orden descendente
            df.sort_values(by=['Prediccion', 'Probabilidad'], ascending=[False, False], inplace=True)
            # convierte la columna de Predicción en "Sexista" o "No Sexista" y redondea la columna de Probabilidad a 3 decimales
            df['Prediccion'] = np.where(df['Prediccion'] == 'LABEL_1', 'Sexista', 'No Sexista')
            df['Probabilidad'] = df['Probabilidad'].round(3)
            # Obtener los datos con probabilidad mayor a 0.50
            df = df[df['Probabilidad'] > 0.50]
            # Obtener los 3 primeros datos con mayor probabilidad sexista
            sexista_df = df[df['Prediccion'] == 'Sexista'].head(3)
            
            # Obtener los 3 primeros datos con mayor probabilidad no sexista
            no_sexista_df = df[df['Prediccion'] == 'No Sexista'].head(3)
            
            # Concatenar ambos dataframes
            muestra_df = pd.concat([sexista_df, no_sexista_df], axis=0)
            col1, col2 = st.columns(2)
            with col1:
                #lista de Tweets mostrados en pantalla
                muestra = st.table(muestra_df.reset_index(drop=True).head(6).style.applymap(color_survived, subset=['Prediccion']))
            with col2:
                resultado = df['Prediccion'].value_counts()
                def autopct_fun(abs_values):
                    gen = iter(abs_values)
                    return lambda pct: f"{pct:.1f}% ({next(gen)})"
                #Gráfico
                colores=["#aae977","#EE3555"]
                fig, ax = plt.subplots()
                fig.set_size_inches(2,2)
                plt.pie(resultado,labels=resultado.index,autopct=autopct_fun(resultado),colors=colores,  textprops={'fontsize': 5})
                ax.set_title("Porcentajes por Categorias en la localidad\n"+buscar_localidad.capitalize(), fontsize=5, fontweight="bold")
                plt.rcParams.update({'font.size':4, 'font.weight':'bold'})
                ax.legend()
                # Muestra el gráfico
                plt.show()
                st.set_option('deprecation.showPyplotGlobalUse', False)
                st.pyplot()

    except AttributeError as e:
        muestra=st.text("No existe ninguna localidad con ese nombre") 
        tabla.append(muestra)
                 
    return tabla 
    
   
def analizar_frase(frase):
    language = detect(frase)
    if frase == "":
        tabla = st.text("Ingrese una frase")
        #st.text("Ingrese una frase")
    elif language == 'es':
        predictions = pipeline_nlp(frase)
        # convierte las predicciones en una lista de diccionarios
        data = [{'Texto': frase, 'Prediccion': prediction['label'], 'Probabilidad': prediction['score']} for prediction in predictions]
        # crea un DataFrame a partir de la lista de diccionarios
        df = pd.DataFrame(data)
        df['Prediccion'] = np.where( df['Prediccion'] == 'LABEL_1', 'Sexista', 'No Sexista')
        # muestra el DataFrame
        tabla = st.table(df.reset_index(drop=True).head(1).style.applymap(color_survived, subset=['Prediccion']))
    else:
        tabla = st.text("Solo Frase en español")
        
    return tabla
    
#Función para correr el formulario
def run():
    # Se inicia una sesión Streamlit
    with st.form("my_form"):
         # Se solicita al usuario que ingrese la búsqueda que desea realizar
        search_words = st.text_input("Introduzca la frase, el usuario o localidad para analizar y pulse el check correspondiente")
         # Se solicita al usuario que ingrese la cantidad de tweets que desea analizar (máximo 50)
        number_of_tweets = st.number_input('Introduzca número de tweets a analizar del usuario Máximo 50', 0,50,0)
        st.write("Escoja una Opción:")
        # Se presenta al usuario las opciones de búsqueda (frase, usuario o localidad) mediante checkboxes
        termino=st.checkbox('Frase')
        usuario=st.checkbox('Usuario')
        localidad=st.checkbox('Localidad')
         # Se agrega un botón de envío para que el usuario pueda iniciar la búsqueda
        submit_button = st.form_submit_button(label='Analizar')
        # Se define una variable de error y se inicializa como falso
        error =False

         # Si el usuario hace clic en el botón de envío, se comprueban los checkboxes y se establece un mensaje de error si es necesario
        if submit_button:
            # Condición para el caso de que esten dos check seleccionados
            if ( termino == False and usuario == False and localidad == False):
                st.text('Error no se ha seleccionado ningun check')
                error=True
            elif ( termino == True and usuario == True and localidad == True):
                st.text('Error se han seleccionado varios check')
                error=True
       
        # Si no se ha producido ningún error, se inicia la búsqueda según la opción seleccionada           
        if (error == False):
          if (termino):
              analizar_frase(search_words)
          elif (usuario):
              tweets_usuario(search_words,number_of_tweets)
          elif (localidad):
              tweets_localidad(search_words)

# Se ejecuta la función run
run()