viernes, 27 de diciembre de 2024

Documentos. Automatización.

Textos. Niveles de automatización

Sin ninguna pretensión de ser exhaustivo ni sistemático, he constatado en la práctica la existencia de diferentes niveles y enfoques en la automatización de textos que requieren (o hacen posible) diferenciar el enfoque de trabajo. La automatización de textos puede ser una tarea muy sencilla o de alta complejidad. En este último nivel, a día de hoy, no considero posible ir más allá de cierto grado de automatización, por lo que es mejor hablar de semi-automatización; pero posiblemente que tampoco sea deseable ir más allá.


Presento en esta entrada el estado actual de la cuestión, mi estado actual de la cuestión, y en las que siguen los diferentes planteamientos de abordaje de la tarea en función de esta tipología. Este epígrafe complementa el anterior de la sección Documentos. Aquel responde a cómo tratar el documento-soporte y éste a su contenido. 

Repito que no pretendo que esta propuesta pueda considerarse un planteamiento técnicamente ortodoxo ni que esté teóricamente fundamentado; tan sólo responde a lo que deriva de mi experiencia a la hora de enfrentarme a la resolución de estos procesos, la cual se limita a un campo profesional muy específico y se basa en un conocimiento técnico también limitado; en la parte positiva puedo decir que se basa en la práctica y en el abordaje de contextos reales de trabajo.

Desde esta perspectiva y con estas debilidades y fortalezas, diré que he constatado que la diferenciación de niveles que explico a continuación es necesaria, ya que responde a situaciones reales y su necesidad deriva de la propia especificidad de los planteamientos de trabajo.

Además, aunque no es independiente de los medios técnicos con los que se puede abordar, sí presenta cierto grado de autonomía respecto a ellos, por lo que se puede considerar una dimensión relevante del enfoque con que se puede abordar el problema. Lo que cada lenguaje aporta al respecto entra dentro del ámbito de la opcionalidad, pero también de la capacidad de cada uno de ellos para desarrollar los procedimientos, unos más funcionales que otros o con diferente nivel de simplicidad vs. complejidad. No es que esto sea irrelevante (que no lo es), pero no contradice el enfoque que planteo, que se centra en la tipología del texto y en los procedimientos de trabajo, que tampoco son únicos.

Pasando de los preámbulos a la concreción, insisto en que se pueden diferenciar diferentes tipos de planteamientos de la tarea de automatizar la creación de textos, que se pueden entender también como niveles de creciente dificultad o complejidad en el abordaje de dicha automatización. Por planteamientos estoy entendiendo los objetivos que se persiguen y con niveles el hecho de que estos se pueden ordenar en sentido creciente de dificultad y grado de automatización, subsumiendo el posterior al anterior, pero precisando también, en función de su mayor complejidad, formas de abordaje diferentes.

El nivel más básico correspondería a lo que llamo personalización y que se concreta como identificación del sujeto o sujetos destinatarios (por así decirlo) del mensaje. Es el más básico por ser suficiente con modificar datos como nombre o nombre y apellidos y normalmente también implica textos de extensión limitada y alta formalización. Un ejemplo podría ser el de las comunicaciones o convocatorias. Cierto que, aun en su simplicidad, presenta variaciones en el abordaje, siendo la generación masiva de textos una de ellas.

El segundo nivel estaría definido como de gramaticalización, que no es otra cosa que el ajuste de la redacción del documento a lo que gramaticalmente se entiende por concordancia de género y a veces también de número. A pesar de la continuidad que presenta con el anterior proceso, implica un salto cuantitativo y  cualitativo en la complejidad del procedimiento, como tendremos ocasión de ver.

El tercer nivel es mucho más complejo y presenta mayor diversidad interna, hasta el punto de resultar reduccionista considerarlo como un nivel único. Me refiero a los procesos que implican la opcionalidad o condicionalidad como factor determinante. Se trata de textos cuantitativa, pero sobre todo cualitativamente mucho más complejos que los anteriores. Además estos procedimiento condicionales no siempre resuelve satisfactoriamente el problema, lo que deja un amplio margen para alcanzar un nivel que podremos considerar mínimo-satisfactorio e incrementa en consecuencia la posibilidad de que la solución se reduzca a la semi-automatización.

Podemos entender que el nivel de logro que podemos alcanzar con la condicionalidad es consecuencia de las limitaciones del procedimiento; y en cierto modo y grado así es, pero debemos plantearnos si es conveniente automatizar absolutamente la composición de textos de alto nivel de complejidad técnica, y no sólo si es posible hacerlo. Tanto desde la perspectiva del coste como desde el planteamiento que deriva de la actuación del profesional, posiblemente la respuesta deba ser negativa: no es viable, necesario y conveniente prescindir de la intervención directa del profesional en la redacción final de documentos como los que implican estos procesos basados en la condicionalidad. De hecho no comparto que sea buena idea prescindir de la intervención personal y directa del profesional y aspirar a dejar esta tarea en manos del mejor algoritmo posible, si es que existe ese algoritmo, e incluso aunque exista.

jueves, 26 de diciembre de 2024

Lenguajes. R.

Vectorización de una matriz

Al igual que sucede con los vectores, también podemos vectorizar las operaciones matemáticas a los componentes de una matriz: al aplicar esa operación a la matriz, se aplica a cada uno de sus elementos, con independencia de la posición fila-columna que ocupen.



Además la vectorización también se aplica a los operadores relacionales, y como en el caso de los vectores, se trata de una operación que no altera la composición original de la matriz, por lo que si nos interesa conservar el resultado de la vectorización para operar después con él, deberemos asociarlo a una nueva matriz, resultante de la vectorización de la primera. 

