Science4Insights commited on
Commit
a465ce2
·
verified ·
1 Parent(s): 49d0e54

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -15
app.py CHANGED
@@ -4,7 +4,7 @@ import numpy as np
4
  import tempfile
5
  import os
6
  from typing import Dict
7
- import openai
8
  from tqdm.auto import tqdm
9
  import plotly.express as px
10
  import plotly.graph_objects as go
@@ -79,26 +79,36 @@ def crear_grafico_frecuencias(df_resultados):
79
  """
80
  Crea gráficos de frecuencias para las respuestas de la encuesta
81
  """
 
82
  columnas_respuestas = [col for col in df_resultados.columns if col.startswith('P')]
83
 
84
  if not columnas_respuestas:
85
  return None
86
 
 
87
  n_preguntas = len(columnas_respuestas)
88
- n_rows = (n_preguntas + 1) // 2
89
  fig = make_subplots(rows=n_rows, cols=2,
90
  subplot_titles=columnas_respuestas,
91
  vertical_spacing=0.1,
92
  horizontal_spacing=0.1)
93
 
 
94
  for i, pregunta in enumerate(columnas_respuestas):
 
95
  freq = df_resultados[pregunta].value_counts()
 
 
96
  porcentajes = (freq / len(df_resultados) * 100).round(1)
 
 
97
  text = [f"{v} ({p}%)" for v, p in zip(freq.values, porcentajes.values)]
