Exemplo n.º 1
0
    def iniciar_evaluacion(self):
        algoritmo = self.comboBoxAlgoritmo.currentText()

        if self.es_regresion:
            if algoritmo == "One R" or algoritmo == "Naive Bayes":
                QMessageBox.critical(self, "Error", \
                    "El algoritmo seleccionado no funciona con problemas de regresión")
                return

        if algoritmo == "One R" and np.dtype(
                np.number) in self.data.dtypes.values:
            QMessageBox.critical(self, "Error", \
                    "One R solo funciona con todas las columnas categóricas")
            return

        self.btnAceptar.setEnabled(False)  # desactiva boton
        self.labelCargando.setVisible(True)
        self.tablaResultados.setModel(None)
        self.labelAlgoritmo.setText("")
        self.repaint()  # para que se actualice la etiqueta

        # def main_HoldOut(data, target, bandera_cat_num, algoritmo, iteraciones, arrayOrden):

        if self.es_regresion == False:
            exactitudFinal, dataframeFinal = main_HoldOut(
                self.data, self.target, self.es_regresion, algoritmo,
                self.numeroIteraciones.value(), self.arrayOrden)
            msg = "{}, exactitud promedio: {}".format(
                self.comboBoxAlgoritmo.currentText(), exactitudFinal)
            self.labelAlgoritmo.setText(msg)
            self.tablaResultados.setModel(TableModelPandas(dataframeFinal))

        # def numerico_HoldOut(data, target, bandera_cat_num, algoritmo, iteraciones):

        else:
            dataframeFinal = numerico_HoldOut(self.data, self.target,
                                              self.numeroIteraciones.value())
            self.tablaResultados.setModel(TableModelPandas(dataframeFinal))
        """
        positivo, negativo = None, None
        if not self.es_multi_clase and not self.es_regresion:
            index_val_positivo = self.comboBoxValorPositivo.currentIndex()
            index_val_negativo = 0 if index_val_positivo == 1 else 1
            val_positivo = self.comboBoxValorPositivo.itemText(index_val_positivo)
            val_negativo = self.comboBoxValorPositivo.itemText(index_val_negativo)

            positivo = val_positivo
            negativo = val_negativo
        
        k_fold = KFoldCrossValidation(self.data, self.target, num_folds,
            algoritmo, positivo, negativo)
        resultado = k_fold.iniciar_validacion()
        """

        #revisar TODO: Here
        #self.mostrar_tablas(resultado)
        self.btnAceptar.setEnabled(True)
        self.labelCargando.setVisible(False)
Exemplo n.º 2
0
    def actualizar_model(self, dataframe):
        self.conjunto.panda = dataframe
        self.model = TableModelPandas(self.conjunto.panda)
        self.tabla.setModel(self.model)

        for atributo in self.conjunto.getAtributos():
            atributo.panda = self.conjunto.panda

        self.actualizar_etiquetas()
Exemplo n.º 3
0
    def mostrar_tablas(self, resultado):
        if self.es_multi_clase and not self.es_regresion:
            tabla = resultado[0]
            exactitud = resultado[1]

            msg = "{}, exactitud promedio: {}".format(
                self.comboBoxAlgoritmo.currentText(), exactitud)
            self.labelAlgoritmo.setText(msg)
            self.tablaResultados.setModel(TableModelPandas(tabla))
        else:
            self.labelAlgoritmo.setText("")
            self.tablaResultados.setModel(TableModelPandas(resultado))
Exemplo n.º 4
0
    def mostrar_tablas_verosimilitudes(self):
        for atributo in self.naive.probabilidades:
            label = QLabel(self)
            label.setText(atributo)
            self.verticalLayout_7.addWidget(label)

            tabla = QTableView(self.scrollAreaWidgetContents_2)
            tabla.setMinimumSize(QtCore.QSize(0, 120))
            modelo = TableModelPandas(self.naive.probabilidades[atributo])
            tabla.setModel(modelo)

            self.verticalLayout_7.addWidget(tabla)
Exemplo n.º 5
0
    def mostrar_tablas_frecuencias(self):
        frecuencias = self.naive.frecuencias
        for atributo in frecuencias:
            label = QLabel(self)
            label.setText(atributo)
            self.verticalLayout_6.addWidget(label)

            tabla = QTableView(self.scrollAreaWidgetContents)
            tabla.setMinimumSize(QtCore.QSize(0, 120))
            modelo = TableModelPandas(frecuencias[atributo])
            tabla.setModel(modelo)

            self.verticalLayout_6.addWidget(tabla)

        label = QLabel(self)
        label.setText(self.target)
        self.verticalLayout_6.addWidget(label)

        tabla = QTableView(self.scrollAreaWidgetContents)
        tabla.setMinimumSize(QtCore.QSize(0, 120))
        modelo = TableModelPandas(self.naive.frec_target)
        tabla.setModel(modelo)

        self.verticalLayout_6.addWidget(tabla)
