Mostrando entradas con la etiqueta Calc. Mostrar todas las entradas
Mostrando entradas con la etiqueta Calc. Mostrar todas las entradas

jueves, 16 de abril de 2026

DATOS. Tratamiento de datos

Datos semi-estructurados

Estrategia básica Macro-Docap

Dejamos el procedimiento ofimático de converisón de las tablas pdf, pero sobre todo doc/docx/odt expresado como tabla-registro en nuestra hoja de cálculo, pendiente de su última transformación: de vertical a horizontal, a fin de presentarse como una tabla de doble entrada en la que cada columna es un campo o variable y cada fila una colección de datos o registro. Este último paso es más que una formalidad, ya que la recuperación y el tratamiento posterior de los datos y la propia creación de la base de datos depende de ello.

Para realizar este cambio Calc dispone de la función Trasponer, el cual IA-Gemini explica del siguiente modo:

  • Primero se selecciona el rango de datos que se quiere transponer
  • Clic derecho + opción Copiar
  • Posicionarse en la celda de destino que ocupará el primer campo de la base de datos
  • Hacer clic derecho y elegir Pegado especial
  • Seleccionar la opción emergente Transponer y hacer clic izquierdo en esa opción

Una vez resuelto el problema de la direccionalidad de este primer registro (y creada de paso la estructura de campos de la base de datos, la adición de nuevos registros nos obliga a procesar las celdas de la configuración vertical de un modo ligeramente diferente:

  • Generamos el nuevo registro (vertical) completo (etiqueta+dato) en la hoja 1 (igual que hicimos antes
  • Pero sólo seleccionamos de esta estrucura la columna de datos, siguiendo el procedimiento anterior
  • En la hoja 2 (base de datos) nos posicionamos en la celda que ocupará el dato del primer campo de este nuevo registro y ejecutamos la misma secuencia de acciones que cuando creamos la base de datos
  • Deberemos respetar escrupulosamente este procedimiento para evitar sustituir un registro antiguo por otro nuevo

Esta es la solución ofimática o manual, pero una vez que nuestros datos básicos ya están en una hoja de cálculo no resulta especialmente complicado automatizar la creación y la actualización de la base de datos (disposición tabular horizontal) mediante una macro y/o un script OOo Basic o la combinación de ambos. Obsérvese que lo que estoy planteando ahora supone un cambio importante respecto al procedimiento manual-ofimático anterior: ahora hablamos de automatizar el procedimiento, no de aplicar sistemática y repetidamente una secuencia de acciones.

Esta alternativa no se diferencia mucho de la que hemos aplicado a la creación de DocAp complejos de evaluación, basados en el desarrollo de un gestor sobre Calc que, entre otras funciones tiene la de recoger sistemáticamente los datos de la aplicación a modo de tabla de datos vertical y, postriormente, trasladarlos a una base de datos acumulativa diseñada en horizontal. Todo ello se basa en un conjunto de script que aquí deberán ser diferentes para cada manipulación de tablas-Word que deseemos realizar, pero como todos ellos se basan en el mismo procedimiento, no será complicado generar un código-base, el cual se ajustará lo necesario para adaptarlo a la nueva base de datos.

La automatización puede dividirse en dos fases y empezar con la selección y copia del contenido del documento .odt, pero lo fundamental se desarrolla asociado a la hoja de cálculo y se basa en un mecanismo básico de captura de datos y escritura de contenido, ambos en base a matriz y bucle. Dado que aquí no estamos desarrollando un procedimiento concretoy completo, me limitaré a construir el código básico de la automatización, el cual responde en esencia a lo que acabo de describir.

Este código está dividido en dos script que aquí creé mediante Grabar macro, en parte para ilustrar la forma más básica de automatizar el procedimiento, por decirlo de alguna manera, el procedimiento más "ofimático". El primer script genera la cabecera de la base de datos (listado de etiquetas de los campos)...



sub CrearEncabezados
rem ----------------------------------------------------------------------
rem definir variables
dim document   as object
dim dispatcher as object
rem ----------------------------------------------------------------------
rem Acceder al dispatcher
document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
rem Acceder a la hoja 1
dim args1(0) as new com.sun.star.beans.PropertyValue
	args1(0).Name = "Nr"
	args1(0).Value = 1
dispatcher.executeDispatch(document, ".uno:JumpToTable", "", 0, args1())

rem Seleccionar las celdas a copiar------------------------------------------
dim args2(0) as new com.sun.star.beans.PropertyValue
	args2(0).Name = "ToPoint"
	args2(0).Value = "$A$1:$A$20"
dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args2())

rem Copiar el contendio de esas celdas----------------------------------------
dispatcher.executeDispatch(document, ".uno:Copy", "", 0, Array())

rem Posicionarse en la hoja 2----------------------------------------------------
dim args3(0) as new com.sun.star.beans.PropertyValue
	args3(0).Name = "Nr"
	args3(0).Value = 2
dispatcher.executeDispatch(document, ".uno:JumpToTable", "", 0, args3())

rem Posicionarse en la celda A1-----------------------------------------------------
dim args4(0) as new com.sun.star.beans.PropertyValue
	args4(0).Name = "ToPoint"
	args4(0).Value = "$A$1"
dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args4())

