[ Foro de Java ]

Crear matriz de distancias

18-Nov-2014 16:24
Angel Bravo
20 Respuestas

Trato de realizar un programa que, en base a dos variables del mismo tamaño, cree una matriz de distancias gracias a un criterio a elegir (distancia euclídea, distancia euclídea al cuadrado, etc...). Mi idea es crear dos métodos. El programa crea una matriz, matriz[indiceVariable][longitudVariables], con las dos variables, "indiceVariable" indica la fila de la matriz, es decir, es un índice que indica qué variable es (X o Y) y longitudVariables es eso, el numero de elementos que tiene cada variable. El primer método en cuestión construye la matriz de distancias llamando al segundo método, el cual contiene el algoritmo del criterio elegido. Por ejemplo, para la distancia euclídea al cuadrado de estas dos variables serían:


Individuo   X1   X2
A                10      5
B                20    20
C                30    10
D                30    15
E                 5      10


Y la matriz de distancias en base a la distancia euclídea al cuadrado ( d (i,j) = (Wi - Wj ) + (Wi - Wj)):

           A            B            C            D            E

A          0
B       325           0
C       425       200          0
D       500       125        25          0
E          50       325      625      650               0



El programa llama dos veces a éste método:    

 
double[][] crearVariable(int indiceVariable) {
   teclado = new Scanner(System.in);
   byte tamaño;
   System.out.print("Número de individuos: ");
   tamaño = teclado.nextByte();
   matriz = new double[indiceVariable][tamaño];
   for (byte i = 0; i < indiceVariable; i++) {
      for (byte j = 0; j < tamaño; j++) {
         System.out.print("Introducir valor " + (j + 1) + " " + "=" + " ");
         matriz[i][j] = teclado.nextInt();
      }
   }
   return matriz;
}
 


Es decir, ya tenemos guardadas las dos variables en "matriz", la idea ahora es construir otra matriz con esos datos en otro método, cada elemento de la nueva matriz será el resultado del segundo método:

 
double[][] crearMatrizDistancias(int indiceVariable) {
   matrizDistancias = new double[matriz[indiceVariable].length][matriz[indiceVariable].length];
   for (byte i = 0; i < matriz[indiceVariable].length; i++) {
      for (byte j = 0; j < matriz[indiceVariable].length; j++) {   
         matrizDistancias[i][j] = distanciaEuclidea(matriz[indiceVariable].length);
      }
   }
   return matrizDistancias;
}
 


El cual usa

 
double distanciaEuclidea(int indiceVariable) {
   double distanciaEuclidea=0;
   ??????????????????????????????
   ??????????????????????????????
   return distanciaEuclidea;
}
 


Y ahí está el problema....no tengo ni idea de cómo hacer este algoritmo, seguro que es una tontería pero me tiene perdidísimo.


21-Nov-2014 10:04
Nacho Cabanes (+30)

No acabo de entender tu planteamiento. A esa función "distanciaEuclidea" , ¿no sería más cómodo pasarle dos vectores, en vez de un índice y que tenga que acceder a otras variables... supongo que globales?


21-Nov-2014 15:25
Angel Bravo

Le cuento mi idea de cómo es el programa. Primeramente crea una clase "Variable" que es la base a otras clases:

 
import java.util.Scanner;
 
class Variable {
 
    byte selección;
    Scanner teclado;
    int[][] vector;
 
//-----------Métodos------------------//
 
// recibe "indiceVariable" es decir, 1 para crear una variable X, 2 para crear 2 (X e Y) o muchas para una matriz  
// (nº de fila que tiene la variable/vector en la matriz), es decir tamaño "i" 
 
    int[][] crearVariable(int indiceVariable) {
        teclado = new Scanner(System.in);
        byte tamaño;
        System.out.print("Número de elementos de la variable: ");
        tamaño = teclado.nextByte();
        vector = new int[indiceVariable][tamaño];
            for (byte i = 0; i < indiceVariable; i++) {
				for (byte j = 0; j < tamaño; j++) {
                System.out.print("Introducir valor " + (j + 1) + " " + "=" + " ");
                vector[i][j] = teclado.nextInt();
            }
        }
        return vector;
    }
 
	// Elemento Aij coincide con el valor que aquí toma j.
	// Mágico. No lo toques.
    void escribirVariable(int indiceVariable) {
        for (byte j = 0; j < vector[indiceVariable].length; j++) {
            System.out.print(vector[indiceVariable][j] + " ");
        }
    }
 
    byte menuUnaVariable() {
        System.out.print("La variable X es: ");
        escribirVariable(0);
        System.out.println();
        System.out.println("1 para medidas tendencia central");
        System.out.println("2 para dispersion");
        System.out.println("3 para todo ");
        selección = teclado.nextByte();
        return selección;
    }
 
    void pantallaResultados() {
        switch (selección) {
            case 1:
                resultadosMedidasCentrales();
                break;
            case 2:
                resultadosMedidasDispersión();
                break;
            case 3:
                resultadosMedidasCentrales();
                resultadosMedidasDispersión();
                break;
        }
    }
 
 
 
    void resultadosMedidasCentrales() {
        System.out.println("La sumatoria es " + sumatoria(1, 0));
        System.out.println("La media aritmetica de la variable es " + mediaAritmetica(0));
 
    }
 
    void resultadosMedidasDispersión() {
        System.out.println("La varianza de la variable es " + varianza(0));
        System.out.println("La desviación típica de la variable es " + desviacionTipica(0));
    }
 
//-----Métodos matemáticos-----//
 
//-----Pide un exponente para poder hacer sumatorias de cuadrados, cubos, etc, sin necesitar otro método-----//
 