Exemplo n.º 6
0
    def cargar_tablas_frecuencias(self):
        frecuencias = one_r.generar_frecuencias(self.data, self.target)

        for i in frecuencias:
            label = QLabel(self)
            label.setText(i)
            self.verticalLayout_4.addWidget(label)

            tabla = QTableView(self.scrollAreaWidgetContents)
            tabla.setMinimumSize(QtCore.QSize(0, 200))
            modelo = TableModelPandas(frecuencias[i])
            tabla.setModel(modelo)

            self.verticalLayout_4.addWidget(tabla)

        self.cargar_reglas(frecuencias)
Exemplo n.º 7
0
    def procesar_instancia(self):
        """ Se ejecuta al presionar el boton aceptar """
        self.tablaDistancias.setModel(None)  # limpia la tabla

        lista_valores = self.obtener_valores_campos()

        if "" in lista_valores:
            QMessageBox.critical(self, "Error",
                                 "Deben llenarse todos los campos")
        else:
            self.knn.set_k(self.spinBoxK.value())

            # Falta verificar que los datos sean del tipo correcto
            resultado, distancias = self.knn.get_prediccion(lista_valores)
            self.labelResultado.setText(str(resultado))

            model = TableModelPandas(distancias)
            self.tablaDistancias.setModel(model)
Exemplo n.º 8
0
    def iniciar_algoritmo(self):
        self.btnAceptar.setEnabled(False) # desactiva boton
        self.tablaResultado.setModel(None)
        self.labelPromedioSilhouette.setText("")
        self.repaint() # para que se actualice la etiqueta

        k = self.spinBoxK.value()
        corridas = self.spinBoxCorridas.value()
        iteraciones = self.spinBoxIteraciones.value()

        self.kmeans.setCorridas(corridas)
        self.kmeans.setIteraciones(iteraciones)

        resultados = self.kmeans.generar_clusters(n_clusters=k)
        self.data["Cluster"] = resultados[0]
        self.data["Silhouette"] = resultados[1]
        promedio_silhouette = resultados[2]

        self.tablaResultado.setModel(TableModelPandas(self.data))
        self.labelPromedioSilhouette.setText("Silhouette score: " + \
            str(promedio_silhouette))

        self.btnAceptar.setEnabled(True)
Exemplo n.º 9
0
    def __init__(self, ruta, conexion=None, query=None, *args, **kwargs):
        QtWidgets.QMainWindow.__init__(self, *args, **kwargs)
        self.setupUi(self)

        self.conexion = conexion
        self.query = query

        self.ruta = ruta
        self.conjunto = ConjuntoDatos(self.ruta, self.conexion, self.query)

        # si hay una conexión y un query entonces mostrar la opción para cambiar el query
        if self.conexion != None and self.query != None:
            self.agregar_opcion_cambiar_query()


        self.respaldos = Respaldos(self.conjunto) # para hacer los respaldos
        self.num_version = 1 # numero de versión para el nombre de los respaldos
        self.num_instancias_agregadas = 0 # cada que se agregan 10 instancias se hace un respaldo
        self.num_instancias_eliminadas = 0 # cada que se eliminar 10 instancias se hacer un respaldo
        self.cargar_respaldos()

        # action nuevo para cargar nuevo dataset
        self.actionNuevo.triggered.connect(self.cargar_nuevo_dataset)

        # conectar eventos para las opciones de los algoritmos
        self.actionZero_R.triggered.connect(self.zeroR)
        self.actionOne_R.triggered.connect(self.mostrar_ventana_oneR)
        self.actionNaive_Bayes.triggered.connect(self.mostrar_ventana_naive_bayes)
        self.actionK_NN.triggered.connect(self.mostrar_ventana_knn)
        self.actionK_Means.triggered.connect(self.mostrar_ventana_kmeans)

        # conectar eventos para las opciones de evalución de algoritmos
        self.actionKFold.triggered.connect(self.mostrar_ventana_kfold)
        self.actionHoldOut.triggered.connect(self.mostrar_ventana_hold_out)

        # id de la instancia en la que se dio clic en la tabla
        self.currentIdRow = None

        # utilizando un modelo los datos en la tabla se cargan muchisimo más rápido
        self.model = TableModelPandas(self.conjunto.panda)
        self.tabla.setModel(self.model)        

        self.llenar_combo_boxes()
        self.mostrar_atributo() # muestra los datos del atributo seleccionado por defecto en el combo box

        #evento cuando se cambia de elemento en el combo box
        self.comboBoxAtributos.currentIndexChanged.connect(lambda x: self.mostrar_atributo())

        #evento boton actualizar atributo
        self.btnActualizar.clicked.connect(self.actualizar_atributo)

        # muestrar la información general del conjunto de datos
        self.labelNumInstancias.setText(str(self.conjunto.getNumInstancias()))
        self.labelNumAtributos.setText(str(self.conjunto.getNumAtributos()))
        
        self.iniciar_target()
        self.lineEditValorFaltante.setText(str(self.conjunto.getSimboloFaltante()))
        self.lineEditRuta.setText(str(self.conjunto.getRutaRespaldos()))

        # toolbar
        self.agregar_actions_toolbar()

        # evento boton eliminar atributo
        self.btnEliminarAtributo.clicked.connect(self.eliminar_atributo)

        # evento boton mostar moda
        self.btnModas.clicked.connect(self.mostrar_ventanas_modas)

        # evento boton descripcion
        self.btnDescripcion.clicked.connect(self.mostrar_descripcion)

        # evento boton valores fuera de dominio
        self.btnFueraDominio.clicked.connect(self.mostrar_fuera_dominio)
        # evento boton valores faltantes
        self.btnFaltantes.clicked.connect(self.mostrar_val_faltantes)

        self.tabla.setSelectionMode(QAbstractItemView.SingleSelection)

        # este menú se muestra al dar clic sobre un id en la tabla
        self.menu_clic_tabla = QMenu("opciones")
        self.action_editar = QAction(QtGui.QIcon('iconos/editar.png'), "Editar")
        self.action_editar.triggered.connect(self.mostrar_editar_instancia)
        self.menu_clic_tabla.addAction(self.action_editar)
        self.tabla.verticalHeader().sectionPressed.connect(self.mostrar_menu_editar)

        #event boton actualizar target, simbolo faltante y ruta
        self.btnActualizarInfo.clicked.connect(self.actualizar_info_general)
        self.btnHistograma.clicked.connect(self.mostrar_histograma)
        self.btnBoxPlot.clicked.connect(self.mostrar_boxplot)

        # conectar evento agregar instancia. Estos eventos se emiten desde otras ventanas
        self.signal_agregar_instancia.connect(self.instancia_agregada)
        self.signal_eliminar_instancias.connect(self.instancias_eliminadas)
        self.signal_editar_instancia.connect(self.actualizar_etiquetas)
        self.signal_agregar_columna.connect(self.atributo_agregado)
        self.signal_reemplazo_faltantes.connect(self.actualizar_model)