98
 
 
99
  row = (i // 2) + 1
100
  col = (i % 2) + 1
101
 
 
102
  fig.add_trace(
103
  go.Bar(
104
  x=freq.index,
@@ -111,9 +121,11 @@ def crear_grafico_frecuencias(df_resultados):
111
  col=col
112
  )
113
 
 
114
  fig.update_xaxes(title_text="Respuestas", row=row, col=col)
115
  fig.update_yaxes(title_text="Frecuencia", row=row, col=col)
116
 
 
117
  fig.update_layout(
118
  title_text="Resultados de la Encuesta",
119
  showlegend=False,
@@ -135,49 +147,61 @@ def procesar_respuesta_api(respuesta):
135
  return responses
136
 
137
  def llamar_api_gpt(descripcion, encuesta, api_key):
138
- openai.api_key = api_key
139
 
140
  system_content = f"Eres una persona que responde a estas características: \n\n{descripcion}\n\nTu misión consiste en responder a una encuesta. Tu respuesta debe tener este formato. Número de pregunta y opción. Por ejemplo, P1. a), P2. b), etcétera.\n\nEs muy importante que respondas poniéndote en el rol que se te ha dado. No añadas espacios ni saltos de página. La encuesta es: "
141
 
142
  try:
143
- response = openai.ChatCompletion.create(
144
  model="gpt-4o",
145
  messages=[
146
- {"role": "system", "content": system_content},
147
- {"role": "user", "content": encuesta}
 
 
 
 
 
 
148
  ],
 
149
  temperature=0,
150
- max_tokens=200,
151
  top_p=1,
152
  frequency_penalty=0,
153
  presence_penalty=0
154
  )
155
- return response.choices[0].message['content'], None
156
  except Exception as e:
157
  return None, str(e)
158
-
159
  def generar_entrevistado(config: Dict[str, Dict[str, float]], n: int) -> pd.DataFrame:
160
  """
161
- Genera un DataFrame con 'n' panelistas
 
 
162
  """
163
  data = []
164
  for i in range(n):
165
  entrevistado = {"id_panelista": f"ID_{i+1:04d}"}
166
 
 
167
  for var, opciones in VARIABLES.items():
168
  if var in config:
169
- dist = list(config[var].values())
170
  suma = sum(dist)
171
  if suma > 0:
172
- dist = [x / suma for x in dist]
173
  else:
 
174
  dist = [1/len(opciones)] * len(opciones)
175
  valor = np.random.choice(opciones, p=dist)
176
  else:
 
177
  valor = np.random.choice(opciones)
178
 
179
  entrevistado[var] = valor
180
 
 
181
  desc = (
182
  f"Se trata de una persona que ha nacido en {entrevistado['Lugar de nacimiento']}, de sexo {entrevistado['Sexo']} que pertenece a la Generación {entrevistado['Generación']}, "
183
  f"que reside en {entrevistado['Comunidad Autónoma']} (España), en un municipio de hábitat {entrevistado['Hábitat']}. "
@@ -193,7 +217,6 @@ def generar_entrevistado(config: Dict[str, Dict[str, float]], n: int) -> pd.Data
193
 
194
  df = pd.DataFrame(data)
195
  return df
196
-
197
  def lanzar_encuesta(df, texto_encuesta, api_key, progress=gr.Progress()):
198
  if not api_key:
199
  return None, None, "Error: Es necesario introducir una API Key de OpenAI", None
@@ -204,18 +227,23 @@ def lanzar_encuesta(df, texto_encuesta, api_key, progress=gr.Progress()):
204
  if not texto_encuesta:
205
  return None, None, "Error: Debes introducir el texto de la encuesta", None
206
 
 
207
  resultados = []
208
  errores = []
209
 
 
210
  total_panelistas = len(df)
211
  progress(0, desc=f"Iniciando encuesta para {total_panelistas} panelistas...")
212
 
 
213
  variables_perfil = [col for col in df.columns if col != 'Descripción']
214
 
 
215
  for i, (_, row) in enumerate(df.iterrows()):
216
  progress((i/total_panelistas),
217
  desc=f"Procesando panelista {i+1} de {total_panelistas}")
218
 
 
219
  respuestas, error = llamar_api_gpt(
220
  row['Descripción'],
221
  texto_encuesta,
@@ -227,8 +255,10 @@ def lanzar_encuesta(df, texto_encuesta, api_key, progress=gr.Progress()):
227
  continue
228
 
229
  try:
 
230
  resp_dict = procesar_respuesta_api(respuestas)
231
 
 
232
  for var in variables_perfil:
233
  resp_dict[var] = row[var]
234
 
@@ -236,6 +266,7 @@ def lanzar_encuesta(df, texto_encuesta, api_key, progress=gr.Progress()):
236
  except Exception as e:
237
  errores.append(f"Error procesando respuesta de {row['id_panelista']}: {str(e)}")
238
 
 
239
  total_exitosos = len(resultados)
240
  total_errores = len(errores)
241
 
@@ -254,11 +285,15 @@ def lanzar_encuesta(df, texto_encuesta, api_key, progress=gr.Progress()):
254
  mensaje_resumen += f"<li>{error}</li>"
255
  mensaje_resumen += "</ul>"
256
 
 
257
  if not resultados:
258
  return None, None, mensaje_resumen, None
259
 
 
260
  df_resultados = pd.DataFrame(resultados)
261
 
 
 
262
  columnas_respuestas = [col for col in df_resultados.columns
263
  if col not in variables_perfil]
264
  orden_columnas = ['id_panelista'] + \
@@ -267,30 +302,35 @@ def lanzar_encuesta(df, texto_encuesta, api_key, progress=gr.Progress()):
267
 
268
  df_resultados = df_resultados[orden_columnas]
269
 
 
270
  fig = crear_grafico_frecuencias(df_resultados)
271
 
 
272
  with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
273
  df_resultados.to_excel(tmp.name, index=False)
274
  file_path = tmp.name
275
 
276
  progress(1.0, desc="¡Proceso completado!")
277
  return df_resultados, file_path, mensaje_resumen, fig
278
-
279
  def crear_interfaz():
280
  with gr.Blocks(title="Generador de Panelistas Sintéticos") as app:
281
  gr.Markdown("## 🧴 Generador de Consumidores de Cremas Antiarrugas")
282
 
 
283
  api_key = gr.Textbox(
284
  label="OpenAI API Key",
285
  placeholder="Introduce tu API Key de OpenAI...",
286
  type="password"
287
  )
288
 
 
289
  n_muestra = gr.Number(label="Tamaño de la muestra", value=100, precision=0)
290
 
 
291
  estado = gr.Textbox(label="Estado del proceso", value="Esperando acción...")
292
  progreso = gr.Progress()
293
 
 
294
  all_checkboxes = []
295
  all_sliders = []
296
  vars_options = []
@@ -300,12 +340,14 @@ def crear_interfaz():
300
  with gr.Group():
301
  gr.Markdown(f"### {var}")
302
 
 
303
  var_checkbox = gr.Checkbox(
304
  label=f"Personalizar {var}",
305
  value=False
306
  )
307
  all_checkboxes.append(var_checkbox)
308
 
 
309
  for op in opciones:
310
  s = gr.Slider(
311
  minimum=0,
@@ -316,11 +358,14 @@ def crear_interfaz():
316
  )
317
  all_sliders.append(s)
318
 
 
319
  vars_options.append((var, opciones))
320
 
 
321
  output_table = gr.Dataframe(label="Panelistas Generados")
322
  download_file = gr.File(label="Descargar Excel", file_count="single")
323
 
 
324
  with gr.Accordion("Realizar Encuesta", open=False):
325
  encuesta_texto = gr.Textbox(
326
  label="Pegue aquí el texto de la encuesta",
@@ -330,27 +375,40 @@ def crear_interfaz():
330
  info_encuesta = gr.HTML(label="Información de la encuesta")
331
  btn_encuesta = gr.Button("Lanzar Encuesta")
332
 
 
333
  with gr.Tab("Resultados"):
334
  resultados_encuesta = gr.Dataframe(label="Resultados Detallados")
335
  download_resultados = gr.File(label="Descargar Resultados Excel")
336
 
 
337
  with gr.Tab("Gráficos"):
338
  graficos_resultados = gr.Plot(label="Gráficos de Frecuencias")
339
 
 
340
  btn_generar = gr.Button("Generar Muestra", variant="primary")
341
 
342
  def generar(n, *values):
 
 
 
 
 
 
343
  n_muestra_int = int(n)
344
  num_vars = len(vars_options)
345
 
 
346
  checks = values[:num_vars]
 
347
  sliders = values[num_vars:]
348
 
349
  config_procesada = {}
350
  idx_slider = 0
351
 
 
352
  for i, (var, opciones) in enumerate(vars_options):
353
  if checks[i]:
 
354
  dist = {}
355
  for op in opciones:
356
  valor_slider = sliders[idx_slider]
@@ -358,16 +416,20 @@ def crear_interfaz():
358
  dist[op] = valor_slider / 100.0
359
  config_procesada[var] = dist
360
  else:
 
361
  idx_slider += len(opciones)
362
 
 
363
  df = generar_entrevistado(config_procesada, n_muestra_int)
364
 
 
365
  with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
366
  df.to_excel(tmp.name, index=False)
367
  file_path = tmp.name
368
 
369
  return df, file_path, "Muestra generada correctamente"
370
 
 
371
  btn_generar.click(
372
  fn=generar,
373
  inputs=[n_muestra] + all_checkboxes + all_sliders,
@@ -382,4 +444,7 @@ def crear_interfaz():
382
 
383
  return app
384
 
385
- app = crear_interfaz()
 
 
 
 
4
  import tempfile
5
  import os
6
  from typing import Dict
7
+ from openai import OpenAI
8
  from tqdm.auto import tqdm
9
  import plotly.express as px
10
  import plotly.graph_objects as go
 
79
  """
80
  Crea gráficos de frecuencias para las respuestas de la encuesta
81
  """
82
+ # Identificar las columnas de respuestas (empiezan con 'P')
83
  columnas_respuestas = [col for col in df_resultados.columns if col.startswith('P')]
84
 
85
  if not columnas_respuestas:
86
  return None
87
 
88
+ # Crear una figura con subplots
89
  n_preguntas = len(columnas_respuestas)
90
+ n_rows = (n_preguntas + 1) // 2 # Dos gráficos por fila
91
  fig = make_subplots(rows=n_rows, cols=2,
92
  subplot_titles=columnas_respuestas,
93
  vertical_spacing=0.1,
94
  horizontal_spacing=0.1)
95
 
96
+ # Para cada pregunta
97
  for i, pregunta in enumerate(columnas_respuestas):
98
+ # Calcular frecuencias
99
  freq = df_resultados[pregunta].value_counts()
100
+
101
+ # Calcular porcentajes
102
  porcentajes = (freq / len(df_resultados) * 100).round(1)
103
+
104
+ # Crear texto para las barras (frecuencia y porcentaje)
105
  text = [f"{v} ({p}%)" for v, p in zip(freq.values, porcentajes.values)]
106
 
107
+ # Determinar posición en el subplot
108
  row = (i // 2) + 1
109
  col = (i % 2) + 1
110
 
111
+ # Añadir barra
112
  fig.add_trace(
113
  go.Bar(
114
  x=freq.index,
 
121
  col=col
122
  )
123
 
124
+ # Actualizar layout del subplot
125
  fig.update_xaxes(title_text="Respuestas", row=row, col=col)
126
  fig.update_yaxes(title_text="Frecuencia", row=row, col=col)
127
 
128
+ # Actualizar layout general
129
  fig.update_layout(
130
  title_text="Resultados de la Encuesta",
131
  showlegend=False,
 
147
  return responses
148
 
149
  def llamar_api_gpt(descripcion, encuesta, api_key):
150
+ client = OpenAI(api_key=api_key)
151
 
152
  system_content = f"Eres una persona que responde a estas características: \n\n{descripcion}\n\nTu misión consiste en responder a una encuesta. Tu respuesta debe tener este formato. Número de pregunta y opción. Por ejemplo, P1. a), P2. b), etcétera.\n\nEs muy importante que respondas poniéndote en el rol que se te ha dado. No añadas espacios ni saltos de página. La encuesta es: "
153
 
154
  try:
155
+ response = client.chat.completions.create(
156
  model="gpt-4o",
157
  messages=[
158
+ {
159
+ "role": "system",
160
+ "content": [{"type": "text", "text": system_content}]
161
+ },
162
+ {
163
+ "role": "user",
164
+ "content": [{"type": "text", "text": encuesta}]
165
+ }
166
  ],
167
+ response_format={"type": "text"},
168
  temperature=0,
169
+ max_completion_tokens=200,
170
  top_p=1,
171
  frequency_penalty=0,
172
  presence_penalty=0
173
  )
174
+ return response.choices[0].message.content, None
175
  except Exception as e:
176
  return None, str(e)
 
177
  def generar_entrevistado(config: Dict[str, Dict[str, float]], n: int) -> pd.DataFrame:
178
  """
179
+ Genera un DataFrame con 'n' panelistas. Para cada variable de VARIABLES:
180
+ - Si está en 'config', usa su distribución personalizada
181
+ - Si no está, usa distribución uniforme
182
  """
183
  data = []
184
  for i in range(n):
185
  entrevistado = {"id_panelista": f"ID_{i+1:04d}"}
186
 
187
+ # Asignar valor a cada variable
188
  for var, opciones in VARIABLES.items():
189
  if var in config:
190
+ dist = list(config[var].values()) # p.ej. [0.3, 0.5, 0.2]
191
  suma = sum(dist)
192
  if suma > 0:
193
+ dist = [x / suma for x in dist] # Normaliza
194
  else:
195
+ # Si suman 0, forzamos uniforme
196
  dist = [1/len(opciones)] * len(opciones)
197
  valor = np.random.choice(opciones, p=dist)
198
  else:
199
+ # Si no está en config => distribución uniforme
200
  valor = np.random.choice(opciones)
201
 
202
  entrevistado[var] = valor
203
 
204
+ # Crear una descripción completa de la persona
205
  desc = (
206
  f"Se trata de una persona que ha nacido en {entrevistado['Lugar de nacimiento']}, de sexo {entrevistado['Sexo']} que pertenece a la Generación {entrevistado['Generación']}, "
207
  f"que reside en {entrevistado['Comunidad Autónoma']} (España), en un municipio de hábitat {entrevistado['Hábitat']}. "
 
217
 
218
  df = pd.DataFrame(data)
219
  return df
 
220
  def lanzar_encuesta(df, texto_encuesta, api_key, progress=gr.Progress()):
221
  if not api_key:
222
  return None, None, "Error: Es necesario introducir una API Key de OpenAI", None
 
227
  if not texto_encuesta:
228
  return None, None, "Error: Debes introducir el texto de la encuesta", None
229
 
230
+ # Lista para almacenar resultados
231
  resultados = []
232
  errores = []
233
 
234
+ # Información inicial
235
  total_panelistas = len(df)
236
  progress(0, desc=f"Iniciando encuesta para {total_panelistas} panelistas...")
237
 
238
+ # Variables de perfil que queremos mantener (todas excepto 'Descripción')
239
  variables_perfil = [col for col in df.columns if col != 'Descripción']
240
 
241
+ # Para cada panelista
242
  for i, (_, row) in enumerate(df.iterrows()):
243
  progress((i/total_panelistas),
244
  desc=f"Procesando panelista {i+1} de {total_panelistas}")
245
 
246
+ # Llamar a la API
247
  respuestas, error = llamar_api_gpt(
248
  row['Descripción'],
249
  texto_encuesta,
 
255
  continue
256
 
257
  try:
258
+ # Procesar respuestas
259
  resp_dict = procesar_respuesta_api(respuestas)
260
 
261
+ # Añadir todas las variables de perfil del panelista
262
  for var in variables_perfil:
263
  resp_dict[var] = row[var]
264
 
 
266
  except Exception as e:
267
  errores.append(f"Error procesando respuesta de {row['id_panelista']}: {str(e)}")
268
 
269
+ # Crear mensaje de resumen
270
  total_exitosos = len(resultados)
271
  total_errores = len(errores)
272
 
 
285
  mensaje_resumen += f"<li>{error}</li>"
286
  mensaje_resumen += "</ul>"
287
 
288
+ # Si no hay ningún resultado exitoso
289
  if not resultados:
290
  return None, None, mensaje_resumen, None
291
 
292
+ # Crear DataFrame de resultados
293
  df_resultados = pd.DataFrame(resultados)
294
 
295
+ # Reordenar las columnas para mejor visualización
296
+ # Primero el ID, luego las variables de perfil, finalmente las respuestas
297
  columnas_respuestas = [col for col in df_resultados.columns
298
  if col not in variables_perfil]
299
  orden_columnas = ['id_panelista'] + \
 
302
 
303
  df_resultados = df_resultados[orden_columnas]
304
 
305
+ # Crear gráficos de frecuencias
306
  fig = crear_grafico_frecuencias(df_resultados)
307
 
308
+ # Guardar a Excel
309
  with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
310
  df_resultados.to_excel(tmp.name, index=False)
311
  file_path = tmp.name
312
 
313
  progress(1.0, desc="¡Proceso completado!")
314
  return df_resultados, file_path, mensaje_resumen, fig
 
315
  def crear_interfaz():
316
  with gr.Blocks(title="Generador de Panelistas Sintéticos") as app:
317
  gr.Markdown("## 🧴 Generador de Consumidores de Cremas Antiarrugas")
318
 
319
+ # API Key de OpenAI
320
  api_key = gr.Textbox(
321
  label="OpenAI API Key",
322
  placeholder="Introduce tu API Key de OpenAI...",
323
  type="password"
324
  )
325
 
326
+ # Tamaño de la muestra
327
  n_muestra = gr.Number(label="Tamaño de la muestra", value=100, precision=0)
328
 
329
+ # Variables para el estado y progreso
330
  estado = gr.Textbox(label="Estado del proceso", value="Esperando acción...")
331
  progreso = gr.Progress()
332
 
333
+ # Listas donde acumulamos todos los checkboxes y sliders
334
  all_checkboxes = []
335
  all_sliders = []
336
  vars_options = []
 
340
  with gr.Group():
341
  gr.Markdown(f"### {var}")
342
 
343
+ # Checkbox: si se quiere personalizar la distribución de esta variable
344
  var_checkbox = gr.Checkbox(
345
  label=f"Personalizar {var}",
346
  value=False
347
  )
348
  all_checkboxes.append(var_checkbox)
349
 
350
+ # Sliders para cada opción de la variable
351
  for op in opciones:
352
  s = gr.Slider(
353
  minimum=0,
 
358
  )
359
  all_sliders.append(s)
360
 
361
+ # Guardar la info para reconstruir 'config' después
362
  vars_options.append((var, opciones))
363
 
364
+ # Salidas iniciales
365
  output_table = gr.Dataframe(label="Panelistas Generados")
366
  download_file = gr.File(label="Descargar Excel", file_count="single")
367
 
368
+ # Nueva sección para la encuesta
369
  with gr.Accordion("Realizar Encuesta", open=False):
370
  encuesta_texto = gr.Textbox(
371
  label="Pegue aquí el texto de la encuesta",
 
375
  info_encuesta = gr.HTML(label="Información de la encuesta")
376
  btn_encuesta = gr.Button("Lanzar Encuesta")
377
 
378
+ # Sección de resultados de la encuesta
379
  with gr.Tab("Resultados"):
380
  resultados_encuesta = gr.Dataframe(label="Resultados Detallados")
381
  download_resultados = gr.File(label="Descargar Resultados Excel")
382
 
383
+ # Nueva pestaña para los gráficos
384
  with gr.Tab("Gráficos"):
385
  graficos_resultados = gr.Plot(label="Gráficos de Frecuencias")
386
 
387
+ # Botón generar muestra
388
  btn_generar = gr.Button("Generar Muestra", variant="primary")
389
 
390
  def generar(n, *values):
391
+ """
392
+ Recibe:
393
+ - n (número)
394
+ - un bloque de checkboxes (uno por variable)
395
+ - un bloque de sliders (uno por opción de cada variable)
396
+ """
397
  n_muestra_int = int(n)
398
  num_vars = len(vars_options)
399
 
400
+ # Los primeros 'num_vars' valores son bool de checkboxes
401
  checks = values[:num_vars]
402
+ # El resto son floats de sliders
403
  sliders = values[num_vars:]
404
 
405
  config_procesada = {}
406
  idx_slider = 0
407
 
408
+ # Recorremos cada variable
409
  for i, (var, opciones) in enumerate(vars_options):
410
  if checks[i]:
411
+ # Personalizar
412
  dist = {}
413
  for op in opciones:
414
  valor_slider = sliders[idx_slider]
 
416
  dist[op] = valor_slider / 100.0
417
  config_procesada[var] = dist
418
  else:
419
+ # No se personaliza => avanzamos el índice de sliders sin usarlos
420
  idx_slider += len(opciones)
421
 
422
+ # Generar DF
423
  df = generar_entrevistado(config_procesada, n_muestra_int)
424
 
425
+ # Guardar a Excel en un archivo temporal
426
  with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
427
  df.to_excel(tmp.name, index=False)
428
  file_path = tmp.name
429
 
430
  return df, file_path, "Muestra generada correctamente"
431
 
432
+ # Conectar los botones
433
  btn_generar.click(
434
  fn=generar,
435
  inputs=[n_muestra] + all_checkboxes + all_sliders,
 
444
 
445
  return app
446
 
447
+ # 4) Lanzar la app
448
+ if __name__ == "__main__":
449
+ app = crear_interfaz()
450
+ app.launch()