lunes, 30 de junio de 2025

Datos. OOo Basic

Tabla de frecuencias


La organización de los datos en una tabla de frecuencias es una buena forma de apreciar la distribución de éstos, además de ser la base para crear gráficos estadísticos, los cuales, a su vez, son la mejor forma de visualizarlos y una herramienta fundamental para el análisis de datos. 


La construcción de una tabla de frecuencias resulta un tanto tediosa si trabajamos con hojas de cálculo (1), aunque disponemos de algunas funciones que nos ayudan en el proceso. Veamos...
  • Deberemos identificar el máximo (MAX()) y el mínimo (MIN()) de la distribución de datos en la variable.
  • A partir de ella, creamos una columna específica en la que escribimos los valores obtenidos en el punto anterior, obviamente del mínimo al máximo (2)
  • Una vez creada esa lista, en cada celda asociada a cada uno de esos valores aplicaremos la función CONTAR.SI() sobre el listado original de datos (3), lo que nos dará como resultado el recuento de ese valor en la distribución, esto es, su frecuencia. 
  • Una vez creada la primera fórmula podemos generalizarla arrastrándola hasta el fin del listado de valores, siempre y cuando hayamos establecido correctamente el valor a buscar en base a la lista de los posibles.
Tenemos algún que otro motivo para crear un script que automatice todo este procedimiento, por ejemplo, aunque no sólo, en OOo Basic, así que vamos a intentarlo.

Partimos de la correcta obtención de los valores de la variable, evitando errores de tipología de datos, según vimos en una [entrada anterior], y utilizamos un procedimiento básico para obtener la frecuencia con lo que se presenta un valor: recorrer toda la matriz de datos (bucle For) buscándolo mediante una fórmula condicional (If x = y Then). Claro que previamente tenderemos que establecer en una matriz los valores a buscar, y además que ese procedimiento se desarrolle de forma automática.

Veamos cómo calcular la dimensión de la matriz que debe contener los valores X, desde el inicial (o mínimo) hasta el final (o máximo), siendo el conjunto los valores anteriores más todos los intermedios. 

Como valor mínimo, salvo caso contrario, tomaremos siempre 1 y calcularemos el valor máximo mediante un bucle que recorre las matriz de los valores (en nuestro caso mNumDocum()) realizando una autocomparación:

For i = LBound(mNumDocum()) To UBound(mNumDocum())
If Max <= mNumDocum(i) Then
Max = mNumDocum(i)
End If
Next

El resultado obtenido será el valor máximo (más elevado) de todos los que presenta la variable (DocFin). Con él dimensionaremos las matrices X (Dim mValorX(Max-1) As Integer) y Fx (Dim mFx(Max-1) As Integer).

Los valores de mValorX() se establecen mediante un bucle simple...

For i = 0 To UBound(mValorX())
mValorX(i) = i+1
Next

... pero los de mFx() requieren, además de una variable auxiliar (Dim f As Integer), una estructura compleja formada por dos ciclos For anidados que contienen en su interior un condicional If

For i = 0 To UBound(mValorX())
For b = 0 To UBound(mNumDocum())
If mNumDocum(b) = mValorX(i) Then
f = f + 1
End If
Next
mFx(i) = f
f = 0
Next
  • El condicional se encarga de comprobar la presencia de determinado valor (ahora X) (If mNumDocum(b) = mValorX(i) Then), realizando un recuento (f = f + 1)
  • Mientras que los bucles garantizan que se recorren todos los valores de la matriz de datos principal (For b = 0 To UBound(mNumDocum()))...
  •  ... y la matriz auxiliar de valores X (For i = 0 To UBound(mValorX()))
  • El resultado del recuento se guarda en las posiciones de la matriz Fx (mFx(i) = f) fuera del bucle interior...
  • ... momento en que, inmediatamente después, se devuelve f a su valor original (f = 0) para reiniciar el proceso.
Aunque lo que sigue no es realmente parte necesaria de este script, sí resulta conveniente para evitar la pérdida de datos que supondría la mera ejecución del script. Me refiero a guardar el resultado en una hoja nueva. De ello se encarga el código que sigue:

Dim oHojas As Object, oHojaNueva As Object
Dim sNombreHoja As String
Dim Col1 As String, Col2 As String
sNombreHoja = "TablaFrec"
oHojas = ThisComponent.getSheets()
oHojas.insertNewByName(sNombreHoja, oHojas.getCount())

oHojaNueva =ThisComponent.getSheets().getByName(sNombreHoja)

Col1 = "Valores X"
Col2 = "F(x)"

Dim oCeldaNueva As Object

oCeldaNueva = oHojaNueva.getCellRangeByName("A1")
oCeldaNueva.setString(Col1)

oCeldaNueva = oHojaNueva.getCellRangeByName("B1")
oCeldaNueva.setString(Col2)

For i = 0 To UBound(mValorX())
oCeldaNueva = oHojaNueva.getCellRangeByName("A" & i +2)
oCeldaNueva.setString(mValorX(i))
Next

For i = 0 To UBound(mFx())
oCeldaNueva = oHojaNueva.getCellRangeByName("B" & i +2)
oCeldaNueva.setValue(mFx(i))
Next

La consecuencia de todo este trabajo es la automatización de la creación de una tabla simple de frecuencias y su escritura en la hoja de cálculo. A partir de ella podremos realizar lo que consideremos pertinente, incluyendo la elaboración de gráficos y el cálculo de los estadísticos (4)

NOTAS

(1) Lo cierto es que resulta más complicado describirla que realizarla, como veremos a continuación. Suele suceder.
(2) Salvo que optemos por crear una tabla de intervalos. En ese caso, el tema se complica un poco más, ya que necesitamos establecer primero esos intervalos y después realizar los agrupamientos de datos que correspondan; cosa que no va a suceder aquí. En esta entrada me voy a limitar a la creación de tablas de frecuencias simples, esto es, sin intervalos. 
(3) CONTAR.SI() es una función estadística. El modo en que se crea esta función, y todas las funciones Calc, está claramente indicado en el Asistente para funciones, así que no me detengo aquí en explicaciones innecesarias. Esta ayuda incluye documentación web complementaria.
(4) En breve desarrollaré un análisis de datos que implica el uso de este script y de otros presentados en entradas anteriores de los cuales éste es continuación.

domingo, 29 de junio de 2025

Datos. OOo Basic

Obtención de los valores


Anteriormente, [en una entrada reciente] comentamos que, por diversas razones, podíamos encontrarnos con datos aparentemente numéricos que no lo son, lo que da lugar a errores de cálculo que pueden llegar a ser difíciles de detectar. Dijimos entonces que, para evitarlo, contábamos con funciones Calc, como VALOR(), que convierten un dato textual a numérico, siempre el dato original sea compatible.


En ese momento aparcamos al opción OOo Basic en aras de la rapidez en la solución al procedimiento, pero ahora, centrándonos en el interés por la automatización de los procesos, debemos retomar esa opción. También en este caso, la fórmula script es más compleja que el uso de Calc y sus funciones, que éstas no nos permiten logar la automatización que es ahora el objetivo a alcanzar.

Sub CuantificaF

Dim oHoja As Object, oCelda As Object
Dim mTipoDato() As Integer
Dim mNumDocum() As Integer
Dim i As Integer
Dim a As string
Dim N As Integer

'En otro caso se calcula N mediante script. Ahora lo establecemos directamente mediante N
N = 481
ReDim mTipoDato(N)
ReDim mNumDocum(N)

'Capturamos el tipo de dato Fx (1 int vs. 2 str)------------------------------------------

oHoja = ThisComponent.getSheets().getByName("Listado")

For i = 0 To UBound(mTipoDato())
oCelda = oHoja.getCellRangeByName("D" & i+2)
mTipoDato(i) = oCelda.getType '1 int vs. 2 str
Next

'Trasladamos los valores Fx a la matriz mNumDocum() al tipo de dato ------------------

For i = 0 To UBound(mTipoDato())
oCelda = oHoja.getCellRangeByName("D" & i+2)
If mTipoDato(i) = 1 Then
mNumDocum(i) = oCelda.getValue
ElseIf mTipoDato(i) = 2 Then
a = oCelda.getString
mNumDocum(i) = CInt(a)
End If
Next

End Sub

No es esta la única forma de plantear el script y podemos simplificarlo significativamente desarrollando los dos subprocesos (los dos bucles) en uno sólo, pero he preferido plantearlo de este modo para facilitar la comprensión de lo que pretendemos lograr.