    double sumatoria(int exponente, int indiceVariable) {
        double sumatoria = 0;
        for (byte j = 0; j < vector[indiceVariable].length; j++) {
            sumatoria = sumatoria + Math.pow(vector[indiceVariable][j], exponente);
        }
        return sumatoria;
    }
 
    double mediaAritmetica(int indiceVariable) {
        double media = 0;
        media = (sumatoria(1,indiceVariable) / vector[indiceVariable].length);
        return media;
    }
 
 
    double varianza(int indiceVariable) {
        double varianza = 0;
        varianza = (sumatoria(2, indiceVariable) / vector[indiceVariable].length - Math.pow(mediaAritmetica(indiceVariable), 2));
        return varianza;
    }
 
    double desviacionTipica(int indiceVariable) {
        double desviacionTipica = 0;
        desviacionTipica = Math.sqrt(varianza(indiceVariable));
        return desviacionTipica;
    }
 
}
 


También existe una clase "VariableXeY" que HEREDA la clase "Variable":

 
import java.util.Scanner;
 
class VariablesXeY extends Variable {
 
//-----------Métodos------------------//
    byte menuDosVariables() {
        teclado = new Scanner(System.in);
        System.out.println("La variables son: ");
        System.out.print("Variable X: ");
        escribirVariable(0);
        System.out.println();
        System.out.print("Variable Y: ");
        escribirVariable(1);
        System.out.println();
        System.out.println("1 para media y sumatorias");
        System.out.println("2 para dispersion");
        System.out.println("3 para todo ");
        selección = teclado.nextByte();
        return selección;
    }
 
    void pantallaResultados() {
        switch (selección) {
            case 1:
                resultadosMedidasCentrales();
                break;
            case 2:
                resultadosMedidasDispersión();
                break;
            case 3:
                resultadosMedidasCentrales();
                resultadosMedidasDispersión();
                break;
        }
    }
 
    void resultadosMedidasCentrales() {
        System.out.println("Media aritmética de X = " + mediaAritmetica(0));
        System.out.println("Media aritmética de Y = " + mediaAritmetica(1));
        System.out.println("Sumatoria de X = " + sumatoria(1, 0));
        System.out.println("Sumatoria de Y = " + sumatoria(1, 1));
    }
 
    void resultadosMedidasDispersión() {
        System.out.println("Varianza de X = " + varianza(0));
        System.out.println("Varianza de Y = " + varianza(1));
        System.out.println("Desviación típica de X = " + desviacionTipica(0));
        System.out.println("Desviación típica de Y = " + desviacionTipica(1));
        System.out.println("Covarianza de X e Y = " + covarianza());
        System.out.println("Coeficiente de correlación de X e Y = " + coeficienteCorrelacion());
    }
 
//-----Pide un exponente para poder hacer cosumatorias de cuadrados, cubos, etc, sin necesitar otro método
//-----recibe el exponente de cada variable así como su índice dentro de la matriz (0, 1, 2...)
//-----como las dos variables tienen la misma longitud (vector[0].length = vector[1].length) y solo hay que indicar
//-----una de ellas, se elige la primera.
 
    double sumatoria2Variables(int exponenteX, int exponenteY, int variableX, int variableY) {
        double sumatoria2Variables = 0;
        for (byte j = 0; j < vector[0].length; j++) {
            sumatoria2Variables = sumatoria2Variables + (Math.pow(vector[variableX][j], exponenteX) * Math.pow(vector[variableY][j], exponenteY));
        }
        System.out.println("sumatoria2Variables = " + sumatoria2Variables);
        return sumatoria2Variables;
    }
 
    double covarianza() {
        double covarianza = 0;
        covarianza = covarianza + (sumatoria2Variables(1, 1, 0, 1));
        covarianza = (covarianza / vector[0].length) - (mediaAritmetica(0) * mediaAritmetica(1));
        //System.out.println("Covarianza = " + covarianza);
        return covarianza;
    }
 
    double coeficienteCorrelacion() {
        double coeficienteCorrelacion = 0;
        coeficienteCorrelacion = (covarianza()) / (desviacionTipica(0) * desviacionTipica(1));
        //System.out.println("Coeficiente de correlación = " + coeficienteCorrelacion);
        return coeficienteCorrelacion;
    }
}
 


Y por último, se crea una clase "Principal" que incluye el main principal y desde ahi se centraliza todo

 
import java.util.Scanner;
 
class Principal {
 
   byte selección;
   Scanner teclado;
 
   public static void main(String[] args) {
      Principal programa = new Principal();
      programa.menu();
   }
 
   void menu() {
        teclado = new Scanner(System.in);
        System.out.print("1 para una variable, 2 para dos variables ==> ");
        selección = teclado.nextByte();
        switch (selección) {
            case 1:
                Variable variableX = new Variable();
                variableX.crearVariable(1);
                variableX.menuUnaVariable();
                variableX.pantallaResultados();
                break;
            case 2:
                VariablesXeY variablesXeY = new VariablesXeY();
                variablesXeY.crearVariable(2);
                variablesXeY.menuDosVariables();
                variablesXeY.pantallaResultados();
                break;
        }
    }
 
}
 


La idea es crear una matriz -llamada vector- utilizando el número de fila como el índice de referencia de la variable, y las columnas como los elementos de la variable -y por extensión, el número de columnas como el tamaño de la variable-. Entiendo la necesidad de minimizar las variables globales hasta prácticamente eliminarlas, ¿pero cómo y por qué hacerlo cuando el vector es usado por todas las clases y sus métodos, y no varía?.

La idea original es, por medio de la clase VariableXeY, utilizar el vector para crear mediante un método la matriz de distancias en base a otro método que establece cómo se calcule esa distancia.


25-Nov-2014 10:46
Nacho Cabanes (+30)