Veamos este procedimiento sobre un ejemplo, partiendo de dos vectores...
  • alumnos <- c(24,22,20,19,21,25)
  • mataprob <- c(67,72,84,53,82,79)
...convertidos en matriz (cole <- rbind(alumnos,mataprob)que vectorizamos mediante un operador lógico (cole < 30), operación que asignamos a una nueva matriz (cole2 <-cole < 30). Observa el resultado de visualizar ambas matrices (cole y cole2) en la consola:


Como ves, mientras que cole conserva los valores iniciales, cole2 contiene el resultado de la vectorización: gracias a esta operación disponemos de una nueva matriz que recoge el resultado de aplicarla a cada uno de los datos de la matriz original.

Pero, ¿qué pasa si realizamos una vectorización (por ejemplo, aplicando ese mismo operador relacional) a una matriz que contiene datos alfanuméricos?. Si te fijaste en la imagen de matrices de la [entrada anterior], matriz resultante de la inclusión de tres vectores, el primero de los cuales estaba compuesto de datos alfanuméricos, todos sus datos habían sido transformados en datos alfanuméricos (de ahí que aparezcan entrecomillados).

Pues bien, si realizamos esta vectorización, el resultado es cuanto menos sorprendente, ya que devuelve FALSE para los datos alfanuméricos (identificador de curso) y TRUE o FALSE, según corresponde, en los numéricos primitivos  convertidos en alfanuméricos. Pero si la vectorización consiste en realizar una operación matemática (vg. cole2 <-cole * 30) el resultado es un mensaje de error, esperable por otra parte, ya que no puede realizar esa operación con los datos no-numéricos. La diferencia entre ambas operaciones es que no se ejecuta la vectorización con ninguno de los datos dado que ninguno es ya numérico.





Lenguajes.R.

Matriz y array

El hecho de que ambos sean conocidos como vectores multidimensionales ya dice mucho del peso del concepto vector en R. Ambas (matriz y array) comparte con el vector ser colecciones de datos homogéneos, esto es: de un mismo tipo, pero si diferencia en que su número de dimensiones no se limita a una.


Una matriz es un vector bidimensional (longitud y altura, podemos decir), por lo que se puede representar como una tabla de doble entrada, en la que la longitud son las columnas y la altura las filas.

Un array es un vector de más de dos dimensiones (n.dimensional, si se prefiere), por lo que se puede representar como un cubo , un hipercubo u otras formas más complejas. Es precisamente esta complejidad la que hace que el array sea una colección de datos poco usada en R, motivo por el cual en esta entrada me limitaré a hablar de la matriz como colección o estructura de datos.

Para crear una matriz empleamos la función matrix(), la cual acepta dos argumentos (nrow y ncol) (1) y los valores de ambos debe ser igual al número de elementos de la matriz, en caso contrario, obtendremos una matriz con datos truncados o con datos añadidos, siendo ésta una limitación a tener en cuenta (2).

Si queremos crear una matriz con unos contenidos específicos (esto es: no derivada de una serie de números (matrix(1:10)), deberemos generar una expresión de cierta complejidad (matrix(c("P1","P2","P3","P4","P5","P6"), nrow = 3, ncol = 2)) que además respete la condición antes indicada en cuanto a la relación entre el número de elementos y al producto de filas * columnas.

También podemos crear una matriz a partir de cierto número de vectores mediante las funciones cbind() y rbind()cbind() trata cada vector como una columna y rbind() como una fila.

Pongamos un ejemplo de conversión de vectores en matrices. Supongamos tres vectores, el primero con el identificador del curso, el segundo con el número de alumnos del grupo y el tercero con el porcentaje de aprobados en Matemáticas:
  • curso<-c("P1","P2","P3","P4","P5","P6")
  • alumnos <- c(24,22,20,19,21,25)
  • mataprob <- c(67,72,84,53,82,79)
Podemos construir una matriz vector-fila (cole <- rbind(curso,alumnos,mataprob)) o vector-columna (cole <- cbind(curso,alumnos,mataprob)) con los resultados que se muestran en la imagen siguiente (3):


Otras funciones que pueden sernos de utilidad para trabajar con matrices son la función class(), que nos permite saber si una colección de datos es considera una matriz (o un array) por R, la función dim(), que nos devuelve el número de filas y columnas de la matriz (o del array) y la función t(), que nos permite trasponer filas-columnas.

NOTAS

(1) Podemos crear una matriz sin estos argumentos, lo que deriva en crear una matriz unidimensional, esto es, el equivalente a un vector.
(2) Si este producto no es igual al número de elementos o bien se omiten los elementos que sobran o se reciclan los que faltan hasta completar las posiciones definidas en filas-columnas.
(3) En este caso, los tres vectores tienen la misma longitud, por lo que la estructura matricial se ajusta al contenido, pero si tuvieran diferente longitud, tendríamos las mismas limitaciones que vimos antes. Una solución para evitar elementos faltantes o duplicados es introduciendo valores NA en el vector que los precise hasta completar el número de elementos necesarios para que se ajusten las longitudes de los vectores constituyentes de la matriz, o los datos de ésta.

Lenguajes. R.

Subconjuntos de un vector

Por diversos motivos, es posible y frecuente que necesitemos trabajar con un subconjuntos de los datos que contiene un vector; para ello necesitamos procedimientos que nos permitan acceder a esos datos. En las dos entradas anteriores ya nos encontramos con situaciones en las que esta temática estaba presente, pero no de forma explícita, por lo que necesitamos abordarla en la actual.



De las diferentes opciones disponibles para la obtención de subconjuntos, el uso de índices es el único que podemos emplear con los vectores, y con ciertas restricciones, como tendremos ocasión de ver aquí.

El índice es un numeral que indica la posición de cada elemento en la colección de datos (en este caso, en el vector) (1), de modo que es posible acceder a cada uno de sus datos empleando el índice (num[1]), u omitirlo también usando el índice como identificador (num[-1]) (2), como tuvimos ocasión de comprobar en una [anterior ocasión].

En realidad poco vamos a añadir en esta entrada a lo ya visto, pero sí interesa tratar el tema de forma específica por la funcionalidad que presenta para el tratamiento de datos.

Supongamos que tenemos un vector con los identificadores de los cursos de E. Primaria (curso<-c("P1","P2","P3","P4","P5","P6")) a partir del cual deseamos crear varios vectores derivados:
  • Un vector con el curso P3 -> curso3 <- curso[3] devuelve "P3"
  • Otro con los tres primeros cursos -> curso13 <- curso[1:3] devuelve "P1" "P2" "P3"
  • Otro con los cursos impares -> cursosimp <- curso[c(1,3,5)]) devuelve "P1" "P3" "P5"
  • Y otro sin los dos cursos finales -> curosinic <- curso[-c(5,6)] devuelve "P1" "P2" "P3" "P4"
Como puedes ver, para obtener un elemento o varios consecutivos empleamos la fórmula vector[Indice/s], estableciendo un intervalo en caso de desear recuperar ese intervalo de elementos, pero cuando deseamos obtener un sub-vector formado por elementos no consecutivos debemos emplear la función c() enmarcada dentro de la estructura básica (vector[c(indices)]); sólo así es posible utilizar la separación de índices mediante coma, que es la forma de hacer referencia a cada índice individual) (3)