Otra posible opción en incluir en el script (o en llamada a función) el cálculo (automatización) del valor N, pero, como explico en comentario, opto por obviar  esa parte del proceso de automatización mediante la fórmula de dar valor directamente a la variable N (N = 481), redimensionando las matrices de acuerdo con ese valor (v.g. ReDim mTipoDato(N)).

Pero lo realmente interesante y definitorio del script se produce a continuación y se desarrolla en el marco de un bucle For (For i = 0 To UBound(mTipoDato())), previo acceso a la hoja (oHoja = ThisComponent.getSheets().getByName("Listado")) e incluyendo el acceso a las celdas en las que se ubican los valores de la variable (oCelda = oHoja.getCellRangeByName("D" & i+2)).

Al contrario de lo que solemos hacer, vamos a obtener primeramente no el dato contenido en la celda, sino información sobre el tipo de dato que contiene. Para ello, [como ya sabemos], debemos emplear la función getType(), que ahora asociamos a los elementos de la matriz (Dim mTipoDato() As Integer) destinada a ese objetivo (mTipoDato(i) = oCelda.getType). De este modo mTipoDato() pasa a contener información sobre el tipo de dato (si str 2, si int 1) que contienen las celdas donde hemos almacenado los valores individuales de nuestra variable.

Aunque, como dije antes, podríamos simplificar el script, he preferido diferenciar el proceso anterior del siguiente, a fin de facilitar la comprensión de la lógica del procedimiento: ahora, mediante un nuevo bucle y tomando como referencia (y condición) el valor 1 vs. 2 que contiene la matriz mTipoDato(), asignamos a los elementos de la matriz mNumDocum() el contenido de cada celda. Si ese contenido es numérico (If mTipoDato(i) = 1 Then), lo hacemos directamente (mNumDocum(i) = oCelda.getValue), pero si es texto (ElseIf mTipoDato(i) = 2 Then), entonces capturamos el dato sobre una variable str (Dim a As string) mediante la función getString() (a = oCelda.getString), como paso previo para, previa conversión, trasladar ese valor (ahora ya int) a la matriz receptora (mNumDocum(i) = CInt(a)).

Datos. OOo Basic

Cálculo del número de elementos


Trabajando con Calc, para saber cuántos elementos tiene una variable (n) únicamente necesitamos echar un vistazo a la columna en la que hemos guardado los datos: normalmente suele ser el identificador numérico Fila menos 1 (vg. 482 - 1 = 481), debido a que la primera fila suele estar ocupada por el identificador o nombre de la variable. El problema es que este sencillo procedimiento no nos permite automatizar determinadas operaciones, o no del todo.


Para automatizar la identificación del valor n (o N) de una variable (cuántos elementos tiene) podemos aplicar el siguiente procedimiento, que es mucho más largo y complejo que el anterior, pero que nos permite prescindir de la participación humana, que es en lo que consiste automatizar un procedimiento.

Sub NumeroElementos

Dim oHoja As Object, oCelda As Object
Dim Hoja As String, Col As String
Dim Tipo As Integer, i As Integer, n As Integer

Hoja = "Listado"
Col = "D"
n = 0
oHoja = ThisComponent.getSheets().getByName(Hoja)

For i = 0 To 1000
oCelda = oHoja.getCellRangeByName(Col & i+2)
Tipo = oCelda.getType
If Tipo <> 0 Then
n = n + 1
ElseIf Tipo = 0 Then
Exit For
End If
Next

MsgBox n

End Sub

Podemos darle otra formulación a este script (por ejemplo, en formularlo como función), y prescindir de determinadas instrucciones (vg. MsgBox n), pero en esencia lo que hacemos es lo siguiente:
  • Accedemos a la hoja (oHoja = ThisComponent.getSheets().getByName(Hoja))
  • ... y a la celda (oCelda = oHoja.getCellRangeByName(Col & i+2))
  • en este caso dentro de un bucle For (For i = 0 To 1000) que nos permite recorrer el conjunto de la columna condicionando el procedimiento de acceso al contenido (Tipo = oCelda.getType)...
  • mediante un condicional If complejo asociado al valor resultante
  • para incrementar un numeral de conteo en caso positivo (If Tipo > 0 Then) -> (n = n + 1)
  • o salir del bucle en caso negativo (ElseIf Tipo = 0 Then) -> (Exit For)