rem Copiar (transponiendo) el contenido copiado ---------------------------------
dispatcher.executeDispatch(document, ".uno:PasteTransposed", "", 0, Array())

end sub


... y el segundo copia el contenido del registro.



sub CrearRegistro
rem ----------------------------------------------------------------------
rem definir variables
dim document   as object
dim dispatcher as object
rem ----------------------------------------------------------------------
rem Acceder al dispatcher
document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

rem Acceder a la hoja 1
dim args1(0) as new com.sun.star.beans.PropertyValue
	args1(0).Name = "Nr"
	args1(0).Value = 1
dispatcher.executeDispatch(document, ".uno:JumpToTable", "", 0, args1())

rem Posicionados en la hoja 1, seleccionar las celdas B1:B20-----------------
dim args2(0) as new com.sun.star.beans.PropertyValue
	args2(0).Name = "ToPoint"
	args2(0).Value = "$B$1:$B$20"
dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args2())

rem Copiar el contendio de esas celdas----------------------------------------
dispatcher.executeDispatch(document, ".uno:Copy", "", 0, Array())

rem Posicionarse en la hoja 2----------------------------------------------------
dim args3(0) as new com.sun.star.beans.PropertyValue
	args3(0).Name = "Nr"
	args3(0).Value = 2
dispatcher.executeDispatch(document, ".uno:JumpToTable", "", 0, args3())

rem Posicionarse en la celda A2-----------------------------------------------------
dim args4(0) as new com.sun.star.beans.PropertyValue
	args4(0).Name = "ToPoint"	
	args4(0).Value = "$A$2"
dispatcher.executeDispatch(document, ".uno:GoToCell", "", 0, args4())

rem Copiar (transponiendo) el contenido copiado---------------------------------
dispatcher.executeDispatch(document, ".uno:PasteTransposed", "", 0, Array())

end sub


Como puedes ver, ambos script son prácticamente el mismo, y de hecho podríamos trabajar sólo con uno, realizando las mínimas modificaciones de posicionamiento, pero me ha parecido preferible diferenciar la creación de la base de datos (en realidad, la escritura de las etiquetas de los campos) de la copia del contenido del registro. Cierto que así el primer script sólo se modifica cuando se crea el proyecto concreto (el intervalo de celdas en la Hoja1: args2(0).Value = "$A$1:$A$20"), mientras que el segundo, además del asjuste del intervalo de celdas (args2(0).Value = "$B$1:$B$20") al inicio del proyecto, es necesario modificar también la identificación de la posición de inicio de copia transpuesta en Hoja2 (args4(0).Value = "$A$2") cada vez que creamos un nuevo registro en la base de datos.

La solución anterior no está completa, ya que se puede automatizar más y mejor, pero es suficiente para ilustrar lo que intento decir, así que para este objetivo es suficiente.

Como ves, los datos semi-estructurados, una vez que es posible manejarlos desde una hoja de cálculo, también es posible automarizar su conversión en datos estructurados, al menos en términos de forma. Otra cosa es qué se puede necesitar para realizar esta automatización (en esta entrada hemos visto la forma más simple de hacerlo) y qué limpiezas posteriores requieran ya formalmente como datos estructurados. Pero estas son otras cuestiones.

miércoles, 15 de abril de 2026

DATOS. Tratamiento de datos

Datos semi-estructurados

Tratamiento ofimático

Antes de contar con recursos para manejar los datos que hemos llamado semi-estructurados y entre los que figuran aquellos que proceden de tablas insertadas en documentos de texto, todo el proceso se realizaba "manualmente", aunque sería más apropiado decir que mediante procedimientos ofimáticos. Dado que lo que se pretende en esta sub-sección de DATOS Y TEXTOS es automatizar el procedimiento (en la medida de lo posible) es muy conveniente recordar los orígenes, ya que nos van a servir como punto de referencia.

Empecemos por decir que, ya sea en documentos generados con un procesador de texto o en otro en formato .pdf, las primeras tablas con las que hemos trabajado han estado originalmente en este formato, cumpliendo funciones de formulario o como recurso para estructurar la presentación de la información. Esto es importante decirlo, ya que pudiera parecer que en el principio estuvieron las hojas de cálculo, cuando en realidad han sido un recurso incorporado tarde, mál y pobremente, y no pocas veces de forma un tanto obligada o forzada por demandas externas de rendición de cuentas (estadillos pedidos de NEE y documentos similares solicitados por la autoridad, sea esta quien fuera, los cuales no siempre se presentaban en una hoja de cálculo).

Por diversas causas, a veces esos datos contenidos en tablas-Word han terminado formando parte de una tabla excel, proceso que hace tiempo fue ocasional y que actualmente es mucho más frecuente, pero que en cuanto a procedimiento aun sigue sin ser resuelto de forma sistemática, y eso que, salvo que se empleen procedimientos específicos, ofimáticos o basados en script, lo cierto es que, aunque no presenta mayor dificultad, sí consume demasiado tiempo.

