2 Cosechar textos

2.1 Introducción

Durante los primeros pasos de programación en R y análisis de textos vas a usar un corpus de textos recolectados en la red. Se trata de los discursos de Navidad que los reyes de España pronuncian cada año la tarde de Nochebuena.

Podrías recupararlos desde el sitio web de la Casa Real, pero es un poco pesado ya que los textos tienen algunos problemillas de diseño y encierran ciertos errores que no permiten trabajar bien. Por ese motivo los he recogido, limpiado y dispuesto para el análisis, con lo que los resultados serán idénticos en todo momento, con independencia del sistema operativo que dispongas. Tan solo tienes que guardarlos en tu ordenador.

Podría ofrecerte un fichero .zip con todos ellos, pero quiero que aprendas a usar R y lo vas hacer desde el primer momento.

Aunque muchos expertos dirán que no es una buena táctica (seguro que alguno dirá que es una aberración), mi recomendación es que crees un directorio en el que tengas todos los materiales. El directorio que usaré durante toda esta serie se llamará cuentapalabras, así evitarás los problemas que supone que R busque algo en un sitio que no es el adecuado e imprima mensajes de error. No te doy instrucción alguna de cómo crear ese directorio ni dónde, lo dejo a tu elección, pues depende del sistema operativo, lo único obligatorio es que se ha de llamar cuentapalabras.

Evita el usar espacios en blanco en los nombres de los ficheros y de los directorios. Pueden ser una auténtica pesadilla el manejarlos. Si tienes interés en que haya separación (visual) entre las palabras, te recomiendo que utlices el guion bajo _ entre cada una de las palabras.

También te recomiendo que no uses letras con diacríticos (áéíóúüñç). Pueden ser altamente problemáticas en los nombre de los ficheros y de los directorios.

2.2 Directorio de trabajo

Ya habrás creado el directorio de trabajo, así que arranca RStudio y establece cuentapalabras como el directorio de trabajo. La forma más sencilla, si no recuerdas dónde lo has escondido, es con la barra de menú de RStudio. Sitúa el ratón sobre Session. Cuando hagas clic, se abrirá el menú desplegable. Localiza Set Working Directory y sitúate sobre esa etiqueta. Esto hará que se despliegue un menú contextual. Selecciona Choose Directory…. Entonces se abrirá el gestor de ficheros de tu ordenador. Busca el directorio cuentapalabras como harías para localizar cualquier otro directorio o fichero y, una vez lo hayas encontrado, haz clic sobre Open. En la consola habrá aparecido algo como

setwd("~/Dropbox/cuentapalabras")

Lo importante es que lo último de la respuesta sea cuentapalabras. Todo lo que esté a su izquierda puede ser absolutamente diferente a lo que te muestro. Depende de cada ordenador y del lugar donde hayas creado el directorio cuentapalabras.

Aquí tienes ya tu primera instrucción o función de R: setwd(). Sirve para establecer el directorio de trabajo. Si te supieras la ruta exacta hasta el directorio de trabajo, lo que tendrías que haber escrito en la consola era (ten en cuenta que lo que hay a continuación es lo que uso en mi ordenador, en el tuyo será otra cosa diferente):

setwd("/Users/JMFR/OneDrive - Universidad de Valladolid")

Para comprobar que realmente estás en cuentapalabras puedes usar la función getwd(). Escribe en la consola:

getwd()

La respuesta que dé tiene que acabar en cuentapalabras. A mí, me ha respondido

## [1] "/Users/JMFR/OneDrive - Universidad de Valladolid/cuentapalabras"

Lo que quiere decir esta línea es que la carpeta cuentapalabras se encuentra dentro de OneDrive de la Universidad de Valladolid y que el usuario del ordenador es JMFR. Lo importante es que el final de la cadena diga cuentapalabras. Insisto, todo lo que haya a la izquierda depende del ordenador y de dónde hayas guardado la carpeta cuando lo creaste. Ya no volveré a preocuparme de este detalle. Acuérdate de ello.

2.3 Crear un directorio

Te dije que crearas el directorio cuentapalabras como sueles crear los directorios en tu sistema operativo. Ahora te voy a mostrar cómo crear un directorio con R. Será un subdirectorio dentro cuentapalabras en el que guardarás los datos, por eso lo llamarás datos. La orden es muy sencilla. Ejecuta en la consola

dir.create("datos")