Salvo por esta limitación interpretativa, el modo de obtener subconjunto del conjunto vector mediante índices es muy simple, lo que nos permite generar con facilidad subconjuntos de datos a partir del contenido del vector.


NOTAS

(1) Recuerda que en R el índice no se inicia en 0, sino en 1. En num <- c("casa","mesa"), el elemento 1 (índice 1) es "casa". En OOo Basic "casa" ocuparía la posición (índice) 0.
(2) La diferencia entre ambas instrucciones es que la primera nos devuelve el dato ubicado en la posición que concreta el índice, mientras que la segunda devuelve todos los datos contenidos en el vector menos el indicado por su índice (en este caso el que ocupa la posición 1)
(3) Si empleáramos la separación de índices mediante coma pero son la función c(), R nos devolvería error, ya que interpreta que estamos solicitando datos de una estructura n-dimensional, y un vector sólo tiene una dimensión (longitud). El signo - es substractivo, de ahí que al anteponerlo a la función c() (-c(5,6)) obtengamos como resultado los elementos del vector menos los indicados como argumentos de dicha función.

Lenguajes. R.

Vectorización

La vectorización consiste en la capacidad que tiene R de aplicar determinadas operaciones a todos los elementos de un vector de forma directa. Esta capacidad ejemplifica nuevamente el carácter de datos básico que tiene el vector en este lenguaje.


Gracias a la vectorización es posible generar operaciones simples o complejas cuyo resultado afecte a todos los componentes del vector. Los operadores admitidos son los operadores aritméticos y los relacionales.

Para num1 <- c(2,4,6,8,10,12)
num1 * 2  devuelve  4 8 12 16 20 24
num1 < 5 devuelve TRUE TRUE FALSE FALSE FALSE FALSE

Pensando en operaciones aritméticas, podemos calcular fácilmente el porcentaje de todos los elementos de un vector o convertir la puntuación directa en puntuación típica de forma muy sencilla. Con los operadores relacionales, podemos valorar una determinada expresión comparativa, diferenciando qué componentes del vector la cumplen y cuáles no, también de forma rápida y sencilla.

También aquí rige lo dicho en la entrada anterior sobre la sustracción de elementos del vector: se trata de un proceso que no altera la composición del vector original, por lo que si nos interesa conservar el resultado de la operación deberemos asignar dicha operación a un segundo vector. Por ejemplo, para el vector num1 anterior...

numdoble <- num1 * 2

num1 devuelve porque conserva  2,4,6,8,10,12

numdoble devuelve  (y conserva)  4 8 12 16 20 24

 

Lenguajes. R.

Vectores. Definición.

Un vector es la colección de datos más sencilla con la que trabaja R. Es unidimensional y contiene datos del mismo tipo, aunque puede constar de metadados que describen los datos que contiene (1).


R es un lenguaje vectorizado, lo que quiere decir que se puede considerar al vector como un tipo básico de datos en este lenguaje; algo que es coherente con el objetivo para el que fue pensado como lenguaje. De hecho, y a la inversa, se puede considerar cualquier variable como un vector de un único dato (2)

Para crear un vector usaremos la función c() (concatenar) (3) y el operador de asignación, por ejemplo:
  • num <- c(1,3,5,7)
  • pal <- c("mesa","silla","cama")
Si lo que necesitamos es crear un vector de números consecutivos podemos hacerlo de este modo: num <- 1:10, que crea un vector con los números 1...10 de forma directa (4)

En R es muy sencillo añadir elementos a un sector ya creado (5), siendo suficiente con reutilizar el vector previamente creado de forma recursiva:
  • num <- c(1,3,5,7)
  • num <- c(num,9)
En este caso añadimos al final del vector num un nuevo dato (9), pero podríamos añadirlo al inicio o añadir más de un dato usando el mismo procedimiento, pero variando la posición o la cantidad de dados. Del mismo modo, podemos crear vectores de vectores (numc <- c(numa,numb)), operación que realizaremos con frecuencia (6).