LibreOffice. Calc.

Celdas. Acceso al contenido.


Para acceder al contenidos de una celda utilizamos diferentes funciones en OOo Basic, la más común, pero no la única es getString().


Gracias a la función getString() podemos pasar a una variable (o a un componente de una matriz) el contenido de una celda que se sea texto. 

sNombre=oCelda.getString()

Pero no es la única función, ya que también disponemos de getValue() para cuando el contenido es un número.

iDato=oCelda.getValue()

Podemos conocer si ese contenido es textual (vs. numérico) usando las funciones Calc ESTEXTO() (que devuelve True/VERDADERO vs. False/FALSO) o ESNUMERO() (de igual pero inverso resultado). 

Como alternativa a estas funciones Calc podemos utilizar la función OOo Basic getType() que devuelve un valor numérico (integer) que nos informa de qué tipo de contenido tiene la celda, dato que nos permite usar la primera o la segunda función OOo Basic:
  • Valor 0 para celda vacía
  • Valor 1 para celda con texto (string)
  • Valor 2 para celda con número (integer)
  • Valor 3 para celda con fórmula como contenido
Por ejemplo...

iTipo = oCelda.getType()

If iTipo = 0 Then

MsgBox("La celda está vacía")

ElseIf iTipo = 1 Then

 Nombre=oCelda.getString()

ElseIf iTipo = 2 Then

 iDato=oCelda.getValue()

End If

 

jueves, 26 de junio de 2025

Datos. OOo Basic

Análisis de datos. Fase previa.



Aunque lo ideal sería que esto no fuera necesario, lo real es que es esta una fase fundamental en todo proceso de análisis de datos, incluso cuando somos nosotros mismos los que hemos realizado la tabla de datos. Incluso en ese caso es necesario realizar el pre-análisis y la limpieza de datos.



Aunque es un tema interesante, no me corresponde a mí desarrollar aquí una explicación de detallada de en qué consiste y cuáles son las fases del análisis de datos. Hay información sencilla y bien desarrollada en la web a disposición de quienes estén interesados en estos temas, así que os animo a bucear en la red (1)

Lo que sí me corresponde (creo) es concretar algunas cuestiones relacionadas con la forma de llevar a la práctica esas fases y ese proceso; más que nada por simplificar y definir cómo se concreta el abordaje de algunas cuestiones; esta del preanálisis y la limpieza de datos, sin ir más lejos.

He observado que algunos de los procedimientos de creación de una base de datos simple (sobre Calc) puede dar lugar a errores que son fáciles de detectar si uno se fía de las apariencias... y de lo que cree haber hecho correctamente, que sí, pero no. Me explico.

Al crear el listado de datos (columnas, generalmente) es posible que inadvertidamente (y por diferentes causas) los estemos escribiendo (o se escriban) en realidad como caracteres alfanuméricos. Para nosotros son números, pero para el ordenador son "letras" y claro, las letras se juntan, pero no se suman... Aparentemente todo está bien, pero no es verdad. Incluso aunque al aplicar formulas (Calc) no se observe error, lo cierto es que podemos estar calculando un estadístico sólo con una parte de los datos, cosa que evidentemente conlleva la comisión de errores muy graves.

Es por ello que animo a que antes de proceder a realizar ningún cálculo sobre los datos de esas tablas que aprendimos a crear sobre Calc, sea manualmente, y más aun si es mediante código, realicemos eso que aquí se llama pre-análisis y que, en este contexto concreto revisión de los datos.

En Calc (o sea, manualmente) podemos hacerlo aplicando a la columna de datos y a cada uno de sus datos, la función VALOR() (vg VALOR(B2)). Esta función convierte un texto en número (2), por lo que tenemos ahora asegurado que todos los valores de nuestra variable (columna) son valores numéricos.

Supongo que habrás advertido que lo que aquí tenemos es la función de conversión de texto (string) a número (integer) que conocemos en OOo Basic como CInt() y en Python como int(), por lo que no te será difícil desarrollar el script de conversión en cualquiera de ambos lenguajes; aunque es posible que prefieras limitarte al trabajo con Calc. Más sencillo y directo sí que es, pero si tenemos que hacerlo con muchas columnas (variables) y con muchos datos, a lo mejor te sale a cuenta crear el script correspondiente. De momento, queda de tu mano.