Ya está. Ahora vas a crear otra carpeta dentro de datos. Se llamará mensajes que es donde vas a guardar los mensajes de Navidad que descargarás y con los que trabajarás desde ahora. Como es un subdirectorio dentro de datos tienes que indicarle a R la ruta completa. Por lo tanto, lo primero que has de decirle es dentro de qué subcarpeta de las que pueda haber en cuentapalabras quieres crearlo. En este caso el subdirectorio que lo albergará es datos/, que es el único que debería existir por ahora, por lo que a continuación de la barra pones el nombre del nuevo subdirectorio mensajes. Como en el caso anterior, ejecuta en la consola esta orden

dir.create("datos/mensajes")

Puedes comprobar que lo has conseguido revisando el panel Files. Haz clic sobre datos y, una vez dentro de datos, haz clic sobre mensajes. Se abrirá la carpeta y verás que está absolutamente vacía. Puedes comprobarlo si ejecturas esta orden en la consola

list.files("datos/mensajes")

La respuesta tiene que ser

## character(0)

Eso quiere decir que no hay nada. Que está vacío. ¡Perfecto!

2.4 Descargar los mensajes

Los mensajes que vas a usar como materia prima los tienes en el repositorio del proyecto 7PartidasDigital. Por ahora (diciembre 2023) son 49 ficheros cuyos nombres tienen el formato nnnn.txt. Las cuatro enes representan un número entre 1975 (año del primer discurso de Juan Carlos I) y 2023 (último, por el momento, de Felipe VI).

Lo primero es crear un objeto que contenga el nombre de todos los ficheros que quieres bajar. Puedes hacerlo de esta manera (corta y pega en el editor de RStudio lo que hay en la caja siguiente y después ejecuta la orden pulsando a la vez control e intro).

annos <- c("1975", "1976", "1977", "1978", "1979", "1980", "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990", "1991", "1992", "1993", "1994", "1995", "1996", "1997", "1998", "1999", "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023")

Nunca uses para los nombres de los objetos en R letras con tildes. Te aseguro que tendrás miles de problemas si lo haces.

Otra cosa importantísima es que los nombres de las variables, y de los objetos no pueden comenzar por un número. Además, dentro de los nombres tampoco puedes usar los signos matemátcos - + / *, pero sí el . y el guion bajo _. No lo olvides, de otro modo tendrás un quebradero de cabeza asegurado.

Esta orden le dice a R que cree un vector de caracteres con la función c() (= concatena) que se llamará annos. El contenido de este vector son los números que hay entre los paréntesis y separados por comas. Puesto que están entrecomillados, R los considerará como caracteres y no como números, por lo que no podrás hacer cuentas con ellos. Puedes comprobar que el contenido de annos son caracteres al ejecutar cualquiera de estas dos funciones

typeof(annos)
class(annos)

El resultado será, en cualquiera de los dos casos,

## [1] "character"

Otra forma de hacerlo sería diciéndole a R que guarde en annos la secuencia de números que va entre 1975 y 2023. Para ello podrías usar esta otra expresión

annos <- 1975:2023

Ya te conté que el operador : le indica a R que debe crear la secuencia números que haya entre el que hay a la izquierda de los dos puntos y el que hay a su derecha. Pero con este sistema hay un pequeño problema, si es que lo es: el contenido de annos son números enteros. Puedes comprobarlo si ejecutas en la consola cualquiera de estas dos funciones

typeof(annos)
class(annos)

El resultado será

## [1] "integer"
integer es lo mismo que enteros. Es decir, todos los números naturales 1, 2, 3, 4, 5…, sus opuestos, es decir, los negativos -1, -2, -3, -4, -5… y el 0.

Este pequeño problema puedes resolverlo con la función as.character() que convierte en caracteres el contenido de un vector cuyos elementos sean números.

annos <- as.character(annos)
Se llama elemento a cada uno de los datos que hay dentro de un objeto de R. En el caso de annos es cada uno de los números que sirve para designar cada año.

Para comprobarlo, vuelve a realizar las pruebas con

typeof(annos)
class(annos)

El resultado deberá ser en ambos casos

## [1] "character"

También puedes hacerlo al revés, convertir un vector que tiene números, pero que en realidad son caracteres, en números, con los que puedes hacer operaciones, con la función as.numeric(). Pero no enredemos más de lo necesario. Ya llegará el momento de usarlo.