Al añadir un dato a un vector o al crear un vector de vectores, se puede dar el caso que el tipo de dato no sea el mismos, lo que aparentemente es una contradicción con el criterio de que un vector contiene datos del mismo tipo. Este problema se resuelve en R de forma automática mediante el mecanismo de coerción (7)

Para ver el contenido de un vector es suficiente con escribir su nombre y pulsar intro; y si queremos acceder a un elemento en concreto escribiremos el nombre del vector y su id entre corchetes (num[1]) y pulsamos intro (8). El mismo resultado obtendremos si utilizamos la función print(), pasando como argumento el nombre del vector (print(num)), en el primer caso, y la expresión num[1] en el segundo.

El acceso a un elemento del vector nos permite generar variables a partir del vector por el simple procedimiento de asignación del componente a la variable (num2 <- num[1]) (9)

Si queremos eliminar un elemento (o varios) de un vector (10), o crear un subvector a partir del original menos determinado/s elemento/s, es suficiente con añadir el signo - al índice de posición del elemento (num[-1]) o a la función c() (num[-c(1)]). Esta sustracción sólo elimina el elemento en el momento en que se ejecuta, pero no altera la composición original del vector, así que si lo que necesitamos es un nuevo vector que no cuente con esos elementos, deberemos asociar la operación anterior a la creación de dicho subvector (numB <- num[-1]).


NOTAS

(1) El tema de los metadados supera los objetivos de este blog, por lo que no será tratado. No obstante es una cuestión de un alto nivel de complejidad, por lo que es frecuente trabajar con vectores sin hacer referencia a sus metadatos.
(2) Si, por ejemplo, utilizamos la función is.vector(3), nos devolverá TRUE, y si solicitamos información sobre su longitud (length(3)) nos devolverá 1. Esto implica que R está tratando el dato 3 como vector. Lo mismo sucederá si asociamos este dato a una variable (num <- 3) y la usamos como argumento de ambas funciones.
(3) También podemos usar las funciones rep() y seq(), que permiten crear patrones.
(4) Esta formulación admite también números decimales y negativos, así como orden inverso.
(5) Lo que contrasta con la complejidad que presenta esta operación en OOo Basic y ejemplifica de nuevo la orientación de R al análisis de datos.
(6) En el supuesto de que numa <- c(1,2) y numb <- c(3,4), numc estaría formado por 1,2,3,4
(7) Conversión implícita de un tipo de dato a otro más flexible. Por ejemplo, si añadimos "once" al vector num (num <- c(num,"once")), todos los datos se convierten a tipo character. De la coerción ya hablamos [en esta entrada]
(8) La identificación de los componentes de los vectores no empiezan en 0, como sí ocurre en otros lenguajes, así que num[1] no devuelve el primer dato que contiene el vector.
(9) De hecho, una variable puede ser considerada como un vector de un único dato.
(10) Por lo que se precisa más adelante, es posible que en vez de decir "eliminar", debería decirse "visualizar el vector sin determinados elementos", ya que no se produce tal eliminación de datos del vector original.

miércoles, 25 de diciembre de 2024

Lenguajes. R.

Colecciones de datos

También en las colecciones de datos (estructuras, si prefieres) de R se aprecia la influencia de ser un lenguaje orientado al análisis estadístico, como tendremos ocasión de comprobar en esta entrada y en las que la siguen.


En R existen diferentes colecciones o estructuras de datos, apreciándose como características diferenciadoras el número de dimensiones y el criterio homogeneidad vs. heterogeneidad de los datos. En función de ellas diferenciaremos entre:
  • Vector (una dimensión, datos homogéneos)
  • Matriz (dos dimensiones, datos homogéneos)
  • Array (n dimensiones, datos homogéneos)
  • Lista (una dimensión, datos heterogéneos)
  • Data frame (dos dimensiones, datos heterogéneos)
En las entradas que siguen analizaré cada una de estas colecciones, siguiendo este mismo orden. Incluiré en cada una de ellas el análisis de los procedimientos disponibles en R para trabajar con subconjuntos de estas colecciones, procedimientos estos muy necesarios en un lenguaje pensado para el análisis estadístico como es R. Esto incluye el uso de nombres y valores índice, operadores lógicos y operadores booleanos. 

No obstante, como veremos, su uso está en función del tipo de colección de datos de que se trate, ya que uso tiene características específicas para cada una de las colecciones de datos; el caso más claro de esto es el uso de índices.

martes, 24 de diciembre de 2024

Lenguajes. R.

Operadores

Los operadores indican al lenguaje qué operación debe realizar con los datos. Aunque sea una simplificación, se puede decir que es la combinación de ambos (datos y operadores) la base que nos permite resolver las tareas con este y con el resto de los lenguajes de programación.


Al igual que en resto de los lenguajes, también en R podemos diferenciar distintos tipos de operadores: aritméticos, relacionales, lógicos y de asignación.

Empezaremos por los operadores de asignación, ya que [ya ha sido empleado]. En realidad uno de los dos posibles (saludo <- "Hola Mundo R"), porque también podemos realizar la asignación mediante el operador = (saludo = "Hola Mundo R"), si bien <- es el operador de asignación que más se usa en R por tradición y para evitar confusiones. En cualquier caso, realizar la operación de asignación es, como sabemos, asignar una referencia que permite al lenguaje encontrar el dato en una posición de la memoria RAM.

Los operadores aritméticos (algebraicos, si se prefiere) son los que se emplean para realizar cálculos matemáticos: 
  • Las cuatro operaciones básicas: + , - , * y /
  • La división entera: %%, que redondea al entero más próximo y omite la parte decimal
  • Y la potenciación: ^, que eleva el número a la n.esima que se indique (2^3)