NOTAS

(1) Y que cada uno proceda como considere más adecuado. Aquí sólo aporto [una referencia en este enlace] meramente a modo de ejemplo. No estaría de más realizar alguna que otra lectura "analógica", que bibliografía especializada no falta. Un ejemplo: Pardo Merino, A. (2009) Análisis de datos: en Ciencias sociales y de la salud I. Ed. Síntesis. Se complementa con los volúmenes II (2010) y III (2012), así que hay lectura para rato... Y muy interesante.
(2) Evidentemente si lo que deseamos convertir es convertible. Una letra o una palabra no lo es. 

martes, 24 de junio de 2025

Datos. Estadística.

Medidas de dispersión (III)


El tercer conjunto de estadísticos de dispersión tienen en común su dependencia del promedio, además de ser los usados con más frecuencia en los análisis de datos  como medidas de dispersión. Me refiero a la varianza, la desviación típica y, en menor medida, el coeficiente de variación de Pearson.


Entendemos por varianza el sumatorio de las diferencias entre los valores o datos y el promedio del conjunto, elevadas al cuadrado para eliminar el efecto del signo de la diferencia y dividido entre el valor N o población (1).


La desviación típica resulta de calcular la raíz cuadrada de la varianza, lo que equivale a revertir la potencia (al cuadrado) aplicada a la diferencia valor-promedio, con la ventaja (para los cálculo) de haber anulado antes el efecto de los valores negativos de dichas diferencias (2).


Finalmente el coeficiente de variación es una medida de dispersión relativa que resulta de dividir la desviación típica entre la media...

CV = S/Med 

... lo que supone calcular el número de veces que la desviación contiene a la media y permite afirmar el grado de representatividad de la media en una distribución (3).

En Calc contamos con fórmulas para el cálculo de la varianza muestral (VAR()) y poblacional (VAR.P()), lo mismo que para el cálculo de la desviación típica (Dt) muestral (DESVEST()) y poblacional (DESVEST.P()), aunque también podemos calcula la Dt aplicar la raíz cuadra al resultado del cálculo de la varianza.

Además podemos realizar el cálculo de forma manual, desarrollando el procedimiento que deriva del cálculo manual de la varianza:
  1. Calculamos la diferencia entre el dato o PD y la media del conjunto (A3-$G$4)
  2. Elevamos al cuadrado esa diferencia () o realizamos la operación equivalente (B3*B3)
  3. Extendemos ambas fórmulas al conjunto de los datos
  4. Calculamos el sumatorio de 2->3 (SUMA(C3:C48))
  5. Y dividimos el resultado 4 entre N (4) para la varianza poblacional o N-1 para la varianza muestral.
A partir del cálculo de la varianza procedemos a calcular la Dt aplicando la fórmula la de la raíz cuadrada (RAIZ(K28)) (5)

Respecto al coeficiente de variación de Pearson, no existe una función Calc específica, pero el cálculo no presenta mayor dificultad aplicando la fórmula que ya conocemos (6).


NOTAS

(1) Esta fórmula es válida para el cálculo de la varianza poblacional, pero si se desconoce y lo que se calcula es la varianza muestral, el sumatorio de las diferencias se divide entre N-1. La varianza es el estadístico de más transcendencia de los tres, ya que sobre su análisis (análisis de varianza) se construyen modelos de análisis de la llamada estadística inferencial.
(2) La misma modificación de la fórmula que para la varianza respecto a la muestra (N-1). La desviación típica o estándar es posiblemente el estadístico de dispersión de más uso. 
(3) Con frecuencia se expresa como porcentaje, resultando de la fórmula Cv = (S/Med)*100. Si Cv = 0 se considera óptima la representatividad de la media; sí Cv =< 0,3 la representatividad de la media es óptima.
(4) Para calcular N usamos la fórmula CONTAR(A$3:A$48)
(5) Esta posibilidad de cálculo manual también lo es de desarrollo de un procedimiento enteramente basado en OOo Basic, pero resulta innecesariamente complejo pudiendo recurrir, aquí también, al uso de las fórmulas Calc dentro de un script.
(6) Ni en Calc ni en Excel. En ambas se debe recurrir al uso manual de la fórmula S/Med