Bufff... vale... me parece una forma un tanto engorrosa de trabajar, pero ahora al menos entiendo tu idea...

Esta noche intento mirarlo con un poco de detenimiento, a ver si puedo ayudarte...


25-Nov-2014 11:36
Angel Bravo

Jajajajajajaja, vaya....y yo que lo hice así porque me parecía la forma más sencilla de trabajar, menuda decepción xD Será interesante poder leer todas las criticas al programa al margen del tema de la matriz de distancias, gracias.


26-Nov-2014 11:18
Nacho Cabanes (+30)

Ten en cuenta que el descomponerlo en clases debería ser para repartir responsabilidades, y en tu caso no estás haciendo eso. Lo has descompuesto como se haría en los años 70, con la diferencia de que has escrito "class" al principio de cada bloque. Como profesor, soy (¿debo ser?) especialmente crítico con ese tipo de prácticas.

Por ejemplo, una clase que manipula datos como tu clase "variable" no debería interaccionar con el usuario, de modo que esa clase se pueda aplicar en otros programas en modo gráfico, interfaz web o el que fuera. La interacción con el usuario la debería hacer otra clase, que sería la que cambiaría cuando se portase el programa a otra plataforma.

De igual modo, el "switch" de "menu" muestra que no estás usando la herencia de forma correcta, porque tienes un nuevo "menuDosVariables" en vez de redefinir el menú que ya aparece en "variable", y un "crearVariable" que recibe un parámetro en vez de comportarse de forma distinta según la clase en la que esté, y "pantallaResultados" parte de un atributo "selección" que puede no tener valor, y este método está reescrito de forma idéntica en la superclase y en la subclase.

Además de eso, desde mi punto de vista, si la clase "variable" representa una única colección de datos, debería contener una matriz unidimensional, en vez de una bidimensional que obliga a pasar siempre como parámetro un índice de posición dentro del array multidimensional, lo que es poco intuitivo y propenso a errores.

Si tengo un respiro, te incluiré una versión alternativa de la clase "variable", un poco más acorde con "mi esquema mental".


26-Nov-2014 11:55
Angel Bravo

Mi idea de descomponer el programa en clases era precisamente ésa de repartir responsabilidades. Cada clase para un tipo de cálculo determinado siendo la clase "Variable" la base con los cálculos básicos de los que derivan las demás clases. Quiero crear un programa bastante grande, con posibilidad de análisis estadísticos complejos de una, dos, o varias variables (análisis de datos multivariantes, matriciales, ANOVA, dendogramas, etc...) y el utilizar la clase de una variable como base para el resto, es porque muchos cálculos se repiten, de ahí que si por ejemplo, en otra clase necesito la media, sumatoria, varianza, etc... de una o varias de las variables de una matriz en un cálculo, no tenga más que llamar al método que hereda de "variable" sólo con mandarle un índice para decirle cual de todas las filas de la matriz -a.k.a. variables- es la elegida ¿No sería esa la forma correcta de hacerlo? ¿de reutilizar código? de ahí que decidiera que incluso siendo una sola variable se comportase como una matriz siendo la fila el índice o identificador.

Con respecto a lo interactuar con el usuario en clases que manejan datos, veo el problema y estoy totalmente de acuerdo. Es más, dar el paso a modo gráfico es obligatorio..pero como aún no lo domino, no me preocupé de ello, pero desde luego que sí quisiera hacerlo así y corregirlo, aunque no sabría muy bien cómo. Encuadrarlo dentro del modo consola y que quedase bien estéticamente fue lo que me hizo crear un nuevo menu "menuDosVariables" en lugar de reutilizar el que venía heredado así como el resto de errores que me señala. Como sabía que todo eso lo iba a eliminar al pasarlo a modo gráfico, no me importó hacerlo de forma más chapucera y concentrarme en lo que sí se iba a quedar.


30-Nov-2014 19:35
Nacho Cabanes (+30)

Mira, Ángel. Aprovechando que es domingo y tenía un rato libre, he reescrito tu programa (por ahora, en su versión sólo para una variable) un poco más a mi gusto.

Así quedaría tu fuente si juntamos todas las clases (las dos) en un único fichero:

 
import java.util.Scanner;
 
class Variable {
 
    byte selección;
    Scanner teclado;
    int[][] vector;
 
//-----------Métodos------------------//
 
// recibe "indiceVariable" es decir, 1 para crear una variable X, 2 para crear 2 (X e Y) o muchas para una matriz  
// (nº de fila que tiene la variable/vector en la matriz), es decir tamaño "i" 
 
    int[][] crearVariable(int indiceVariable) {
        teclado = new Scanner(System.in);
        byte tamaño;
        System.out.print("Número de elementos de la variable: ");
        tamaño = teclado.nextByte();
        vector = new int[indiceVariable][tamaño];
            for (byte i = 0; i < indiceVariable; i++) {
                for (byte j = 0; j < tamaño; j++) {
                System.out.print("Introducir valor " + (j + 1) + " " + "=" + " ");
                vector[i][j] = teclado.nextInt();
            }
        }
        return vector;
    }
 
    // Elemento Aij coincide con el valor que aquí toma j.
    // Mágico. No lo toques.
    void escribirVariable(int indiceVariable) {
        for (byte j = 0; j < vector[indiceVariable].length; j++) {
            System.out.print(vector[indiceVariable][j] + " ");
        }
    }
 
    byte menuUnaVariable() {
        System.out.print("La variable X es: ");
        escribirVariable(0);
        System.out.println();
        System.out.println("1 para medidas tendencia central");
        System.out.println("2 para dispersion");
        System.out.println("3 para todo ");
        selección = teclado.nextByte();
        return selección;
    }
 