Los operadores relacionales permiten valorar la comparación entre los datos en términos dicotómicos (V/F): < (menor que), <= (menor o igual que), > (mayor que), >= (mayor o igual que), == (igual que) y su opuesto != (no igual que).

Estos operadores se comportan de forma específica con datos string, ya que lo que hacen es comparar por el criterio "orden alfabético", de modo que "casa" > "barco" devuelve TRUE, ya que "casa" es posterior (superior o mayor) a "barco" por ese criterio. Además no tiene sentido realizar comparaciones con datos tipo factor, por lo que obtendremos NA como resultado y un mensaje de error.

Los operadores lógicos son empleados para describir relaciones lógicas (álgebra booleana), expresadas en términos V/F:
  • x | z -> O lógico (x o z). Devuelve TRUE si alguno de los miembros es TRUE y sólo devuelve FALSE si todos los miembros son FALSE.
  • x & z -> Y lógico ( x y z). Devuelve FALSE si alguno de los miembros es FALSE y TRUE sólo cuando todos los miembros son TRUE.
  • !x -> Negación lógica (x no es verdadero)
  • isTRUE(x) -> Afirmación lógica (x es verdadero)
Los operadores lógicos pueden ser usados con datos numéricos, lógicos y complejos. Además los operadores lógicos se pueden combinar entre sí y también con los operadores relacionales, ya que ambos devuelven V/F.

Como en matemáticas y en el resto de los lenguajes de programación, existe un orden de prelación en el uso de los operadores que podemos alterar empleando paréntesis.
  1. ^
  2.  * /
  3. + -
  4. < > <= >= == !=
  5. !
  6. &
  7. |
  8. <-

Lenguajes. R.

Datos y variables

Un lenguaje de orientación específica al análisis estadístico como es R presenta también una orientación específica al tratamiento de los datos con los que trabaja. Esto incide en qué tipo de datos considera y cómo los trata. Todo ello tiene implicaciones en el planteamiento de las variables y las colecciones de datos. En esta entrada hablaremos específicamente de las variables en R.


Los datos básicos de R son los siguientes:
  • Caracter (cadena de texto) (character) -> "La casa de la pradera" / "A"
  • Enteros (integer) -> 22
  • Decimal (numeric) -> 1.35
  • Factor (factor) -> uno
  • Lógico (logical) -> TRUE/FALSE
  • Perdido (NA) -> NA
  • Vacío (NULL) -> null
Además R también cuenta con otros tipos de datos como numéricos complejos (parte real y parte imaginaria), raw (byte), fecha y raster. De todos estos, posiblemente los más utilizados sean los datos de fecha.

La diferencia entre los datos integer y numeric es que los segundos son números con parte decimal, por lo que también se les conoce como  float (coma flotante).

La característica principal de los datos string/character es que van entrecomillados.

Un factor es un datos específico de R y se puede definir como un datos numérico que representa una etiqueta o nivel. Vg. 1 para masculino, 2 para femenino. Estos datos facilitan el ahorro de memoria del procesador.

Los datos NA y null son datos específicamente estadísticos. NA equivale a los datos perdidos y null representa la ausencia de datos. Ambos cobran sentido cuando trabajamos con tablas de datos complejas y permiten la identificación de esos tipos de "datos" y su tratamiento estadístico.

Los datos lógicos (TRUE/FALSE) permiten verificar si se cumple o no una condición determinada, facilitando el trabajo con al álgebra booleana.

Para conocer qué tipo de dato está asociado a una variable se usa la función class() (class(3)) que devuelve un string con el nombre del tipo de datos. También disponemos de la función is.TipoDato() (is.numeric(5)) que nos devuelve TRUE/FALSE.

En R se denomina coerción a la transformación de un tipo de dato en otro, y se realiza de forma implícita, del tipo de dato más restrictivo al nivel más flexible (lógico->entero->numérico->carácter) y no en orden inverso.