lunes, 23 de junio de 2025

Datos. Estadística

Medidas de dispersión (II)


La segunda referencia para el análisis de la dispersión de los datos son los cuantiles o medidas de posición (no central). En concreto hablamos de la distancia a la que se encuentran dos medidas cuantiles, como son los cuartiles y los deciles.


En el primer caso se concreta como distancia entre el Q1 y el Q3, lo que da lugar a la medida denominada recorrido intercuartílico

Ri = Q3 - Q1

La segunda medida de dispersión se denomina rango entre percentiles y es la distancia que hay entre el percentil 10 y el percentil 90

Ri = Pc90 - Pc10

En ambos casos no disponemos de funciones Calc para su cálculo directo, pero podemos obtener estos estadísticos a partir de las funciones que nos permiten calcular los cuantiles implicados en las operaciones (1).

La ventaja de estas medidas de dispersión es que se evita la influencia de los valores extremos, deficiencia que sí se produce en las medidas de dispersión basadas en los valores MIN() y MAX() (2). No obstante, estas medidas no son las únicas disponibles ni las más empleadas, especialmente cuanto trabajamos con variables numéricas (intervalo y razón). A estas últimas dedicaremos la siguiente entrada.

NOTAS

(1) Puedes consultar [esta entrada]

(2) Que ya vimos en la [entrada precedente]

Datos. Estadística

Medidas de dispersión (I)


Las medidas de posición (1) normalmente no son suficientes para describir adecuadamente una colección de datos, siendo necesario conocer cómo se distribuyen estos. Además, cuanto más dispersos se encuentran los datos, menos representativas del conjunto resultan las medidas de posición, especialmente las medidas de tendencia central.


Es por esto que inicio con esta entrada el estudio de un conjunto de medidas que analizan esa distribución o dispersión de los datos. Aunque no son las únicas medidas que necesitamos conocer para afrontar el análisis descriptivo univariado de un conjunto de datos (2), sí son críticas para poder iniciarlo en términos prácticos y tomando como eje el análisis de la distribución de los datos, que es nuestro objetivo inmediato.

Dado que estas medidas son varias, he preferido dividir el abordaje del tema en varias entradas. En la actual, que es la primera, nos ocuparemos de dos medidas de dispersión en concreto: el recorrido o rango y el coeficiente de apertura.

Ambos precisan el conocimiento de dos referentes básicos, que son el valor mínimo y el valor máximo del intervalo; estadísticos que, de por sí, no presentan mayor dificultad para su cálculo, cualidad que comparten con los estadísticos derivados antes indicados: es suficiente con haber ordenado los datos de nuestra distribución (3) en orden creciente (de menor a mayor) e identificar los valores mínimo y máximo de los que existen realmente datos, esto es, que se observan con cierta frecuencia) (4).

Aunque, como ha quedado claro antes, realizar estos cálculos es sumamente sencillo, Calc (5) dispone de funciones para automatizar el cálculo del mínimo (MIN()) y del máximo (MAX()) de un conjunto de datos, mientras que su cálculo mediante OOo Basic se vuelve relativamente complejo. Por este motivo utilizaremos esas fórmulas cuando necesitemos saber esos valores tanto cuando realicemos el análisis directamente mediante fórmulas Calc como cuando lo hagamos mediante un script (6).

Necesitamos conocer estos valores por ser la base para el cálculo del recorrido o rango...

R = MAX() - MIN()

... y del coeficiente de apertura...

Cap = MAX()/MIN()

... estadísticos que, como se puede apreciar, no presentan mayor dificultad para su cálculo, pero que tampoco nos permiten una adecuada descripción de los datos, ya que son muy sensibles a los valores extremos, por lo que para el análisis descriptivo de la muestra, además de necesitar la combinación de su análisis con el de las medidas de tendencia central (especialmente la moda y la mediana), también es posible que necesitemos considerar otras medidas de dispersión. De ellas hablaremos en la entrada que sigue a la actual.


NOTAS