Exemplo n.º 10
0
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    NUMERICO = 1
    CATEGORICO = 2

    # posiciones de las opciones del combo box donde se elige el tipo de atributo
    POS_NUMERICO_COMBO = 0
    POS_CATEGORICO_COMBO = 1

    signal_agregar_instancia = pyqtSignal() # se emite cuando se agrega una instancia
    signal_eliminar_instancias = pyqtSignal(int) # se emite cuando se elimina una instancia
    signal_editar_instancia = pyqtSignal() # se emite cuando se edita una instancia
    signal_agregar_columna = pyqtSignal(str) # se emite cuando se agrega una instancia
    signal_reemplazo_faltantes = pyqtSignal(pd.DataFrame)

    def __init__(self, ruta, conexion=None, query=None, *args, **kwargs):
        QtWidgets.QMainWindow.__init__(self, *args, **kwargs)
        self.setupUi(self)

        self.conexion = conexion
        self.query = query

        self.ruta = ruta
        self.conjunto = ConjuntoDatos(self.ruta, self.conexion, self.query)

        # si hay una conexión y un query entonces mostrar la opción para cambiar el query
        if self.conexion != None and self.query != None:
            self.agregar_opcion_cambiar_query()


        self.respaldos = Respaldos(self.conjunto) # para hacer los respaldos
        self.num_version = 1 # numero de versión para el nombre de los respaldos
        self.num_instancias_agregadas = 0 # cada que se agregan 10 instancias se hace un respaldo
        self.num_instancias_eliminadas = 0 # cada que se eliminar 10 instancias se hacer un respaldo
        self.cargar_respaldos()

        # action nuevo para cargar nuevo dataset
        self.actionNuevo.triggered.connect(self.cargar_nuevo_dataset)

        # conectar eventos para las opciones de los algoritmos
        self.actionZero_R.triggered.connect(self.zeroR)
        self.actionOne_R.triggered.connect(self.mostrar_ventana_oneR)
        self.actionNaive_Bayes.triggered.connect(self.mostrar_ventana_naive_bayes)
        self.actionK_NN.triggered.connect(self.mostrar_ventana_knn)
        self.actionK_Means.triggered.connect(self.mostrar_ventana_kmeans)

        # conectar eventos para las opciones de evalución de algoritmos
        self.actionKFold.triggered.connect(self.mostrar_ventana_kfold)
        self.actionHoldOut.triggered.connect(self.mostrar_ventana_hold_out)

        # id de la instancia en la que se dio clic en la tabla
        self.currentIdRow = None

        # utilizando un modelo los datos en la tabla se cargan muchisimo más rápido
        self.model = TableModelPandas(self.conjunto.panda)
        self.tabla.setModel(self.model)        

        self.llenar_combo_boxes()
        self.mostrar_atributo() # muestra los datos del atributo seleccionado por defecto en el combo box

        #evento cuando se cambia de elemento en el combo box
        self.comboBoxAtributos.currentIndexChanged.connect(lambda x: self.mostrar_atributo())

        #evento boton actualizar atributo
        self.btnActualizar.clicked.connect(self.actualizar_atributo)

        # muestrar la información general del conjunto de datos
        self.labelNumInstancias.setText(str(self.conjunto.getNumInstancias()))
        self.labelNumAtributos.setText(str(self.conjunto.getNumAtributos()))
        
        self.iniciar_target()
        self.lineEditValorFaltante.setText(str(self.conjunto.getSimboloFaltante()))
        self.lineEditRuta.setText(str(self.conjunto.getRutaRespaldos()))

        # toolbar
        self.agregar_actions_toolbar()

        # evento boton eliminar atributo
        self.btnEliminarAtributo.clicked.connect(self.eliminar_atributo)

        # evento boton mostar moda
        self.btnModas.clicked.connect(self.mostrar_ventanas_modas)

        # evento boton descripcion
        self.btnDescripcion.clicked.connect(self.mostrar_descripcion)

        # evento boton valores fuera de dominio
        self.btnFueraDominio.clicked.connect(self.mostrar_fuera_dominio)
        # evento boton valores faltantes
        self.btnFaltantes.clicked.connect(self.mostrar_val_faltantes)

        self.tabla.setSelectionMode(QAbstractItemView.SingleSelection)

        # este menú se muestra al dar clic sobre un id en la tabla
        self.menu_clic_tabla = QMenu("opciones")
        self.action_editar = QAction(QtGui.QIcon('iconos/editar.png'), "Editar")
        self.action_editar.triggered.connect(self.mostrar_editar_instancia)
        self.menu_clic_tabla.addAction(self.action_editar)
        self.tabla.verticalHeader().sectionPressed.connect(self.mostrar_menu_editar)

        #event boton actualizar target, simbolo faltante y ruta
        self.btnActualizarInfo.clicked.connect(self.actualizar_info_general)
        self.btnHistograma.clicked.connect(self.mostrar_histograma)
        self.btnBoxPlot.clicked.connect(self.mostrar_boxplot)

        # conectar evento agregar instancia. Estos eventos se emiten desde otras ventanas
        self.signal_agregar_instancia.connect(self.instancia_agregada)
        self.signal_eliminar_instancias.connect(self.instancias_eliminadas)
        self.signal_editar_instancia.connect(self.actualizar_etiquetas)
        self.signal_agregar_columna.connect(self.atributo_agregado)
        self.signal_reemplazo_faltantes.connect(self.actualizar_model)


    def cargar_nuevo_dataset(self):
        """Cierra la ventana actual y abre dialogo para abrir nuevo archivo de propiedades"""
        from dialogo_elegir_propiedades import DialogoElegirPropiedades
        self.dialogo = DialogoElegirPropiedades()
        self.close()
        self.dialogo.show()

    def agregar_opcion_cambiar_query(self):
        action_nuevo_query = QAction(self)
        action_nuevo_query.setText("Cambiar query")
        action_nuevo_query.triggered.connect(self.mostrar_ventana_query)
        self.menuArchivo.addAction(action_nuevo_query)

    def mostrar_ventana_query(self):
        from ventana_base_datos import VentanaBaseDatos
        self.conexion.reconnect() # reconecta a la base de datos
        self.ventana = VentanaBaseDatos(self.conexion, self.ruta)
        self.close()
        self.ventana.show()

    def instancia_agregada(self):
        """Este método se ejecuta cada que se agrega una instancia"""
        self.actualizar_etiquetas() # actualizar moda, media, media, num de instancias, etc

        self.num_instancias_agregadas += 1
        # hace respaldo cuando se han agregado 10 instancias
        if self.num_instancias_agregadas >= 10:
            self.hacer_respaldo("instancias_agregadas")
            self.num_instancias_agregadas = 0

    def instancias_eliminadas(self, num):
        """Este método se ejecuta cada que se elimina una instancia"""
        self.actualizar_etiquetas() # actualiza moda, media, media, num de instancias, etc

        self.num_instancias_eliminadas += num
        # hace respaldo cuando se eliminan 10 o más instancias
        if self.num_instancias_eliminadas >= 10:
            self.hacer_respaldo("instancias_eliminadas")
            self.num_instancias_eliminadas = 0

    def mostrar_menu_editar(self, index):
        """Menú que se muestra al dar clic sobre el id de una instancia en la tabla"""
        self.currentIdRow = self.conjunto.panda.index[index]
        self.menu_clic_tabla.exec_(QCursor.pos())

    def mostrar_fuera_dominio(self):
        nombre_atributo = self.comboBoxAtributos.currentText()
        atributo = self.conjunto.getAtributo(nombre_atributo)
        val_fuera_dominio = atributo.getValoresFueraDominio()
        self.ventana_fuera_dominio = VentanaFueraDominio(val_fuera_dominio, nombre_atributo)
        self.ventana_fuera_dominio.show()

    def mostrar_descripcion(self):
        self.ventana_descripcion = VentanaDescripcion(self.conjunto)
        self.ventana_descripcion.show()

    def mostrar_val_faltantes(self):
        nombre_atributo = self.comboBoxAtributos.currentText()
        atributo = self.conjunto.getAtributo(nombre_atributo)
        valores_faltantes = atributo.getValoresFaltantes()
        self.ventana_faltantes = VentanaFaltantes(valores_faltantes, nombre_atributo)
        self.ventana_faltantes.show()

    def eliminar_atributo(self):
        nombre_atributo = self.comboBoxAtributos.currentText()
        index_combo_box = self.comboBoxAtributos.currentIndex()

        # obtiene el index de una columna por nombre
        actual_index = self.conjunto.getIndiceAtributo(nombre_atributo)
        if self.model.removeColumns(actual_index, actual_index): # si se eliminó la columan correctamente
            self.conjunto.eliminarAtributoDeDiccionario(nombre_atributo)
            self.conjunto.eliminarAtributoDePropiedades(actual_index) # elimina el atributo del archivo de propiedades
            self.comboBoxAtributos.removeItem(index_combo_box)

            # del combo box que tiene las opciones para el target se quita el atributo eliminado
            index_target = self.comboBoxTarget.findText(nombre_atributo)
            if index_target != -1:
                self.comboBoxTarget.removeItem(index_target)
                if nombre_atributo == self.conjunto.getTarget(): # si se eliminó el atributo target
                    self.conjunto.setTarget("")
                    self.comboBoxTarget.setCurrentIndex(0)

            # actualiza la etiqueta de numero de atributos
            self.labelNumAtributos.setText(str(self.conjunto.getNumAtributos()))

            self.hacer_respaldo("eliminar_" + nombre_atributo)

        else:
            print("Ocurrio un error")

        
    def atributo_agregado(self, nombre):
        """Esta función se llama inmediatamente después de haber agregado un nuevo atributo"""
        atributo = self.conjunto.getAtributo(nombre)

        if atributo.getTipo() == "numerico":
            icon = QIcon("iconos/numerico.ico")
            tipo = self.NUMERICO
        else:
            icon = QIcon("iconos/categorico.ico")
            tipo = self.CATEGORICO

        self.comboBoxTarget.addItem(icon, nombre)
        self.comboBoxAtributos.addItem(icon, nombre, userData=tipo)
        self.actualizar_etiquetas()
        self.labelNumAtributos.setText(str(self.conjunto.getNumAtributos()))

        self.hacer_respaldo("agregar_" + nombre)


    def iniciar_target(self):
        """Inicializa el combo box del target con el atributo correcto"""
        target = self.conjunto.getTarget()
        if target != None and target != "" and target in self.conjunto.getNombresAtributos():
            self.comboBoxTarget.setCurrentText(target)
        else:
            print("target no valido")
        

    def agregar_actions_toolbar(self):
        """Agrega las acciones (nueva instancia, nuevo atributo, etc) al toolbar"""
        self.eliminar_instancia_action = QAction(QtGui.QIcon('iconos/remove.ico'), "Eliminar instancias")
        self.eliminar_instancia_action.triggered.connect(self.mostrar_eliminar_instancias)
        self.toolBar.addAction(self.eliminar_instancia_action)

        self.agregar_instancia_action = QAction(QtGui.QIcon('iconos/add.ico'), "Agregar instancias")
        self.agregar_instancia_action.triggered.connect(self.mostrar_agregar_instancia)
        self.toolBar.addAction(self.agregar_instancia_action)

        self.toolBar.addSeparator()
        self.toolBar.addSeparator()
        self.agregar_columna_action = QAction(QtGui.QIcon('iconos/add_column.png'), "Agregar columna")
        self.agregar_columna_action.triggered.connect(self.mostrar_agregar_columna)
        self.toolBar.addAction(self.agregar_columna_action)

        self.toolBar.addSeparator()
        self.toolBar.addSeparator()
        self.btnCorrelacion = QtWidgets.QPushButton(self.toolBar)
        self.btnCorrelacion.setText("Correlación de Pearson")
        self.btnCorrelacion.clicked.connect(self.mostrar_ventana_correlacion)
        self.toolBar.addWidget(self.btnCorrelacion)

        self.btnTschuprow = QtWidgets.QPushButton(self.toolBar)
        self.btnTschuprow.setText("Coeficiente de contingencia de Tschuprow")
        self.btnTschuprow.clicked.connect(self.mostrar_ventana_tschuprow)
        self.toolBar.addWidget(self.btnTschuprow)

        self.btnReemplazoFaltantes = QtWidgets.QPushButton(self.toolBar)
        self.btnReemplazoFaltantes.setText("Reemplazar valores faltantes")
        self.btnReemplazoFaltantes.clicked.connect(self.mostrar_reemplazo_faltantes)
        self.toolBar.addWidget(self.btnReemplazoFaltantes)

    def mostrar_ventanas_modas(self):
        """Muestra una ventana con las modas"""
        nombre_atributo = self.comboBoxAtributos.currentText()
        atributo = self.conjunto.getAtributo(nombre_atributo)
        modas = atributo.getModa()
        self.ventana = VentanaModa(modas)
        self.ventana.show()

    def mostrar_ventana_correlacion(self):
        self.ventana = VentanaCorrelacionPearson(self.conjunto)
        self.ventana.show()

    def mostrar_ventana_tschuprow(self):
        self.ventana = VentanaCoeficienteTschuprow(self.conjunto)
        self.ventana.show()

    def mostrar_eliminar_instancias(self):
        """Muestra la ventana para eliminar instancias"""
        self.ventana = VentanaEliminarInstancias(self.model, self.signal_eliminar_instancias)
        self.ventana.show()

    def mostrar_agregar_instancia(self):
        """Muestra la ventana para agregar un nueva instancia"""
        self.ventana = VentanaAgregarInstancia(self.conjunto, self.model, self.signal_agregar_instancia)
        self.ventana.show()

    def mostrar_agregar_columna(self):
        """Muestra la ventana para agregar un nueva instancia"""
        self.ventana = VentanaAgregarAtributo(self.conjunto, self.model, self.signal_agregar_columna)
        self.ventana.show()

    def mostrar_editar_instancia(self):
        """Muestra la ventana para agregar un nueva instancia"""
        self.ventana = VentanaEditarInstancia(self.currentIdRow, self.conjunto, self.model, self.signal_agregar_instancia)
        self.ventana.show()

    def actualizar_etiquetas(self):
        """Actualiza las etiquetas despues de insertar, editar o eliminar una instancia"""
        if self.comboBoxAtributos.count() == 0: # si no hay atributos termina
            return

        nombre_atributo = self.comboBoxAtributos.currentText()
        tipo_atributo = self.comboBoxAtributos.currentData()
        atributo = self.conjunto.getAtributo(nombre_atributo)

        if tipo_atributo == self.NUMERICO:
            self.actualizarMetricas(atributo)

        self.actualizar_label_fuera_dominio(atributo)
        self.actualizar_label_valores_faltantes(atributo)
        self.labelNumInstancias.setText(str(self.conjunto.getNumInstancias()))

    def llenar_combo_boxes(self):
        """Llena los combo box con los nombres de los atributos separados por tipo.
        También llena las opciones para elegir el target"""
        for atributo in self.conjunto.getAtributos():
            if atributo.getTipo() == "numerico":
                self.comboBoxAtributos.addItem(QIcon("iconos/numerico.ico"), atributo.getNombre(), userData=self.NUMERICO)
                self.comboBoxTarget.addItem(QIcon("iconos/numerico.ico"), atributo.getNombre())
            else:
                self.comboBoxAtributos.addItem(QIcon("iconos/categorico.ico"), atributo.getNombre(), userData=self.CATEGORICO)
                self.comboBoxTarget.addItem(QIcon("iconos/categorico.ico"), atributo.getNombre())

    def actualizar_info_general(self):
        """Actualizar el target, simbolo faltante y ruta"""
        target_actual = self.conjunto.getTarget()
        sim_faltante_actual = self.conjunto.getSimboloFaltante()
        ruta_actual = self.conjunto.getSimboloFaltante()
        debe_actualizar = False

        nuevo_target = self.comboBoxTarget.currentText()
        if nuevo_target != target_actual: # si el nuevo target es diferente al actual
            if self.comboBoxTarget.currentIndex() == 0: # si se va a quitar el target (la opcion 0 corresponde a la opcion "Ninguno")
                self.conjunto.setTarget("")
            else:
                self.conjunto.setTarget(nuevo_target)

        nuevo_simbolo_faltante = self.lineEditValorFaltante.text()
        if nuevo_simbolo_faltante != sim_faltante_actual: # si el nuevo simbolo es diferente al actual
                self.conjunto.setSimboloFaltante(nuevo_simbolo_faltante)
                debe_actualizar = True # debe actualizar etiquetas de moda, media, median, etc.

        nueva_ruta = self.lineEditRuta.text()
        if nueva_ruta != ruta_actual: # solo actualizar si la nueva ruta es diferente a la actual
            if nueva_ruta != "": # no se permite valor en blanco
                self.conjunto.setRutaRespaldos(nueva_ruta)
            else: # restaura el valor de la ruta actual
                self.lineEditRuta.setText(str(self.conjunto.getRutaRespaldos()))

        if debe_actualizar:
            self.actualizar_etiquetas()

    def actualizar_label_fuera_dominio(self, atributo):
        try:
            fuera_dominio = len(atributo.getValoresFueraDominio())
            total = self.conjunto.getNumInstancias()
            porcentaje = (fuera_dominio * 100) / total
            texto = str(fuera_dominio) + " (" + str(round(porcentaje, 2)) +"%)"
            self.labelFueraDominio.setText(texto)
            self.btnFueraDominio.setEnabled(True)
        except re.error: # si la expresión regular es inválida
            QMessageBox.warning(self, "Dominio incorrecto",
                "El dominio no es una expresión regular válida")
            self.labelFueraDominio.clear()
            self.btnFueraDominio.setEnabled(False)


    def actualizar_label_valores_faltantes(self, atributo):
        faltantes = len(atributo.getValoresFaltantes())
        total = self.conjunto.getNumInstancias()
        porcentaje = (faltantes * 100) / total
        texto = str(faltantes) + " (" + str(round(porcentaje, 2)) +"%)"
        self.labelValoresFaltantes.setText(texto)

    def actualizar_label_media(self, atributo):
        """Acutualiza el valor de la media"""
        media = str(atributo.getMedia())
        self.labelMedia.setText(media)

    def actualizar_label_mediana(self, atributo):
        """Acutualiza el valor de la mediana"""
        mediana = str(atributo.getMediana())
        self.labelMediana.setText(mediana)

    def actualizar_label_desviacion(self, atributo):
        desviacion = str(atributo.getDesviacionEstandar())
        self.labelDesviacionEstandar.setText(desviacion)

    # Este metodo se llama cada vez que se cambia de atributo del combo box
    def mostrar_atributo(self):
        """"Muestra los datos del atributo actual que esta en el combo box"""
        if self.comboBoxAtributos.count() == 0: # si no hay elementos en el combo box se desactiva
            self.editNombreAtributo.setText("")
            self.comboBoxTipoAtributo.setEnabled(False)
            self.editDominioAtributo.setText("")
            self.groupBoxAtributos.setEnabled(False)
            #falta limpiar mas etiquetas
        
        else:
            if not self.groupBoxAtributos.isEnabled():
                self.groupBoxAtributos.setEnabled(True)

            nombre_atributo = self.comboBoxAtributos.currentText()
            atributo = self.conjunto.getAtributo(nombre_atributo)
            self.editNombreAtributo.setText(atributo.getNombre())
            self.editDominioAtributo.setText(atributo.getDominio())

            self.actualizar_label_fuera_dominio(atributo)
            self.actualizar_label_valores_faltantes(atributo)

            # si el atributo es numerico muestra su moda, media, mediana, etc..
            if self.comboBoxAtributos.currentData() == self.NUMERICO:
                self.actualizarMetricas(atributo)
                self.btnHistograma.setVisible(False)
                self.btnBoxPlot.setVisible(True)
                self.comboBoxTipoAtributo.setCurrentIndex(self.POS_NUMERICO_COMBO)
            else: # si es categorico esconde la moda, media, mediana, etc.
                self.contenedorMetricas.setVisible(False)
                self.btnHistograma.setVisible(True)
                self.btnBoxPlot.setVisible(False)
                self.comboBoxTipoAtributo.setCurrentIndex(self.POS_CATEGORICO_COMBO)


    def actualizarMetricas(self, atributo):
        """Muestra el contenedor donde esta la moda, media, mediana, etc.
        y actualiza los valores"""
        self.contenedorMetricas.setVisible(True)
        self.actualizar_label_mediana(atributo)
        self.actualizar_label_media(atributo)
        self.actualizar_label_desviacion(atributo)

    
    def actualizar_atributo(self):
        """Evento clic del boton para actualizar un atributo"""
        dominio = self.editDominioAtributo.text()
        nom = self.editNombreAtributo.text()
        nombre_atributo = self.comboBoxAtributos.currentText()
        atributo = self.conjunto.getAtributo(nombre_atributo)

        if nom == "": # no se puede poner un nombre vacio a un atributo
            self.editNombreAtributo.setText(atributo.getNombre())
            QMessageBox.information(self, "Error", "El nombre no puede estar vacío")
            return

        if self.comboBoxTipoAtributo.currentIndex() == self.POS_NUMERICO_COMBO:
            tipo = "numerico"
        else:
            tipo = "categorico"
        
        index = self.comboBoxAtributos.currentIndex()
        index_target = self.comboBoxTarget.findText(nombre_atributo)

        # actualizar nombre
        if nom != atributo.getNombre(): # solo cambia el nombre si es diferente al anterior
            if not nom in self.conjunto.getNombresAtributos(): # no puede haber nombres de atributo repetidos
                atributo.setNombre(nom)
                self.comboBoxAtributos.setItemText(index, nom)
                self.comboBoxTarget.setItemText(index_target, nom) # actualiza
                self.actualizar_info_general() # actualiza el target si el atributo modificado era el target
            else:
                QMessageBox.information(self, "Error", "El nombre ya existe")

        icono = None # nuevo icono del 
        # actualizar tipo
        if tipo != atributo.getTipo(): # solo cambia el tipo si es diferente al anterior
            atributo.setTipo(tipo) # al cambiar el tipo es necesario actualizar la instancia atributo
            atributo = self.conjunto.getAtributo(atributo.getNombre()) # actualiza el atributo ya que al cambiar de tipo se cambia el tipo de instancias
            if tipo == "categorico": # si el nuevo tipo es categorico entonces debe agregarlo a las opciones del combo box target
                nuevo_tipo = self.CATEGORICO
                icono = QIcon("iconos/categorico.ico")
                self.contenedorMetricas.setVisible(False) # oculta la moda, media, mediana, ...
                self.btnBoxPlot.setVisible(False) # oculta el boton boxplot
                self.btnHistograma.setVisible(True) # muetra el boton histograma
            else:
                nuevo_tipo = self.NUMERICO
                icono = QIcon("iconos/numerico.ico")
                self.actualizarMetricas(atributo) # muestra la moda, media, mediana, ...
                self.btnHistograma.setVisible(False) # oculta el boton histograma
                self.btnBoxPlot.setVisible(True) # muestra el boton boxplot

            self.comboBoxAtributos.setItemData(index, nuevo_tipo)
            self.comboBoxAtributos.setItemIcon(index, icono)

            # actualiza el icono del combo box para elegir el target
            self.comboBoxTarget.setItemIcon(index_target, icono)
                
        # actualizar dominio
        if dominio != atributo.getDominio(): # solo cambia el dominio si es diferente
            atributo.setDominio(dominio)
            self.actualizar_label_fuera_dominio(atributo)


        print("Target: ", self.conjunto.getTarget())
    
    def mostrar_boxplot(self):
        self.ventana = boxplot(self.conjunto, self.comboBoxAtributos.currentText())
        self.ventana.show()

    def mostrar_histograma(self):
        self.ventana = histograma(self.conjunto, self.comboBoxAtributos.currentText())
        self.ventana.show()

    def hacer_respaldo(self, nombre, guardar_indices=True):
        """Crea un respaldo del csv y del archivo de propiedades"""
        nombre_respaldo = str(self.num_version) + "_" + nombre
        if self.respaldos.hacer_respaldo(nombre_respaldo, guardar_indices):
            self.num_version += 1
            action = QAction(self)
            action.setText(nombre_respaldo + ".json")
            action.triggered.connect(lambda x: self.iniciar_version(nombre_respaldo + ".json"))
            self.menuVersiones.addAction(action)
        else:
            print("error al crear respaldo")

    def iniciar_version(self, nombre):
        """Método que se ejecuta al iniciar una versión de los respaldos desde el menubar"""
        self.close()
        self.ventana = MainWindow(self.conjunto.getRutaRespaldos() + nombre)
        self.ventana.show()

    def cargar_respaldos(self):
        """Carga los nombres de los respaldos que hay en la ruta de respaldos"""
        ruta_respaldos = self.conjunto.getRutaRespaldos()

        # itera a traves de todos los respaldos y los agrega al menubar de las versiones
        if os.path.isdir(ruta_respaldos): # si existe la ruta
            for respaldo in glob(ruta_respaldos + "*.json"):
                nombre_respaldo = os.path.basename(respaldo) # extrae solo el nombre del archivo
                action = QAction(self)
                action.setText(nombre_respaldo)

                # chk es necesario para que triggered envie su estado actual (True o False)
                # el argumento por defecto es necesario para que lambda obtenga una copia de
                # la variable actual del ciclo
                action.triggered.connect(lambda chk, nombre_respaldo=nombre_respaldo: self.iniciar_version(nombre_respaldo))
                self.menuVersiones.addAction(action)
                self.num_version += 1

    def es_target_numerico(self):
        if np.issubdtype(pandas.to_numeric(
            self.conjunto.panda[self.conjunto.getTarget()], errors="ignore").dtype, np.number):
            return True
        return False


    def mostrar_reemplazo_faltantes(self):
        self.ventana = VentanaReemplazoFaltantes(self.conjunto.panda,
            self.conjunto.getTarget(), self.conjunto.getSimboloFaltante(),
            self.signal_reemplazo_faltantes)
        self.ventana.show()

    def actualizar_model(self, dataframe):
        self.conjunto.panda = dataframe
        self.model = TableModelPandas(self.conjunto.panda)
        self.tabla.setModel(self.model)

        for atributo in self.conjunto.getAtributos():
            atributo.panda = self.conjunto.panda

        self.actualizar_etiquetas()


    ###############################################
    # EVENTOS PARA LOS ALGORITMOS #
    def comprobar_target(self):
        target = self.conjunto.getTarget()
        if target == None or target == "" or target not in self.conjunto.panda.columns:
            QMessageBox.critical(self, "Error", "El target no esta definido")
            return False

        return True

    def zeroR(self):
        if self.comprobar_target():
            msg = "Frecuencias para el atributo " + self.conjunto.getTarget()
            frecuencias = zero_r.generar_frecuencias(self.conjunto.panda,
                self.conjunto.getTarget())

            msg += "\n\n"
            for key, val in frecuencias.items():
                msg += str(key) + ":  " + str(val) + "\n"

            clase, umbral = zero_r.obtenerMayor(frecuencias)
            msg += "\nUmbral: " + str(round(umbral, 4))

            QMessageBox.information(self, "Zero-R", msg)

    def mostrar_ventana_oneR(self):
        if self.comprobar_target():
            self.ventana = VentanaOneR(self.conjunto.panda,
                self.conjunto.getTarget())
            self.ventana.show()

    def mostrar_ventana_naive_bayes(self):
        if self.comprobar_target():
            if self.es_target_numerico():
                QMessageBox.critical(self, "Error",
                    "Naive Bayes solo funciona con problemas de clasificación")
            else:
                self.ventana = VentanaNaiveBayes(self.conjunto.panda,
                    self.conjunto.getTarget())
                self.ventana.show()

    def mostrar_ventana_knn(self):
        if self.comprobar_target():
            self.ventana = VentanaKNN(self.conjunto.panda,
                self.conjunto.getTarget())
            self.ventana.show()

    def mostrar_ventana_kfold(self):
        if self.comprobar_target():
            self.ventana = VentanaKFold(self.conjunto.panda,
                self.conjunto.getTarget())
            self.ventana.show()
    
    def mostrar_ventana_hold_out(self):
        if self.comprobar_target():
            self.ventana = VentanaHoldOut(self.conjunto.panda,
                self.conjunto.getTarget())
            self.ventana.show()


    def mostrar_ventana_kmeans(self):
        target = self.conjunto.getTarget()
        if target not in self.conjunto.panda.columns: target = None
        print("target:", target)

        # intentar convertir a numerico
        self.conjunto.panda = self.conjunto.panda.apply(pd.to_numeric, errors="ignore")

        # si existe una columna categórica muestra un error
        if np.dtype(np.object) in self.conjunto.panda.loc[:, self.conjunto.panda.columns != target].dtypes.values:
            QMessageBox.critical(self, "Error",
                "K Means solo funciona con atributos numéricos")
        else:
            self.ventana = VentanaKMeans(self.conjunto.panda, target)
            self.ventana.show()