    void pantallaResultados() {
        switch (selección) {
            case 1:
                resultadosMedidasCentrales();
                break;
            case 2:
                resultadosMedidasDispersión();
                break;
            case 3:
                resultadosMedidasCentrales();
                resultadosMedidasDispersión();
                break;
        }
    }
 
 
 
    void resultadosMedidasCentrales() {
        System.out.println("La sumatoria es " + sumatoria(1, 0));
        System.out.println("La media aritmetica de la variable es " + mediaAritmetica(0));
 
    }
 
    void resultadosMedidasDispersión() {
        System.out.println("La varianza de la variable es " + varianza(0));
        System.out.println("La desviación típica de la variable es " + desviacionTipica(0));
    }
 
//-----Métodos matemáticos-----//
 
//-----Pide un exponente para poder hacer sumatorias de cuadrados, cubos, etc, sin necesitar otro método-----//
 
    double sumatoria(int exponente, int indiceVariable) {
        double sumatoria = 0;
        for (byte j = 0; j < vector[indiceVariable].length; j++) {
            sumatoria = sumatoria + Math.pow(vector[indiceVariable][j], exponente);
        }
        return sumatoria;
    }
 
    double mediaAritmetica(int indiceVariable) {
        double media = 0;
        media = (sumatoria(1,indiceVariable) / vector[indiceVariable].length);
        return media;
    }
 
 
    double varianza(int indiceVariable) {
        double varianza = 0;
        varianza = (sumatoria(2, indiceVariable) / vector[indiceVariable].length - Math.pow(mediaAritmetica(indiceVariable), 2));
        return varianza;
    }
 
    double desviacionTipica(int indiceVariable) {
        double desviacionTipica = 0;
        desviacionTipica = Math.sqrt(varianza(indiceVariable));
        return desviacionTipica;
    }
 
}
// =================================================================
 
class Principal {
 
   byte selección;
   Scanner teclado;
 
   public static void main(String[] args) {
      Principal programa = new Principal();
      programa.menu();
   }
 
   void menu() {
        teclado = new Scanner(System.in);
        System.out.print("1 para una variable, 2 para dos variables ==> ");
        selección = teclado.nextByte();
        switch (selección) {
            case 1:
                Variable variableX = new Variable();
                variableX.crearVariable(1);
                variableX.menuUnaVariable();
                variableX.pantallaResultados();
                break;
            /*case 2:
                VariablesXeY variablesXeY = new VariablesXeY();
                variablesXeY.crearVariable(2);
                variablesXeY.menuDosVariables();
                variablesXeY.pantallaResultados();
                break;*/
        }
    }
 
}
 


Y esta otra es mi versión. Los cambios los tienes detallados en los comentarios al principio del fuente, pero se pueden resumir en separar la parte visual de la parte lógica, eliminar el índice de variable (al fin y el cabo, es todavía una variable de una dimensión) y hacer algunas correcciones estilísticas:

 
// Cambios realizados:
// - Separada lógica del componente visual
// - Convertidas a variables locales tantas como sea posible
// - Como es una única variable, pasa a ser un array unidimensional
// - Creados getters y setters
// - Añadido un constructor para inicializar
// - Los métodos "void" son verbos que indiquen la acción exacta: 
//   "mostrarMedidasCentrales" en vez de "resultadosMedidasCentrales"
// - Eliminados acentos y eñe en los nombres de variables y métodos
// - Reducida longitud de líneas a unas 80 columnas
 
import java.util.Scanner;
 
class Variable {
 
    double[] vector;
 
    public Variable(int tamanyo) {
        vector = new double[tamanyo];
    }
 
    public double GetData(int pos) {
        return vector[pos];
    }
 
    public void SetData(int pos, double valor) {
        vector[pos] = valor;
    }
 
    public int GetLength() {
        return vector.length;
    }
 
    //----- Pide un exponente para poder hacer sumatorias de cuadrados, 
    // cubos, etc, sin necesitar otro método-----//
    double sumatoria(int exponente) {
        double sumatoria = 0;
        for (byte j = 0; j < vector.length; j++) {
            sumatoria = sumatoria + Math.pow(vector[j], exponente);
        }
        return sumatoria;
    }
 
    double mediaAritmetica() {
        double media = 0;
        media = (sumatoria(1) / vector.length);
        return media;
    }
 
 
    double varianza() {
        double varianza = 0;
        varianza = (sumatoria(2) / vector.length - 
            Math.pow(mediaAritmetica(), 2));
        return varianza;
    }
 
    double desviacionTipica() {
        double desviacionTipica = 0;
        desviacionTipica = Math.sqrt(varianza());
        return desviacionTipica;
    }
 
}
 
// =================================================================
 
 
 
class Interfaz1Variable {
 
    Variable v;
 
    void crearVariable() {
        Scanner teclado;
        teclado = new Scanner(System.in);
        int tamanyo;
        System.out.print("Número de elementos de la variable: ");
        tamanyo = teclado.nextInt();
        v = new Variable(tamanyo);
            for (byte j = 0; j < tamanyo; j++) {
                System.out.print("Introducir valor " + (j + 1) + " " + "=" + " ");
                v.SetData(j, teclado.nextInt() );
        }
    }
 
    void escribirVariable() {
        for (byte j = 0; j < v.GetLength(); j++) {
            System.out.print(v.GetData(j) + " ");
        }
        System.out.println();
    }
 
    void escogerYRealizarOperacion() {
        Scanner teclado;
        teclado = new Scanner(System.in);
        System.out.print("La variable X es: ");
        escribirVariable();
        System.out.println();
        System.out.println("1 para medidas tendencia central");
        System.out.println("2 para dispersion");
        System.out.println("3 para todo ");
        byte seleccion = teclado.nextByte();
        switch (seleccion) {
            case 1:
                mostrarMedidasCentrales();
                break;
            case 2:
                mostrarMedidasDispersion();
                break;
            case 3:
                mostrarMedidasCentrales();
                mostrarMedidasDispersion();
                break;
        }
    }
 