Puesto que lo que vas a bajar son ficheros de texto plano, que están identificados con .txt, vas a añadirle este sufijo a todos los elementos que hay en el vector annos. Se consigue con la función paste().

annos <- paste(annos, "txt", sep = ".")
Por texto plano entenderemos cualquier texto que carezca de elementos decorativos del tipo cursivas, negritas, centrado, justificado, tamaño de letra… Son ficheros en los que solo hay caracteres alfanuméricos, signos de puntuación, espacios en blanco y cambios de línea.

Lo que le has pedido a R es que añada a cada uno de los elementos que hay en annos el sufijo txt y que entre ambos ponga un punto .. La función paste() requiere tres atributos que van siempre separados por comas: a quien le quieres pegar, en este caso a todos los valores de annos; lo que quieres pegar, el sufijo "txt"; y el pegamento que utilizarás ., que lo estableces con el atributo sep = "".

Si ahora ejecutas en la consola

annos

verás que todos los números de annos tienen el sufijo .txt incorporado.

##  [1] "1975.txt" "1976.txt" "1977.txt" "1978.txt" "1979.txt" "1980.txt" "1981.txt" "1982.txt" "1983.txt"
## [10] "1984.txt" "1985.txt" "1986.txt" "1987.txt" "1988.txt" "1989.txt" "1990.txt" "1991.txt" "1992.txt"
## [19] "1993.txt" "1994.txt" "1995.txt" "1996.txt" "1997.txt" "1998.txt" "1999.txt" "2000.txt" "2001.txt"
## [28] "2002.txt" "2003.txt" "2004.txt" "2005.txt" "2006.txt" "2007.txt" "2008.txt" "2009.txt" "2010.txt"
## [37] "2011.txt" "2012.txt" "2013.txt" "2014.txt" "2015.txt" "2016.txt" "2017.txt" "2018.txt" "2019.txt"
## [46] "2020.txt" "2021.txt" "2022.txt" "2023.txt"
¡Ah! Podías haberte ahorrado el pasar los números a caracteres con as.character() porque cuando juntas elementos que son numéricos con otros que son de caracteres, el vector numérico se convierte en vector de caracteres. A esto se le llama coerción, es decir, fuerzas un tipo de dato a convertirse en otro de acuerdo con la gradación lógico -> entero -> numérico -> cadena de texto (= logical -> integer -> numeric -> character). Por ahora puedes olvidarlo, pero verás como en el futuro te acuerdas de lo útil que puede ser la coerción.

Ya tienes los nombres de todos los ficheros. Ahora necesitas la primera parte de la dirección del repositorio en el que están los ficheros. La guardarás en un vector que llamarás ruta.

ruta <- "https://raw.githubusercontent.com/7PartidasDigital/AnaText/master/datos/mensajes/"

Ya tienes los dos elementos básicos para descargar los ficheros de todos los mensajes de Navidad. La ruta y el nombre de cada uno de ellos guardados en annos. Además, has creado el espacio en el que los guardarás en tu ordenador datos/mensajes. Ahora tienes que bajarlos. Puesto se trata de repetir la misma secuencia de acciones:

  1. Leer un mensaje
  2. Grabar el mensaje

necesitas un mecanismo que lo haga automáticamente.

2.4.1 Bucle for

Cuando se desea que un script realice repetidamente una serie de instrucciones lo mejor es usar un bucle for. La estructura básica de uno de estos bloques es

for (variable in vector) {
  # AQUÍ VAN LAS INSTRUCCIONES QUE HA DE EJECUTAR
}

Todo bucle for se inicia con la instrucción for. Entre los paréntesis van los elementos que lo controlan. Lo que aparece como variable es el contador que llevará el control de cuántas veces se han repetido las instrucciones que están entre las llaves { }. Por lo general se suele usar una letra simple, lo más normal es que sea i, pero nada impide que se llame como quieras, pero cuanto más simple mantengas estas variables de control mejor. El número máximo de veces que se repetirá viene determinado por los límites que se indiquen en vector. Así, si quieres que se algo se ejecute 10 veces, en lugar de vector tienes que poner 1:10, que quiere decir: da vueltas mientras que no pases de 10, o lo que es lo mismo: da 10 vueltas.

Como sabes que vas a bajar 49 ficheros, podrías definir el bucle de esta manera,

for (i in 1:49) {
  # AQUÍ VAN LAS INSTRUCCIONES QUE HA DE EJECUTAR
}