(1) Con medidas de posición me refiero a las medidas de tendencia central y a las medidas de posición no central vistas en entradas anteriores [en esta sección].
(2) Para llegar a este nivel aun necesitamos conocer las medidas de concentración y de forma. De ellas nos ocuparemos en otra subsección de la actual.
(3) Aunque se trate de una simpleza, me parece pertinente recordar que estamos hablando de una única colección de datos, dado que nos situamos dentro del análisis univariado, esto es, el análisis de una única variable.
(4) El cual no tiene por qué coincidir con el valor mínimo teórico. Del mismo modo sucede con el máximo. Por ejemplo, una prueba tiene como valor o resultado mínimo el 0 (0 aciertos) y el máximo el número máximo de puntos posibles, que son tantos como permita el número de ítem y el valor resultante del sumatorio de todas las puntuaciones de cada uno de los ítem. Por ejemplo, en una prueba de 25 ítem, el máximo teórico sería 25 si cada uno de los ítem vale 1 punto; pero valdrá 50 si cada ítem vale 2 puntos. La diferencia entre los valores teóricos mínimos y máximos frente a los valores (reales) del mínimo y del máximo es en si misma interesante para el análisis de los resultados de un colectivo en una prueba, pero carecen de interés para el tema que nos ocupa en esta entrada; no obstante es necesario diferenciar conceptos.
(5) También Excel
(6) Para ello recuerda los visto [en esta entrada]

domingo, 22 de junio de 2025

Expedientes


DocAp para la sistematización del expediente





Caben diferentes enfoques, que no sólo distintos procedimientos en relación con esta cuestión, así que la que aquí se muestra es una de ellas; una ciertamente muy básica y de modestas pretensiones, pero interesante por lo que se ha conseguido con ella y por la información que me ha permitido recoger, permitiendo vislumbrar una tercera dimensión del trabajo con los expedientes SEO: su análisis y con él, el análisis de la práctica de los SEO tal y como queda reflejada en la documentación que deriva de ella.

Pero no es esta la cuestión que abordaré en esta entrada; en ella me limitaré a revisar el DocAp primitivo, necesidad que deriva del tiempo transcurrido desde su formulación pensando en un posible desarrollo futuro.

Se trata, como quedó dicho [en su momento] de un DocAp complejo un tanto atípico, ya que la complejidad en este caso no deriva del empleo de más de un servicio, sino del empleo de un mismo servicio (Calc) (1) con dos funciones: gestionar la recogida de datos (Gestor) y presentar los datos individuales a modo de ficha de alumno (Ficha). Paso a analizar cada una de ellas en detalle.

El Gestor es el soporte principal del DocAp, ya que desde él se realiza el análisis del contenido del expediente. Y es esta una característica y limitación en la que ahora me debo detener: este DocAp no permite automatizar la estructuración y el uso del expediente, se limita a sistematizar la recogida de información de su contenido; pero al facilitar ésta ayuda a sistematizar esa estructuración aunque no a automatizarla. De hecho crea la ilusión de que cada uno de los expedientes "reales" es idéntico en organización al modelo, cuando en realidad los expedientes difieren del mismo (2).

Este soporte aprovecha de Calc la facilidad con la que se puede implementar un formulario en este servicio, estableciendo relación entre los controles y las celdas. De esta forma nos ahorramos codificar una parte importante del procedimiento, que queda a cargo del formulario, sus controles (que se ubican en la hoja identifica) y de la asociación entre éstos y las celdas de la segunda hoja del documento (Datos). Es sobre esta (y sobre los datos que contiene) que se ejecuta la parte del script encargada de capturar los datos que previamente introducimos manualmente como usuarios en los controles del formulario (3)

oHojaDatos = ThisComponent.getSheets().getByName("Datos")

For i = 1 To 3
oCelda = oHojaDatos.getCellRangeByName("B" & i)
sDatosPer(i-1) = oCelda.getString()
Next
For i = 4 To 39
oCelda = oHojaDatos.getCellRangeByName( "B"& i )
iDatosArch(i-4) = oCelda.getString()
Next

En realidad este es el núcleo principal del DocAp, pudiendo considerar el resto como accesorio, aunque sea este un papel secundario muy importante para cómo se configura el DocAp y su funcionamiento. Un ejemplo de ello es la creación de una base de datos sobre parte de los datos recogidos y su escritura en la hoja Listado...

mListado(0) = sDatosPer(0)
mListado(1) = sDatosPer(1)