    void mostrarMedidasCentrales() {
        System.out.println("La sumatoria es " + v.sumatoria(0) );
        System.out.println("La media aritmetica de la variable es " + 
            v.mediaAritmetica() );
    }
 
    void mostrarMedidasDispersion() {
        System.out.println("La varianza de la variable es " + v.varianza() );
        System.out.println("La desviación típica de la variable es " + 
            v.desviacionTipica() );
    }
}
 
// =================================================================
 
class Principal {
 
   public static void main(String[] args) {
      Interfaz1Variable i = new Interfaz1Variable();
      i.crearVariable();
      i.escribirVariable();
      i.escogerYRealizarOperacion();
   }
 
}
 


Este código debería ser más fácil de ampliar y de corregir. Ahora puedes decidir si una "variable de dos dimensiones" hereda de ésta y amplía funcionalidades, o bien si la encapsula, incluyendo dos variables unidimensionales.


30-Nov-2014 19:50
Angel Bravo

Bien bien bien....lo estoy viendo....¿Es preferible cada clase en un archivo diferente?, en este caso, tres clases = tres archivos. El último párrafo... ¿podría explicarme un poco más las dos opciones? ¿Ampliar funcionalidades sería crear otra clase que sobreescribe el atributo "vector" del objeto "v" de la clase "Variable"?


01-Dec-2014 13:42
Nacho Cabanes (+30)

Lo de incluir varias clases en un fichero no es lo correcto siendo puristas, pero es práctico cuando quieres hacer una prueba rápida sin tener que crear todo un proyecto para ella.  ;-)

En cuanto a las alternativas para la nueva clase:

- Se puede heredar de la otra clase si "se parecen lo suficiente" (misma parte visible, posiblemente ampliada con detalles adicionales, pero no hay "cosas que sobren").

- Si la parte visible de la nueva clase no se parece demasiado (por ejemplo, no quieres permitir un GetData que reciba sólo un índice, o no va a existir una "varianza" aplicable a toda la distibución a la vez), es que realmente no es un caso particular de la clase original, y en ese caso sería más práctico crear una nueva clase, aunque internamente contenga a la clase original si quieres aprovechar algunas de sus funcionalidades.


01-Dec-2014 18:23
Angel Bravo

Imagino que cada tipo de cálculo será lo bastante diferente como para requerir una interfaz propia en lugar de como lo hacía antes, además de separarla de su parte "matemática", eso lo veo, toda la razón. Pero la clase "variable" se supone que es la básica en la medida que tiene los métodos que todas las demás clases de "manipulación de datos" van a necesitar, por ejemplo, si quiero calcular la covarianza de dos variables X e Y,  necesitaré crear una matriz y utilizar los métodos que ya existen en la clase "variable" para X y también para Y. ¿Cómo sabrá el método media (por ejemplo) qué variable es si se elimina el indice que antes recogía?.

Habría que sobreescribir todos los métodos en esta nueva clase llamada "VariableXeY" y que el punto de partida del resto de clases -para multivariante, ANOVA, ANCOVA, etc- no partiesen de la clase "Variable" sino de esta clase pero, ¿no sería eso trabajo desperdiciado al no poder reutilizarlo?¿No compensa sobradamente el que cada método de la superclase reciba esa información cuando una subclase lo necesite?


08-Dec-2014 16:29
Angel Bravo

Profesor, me gustaría resolver la duda original que motivó el hilo y que dejamos aparcada convenientemente para solucionar otras cuestiones del programa, así como las dudas que también siguen en el aire -y que agradezco haber tenido interés en solucionar-.


08-Dec-2014 18:12
Nacho Cabanes (+30)

Venga, prepara una clase Variable2Dimensiones ahora que has visto una forma alternativa de plantearla, y yo le echo un vistazo y te digo lo que me parezca mejorable.


08-Dec-2014 19:00
Angel Bravo

Me puse a ello...dividí su código en tres clases, cambié y añadí algunas cosas, y creé dos clases más para cálculos con variables bidimensionales (una para los cálculos -VariablesXeY- y otra para la interfaz -Interfaz2Variables-). Pero sigo resistiéndome a ver el vector de la clase "Variable" como un elemento unidimensional y eliminar un índice en los cálculos pues gracias a ello puedo usarlos en cálculos más avanzados en otras clases simplemente heredando esa clase como base. No veo manera más simple y clara que hacerlo así, y al recomendarme quitarlo me he quedado parado.


10-Dec-2014 18:38
Angel Bravo

Así está el programa ahora mismo:

Consta de 5 clases: Principal, Calculos1Variable, Interfaz1Variable, Calculos2Variables e Interfaz2Variables.

 
class Principal {
 
    public static void main(String[] args) {
        Principal programa;
        programa = new Stadistic();
        programa.menu();
    }
 
    void menu() {
        byte selección;
        Scanner teclado;
        teclado = new Scanner(System.in);
        System.out.print("1 para una variable, 2 para dos variables, ");
        System.out.print("3 para muestreo  ==> ");
        selección = teclado.nextByte();
        switch (selección) {
            case 1:
                Interfaz1Variable i1V;
                i1V = new Interfaz1Variable();
                i1V.crearVariable(1);
                i1V.escribirVariable(0);
                i1V.escogerYRealizarOperacion();
                break;
            case 2:
                Interfaz2Variables i2V = new Interfaz2Variables();
                i2V.crearVariable(2);
                i2V.escogerYRealizarOperacion();
                break;
        }
    }
}
 
 
class Calculos1Variable {
 
    double[][] vector;
 