Veamos en qué puede consistir el procedimiento manual-ofimático, partiendo de disponer ya de una copia en texto del contenido de las tablas-Word, resultado de aplicar las funcionalidades Seleccionar->Copiar. Aunque caben varias opciones, para evitar que se copien elementos gráficos no deseados, propongo pegar el contenido en un documento .txt (utilidad Bloc de notas, por ejemplo) como segundo paso.

Lo normal es que lo que se copie reproduzca grosso modo la distribución original del texto, lo que no es aceptable en términos de transformación de los datos en datos estructurados. Por eso motivo propongo actuar manualmente sobre el documento .txt para transformar el contenido en una sucesión de líneas de texto etiqueta-dato, lo que supone eliminar muchos espacios en blanco, diferenciar bloques etiqueta-dato que pueden aparentar ser una unidad, separarlos por líneas y hasta transformar algunos contenidos (por ejemplo las "X" que simulan la elección de una (aparente) opción.

Debo decir que si la tabla cuenta con elementos de formulario es posible que esta estrategia no funcione bien, pero de momento vamos a apartar este tipo de situaciones para no complicar innecesariamente la explicación. Por ahora, los controles de formulario nos obligan a reconstruir el contenido manualmente, así que cuantos menos controles de formulario tenga nuestra tabla mejor para nosotros.

También vamos a eludir ahora la necesidad de valorar algunas opciones de estructuración de los datos cuando se plantean varios como opcionales, tomando provisionalmente partido por la opción más simple: mantener sólo la opción elegida y eliminar el resto.

Una vez asumidas estas opciones y organizados los datos según los criterios antes indicados, dispondermos de un documento .txt simple. De uno o de varios, dependiendo de si optamos por tratar toda la información como un único bloque de datos, o diferenciar cada una de las tablas que originalmente presentaba el documento. Y digo esto porque, de optar por la segunda posibilidad considero preferible diferenciar cada bloque-tabla en un archivo .txt diferente y seguir el procedimiento de transformación también de forma diferenciada. Así mantenemos la unidad de procedimiento y eliminamos espacios en blanco inncesarios o diferenciadores de tablas que a la larga terminan creando confusión en el acceso a los datos.

Partiendo en este caso de la unidad de conjunto de los datos, lo que equivale a considerarlos como una tabla única, una vez limpio el documento .txt (el cual guardaremos al menos provisionalmente hasta disponer de la base de datos/tabla Excel), ejecutamos Seleccionar todo-Copiar, creamos un documento word (Writer) y pegamos el contenido copiado antes en el portapapeles.

Fíjate que usamos el procesador de texto y no directamente la hoja de cálculo para evitar posibles errores en la gestión de la información (que se copie todo el texto en una celda o la pareja etiqueta-dato en una misma celda. Si trasladamos los datos al procesador de texto, podremos realizar una manipulación previa al traslado a la hoja de cálculo que evitará esos problemas de gestión. No obstante, es posible que la copia directa del contenido del .txt en Excel-Calc se resuelva satisfactoriamente y se genere automáticamente un listado vertical en el que se diferencian en sendas celdas la etiqueta y el dato.

Si no ese el caso, o si no quieres correr riesgos, lo que deberás hacer es separar TODAS las parejas etiqueta-dato con un caracter, siempre con el mismo (etiqueta:dato, etiqueta,dato...). Con esto suele ser sificiente para que la utilidad de hoja de cálculo interprete que se trata de una estructura campo-contenido (por lo que incluso te puedes ahorrar el uso del procesador de texto), pero de nuevo si quieres asegurar el existo, lo que debes hacer es seleccionar todo el texto y aplicar la utilidad Writer/Word Tablas|Convetir|Texto en tablas, y seleccionar como separador el caracter empleado en la diferenciación etiqueta-dato (por similitud con el formato .csv te sugiero emplear la coma (,) como separador.

Repito, aunque es posible que otras opciones funciones igualmente, este es el procedimiento que te garantiza que todo salga bien. Con él crearás una tabla-registro donde los campos están dispuestos verticalmente, lo que facilita la lectura del registro pero no la creación de una base de datos en la que acumular los diferentes registros que vayamos creando. Este tipo de base de datos se organiza en horizontal, siendo cada columna un campo y cada fila in conjunto de datos o registro. Transformar una en otra es tema para un posterior desarrollo del procedimiento.

miércoles, 10 de diciembre de 2025

Lenguaje

Fonología

PLON-R. Fonología (IV)




En la [entrada anterior] quedó clara la opción de trabajo con el test en su fase input. Esto nos permite diferenciar entre esa fase, ni digitalizada ni informatizada, de las del análisis de datos, aunque si queremos evitar la pérdida de esa información que hemos descrito como de máximo interés, deberíamos proceder a registrarla digitalizada de forma íntegra.


Pero esa es una cuestión que no tiene por qué ocuparnos ahora y que, además, se puede resolver fácilmente con un sistema de recogida de datos basado en Calc, a modo de base de datos, y que no tiene por qué  ser demasiado sofisticado.

Lo que sí puede serlo, por lo que no me resigno a dejarlo pendiente, es desarrollar la opción que rechacé por inconveniente en la entrada precedente y que implica desarrollar lo que la [opción primera] nos ofrece.

Que conste que este paréntesis en el que planteo la automatización del input de PLON-R. Fonología, o si se prefiere de la reformulación de este test con objetivos específicos, que es lo que en realidad trato de desarrollar aquí, es sólo eso, un paréntesis, del cual nos olvidaremos en la entrada siguiente, porque no es en realidad la opción más adecuada. 

No lo es, es cierto, pero sí es la más interesante desde el punto de vista de la automatización de la evaluación. Por eso no me resisto a plantearla ahora para que no quede en el tintero. Y lo haré de un modo sencillo, sin pensar demasiado en lo que exigiría un recurso finalizado y plenamente funcional. Empezando por trabajar ahí donde lo dejamos con el soporte Calc inicial, con todos los ítem de la prueba y sin más pretensión que automatizar la presentación y la puntuación. Y también ahí donde quedó la explicación de la creación de una presentación automatizada sobre Draw (ver entrada). Con estas advertencias quiero decir que no me voy a prodigar en explicaciones que se pueden dar por sabidas. Es una cuestión de economía de tiempos.

Retomo el soporte Calc tal y como lo vimos en [esta entrada]


Añado un módulo al IDE del documento y copio en él el código relevante de la automatización de la presentación. Además copio en una carpeta (img) todas las imágenes que necesito, configurando así el paquete de la documentación necesaria.

Ahora, sobre la hoja realizo las siguientes modificaciones:
  • Reduzco la hoja Portada a lo que muestra esta captura de pantalla. El botón de comando me servirá en su momento para acceder al script principal.

  • Modifico las fórmulas de la identificación de respuesta (SI(E4="";"";SI(E4="1";D4;"Error"))) y de los sumatorios parciales en Fonología para adaptarlas al tipo de dato que introduce el procedimiento de automatización (fórmula CONTAR.SI(E$4:E$24;"1")).
  • Añado una nueva hoja (Matrices) que contiene la estructura matricial de categorización de los ítem de la prueba (sin modificar su contenido). El resultado no es muy diferente al que mostramos al final de la [entrada anterior].



El código OOo Basic del script principal (Acceso) queda modificado como sigue:

Sub Acceso

Dim oHoja As Object
Dim sHoja As String
Dim oCelda As Object
Dim col As String
Dim i As Integer

'Matrices para acceso a datos
Dim mEdad(60) As Integer
Dim edad As Integer
Dim mPal(60) As string
Dim Pal As string
'Matriz para puntuar la prueba
Dim mPtos(60) As String
Dim r As Integer

'Acceso al libro (ThisComponent), y a la hoja (sHoja)

sHoja = "Matrices"
oHoja =ThisComponent.getSheets().getByName(sHoja)

'Acceso al contenidos de las listas

For i = 0 to UBound(mEdad())
'Acceso a los datos de edad
oCelda =  oHoja.getCellRangeByName( "B" & i+1)
edad = oCelda.getValue()
mEdad(i) = edad
'Acceso a los datos Palabras
oCelda =  oHoja.getCellRangeByName( "J" & i+1)
Pal = oCelda.getString()
mPal(i) = Pal
Next

'Muestra imágenes y puntuación de ítem en función de la edad

Dim edad_sujeto As Integer

edad_sujeto = CInt(InputBox("Edad del alumno o alumna (sólo años)"))

For i = 0 To UBound(mEdad())
If mEdad(i) <= edad_sujeto Then
AbrirImagen(mPal(i))
r = MsgBox ("Puntuación del ítem", 4 + 32 , "PLON-R. FONOLOGÍA")
If r = 6 Then
mPtos(i) = "1"
ElseIf r = 7 Then
mPtos(i) = "0"
Else
mPtos(i) = "-"
End If
End If
Next

'Pasar la puntuación a la hoja Fonología

sHoja = "Fonologia"
oHoja =ThisComponent.getSheets().getByName(sHoja)

For i = 0 To UBound(mPtos())
    oCelda =  oHoja.getCellRangeByName( "E" & i+4)
    oCelda.setString(mPtos(i))
Next
End Sub

Al código del script de base se le han añadido los siguientes componentes:
  • Una nueva matriz para recoger las puntuaciones (Dim mPtos(60) As String)
  • Un procedimiento de puntuación basado en MsgBox() en  el bucle que recorre las imágenes que se muestran al alumno:
r = MsgBox ("Puntuación del ítem", 4 + 32 , "PLON-R. FONOLOGÍA")
         If r = 6 Then
mPtos(i) = "1"
    ElseIf r = 7 Then
mPtos(i) = "0"
    Else
mPtos(i) = "-"
        End If
  • Y, al final del script, un procedimiento para acceder a la hoja Fonología y para pasar la puntuación de cada ítem a las celdas de la columna E, este último basado en un bucle (For i = 0 To UBound(mPtos())).
Además he modificado la ruta absoluta que da acceso a la colección de imágenes en la subrutina AbrirImagen() para adaptarla al actual posicionamiento de las imágenes (sDocum = "D:/Docap/Presenta_PLONR/img/" & img &".png")

Materiales.

Entre otras posibilidades de cambio, pero esta no como opción, debes incluir una carpeta (img) en la que incluir las imágenes asociadas a las palabras del test. También puedes aumentar el tiempo en que se muestra la imagen (ahora wait 1000) y realizar todas las modificaciones del código que consideres pertinentes. Yo por mi parte cumplo dejándote [acceso a este documento].

domingo, 7 de diciembre de 2025

MAV. OOo Basic


Presentación de secuencias de imágenes





Podemos realizar presentaciones de forma totalmente automatizada sin necesidad de crearlas previamente, con imágenes adaptadas al cumplimiento de determinadas condiciones. De este modo es posible automatizar la presentación del input, ajustándolo a necesidades específicas y cambiantes.


Está mal que utilice el icono de Impress para ilustrar el ítem de la entrada, ya que precisamente no es éste el servicio al que se accede (por cierto que por defecto). Tampoco se trata de presentaciones en sentido estricto, aunque se puede simular el funcionamiento básico de una presentación sencilla.

En realidad el servicio que se abre por defecto es Draw, ya que es éste el que en LibreOffice está asociado a los formatos más comunes de imágenes, de modo que, al cargar una imagen lo que sucede es que el sistema de LibreOffice recurre al servicio que tiene disponible.

Pero realmente esto no es lo importante. Sí lo es la utilidad que tiene este procedimiento y su versatilidad para adaptar el contenido (gráfico) del ítem que podemos presentar. No sólo no necesitamos tener preparada una determinada presentación, es que podemos adaptarla de forma automatizada (e interactiva) de acuerdo con las condiciones que deseemos, siempre que, claro está, hayamos organizado convenientemente los materiales gráficos.

Como siempre es mejor explicar sobre la base de un proyecto concreto, en este caso lo voy a hacer sobre el que me ha llevado hasta aquí, aunque ahora tome un breve desvío: la automatización de la presentación de las imágenes del test PLON-R Fonología.

Para simplificar me limito a una cuestión de base en este test: los ítem de trabajo estén en función de la edad del niño, aunque de forma acumulativa. Me explico: a los niños de tres años se les aplican los ítem de su grupo de edad, a los de cuatro los ítem de tres y los ítem de cuatro años, y así sucesivamente hasta llegar a los seis años, edad máxima de la batería PLON-R.

Realmente el procedimiento que estoy desarrollando en relación con ese test es más complejo y diferente en cuanto a prioridades, pero lo que aquí te presento ha surgido a raíz de ese trabajo, motivo por el que será ese nuestro actual contexto: generar presentaciones de imágenes en función de la edad del sujeto.

Se trata, en realidad, de una especie de simulacro (en miniatura) de aplicación de un test de Fonología, ya que no es posible utilizar las mismas imágenes y no tengo ningún interés en sobrecargar el trabajo con una extensa lista de imágenes. Pero estas limitaciones no suponen ninguna merma para la potencialidad de la propuesta.

Empezaré por describir los soportes: una hoja de cálculo como gestor, que incluyen el listado de componentes (hoja Listados) y el código, y una carpeta que contiene las imágenes que vamos a emplear, exactamente 15 imágenes, a razón de 5 por grupo de edad (3, 5 y años).


Aunque, lo verdaderamente interesante está oculto en el IDE de plon_lista.ods, también es importante el listado de edades y de palabras, ya que a ellos se acude para acceder a los contenidos. Este acceso se realiza desde código, ubicado con el IDE tal y como muestra la imagen que precede, concretamente en sus dos módulos (Edad y cargarImg).

El primero, el fundamental, contiene un único script (AccesoPorEdad) que es el que tienes que activar para que funciones el conjunto. En cargarImg se encuentran los auxiliares que hacen posible la carga de las imágenes: una subrutina (AbrirImagen) y una función (AbrirImg): la primera es llamada desde el script y la segunda desde la subrutina. Veamos primero el script.

Sub AccesoPorEdad

Dim oHoja As Object
Dim sHoja As String
Dim oCelda As Object
Dim col As String
Dim i As Integer

'Matrices para acceso a datos (EDAD y PALABRAS)
Dim mEdad(14) As Integer
Dim edad As Integer
Dim mPal(14) As string
Dim Pal As string
 
'Acceso al libro (ThisComponent), y a la hoja (sHoja)

sHoja = "Listados"
oHoja =ThisComponent.getSheets().getByName(sHoja)

'Acceso al contenidos de las listas

For i = 0 to UBound(mEdad())
'Acceso a los datos de edad
oCelda =  oHoja.getCellRangeByName( "A" & i+1)
edad = oCelda.getValue()
mEdad(i) = edad
'Acceso a los datos Palabras
oCelda =  oHoja.getCellRangeByName( "B" & i+1)
Pal = oCelda.getString()
mPal(i) = Pal
Next

'Muestra de elementos (palabras) en función de la edad

Dim edad_sujeto As Integer

edad_sujeto = CInt(InputBox("Edad del alumno/alumna (sólo LOS años)"))

For i = 0 To UBound(mEdad())
If mEdad(i) <= edad_sujeto Then
AbrirImagen(mPal(i)) 'Llamada a subrutina
End If
Next

End Sub
Tras acceder al libro y a la hoja que contiene las tablas (oHoja =ThisComponent.getSheets().getByName(sHoja)), mediante un bucle (For i = 0 to UBound(mEdad())) accedemos al contenido de las listas (esto es, a las celdas) para pasar los datos a sendas matrices (Dim mEdad(14) As Integer Dim mPal(14) As string), mediante un procedimiento sencillo y repetitivo pero muy útil, que se realiza en tres pasos:
  • Acceso a celda: oCelda =  oHoja.getCellRangeByName( "A" & i+1)
  • Asignar el contenido de la celda a variable: edad = oCelda.getValue()
  • Y paso del contenido de la variable a la matriz: mEdad(i) = edad
Una vez que ya hemos pasado los datos a las matrices toca el turno de establecer la condición que va a determinar la segunda parte del procedimiento (la carga de las imágenes).

En este caso, lo que determina la condición en la edad del alumno, así que la solicitamos al usuario (edad_sujeto = CInt(InputBox("Edad del alumno/alumna (sólo LOS años)"))).

Con este dato disponible iniciamos un segundo bucle (For i = 0 To UBound(mEdad())) que va a llamar reiteradamente a la subrutina (AbrirImagen(mPal(i))) pasando como parámetro el nombre de la imagen (siempre que se cumpla el criterio establecido (la edad, menor o igual) mediante un condicional (If mEdad(i) <= edad_sujeto Then)

Lo que sucede a continuación es responsabilidad de la subrutina y de la función a la que ésta llama, pero está todo ello presidido por el cumplimiento de la condición que establecemos en el script y en la reiteración de ese proceso mientras se cumpla esa condición. Veamos esos auxiliares:

Sub AbrirImagen(img as String)
Dim sDocum As String
sDocum = "D:/Docap/presenta_plon/img/" & img &".png"
AbrirImg(sDocum) 'Llama a la función
End Sub
La subrutina se encarga de establecer la ruta absoluta en la que se encuentra la imagen (en este caso un directorio de un soporte externo de memoria: sDocum = "D:/Docap/presenta_plon/img/" & img &".png") y de llamar a la función que se encarga de cargar la imagen cuyo identificador hemos pasados desde el listado.

Function AbrirImg(cRuta As String,Optional aOpciones()) As Object
Dim oDocumento As Object
cRuta = ConvertToURL(cRuta)
If IsMissing (aOpciones) Then aOpciones = Array()
              AbrirCualquierDoc = StarDesktop.LoadComponentFromURL(cRuta,"_blank",0, aOpciones())
      oDocumento = AbrirCualquierDoc  
wait 1000
oDocumento.close(True)

End Function
El correcto funcionamiento de este conjunto depende del escrupuloso respeto del código de esta función, así que debes copiarla tal cual, a excepción del tiempo de espera (wait 1000) que puedes no establecer o hacerlo de mayor o menor duración, y del cierre de los documentos Draw  que se van abriendo y que mediante esta instrucción (oDocumento = AbrirCualquierDoc -> oDocumento.close(True)), que también puedes omitir, se cierran automáticamente transcurrido el tiempo establecido.

Documentos. Este [archivo comprimido] contiene el DocAp, incluyendo la carpeta de imágenes. Recuerda que si lo guardas en otra estructura de carpetas debes adaptar la instrucción correspondiente (subrutina AbrirImagen() instrucción asociada a sDocum).




viernes, 5 de diciembre de 2025

Lenguaje

Discriminación auditiva

Test de Wepman (ADT) (IV)



Muchas son las posibles mejoras que podemos introducir en este material, ya que muchas son sus limitaciones, así que me voy a plantear mejorar el análisis de resultados.


En este caso no se trata de automatizar esta fase del proceso, puesto que esto ya está logrado, si bien se podría mejorar, por ejemplo, sustituyendo las funciones Calc por código OOo Basic, puesto que sabemos que este recurso tiene ciertas limitaciones cuando los análisis son de cierta complejidad y los informes que de ellos derivan tienen cierta extensión. Ahora bien, con los datos con que se trabaja en este soporte y el informe que de su análisis deriva no estaría justificado ese cambio. Menos aun si pensamos que la utilidad de este test y su frecuencia de uso son bastante limitadas desde la perspectiva de la intervención del SEO.

Pero es posible que esta situación cambie si fuera necesario realizar modificaciones en el análisis de los datos, lo que supondría:
  • Identificar los datos con los que trabajar y definir los nuevos cálculos a realizar con ellos.
  • Crear el código para automatizar estos cálculos y su análisis.
  • Modificar el informe de resultados de la prueba y automatizar su redacción.
Estos cambios implican realizar modificaciones que se pueden abordar, cierto es, con los mismos recursos con los que se creó inicialmente el soporte, pero se pueden considerar también una oportunidad para mejorar el procedimiento.

Para plantear cambios en el procedimiento de análisis me voy a centrar en la existencia de dos tipos de ítem en la prueba, dado que considero de interés tener en cuenta esta diferencia dada la función que cumple en relación con lo que considero el principal punto débil del test: el elevado riesgo de que las respuestas al azar incidan en gran medida en los resultados.

Al tratarse de una respuesta dicotómica (igual vs. diferentes), además de garantizar la correcta comprensión de ambos conceptos (tarea que corresponde resolver explícitamente mediante entrenamiento previo), se hace necesario incluir algún tipo de procedimiento que evite que las respuestas aleatorias definan desapercibidamente el resultado. Posiblemente la forma que el autor ha implementado es incluir pares iguales, de modo que está justificado un procedimiento de respuesta tan débil, pero necesario en función de la edad de los sujetos a los que se aplica la prueba. 

No existe problema en solicitar al niño que diga si dos palabras le suenan igual o diferentes, pero sí en que identifique entre dos palabras que se diferencian por un sonido, cuales son los sonidos que las diferencian. En realidad este sería el tipo de respuesta que confirmaría si el niño presenta o no habilidad vs. dificultad para la discriminación auditiva, pero resulta inviable como procedimiento de evaluación en determinadas edades.

Pero al incluir 11 pares iguales (restan 29 pares diferentes) se modifica la referencia para el análisis, dado que las causas de posible error son asimilables a a las previstas (discriminación auditiva) en los pares en los que existe realmente una diferencia, pero no podemos tener esa seguridad cuando el error se comente en pares realmente iguales: en ellos si el alumno dice que esas palabras son diferentes no podemos hacer una atribución causal necesariamente relacionada con una falsa dificultad de discriminación auditiva sin proceder a solicitar una respuesta explicativa, respuesta que la propia formulación del test impide. En este caso el error podría deberse a que sus respuestas sean al azar o por otras causas (¿atencionales?), o por ambas...

Lo que propongo es considerar los errores de forma diferenciada en función del tipo de ítem en el que se produzcan, de modo que no tengan en ambos casos el mismo significado, ya que no lo tienen conceptualmente, o pueden no tenerlo. Es factible atribuirlo a un déficit de discriminación/diferenciación entre dos sonidos cuando tal diferencia existe, pero no cuando no existe; en ese caso es factible considerar el error debido a otras causas. 

Si el peso de ese segundo tipo de error en el conjunto de errores cometidos es igual o superior al peso de los ítem iguales en la prueba, se puede considerar que los errores no lo son por déficit de discriminación auditiva, pero si es inferior y la mayoría de los errores se deben a fallos de discriminación de pares diferentes, entonces sí podríamos hablar de déficit de discriminación auditiva. En todo caso, ese déficit se observaría en relación a los pares de fonemas afectados.

Considero que el replanteamiento del análisis que ahora propongo está en línea con el objetivo de incrementar la utilidad de la información que el SEO aporta a los especialistas del ámbito sanitario. Por ello ahora planteo:
  • Calcular el acierto total y el error total y sus respectivos porcentaje.
  • Cuando el error total alcance un porcentaje determinado (vg. sea superior al 15%), se procede a identificar la fuente del error: sobre ítem igual vs. ítem diferentes y se calcula su peso porcentual respectivo sobre el total de errores.
  • Comparar el porcentaje de error debido a los pares iguales frente al peso de esos ítem en la prueba (11/40 - > 27,5%). Si el % de esos errores sobre el total de errores es superior a ese nivel, se considera que factores no necesariamente identificables como déficit de discriminación auditiva pueden estar teniendo un peso relevante en el conjunto de errores que comete el sujeto, siendo un factor importante en la explicación causal de los mismos. En caso contrario se considera que es probable que los errores observados lo sean específicamente por dificultades de discriminación auditiva. 
  • Seleccionar como pares de fonemas alterados (no discriminados correctamente) únicamente aquellos que presentan oposición fonológica. Informar cualitativamente sobre ellos iniciando la enumeración con una expresión similar a la siguiente: "En todo caso, los posibles errores de discriminación auditiva se presentaría ante los siguientes pares de fonemas:"
Para el desarrollo del algoritmo me basaré en el lenguaje OOo Basic. Mediante este algoritmo se automatizará la puntuación de la prueba y el análisis según los procedimientos previstos anteriormente. Igualmente se automatizará la redacción del informe de resultados. Aquellos cálculos que puedan ser resueltos mediante fórmulas Calc lo serán, por lo que OOo Basic sólo se empleará en esos casos como procedimiento para acceder a las celdas que contienen el resultados. Opto por este procedimiento no por ser el más adecuado, sino por ser el más cercano al original. Se puede considerar que en este caso estamos planteando una solución próxima a la original en términos de desarrollo de nuestra competencia digital.

Muestro en la imagen que sigue la estructura de script que componen esta propuesta, un DocAp que calificamos como complejo dado que usamos dos servicios: Calc y Writer, aunque éste último únicamente a modo de soporte para el informe del test.


Queda señalado en la imagen el script principal (acceso) que será el primero que explique y aquel que deberás activar bien desde Herramientas | Macros | Ejecutar macro (que es el procedimiento propuesto por defecto), bien creando un botón de comando en el soporte Calc asociado a dicho script. 

Además puedes observar otros tres módulos, dos de ellos contienen sedas funciones, cuyos nombres las ilustran, y el tercero (modTexto) contiene una subrutina que permite generar el informe de resultados del test.

Pasemos a analizar más detenidamente el script acceso por ser el principal. Del análisis de los restantes te ocupas tú si te parece necesario.

Sub acceso

Dim oHoja As Object
Dim sHoja As String

Dim Celdas() As String
Dim oDatos(6) As Object
Dim mDatos(6) As String
Dim i As Integer

Dim Aciertos As Integer
Dim Fallos As Integer
Dim Aciertos_p As Double
Dim porcentaje_lim As Double
Dim iValoraError As Integer
Dim sListaErrores As String

Dim sDatosId As String
Dim sResultado As String
Dim sAnalisis As String
Dim sTextoInfo As String

sHoja = "Datos"

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

'Lista de celdas

Celdas() = Array("B6","C6","E6","B7","E7","C11")

'Construcción del listado de celdas con datos relevantes

For i = LBound(Celdas()) To UBound(Celdas())
oDatos(i) = oHoja.getCellRangeByName(Celdas(i))
mDatos(i)=oDatos(i).getString()
Next

Aciertos = CInt(mDatos(5))
Fallos = 40 - Aciertos
Aciertos_p = (Aciertos/40)*100

'Exposición de datos (y resultados)

Dim sTxt(4) As String

sTxt(0) = "El test de Wepman es una sencilla prueba diseñada para evaluar la discriminación auditiva. En ella se plantea al alumno una tarea de decisión (IGUAL-DIFERENTE). Se considera el 85% de éxito como límite entre la correcta discriminación (>= límite) y la presencia de dificultades (<límite)."
sTxt(1) = mDatos(0) & " ha resuelto satisfactoriamente " & mDatos(5) & " ítem de los 40 de que consta la prueba, lo que, en principio, "

porcentaje_lim = 85

If Aciertos_p >= porcentaje_lim Then
sTxt(2) = "permite considerar que no presenta dificultades de discriminación auditiva."
ElseIf Aciertos_p < porcentaje_lim Then
iValoraError = tipo_error(Fallos)
If iValoraError = 0 Then
sTxt(3) = "Realizado este análisis, a la vista de la naturaleza de los errores cometidos, es posible considerar que éstos se deban al azar o a otras causas no necesariamente atribuibles a dificultades de discriminación auditiva."
Else
sListaError = ListaErrores() 
sTxt(4) = sListaError
sTxt(3) = CHr(13) & "Realizado este análisis, el tipo de errores cometidos permiten suponer que es probable que existan dificultades específicas de discriminación auditiva que afectarían a los siguientes pares de fonemas: " & sTxt(4)
End if
sTxt(2) = "obliga a analizar los datos obtenidos en el test, especialmente los errores cometidos, ya que éstos superan el límite establecido (15% como error máximo). " & chr(13) & sTxt(3)
End If

'Textos para informe

sDatosId = "Test de Discriminación auditiva de Wepman" & CHr(13) & CHr(13) & "Datos de identificación: " & CHr(13) & "Alumno: " &_
mDatos(0) & " " & mDatos(1) & "   Edad: " & mDatos(2)
sResultado = "RESULTADOS: " & CHr(13) & "Puntuación: " & mDatos(5) & "/40 aciertos." 

sAnalisis = sTxt(0) & CHr(13) & sTxt(1) & sTxt(2)

sTextoInfo = sDatosId & CHr(13) & CHr(13) & sResultado & CHr(13) & CHr(13) & sAnalisis

CrearInfo(sTextoInfo)

End Sub

Como puedes ver, este script se divide en tres partes: 

  • En la primera se accede a datos relevantes del soporte Calc, necesarios para el desarrollo del procedimiento, incluida la automatización del informe.
  • En la segunda se construye el contenido del análisis de los resultados, y dentro de este proceso (el más complejo del script) se accede, cuando resulta necesario, a las funciones para obtener los datos que son precisos: el peso del error de los ítem IGUAL (tipo_error(Fallos)) y el listado de fonemas afectados por errores de discriminación (ListaErrores()). Ambas funciones están destacadas en el script en negrita.  Dado que ambos subprocesos están en función del cumplimiento de determinadas condiciones, todo ello se encuentra enmarcado dentro de una estructura condicional compleja que se inicia con la instrucción If Aciertos_p >= porcentaje_lim Then
  • La tercera y final consiste en pasar el contenido textual mediante parámetro a la subrutina de escritura del informe (CrearInfo(sTextoInfo)). Obsérvese, por su interés, en modo en que se construye los textos que finalmente se concretan como parámetro. Por ejemplo sTextoInfo = sDatosId & CHr(13) & CHr(13) & sResultado & CHr(13) & CHr(13) & sAnalisis.
Documentos.
  • Soporte Calc (DocAp)
  • Ejemplo de informe. El informe puede ser modificado en cuanto a formato y transformado en pdf, todo ello desde Writer y manualmente, aunque se pueden crear macros para ejecutar estas tareas, las cuales deberán situarse el directorio IDE Mis macros y diálogos.