Para forzar explícitamente una determinada transformación empleamos la función as.TipoDato()

  • as.integer()
  • as.numeric()
  • as.character(
  • as.factor()
  • as.logical()
  • as.null()
  • ...

Si la conversión (coerción) está permitida, nos devuelve el dato convertido, pero si no lo está nos devuelve un aviso de error y el valor NA.



lunes, 23 de diciembre de 2024

Lenguajes. R.

Conceptos básicos


Voy a tratar en esta entrada algunas cuestiones que se pueden considerar básicas para el trabajo con R. Me refiero concretamente al modo en que este lenguaje trabaja con la documentación que genera, esto es: al directorio de trabajo y a la creación y acceso a los script (1).


El directorio o carpeta de trabajo es el espacio de memoria de nuestro ordenador en el que se encuentran los archivos con los que estamos trabajando y donde R los buscará para cargarlos y grabarlos.

Para acceder a este directorio emplearemos la función getwd(), que se pasa sin especificar atributos y devuelve una cadena de texto con la ruta de acceso. Si se desea, se puede cambiar de directorio con la función setwd(), especificando el parámetro dentro del paréntesis; dicho parámetro consiste en una cadena de texto con la ruta deseada (vg. "setwd("C:\mi_directorio").

También podemos conocer el contenido del directorio de trabajo mediante la función list.files() (sin argumento), que muestra todos los archivos que contiene y mediante list.dirs() (sin argumentos), que muestra todos los subdirectorios.

Cada vez que ejecutamos R estamos creando una sesión de trabajo, que no es otra cosa que una instancia de su entorno. Todo lo que creemos en esa sesión se almacena en la memoria RAM del ordenador. Este modo de funcionamiento nos permite tener activa más de una sesión. 

Cuando cerramos R, también cerramos la sesión de trabajo correspondiente, pero podemos acceder a una sesión anterior accediendo al archivo .Rdata que genera, con lo que podremos continuar trabajando en su desarrollo.

Para conocer los objetos y funciones que contiene una sesión de trabajo emplearemos la función ls() (sin argumentos), que nos devuelve una lista con los identificadores de los objetos guardados (no los archivos). Dichos objetos configuran el entorno de trabajo de la sesión.

Estos conceptos son un tanto extraños para el profano y simplemente se pretende aquí que sea conocedor de su existencia y formas básicas de manejo; pero lo que no debe resultarnos extraño es el concepto de script, término al que ya estamos acostumbrados. Un script es un documento de texto con extensión .R (mi_script.R) que este lenguaje interpreta como propio. Como ya dije [en esta entrada], es aconsejable generar script como modo de guardar el trabajo desarrollado.

Para acceder desde la consola a un script creado usaremos la función source(), pasando como parámetro la ruta del mismo (vg source("C:/Mis_ScriptR/mi_script.R"), aunque RStudio proporciona formas más directas y conocidas de acceso (2).

Tanto por la simplicidad de su conformación básica como por el estilo de trabajo del lenguaje, R cuenta con múltiples paquetes o conjuntos de script con los que es posible realizar múltiples tareas. 

No obstante, la mayoría de estos paquetes no están cargados a priori en R, por lo que es necesario importarlos o instalarlos empleando la función install.packages() que recibe como argumento el nombre del paquete (vg install.packages("readr")). 

Una vez instalado, podrás hacer uso del paquete mediante la función library() (en el caso anterior library(readr)) (4); esto nos permite acceder y usar las funciones de dicho paquete.

NOTAS

(1) Sigo en estos temas a [Mendoza Vega, JB. R para principiantes]
(2) Que serán las que empleemos con más frecuencia: File | Open File, o File | Recent Files si se trata de script con los que hemos trabajado recientemente.
(3) Los paquetes oficiales de R se encuentran alojados en CRAN, lo cual nos garantiza que han pasado rigurosos controles antes de ser puestos a disposición de la comunidad de usuarios. Puedes acceder a CRAN desde [este enlace]. Desde [este otro] tienes acceso a un amplio listado de los paquetes disponible
(4) Observa que en la instalación el nombre del paquete es tratado como cadena de texto, por lo que va entrecomillado, pero no así al cargarlo mediante la función library(), por lo que ahora el argumento no se entrecomilla. Por claridad y estilo de trabajo, en R se debe usar la función library() cada vez que se desee trabajar con las funciones de un paquete; no basta con haberlo instalado para acceder a ellas.

domingo, 22 de diciembre de 2024

Lenguajes. R.

RStudio


Podemos trabajar con las herramientas que proporciona R, pero su IDE no es la única opción y tampoco la más práctica, dada su simplicidad.


Como alternativa RStudio es un IDE que nos ofrece herramientas integradas para escribir y revisar código, administrar los archivos que necesitamos para nuestros proyecto y gestionar el entorno de trabajo, además de otras funciones de gran interés.

Por todo ello, es preferible, aunque no imprescindible, usar RStudio por lo a partir de ahora, y salvo excepciones, desarrollaré el trabajo con el lenguaje R desde este entorno. Puedes descargar RStudio desde [este enlace].

No me parece necesario reproducir en este blog lo que ya está perfectamente resuelto en otros, así que, por ahorro de tiempo y espacio, y por ser consciente de que la solución a esta necesidad ya está bien resuelta en otros espacios, te aconsejo que accedas desde [este enlace] y una fuente fiable y de calidad para aprender en qué consiste y cómo se trabaja con este IDE. Se trata de documentación para trabajar on-line, así que no tendrás que descargar nada, pero si prefieres otro tipo de documentación, puedes acceder a ella desde este [otro enlace]. Queda a tu elección.


sábado, 21 de diciembre de 2024

Lenguajes. R.

Primeros pasos en R

Como es tradición, empezaremos por crear nuestro primer script en R, el conocido "Hola Mundo". Este script nos permitirá conocer algunos componentes y formas de funcionar de este lenguaje.


Para escribir nuestra primera orden en R, abrimos la consola y escribimos lo siguiente:

> print("Hola Mundo R")
[1] "Hola Mundo R"
> 

El símbolo > nos indica que el programa está listo para recibir instrucciones, así que escribimos nuestra primera instrucción (print("Hola Mundo R")) en la que hacemos uso de la función print() para escribir nuestro saludo. Al tratarse de una cadena de texto, deberemos entrecomillarla. Pulsamos Intro e inmediatamente recibimos la respuesta en la línea siguiente, precedida de [1] , que hace la función de identificador de la respuesta ([1] "Hola Mundo R"). El programa vuelve a quedar a la espera de recibir instrucciones (>), así que si ahora escribimos...

>saludo <- "Hola Mundo R" #Primera orden en R
> print(saludo)
[1] "Hola Mundo R"

... lo que estamos haciendo es repetir el procedimiento anterior, pero ahora empleando una variable (saludo) a la que asignamos (<-) un contenido textual, lo que hace que esa variable sea considerada de tipo Character (denominación más apropiada que String) (saludo <- "Hola Mundo R"). El texto que sigue, precedido de # es un comentario.

Para ver por pantalla la salida deberemos utilizar de nuevo la función print() (> print(saludo)), con la que obtenemos la misma salida o respuesta que antes ([1] "Hola Mundo R").

Cuando la consola contenga demasiadas líneas, podemos limpiarla mediante Editar | Limpiar consola

Esta forma de trabajar con R (modo consola), aunque es perfectamente válida para las fases iniciales del aprendizaje, presenta evidentes limitaciones, por lo que es normalmente trabajaremos con el Editor R, que es una ventana emergente que se activa desde Archivo | Nuevo script y que nos permite crear script que podremos guardar para recuperar en otro momento mediante Archivo | Abrir script.

Para correr un script previamente guardado, deberemos situarnos en la línea que deseemos correr y ejecutar el comando Correr línea o seleccionar (tercer icono bajo las etiquetas de comandos). Si simplemente nos posicionamos en una línea, se reproducirá ésta en la consola y se ejecutará si tiene alguna instrucción o función ejecutable. Si seleccionamos un conjunto de líneas se reproducirán y ejecutará todas ellas automática y sucesivamente.

Una vez creado el script podremos guardarlo mediante el comando Guardar script (segundo icono) o mediante Archivo | Guardar / Guardar como (si queremos renombrarlo). Finalmente podremos cerrarlo mediante Archivo | Cerrar script.

viernes, 20 de diciembre de 2024

Lenguajes. R.

Presentación

R en un lenguaje de uso libre (licencia GNU GLP),interpretado y específicamente orientado al análisis estadístico, que funciona en Windows, Linux y MacOS. Se puede descargar desde [este enlace] de forma gratuita. En este enlace encontrarás también abundante documentación sobre R (1).


Aunque primero debes descargar el software del lenguaje, te recomiendo que también descargues (a continuación) el del IDLE [RStudio], ya que seguramente será el entorno en el que termines trabajando (2).

El lenguaje R está siendo empleado en el campo de la mal llamada "inteligencia de datos" (como también Python), concretamente en el denominado big data (tratamiento de grandes y complejas masas de datos). Obviamente no es nuestro caso, pero lo que sirve para lo mucho, también sirve para lo poco, al menos R sí.

Es de desatacar que el uso de R no se ajusta a lo esperado en un lenguaje, ya que más que crear programas, lo que hacemos con R es trabajar con los datos mediante instrucciones y funciones, obteniendo resultados de cálculos estadísticos y complejas representaciones gráficas de alta calidad. Es algo así como una herramienta para el análisis de datos.

R es un lenguaje orientado a objetos, pero eso no afecta a cómo lo podemos emplear. En un primer acercamiento, no es especialmente diferentes a otros lenguajes, por lo que no nos vamos a encontrar con dificultades especiales. Al contrario: sorprende la facilidad con la que podemos acceder y analizar nuestras tablas de datos, siendo en esto mucho más sencillo y funcional que OOo Basic.

Resumiendo, nos interesar este primer y básico acercamiento a R para el trabajo con datos. No será el lenguaje ni el modo de trabajo que implica lo que nos cree dificultades; todo lo contrario.

Para más información puedes consultar la documentación R en [Anexos].

NOTAS

(1) Documentación en inglés. Si te interesa documentación en español, te recomiendo [este enlace] donde Freddy Hernández tiene publicada documentación muy interesante que se debe consultar on-line.
(2) Aunque ambas operaciones no presentan mayor dificultad, te aconsejo que te leas la información disponible en los enlaces que te proporciono o en los que resulten de tu búsqueda en la red. En este blog no me detendré en estas cuestiones.

Análisis. Datos.

Acceso a tabla: columnas y filas.

Antes de iniciar el análisis de datos, y como paso previo, trabajando con OOo Basic es necesario aprender a acceder a las columnas (campos o variables, según se prefiera) y a las filas (o registros).


Ambos accesos se circunscriben al trabajo con OOo Basic como herramienta para el análisis de datos, ya que en otros lenguajes (R y Python) no son necesarios como tales, puesto que el proceso se resuelve de forma directa mediante las funciones correspondiente (1).

Lo que sí van a tener los tres lenguajes es que en el proceso de análisis de datos también en OOo Basic trabajaré directamente desde el IDE como espacio para la generación de script y el desarrollo del propio proceso de análisis. Como consecuencia de ello, y al contrario del planteamiento de trabajo que implica el desarrollo de docap, aquí no existirá otra interface que el propio IDE y el recurso a ventanas emergentes (MsgBox) como medio para que OOo Basic genere las salidas de datos. Esto significa que, en principio, no se prestará atención a la entrada de datos y a la salida sólo mediante escritura en la hoja de cálculo cuando resulte pertinente; el resto queda a la escritura de código y a la recopilación manual por parte del usuario. Es este un procedimiento extraño al objetivo para el que fue ideado OOo Basic, pero muy común en los lenguajes Python y R (especialmente este último) y empleándolo me quiero aproximar a ese modo de trabajo, eludiendo de paso la carga de trabajo que supone el diseño de interfaces de usuario. Se espera que a estas alturas del proceso de aprendizaje LO - OOo Basic este modo de uso del lenguaje no suponga ningún problema para el usuario.

Para concretar y ejemplificar ambos procedimientos de acceso a los datos emplearé una tabla ficticia que recoge el registro de actuaciones mensuales de un OE en un determinado centro a lo largo del curso (2). Sobre esta base, empezaré por explicar el acceso a una columna de datos, que es, posiblemente, el procedimiento más empleado en el análisis de datos.

Sub AccesoCol

Dim vN As Integer, i As Integer
Dim vHoja As String, vCol As String, vTipoDat As String
Dim mDatosCol() As Variant
Dim oHoja As Object, oCelda As Object

vN = 9
vHoja = "Datos"
vCol = "E"

ReDim mDatosCol(vN)

vTipoDat = "N" 'Tipo de datos a capturar: T - Texto y N - Numérico

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

For i = 0 To UBound(mDatosCol())
oCelda = oHoja.getCellRangeByName(vCol & i+2)
If vTipoDat = "N" Then
mDatosCol(i) = CInt(oCelda.getString)
ElseIf vTipoDat = "T" Then
mDatosCol(i) = oCelda.getString
End If
Next

End Sub

Tras la declaración de las variables, asignamos valores a las variables que nos van a permitir dimensionar la matriz de datos (ReDim mDatosCol(vN)) y acceder a la hoja (vHoja = "Datos") y  a la columna (vCol = "E")  donde se encuentran dichos datos. Después seleccionamos el tipo de datos al que se va a acceder (vTipoDat = "N") (3), a fin de facilitar el uso de procedimiento adecuado a ese tipo de datos, según veremos un poco más abajo.

Lo siguiente que hacemos es acceder a la hoja (oHoja = ThisComponent.getSheets().getByName(vHoja)) y después, mednte un bucle (For i = 0 To UBound(mDatosCol())), a cada una de las celdas de la columna seleccionada (oCelda = oHoja.getCellRangeByName(vCol & i+2)). Como la instrucción de acceso es diferente en función del tipo de datos (textos o números), empleamos un condicional para acceder a números (If vTipoDat = "N" Then) -> (mDatosCol(i) = CInt(oCelda.getString)) u otro para acceder a textos (ElseIf vTipoDat = "T" Then) -> (mDatosCol(i) = oCelda.getString).

Y ya estamos en disposición de trabajar con esta matriz de datos que consideramos valores de la variable (en este caso) Actividad Tipo 1 (AT1), sea esta lo que sea.

Si quisiéramos acceder a una segunda variable, deberíamos crear una segunda matriz de datos y proceder del mismo modo, lo que implica complicar el script. En caso de que este script resulte demasiado complejo y se reduzca la funcionalidad de uso, sería conveniente plantearse la creación de una función o una subrutina en la que concretar aquellos procedimientos que se repiten varias veces en dicho script (4).

El acceso a una determinada fila | registro de la tabla, en principio, no es muy diferentes al modo que utilizamos para grabar los datos en una determinada fila, según quedó explicado en entradas anteriores [por ejemplo en esta], y que empleamos para crear el docap de generación de la tabla de datos [ver aquí], pero presenta algunas diferencias debidas al cambio de tratamiento de los datos.

Sub AccesoFil

Dim oHojaBD As Object, oCeldaInicio As Object, oCeldaRegistro As Object
Dim vN As Integer, i As Integer
Dim vHoja As String
Dim a As Integer, b As Integer, c As Integer, vFil As Integer
Dim mDatos() As String

vHoja = "Datos" ' Nombre de la hoja de datos

oHojaBD = ThisComponent.getSheets().getByName(vHoja)

vN = 7 'Nº de elementos de la fila (campos del registro)
ReDim mDatos(vN)

c = 100
vFil = 5 'Id de la fila (registro) a seleccionar

For b = 0 To c
If b + 1 = vFil Then
For i = 0 To UBound(mDatos())
oCeldaRegistro = oHojaBD.getCellByPosition(i,vFil)
mDatos(i) = oCeldaRegistro.getString()
Next
End If
Next

End Sub

Primero accedemos a la hoja (oHojaBD = ThisComponent.getSheets().getByName(vHoja)) identificada previamente en la variable (vHoja = "Datos") y después redimensionamos la matriz (ReDim mDatos(vN)) en función del número de campos del registro (vN = 7) y terminamos identificando el Id el registro deseado (vFil = 5).

Después ejecutamos la estructura ciclo-Condicional-ciclo que nos va a permitir acceder al contenido de los campos de ese registro. Para ello recorremos la base de datos (For b = 0 To c) hasta encontrar el valor del contador b que coincida con el Id del registro buscado (vFil) estableciendo en ese momento la condicionalidad (If b + 1 = vFil Then) dentro de la cual ejecutamos un segundo bucle (For i = 0 To UBound(mDatos())) que recorre las columnas (oCeldaRegistro = oHojaBD.getCellByPosition(i,vFil)) asignando a la matriz el contenido de las celdas correspondientes a ese registro (mDatos(i) = oCeldaRegistro.getString()).

También aquí el script deberá repetirse tantas veces como registros deseemos capturar, por lo que puede volverse poco manejable. En ese caso también será conveniente reconvertir el script en una función (ver nota 4).

Documento. Desde [este enlace] puedes acceder al documento que contiene ambos script.

NOTAS

(1) El trabajo con estos lenguajes se verá en próximas entradas, pero por ahora me voy a limitar a trabajar con OOo Basic.
(2) Aunque la base es el docap para crear una tabla expuesto en [esta entrada], el código que contiene los procedimientos de acceso se ubica en un módulo específico (ModAccesoDatos), diferentes del módulo que contiene los script y subrutinas del docap original (renombrado ahora como ModCrearTabla). Al código anterior no haré mención en la entrada actual.
(3) La forma en que se plantea este subproceso deriva directamente del modelo de trabajo empleado (trabajo en OOo Basic directamente desde el IDE) , incluyendo el uso de un comentario que clarifica cómo se debe proceder. De prestar una mínima atención al modo clásico de trabajo deberíamos crear un formulario de entrada de datos o recurrir a la función InputBox(), pero ahora eso es innecesario, ya que se espera que el usuario introduzca los datos directamente en el script.
(4) Solución que aplicaremos cuando llegue el momento. Por ahora es suficiente con exponer cómo acceder a los datos de una columna | variable | campo y te aconsejo que practiques este procedimiento hasta tener seguridad en su manejo.