 //==========Constructor=================
    public Calculos1Variable(int indiceVariable, int tamanyo) {
        vector = new double[indiceVariable][tamanyo];
    }
 
    public double GetData(int indiceVariable, int pos) {
        return vector[indiceVariable][pos];
    }
 
    public void SetData(int indiceVariable, int pos, double valor) {
        vector[indiceVariable][pos] = valor;
    }
 
    public int GetLength(int indiceVariable) {
        //int n = vector[indiceVariable].length;
        //return n;
        return vector[indiceVariable].length;
    }
 
    //----- Pide un exponente para poder hacer sumatorias de cuadrados, 
    // cubos, etc, sin necesitar otro método-----//
    double sumatoria(int indiceVariable, int exponente) {
        double sumatoria = 0;
        for (byte j = 0; j < vector[indiceVariable].length; j++) {
            sumatoria = sumatoria + Math.pow(vector[indiceVariable][j], exponente);
        }
        return sumatoria;
    }
 
    double mediaAritmetica(int indiceVariable) {
        double media;
        media = (sumatoria(indiceVariable, 1) / vector[indiceVariable].length);
        return media;
    }
 
    //Suma de diferencias respecto a la media
    double difRespecMedia(int indiceVariable, int exponente) {
        double difRespecMedia = 0;
        for (byte j = 0; j < vector[indiceVariable].length; j++) {
            difRespecMedia = difRespecMedia + Math.pow(vector[indiceVariable][j] - mediaAritmetica(indiceVariable), exponente);
        }
        return difRespecMedia;
    }
 
    double varianza(int indiceVariable) {
        double varianza;
        varianza = (difRespecMedia(0, 2)) / vector[indiceVariable].length;
        return varianza;
    }
 
 
    double desviacionTipica(int indiceVariable) {
        double desviacionTipica;
        desviacionTipica = Math.sqrt(varianza(indiceVariable));
        return desviacionTipica;
    }
}
 
 
import java.util.Scanner;
 
public class Interfaz1Variable {
 
    Calculos1Variable variable1;
 
    void crearVariable(int indiceVariable) {
        Scanner teclado;
        teclado = new Scanner(System.in);
        int tamanyo;
        System.out.print("Número de elementos de la variable: ");
        tamanyo = teclado.nextInt();
        variable1 = new Calculos1Variable(indiceVariable, tamanyo);
        for (byte i = 0; i < indiceVariable; i++) {
            for (byte j = 0; j < tamanyo; j++) {
                System.out.print("Introducir valor " + (j + 1) + " " + "=" + " ");
                variable1.SetData(i, j, teclado.nextInt());
            }
        }
    }
 
    void escribirVariable(int indiceVariable) {
        for (byte j = 0; j < variable1.GetLength(indiceVariable); j++) {
            System.out.print(variable1.GetData(indiceVariable, j) + " ");
        }
        System.out.println();
    }
 
    void escogerYRealizarOperacion() {
        Scanner teclado;
        teclado = new Scanner(System.in);
        System.out.print("La variable X es: ");
        escribirVariable(0);
        System.out.println();
        System.out.println("1 para medidas tendencia central");
        System.out.println("2 para dispersion");
        System.out.println("3 para todo ");
        byte seleccion = teclado.nextByte();
        switch (seleccion) {
            case 1:
                mostrarMedidasCentrales();
                break;
            case 2:
                mostrarMedidasDispersion();
                break;
            case 3:
                mostrarMedidasCentrales();
                mostrarMedidasDispersion();
                break;
        }
    }
 
    void mostrarMedidasCentrales() {
        System.out.println("La sumatoria es " + variable1.sumatoria(0, 1));
        System.out.println("La media aritmetica de la variable es "
                + variable1.mediaAritmetica(0));
    }
 
    void mostrarMedidasDispersion() {
        System.out.println("La varianza poblacional de la variable es " + variable1.varianza(0));
        System.out.println("La desviación típica poblacional de la variable es " + variable1.desviacionTipica(0));
    }
}
 
 
class Calculos2Variables extends Calculos1Variable {
 
    //==========Constructor=================
    public Calculos2Variables(int indiceVariable, int tamanyo) {
        super(indiceVariable, tamanyo);
        vector = new double[indiceVariable][tamanyo];
    }
 
//-----------Métodos------------------//
//-----Pide un exponente para poder hacer cosumatorias de cuadrados, cubos, etc, sin necesitar otro método-----//
//-----recibe el exponente de cada variable así como su índice dentro de la matriz (0, 1, 2...)-----// 
//-----como las dos variables tienen la misma longitud (vector[0].length = vector[1].length) y solo hay que indicar
//-----una de ellas, se elige la primera.
    double sumatoria2Variables(int variableX, int variableY, int exponenteX, int exponenteY) {
        double sumatoria2Variables = 0;
        for (byte j = 0; j < vector[0].length; j++) {
            sumatoria2Variables = sumatoria2Variables + (Math.pow(vector[variableX][j], exponenteX) * Math.pow(vector[variableY][j], exponenteY));
        }
        return sumatoria2Variables;
    }
 
    double covarianza() {
        double covarianza = 0;
        covarianza = covarianza + (sumatoria2Variables(0, 1, 1, 1));
        covarianza = (covarianza / vector[0].length) - (mediaAritmetica(0) * mediaAritmetica(1));
        return covarianza;
    }
}
 
 
import java.util.Scanner;
 
public class Interfaz2Variables {
 
    Calculos2Variables variables2;
 
    void crearVariable(int indiceVariable) {
        Scanner teclado;
        teclado = new Scanner(System.in);
        int tamanyo;
        System.out.print("Número de elementos de la variable: ");
        tamanyo = teclado.nextInt();
        variables2 = new Calculos2Variables(indiceVariable, tamanyo);
        for (byte i = 0; i < indiceVariable; i++) {
            for (byte j = 0; j < tamanyo; j++) {
                System.out.print("Introducir valor " + (j + 1) + " " + "=" + " ");
                variables2.SetData(i, j, teclado.nextInt());
            }
        }
    }
 