iSumaDocInic = iDatosArch(0) + iDatosArch(1) + iDatosArch(2) + iDatosArch(3)
iSumaDocFin = iDatosArch(4) + iDatosArch(5) + iDatosArch(6) + iDatosArch(7)
iDifDoc = iSumaDocInic - iSumaDocFin

mListado(2) = iSumaDocInic
mListado(3) = iSumaDocFin
mListado(4) = iDifDoc

oHojaLista = ThisComponent.getSheets().getByName("Listado")

For i= 1 To 500

oCeldaInicio = oHojaLista.getCellByPosition(0,i)
sContenido = oCeldaInicio.getString()

If sContenido = "" Then
For a =0 To 4
oCeldaInicio = oHojaLista.getCellByPosition(a,i)
oCeldaInicio.setString(mListado(a))
Next

Exit For
End If
Next

... de lo que se ocupa la siguientes parte del script (4).

El segundo desarrollo de este proceso secundario (no tan secundario) es aun más interesante, ya que implica el acceso al segundo documento (ModeloExpediente.ods) y el traslado de los datos recogidos mediante Gestor para construir la ficha de expediente de cada alumno. De esto se ocupa la última sección o parte del script principal (5) (6)

mOpciones(0).Name="AsTemplate"
mOpciones(0).Value=True
mOpciones(0).Name="MacroExecutionMode"
mOpciones(0).Value=4

sRuta = ConvertToUrl("D:\ModeloExpediente.ods")
oDoc = StarDesktop.loadComponentFromURL( sRuta, "_blank", 0, mOpciones() )

sHoja = "Datos"

oHoja =oDoc.getSheets().getByName(sHoja)

For i=0 To 2
oDatosPer(i) = oHoja.getCellRangeByName("A" & i+1)
oDatosPer(i).setString(sDatosPer (i))
Next


For i = 0 To 35
oDatosNum(i)= oHoja.getCellRangeByName("A" & i+4)
oDatosNum(i).setString(iDatosArch(i))
Next

sNombreArchivo = InputBox ("Nombre del Archivo")
sRuta = ConvertToUrl( "D:\Expedientes\" & sNombreArchivo & ".ods" )

If Len(Dir(sRuta)) = 0 Then
oDoc.storeAsURL( sRuta, mOpciones() )
MsgBox "El archivo " & sNombreArchivo & ".ods se ha guardado correctamente en el directorio solicitado."
Else
MsgBox "El archivo ya existe. No ha sido sustituido."
End If

Para completar la explicación del DocAp en esta entrada sería necesario dedicar tiempo a explicar el funcionamiento del código del documento ModeloExpediente.ods, pero en realidad no es necesario, ya que éste es irrelevante para el objetivo que me he propuesto con esta revisión (7).

NOTAS

(1) En realidad sí se emplean dos servicios (Calc y Writer), pero en la práctica Writer tiene una función secundaria, por lo que en realidad se puede prescindir de su participación, cosa que haré en este reanálisis del DocAp.
(2) Algunos mucho. De hecho un número importante de expedientes son mucho más simples que el modelo; y lo son para ajustarse a la realidad de la documentación que contienen, que frecuentemente no es tanta ni tan diversa como para que sea necesaria una estructura de directorios tan pormenorizada como la que se da por supuesta en el DocAp.
(3) Esta parte del script se identifica como PASO 1.
(4) PASO 2. En él primero se crea el contenido y después se escribe en la hoja Listado.
(5) PASO 3. Se ocupa de acceder al documento creado ModeloExpediente.ods como plantilla y permitiendo la activación de las macros que contiene. El resto del procedimiento no es muy diferente al visto para acceder a los datos de la hoja Datos de GestorExpedientes.ods (Gestor), pero invirtiendo el proceso, ya que ahora escribimos datos en celdas de la hoja oculta Datos de ModeloExpediente.ods.
(6) Al eliminar los comentarios de esta reproducción del código, puede parecer más complicado, por lo que remito al archivo original para consultar el script completo (comentarios incluidos) desde el IDE.
(7) En otro momento sí trataré sobre este documento, pero desde una perspectiva diferente de la que representa el análisis realizado en esta entrada. En todo caso, el acceso y descarga de los documentos Calc de este DocAp puede hacerse [desde este enlace], sección Documentos.