pero como la cantidad de mensajes de Navidad irá cambiando con los años, y dado que tienes en el vector annos el nombre de todos los ficheros que contienen cada mensaje, puedes utilizar la función length() para averiguar cuántos elementos hay en ese objeto. Ejecuta en el consola

length(annos)

La respuesta tiene que ser

## [1] 49

Por lo tanto, puedes expresar el vector de control con 1:length(annos), con lo que la estructura básica del bucle tendrá esta apariencia

for (i in 1:length(annos)) {
  # AQUÍ VAN LAS INSTRUCCIONES QUE HA DE EJECUTAR
}

Ahora tienes que definir las dos acciones que ha de realizar el bucle:

  1. Leer un mensaje del repositorio externo
  2. Grabarlo en el disco duro.

2.5 Leer y escribir

Para leer cada uno de los mensajes usarás la función readLines() y lo que leas en cada vuelta tienes que almacenarlo temporalmente en algún lugar. Ese sitio intermedio es un objeto que llamarás entrada y cuyo contenido cambiará en cada vuelta del bucle.

La función readLines() tiene un argumento obligatorio: el nombre del fichero que ha de leer. Como lo que has de bajar son varios ficheros que se encuentran en un repositorio externo, tienes que anteponer al nombre de cada fichero la ruta para que lo localice. En este caso se consigue con la unión del contenido de ruta y de cada uno de los valores de annos. Para unirlos tienes que usar un pegamento que ya conoces: la función paste()

entrada <- readLines(paste(ruta,
                           annos[i],
                           sep = ""))

Con esto ya podrías leer los ficheros. Sin embargo, si tienes un ordenador Windows los ficheros serán inútiles porque Windows tiene un sistema peculiar a la hora de manejar las letras con diacríticos.

Uno de los mayores quebraderos de cabeza con los ordenadores que funcionan bajo Windows es el sistema de codificación de los caracteres con diacríticos. Windows usa el sistema WINDOWS-1252 y el llamado ISO-8859-1 y, a veces, Latin1 mientras que el resto, incluida internet, utilizan el sistema UTF-8. Cuando sea absolutamente necesario que los datos tengan la codificación privativa de Windows, lo indicaré expresamente.

Pero es un problema que se puede solucionar con sencillez, basta con añadirle a readLines() el argumento encoding = "UTF-8" con lo que se solventa el problema. Así que la expresión para leer los ficheros es

entrada <- readLines(paste(ruta,
                           annos[i],
                           sep = ""),
                     encoding = "UTF-8")

Podrías haber copiado todo en una sola línea, pero a veces las instrucciones son muy largas porque anidan varias funciones y cada una de ellas puede tener varios atributos. Puesto que los atributos se separan con comas, te recomiendo que introduzcas un intro después de cada coma. Al hacerlo así, RStudio sangrará las líneas y te será más fácil leer, y depurar, el código.

Lo estaré usando constantemente porque poco a poco se irán complicando las líneas de código.

Quizá te haya llamado la atención el que tras annos aparezca [i]. Esa i tiene el mismo valor que tenga la i que controla el bucle for y lo que hace es indicarle a R que seleccione el elemento de annos que se encuentre en la posición que indique el valor de i.

Parece enredado, pero es más sencillo de lo que crees. En varias ocasiones has visto que cuando se imprimen en la consola los resultados aparece en el margen izquierdo un número encerrado entre corchetes [n] . Para que lo veas en acción ejecuta en la consola

annos

annos tiene 49 elementos:

##  [1] "1975.txt" "1976.txt" "1977.txt" "1978.txt" "1979.txt" "1980.txt" "1981.txt" "1982.txt" "1983.txt"
## [10] "1984.txt" "1985.txt" "1986.txt" "1987.txt" "1988.txt" "1989.txt" "1990.txt" "1991.txt" "1992.txt"
## [19] "1993.txt" "1994.txt" "1995.txt" "1996.txt" "1997.txt" "1998.txt" "1999.txt" "2000.txt" "2001.txt"
## [28] "2002.txt" "2003.txt" "2004.txt" "2005.txt" "2006.txt" "2007.txt" "2008.txt" "2009.txt" "2010.txt"
## [37] "2011.txt" "2012.txt" "2013.txt" "2014.txt" "2015.txt" "2016.txt" "2017.txt" "2018.txt" "2019.txt"
## [46] "2020.txt" "2021.txt" "2022.txt" "2023.txt"