    void escribirVariable(int indiceVariable) {
        for (byte j = 0; j < variables2.GetLength(indiceVariable); j++) {
            System.out.print(variables2.GetData(indiceVariable, j) + " ");
        }
        System.out.println();
    }
 
    void escogerYRealizarOperacion() {
        Scanner teclado;
        teclado = new Scanner(System.in);
        System.out.println("La variables son: ");
        System.out.print("Variable X: ");
        escribirVariable(0);
        System.out.println();
        System.out.print("Variable Y: ");
        escribirVariable(1);
        System.out.println();
        System.out.println("1 para media y sumatorias");
        System.out.println("2 para dispersion");
        System.out.println("3 para todo ");
        byte seleccion = teclado.nextByte();
        switch (seleccion) {
            case 1:
                resultadosMedidasCentrales();
                break;
            case 2:
                resultadosMedidasDispersión();
                break;
            case 3:
                resultadosMedidasCentrales();
                resultadosMedidasDispersión();
                break;
        }
    }
 
    void resultadosMedidasCentrales() {
        System.out.println("Media aritmética de X = " + variables2.mediaAritmetica(0));
        System.out.println("Media aritmética de Y = " + variables2.mediaAritmetica(1));
        System.out.println("Sumatoria de X = " + variables2.sumatoria(0, 1));
        System.out.println("Sumatoria de Y = " + variables2.sumatoria(1, 1));
    }
 
    void resultadosMedidasDispersión() {
        System.out.println("Varianza de X = " + variables2.varianza(0));
        System.out.println("Varianza de Y = " + variables2.varianza(1));
        System.out.println("Desviación típica de X = " + variables2.desviacionTipica(0));
        System.out.println("Desviación típica de Y = " + variables2.desviacionTipica(1));
        System.out.println("Covarianza de X e Y = " + variables2.covarianza());
    }
}
 


Cuestiones:

-Como ya le dije, he vuelto a incluir el indiceVariable para que la clase "Calculos2Variables" pueda usar los cálculos de su superclase a conveniencia, no encuentro forma mejor de hacerlo.
-Me quedé "pillaete" con un error en el Constructor hasta que entendí que había que llamar también al de la superclase, ¿hacerlo como lo he hecho -super(indiceVariable, tamanyo);- es lo correcto?
-El nombre de los objetos i1V, i2V, etc...no me parecen apropiados, ¿sugerencias?
-¿Por qué es necesario limitar la longitud de las líneas de código a 80 columnas?
-Los 70's molaban, ¿Qué problema hay en organizar y estructurar el código de esa forma?.


10-Dec-2014 18:52
Angel Bravo

-Ah, lo olvidaba...¿El  método "menú" de la clase principal podría/debería verse como una clase aparte al estilo de las interfaces?


11-Dec-2014 21:21
Angel Bravo

-Estaba pensando en crear una superclase llamada "InterfazBasica" y que "Interfaz1Variable" e "Interfaz2Variables" heredasen de ella, pues son clases muy similares, pero no veo cómo lidiar con la creación y uso de los objetos Calculos1Variable, Calculos2Variables, etc... sin tener que redefinir -por ejemplo- el método "crearVariable" cada vez que una clase lo herede.

P.D. Mis hilos siempre son condenadamente largos, discúlpeme si me columpio en exceso con las preguntas.


15-Dec-2014 14:29
Nacho Cabanes (+30)

Vuelvo a estar aquí, Ángel.

No te preocupes por los hilos largos. La dificultad para contestarte no son los hilos, sino que no se trata de un único fuente que tenga algún problema puntual, sino de un proyecto completo, lo que obliga a volver a construir el proyecto para poder probarlo, o a juntar todo en un único fuente.

Además, es tu caso se complica eso de juntar todo en un fuente, porque usas cosas no estándar que "rompen" la compilación, como nombres de variables y de métodos que contienen acentos, o una clase que se llaman Principal cuando parece que debería llamarse "Stadistic", o clases públicas, que no pueden ser parte del mismo fuente que otras clases.

Todo eso supone que se tarde más de 10 minutos sólo en "reconstruir el fuente" para poderlo probar, y hace que tarde más en poder sentarme a contestarte.

Funcionar, funciona, pero sigue habiendo alguna cosa "que me chirría un poco", como eso de que "Calculos2Variables" herede de "Calculos1Variable", con lo que estás obligando a duplicar información (tendrás un array bidimensional y otro unidimensional) o a falsear el problema (¿para qué necesita "Calculos1Variable" un array bidimensional? ¿verdad que es sólo "para poder heredar"?). También hay alguna inconsistencia, como algún método que "hace cosas" pero su nombre no es un verbo, lo que resulta poco aclarador, como "resultadosMedidasDispersion", que muestra datos en pantalla, mientras que "covarianza" no muestra nada, sino que calcula y devuelve un resultado.

En cuanto a tus preguntas:

Como ya le dije, he vuelto a incluir el indiceVariable para que la clase "Calculos2Variables" pueda usar los cálculos de su superclase a conveniencia, no encuentro forma mejor de hacerlo.

Ya te dije mi opinión. Son problemas parecidos, pero sólo parecidos. Yo no acabo de ver razonable que herede una clase de la otra, y menos aún que para una variable necesites pasar dos parámetros, indicando siempre un índice que, en un problema normal, será siempre 0.

Me quedé "pillaete" con un error en el Constructor hasta que entendí que había que llamar también al de la superclase, ¿hacerlo como lo he hecho -super(indiceVariable, tamanyo);- es lo correcto?