Como puedes ver, cada uno de ellos está identificado con un número índice, que es el que está encerrado entre corchetes [n], aunque solo se imprime en el comienzo de cada línea, como ya te he contado. Este número es valiosísimo a la hora de programar porque permite acceder a cualquiera de los elementos de un vector en cualquier momento. Esta manera de acceder al contenido de un vector la usarás una y otra vez, sobre todo en los bucles for.

Ahora solo queda grabar los ficheros, uno tras de otro, en datos/mensajes. Para conseguirlo necesitas la función writeLines() que exige dos argumentos:

  1. qué grabar, en este caso el contenido de entrada y
  2. cómo se llamará cada fichero, información que tienes en annos.

Pero como los quieres guardar en un directorio diferente al de trabajo, en datos/mensajes, tienes que unir ambos elementos con paste() para así ofrecerle a R toda la información pertinente. La expresión para grabar los mensajes en el disco duro es

writeLines(entrada,
           paste("datos/mensajes",
                 annos[i],
                 sep = "/"))

Fíjate que en esta ocasión no puse tras mensajes la / que indica cambio de nivel de directorio, por eso la he incluido en el argumento sep = "/". Esta vez el pegamento es la /.

El bucle for completo es

for (i in 1:length(annos)){
  entrada <- readLines(paste(ruta,
                           annos[i],
                           sep = ""),
                     encoding = "UTF-8")
  writeLines(entrada,
           paste("datos/mensajes",
                 annos[i],
                 sep = "/"))
}

Para ejecutarlo te puedes situar en la primera o en la última línea del bucle y pulsar el icono →Run que se encuentra en la línea de menú del editor de RStudio, o puedes pulsar a la vez control (cmd en MacOS) e intro.

Ten un poco de paciencia. Depedendiendo del ordenador y de la conexión a la red puede tardar entre muy poco y unos cinco minutos.

Cuando acabe de descargar todos los ficheros, es posible que aparezca en la consola este mensaje:

## Warning in readLines(paste(ruta, annos[i], sep = ""), encoding = "UTF-8"):
## incomplete final line found on 'https://raw.githubusercontent.com/
## 7PartidasDigital/AnaText/master/datos/mensajes/2019.txt'

No te preocupes. Es un aviso de que el fichero correspondiente al discurso del año 2019 (2019.txt) no acaba en una línea en blanco. No supone ningun problema. Para que compruebes que todo ha ido bien ejecuta esta orden

list.files("datos/mensajes")

Si el resultado es

##  [1] "1975.txt" "1976.txt" "1977.txt" "1978.txt" "1979.txt" "1980.txt" "1981.txt" "1982.txt" "1983.txt"
## [10] "1984.txt" "1985.txt" "1986.txt" "1987.txt" "1988.txt" "1989.txt" "1990.txt" "1991.txt" "1992.txt"
## [19] "1993.txt" "1994.txt" "1995.txt" "1996.txt" "1997.txt" "1998.txt" "1999.txt" "2000.txt" "2001.txt"
## [28] "2002.txt" "2003.txt" "2004.txt" "2005.txt" "2006.txt" "2007.txt" "2008.txt" "2009.txt" "2010.txt"
## [37] "2011.txt" "2012.txt" "2013.txt" "2014.txt" "2015.txt" "2016.txt" "2017.txt" "2018.txt" "2019.txt"
## [46] "2020.txt" "2021.txt" "2022.txt" "2023.txt"

Enhorabuena. Has escrito tu primer script en R y has bajado todos los mensajes de Navidad que vas a analizar.

2.6 Guardar el script

Como te ha llevado un buen rato escribir el script, te recomiendo que lo grabes en el ordenador. Pero como debes que mantener cierto orden y limpieza en la zona de trabajo. Una cosa son los datos, que tienes en datos (y que se irán multiplicando), y otra los scripts que vas a ir copiando y desarrollando a lo largo de los capítulos siguientes. Creo que lo mejor es que guardes los scripts en una subcarpeta de cuentapalabras que llame codigo. Ya sabes como crearla con R.

dir.create("codigo")

Ahora, para guardar el script haz clic sucesivamente en File > Save as, y selecciona el directorio codigo que acabas de crear. Ponle al script un nombre más informativo que el Untitled1 que ofrece el sistema por defecto y haz clic en Save. Ya lo tienes guardado. Ahora puedes volver sobre el fichero del script para ampliarlo, mejorarlo, corregirlo, reutilizarlo…