Sí. Si la clase base no tiene un constructor vacío, debes usar "super" para indicar en qué constructor no vacío quieres apoyarte.

El nombre de los objetos i1V, i2V, etc...no me parecen apropiados, ¿sugerencias?

Yo prefiero nombres largos y claros. Ten en cuenta que apenas los vas a escribir una vez. A partir de ahí, el autocompletado de cualquier editor te ayuda a no teclearlo entero muchas veces, y un programa se escribe una sola vez, pero se lee muchas (para ampliarlo, corregirlo, etc) así que debería resultar legible.

¿Por qué es necesario limitar la longitud de las líneas de código a 80 columnas?

No es necesario. Es altamente recomendable, porque si no, puede no verse todo en pantalla (y el scroll vertical es mucho menos "costoso" que el horizontal), o descolocarse por completo en impresora. Es una cuestión de productividad a la hora de buscar errores o analizar el programa para entenderlo y/o ampliarlo.

Los 70's molaban, ¿Qué problema hay en organizar y estructurar el código de esa forma?.

Tu código es tuyo. Puedes estructurarlo como quieras. El problema es cuando intervienen otras personas, y en ese caso debes intentar buscar "la mínima sorpresa". Y si es un proyecto grande (este apenas son 260 líneas) hay que planificar bien cómo se va a dividir, para poder repartir trabajo.

Ah, lo olvidaba...¿El  método "menú" de la clase principal podría/debería verse como una clase aparte al estilo de las interfaces?

Posiblemente sí. La pega es que estás llegando ya a casi 7 clases para un programa que podría estar hecho "a la antigua usanza" en menos de 100 líneas de código. Sólo tiene sentido fragmentar tanto cuando vas a reutilizar código (por ejemplo, si ese proyecto, como comentaste, se va a portar a modo gráfico).

Estaba pensando en crear una superclase llamada "InterfazBasica" y que "Interfaz1Variable" e "Interfaz2Variables" heredasen de ella, pues son clases muy similares, pero no veo cómo lidiar con la creación y uso de los objetos Calculos1Variable, Calculos2Variables, etc... sin tener que redefinir -por ejemplo- el método "crearVariable" cada vez que una clase lo herede.

Te remito a la última respuesta. Quizá sea "rizar el rizo" demasiado. Si son clases de 20 líneas, posiblemente no merecerá la pena. Yo creo que este programa se puede hacer con 3 clases: Variable1D, Variable2D (ninguna de las dos debería acceder a pantalla ni teclado), y el programa principal, que usa éstas para realizar cálculos a petición del usuario.


15-Dec-2014 15:06
Angel Bravo

Gracias por la respuesta.

Hacer tantas clases y fragmentar el código tanto es porque la intención no es hacer un programa pequeño, sino poco a poco hacer uno bastante grande, por eso no tiene sentido ahora pero lo tendrá. Quiero por tanto, sentar las bases, de ahí mi interés en tener una clase "Variable" de la que se desprenda el resto y se reutilice, pues sus métodos sí o sí, se van a usar en otras clases. Quizá peque de ambición al querer abarcar demasiado para el nivel que tengo, pero bueno...de algo hay que morir...además, considero portarlo a Android por lo que tengo en mente el modo gráfico.

"Son problemas parecidos, pero sólo parecidos. Yo no acabo de ver razonable que herede una clase de la otra, y menos aún que para una variable necesites pasar dos parámetros, indicando siempre un índice que, en un problema normal, será siempre 0. "

Por ejemplo, tengo pensado crear una clase para el cálculo ANOVA -no sé hasta qué punto conoce la estadística- lo que implica el cálculo de medias, varianzas y cuadrados de varios niveles y/o variables...qué mejor que tener esos cálculos ya heredados de otra clase con un índice listo para usar. Eso supone obligar a ver el análisis unidimensional como una matriz pero a cambio da potencial de uso al resto de clases. Casi podríamos decir que el análisis de una variable es la herramienta para los cálculos "de verdad" ANOVA, ANCOVA, Muestreo bietápico, estratificado, matriz de distancias, multivariante...etc.

Con respecto al jaleo de nombres es un error mío al usar varios programas a la vez (Geany y Netbeans, que te obligan a crear un proyecto, y demás)  si quiere lo puedo ordenar y volverlo a colgar para que resulte más cómodo...además de que le pueda ser útil a alguien más.


15-Dec-2014 15:13
Nacho Cabanes (+30)

Tengo la estadística a nivel universitario muy oxidada ya.

Pero recuerda que:

a) Cada clase debe tener una misión clara, debería ser comprensible por cualquier usuario de tu clase, y no debería mostrar detalles internos. En ese sentido, "public double GetData(int pos)" parece más razonable para obtener un dato de una variable unidimensional que "public double GetData(int indiceVariable, int pos)", mucho más oscuro (¿por qué un indice de variable en una variable unidimensional?).

b) El heredar de una clase hará que sus métodos sean accesibles desde la subclase. En este caso, no deberías hacer que Variable2D sea subclase de Variable1D, o estarías dejando usar un "public double GetData(int pos)" que no tiene sentido usar en una 2D.

Por eso insisto, en que para mí, una variable 2D contiene 2 (o más) variables 1D, pero no hereda de ellas. E insisto, que en que si añades un "indiceVariable", estás quitando el significado a una variable1D.


15-Dec-2014 19:18
Angel Bravo

¿Y tirar por la calle de enmedio?, que la clase de una sola variable vaya aparte, y que todas las demás hereden de la clase de dos variables, colocando ahí esos cálculos básicos con el índice para que el resto de clases hereden de ella y no de "variable1D", y dejando ésta unidimensional sin ningún índice.






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)