7 Otros tópicos

Las reglas que aprendimos para escribir el pseudocódigo nos permiten traducir nuestros algoritmos a cualquier lenguaje de computación de manera muy general y sencilla. Sin embargo, cada lenguaje de programación tiene diseñado su propio conjunto de funciones y estructuras de datos que facilitan algunas tareas. Una vez que hemos incorporado los conceptos básicos de la programación, podemos dedicarnos a aprender las profundidades de un lenguaje en particular.

En este capítulo vamos mencionar algunas cosas útiles sobre R y otras cuestiones.

7.1 La consola

Cuando prendemos nuestra computadora nos encontramos con una interfaz gráfica implementada por el sistema operativo para que podamos hacer lo que necesitemos de manera sencilla usando ventanas y menúes, interactuando con el sistema a través del mouse, teclado, micrófono o pantalla táctil. Sin embargo, es posible usar la compu de otra forma, escribiendo comandos especiales en una ventanita, llamada consola que es capaz de interpretarlos para hacer cualquier tipo de actividad, sin utilizar la interfaz gráfica. Años atrás, esta era la única manera disponible de usar la computadora (por ejemplo, con sistema operativo MS-DOS de Windows).

Saber usar la consola es muy útil para automatizar actividades, realizar tareas administrativas, manipular varios archivos u objetos en simultáneo, lanzar a correr proyectos grandes, conectarse de manera remota a un servidor, etc. La primera vez que uno se mete en esto puede ser intimidante, pero no hay que olvidar que ya estamos acostumbrados a usar la consola de R, con lo cual esto de escribir comandos e interpretar respuestas es algo familiar.

Los términos terminal, consola, shell y línea de comandos son términos que a veces se usan como sinónimos sin demasiada preocupación, como si fuesen distintas formas de llamar a lo mismo: una ventanita donde puedo escribir comandos y hacer que sucedan cosas en la computadora. Sin embargo, hay pequeñas diferencias entre estos conceptos, que acá tratamos de resumir (aunque ni siquiera entre informáticos hay mucho acuerdo en las definiciones):

  • Shell (intérprete de línea de comandos): es software, es un programa que corre otros programas, procesa los comandos que recibe y devuelve resultados Ejemplo: Bash (la más común en sistemas Linux), sh, PowerShell, etc.
  • Terminal: un programa que propicia la transferencia de input/output entre el usuario y la shell. No ejecuta comandos, pero recibe los comandos que el shell va a procesar. Ejemplos: Command prompt, guake, gnome-terminal, etc. Antiguamente, se trataba de las múltiples estaciones con un monitor y un teclado que una gran computadora tenía para ser usada por varias personas.
  • Consola: un tipo particular de terminal, con una ventana escribir inputs y leer outputs. Históricamente, se trataba de un panel físico (consola) con controles. Siri o Cortana podrían considerarse terminales, pero no son consolas, ya que no hay que escribir para mandar comandos. Terminal y consola se usan prácticamente como sinónimos.

Para abrir una terminal en Linux se puede usar el atajo ctrl + alt + t y en Windows se puede escribir cmd en Inicio.

Abrir la terminal en Windows. En computadoras con Windows en español, en lugar de Command Prompt dice Símbolo del sistema.

Figura 7.1: Abrir la terminal en Windows. En computadoras con Windows en español, en lugar de Command Prompt dice Símbolo del sistema.

Así luce la línea de comandos de Windows:

Línea de comandos de Windows.

Figura 7.2: Línea de comandos de Windows.

Los comandos se escriben en la última línea a continuación del símbolo >, que a su vez está precedido por la ruta a la carpeta que es el directorio de trabajo actual de la terminal, en este caso, C:\Users\Marcos (carpeta Marcos, dentro de la carpeta Users, en el disco C).

Si bien hay muchísimos comandos para utilizar en la terminal, acá vamos a mencionar algunos como ejemplo:

  • Mostrar en qué carpeta (directorio) de la compu estamos situados: pwd en Linux o cd en Windows
  • Listar todos los archivos y carpetas que tenemos en el directorio actual: ls en Linux o dir en Windows
  • Entrar a una subcarpeta desde el directorio en el que estamos: cd nombresubcarpeta
  • Ver la ayuda de los comandos: help
  • Limpiar la consola: cls en Windows o clear en Linux
  • Cerrar la consola: exit

Por ejemplo, podemos ver todos los archivos que existen en el directorio actual con ls:

Contenido del directorio actual (Windows).

Figura 7.3: Contenido del directorio actual (Windows).

Para los siguientes ejemplos, trabajaremos en una carpeta llamada Ejemplos, cuyo path es C:\Users\Marcos\Trabajo\Ejemplos que tiene la siguiente composición:

Carpeta en la cual deseamos trabajar.

Figura 7.4: Carpeta en la cual deseamos trabajar.

Podemos convertir a dicha carpeta como nuestro nuevo directorio de trabajo con el comando cd (change directory):

Cambio de directorio de trabajo y listado de archivos en el mismo.

Figura 7.5: Cambio de directorio de trabajo y listado de archivos en el mismo.

En Windows hay una forma más directa de abrir la terminal y que ya tenga seteada como directorio de trabajo a una carpeta deseada. Antes de abrir la terminal, vamos con el Explorador de archivos a la carpeta en cuestión, nos posicionamos en la barra del explorador, escribimos cmd y le damos ENTER. Automáticamente se abrirá la terminal, con esta carpeta como directorio de referencia.

Abrir la terminal desde una carpeta en particular en Windows.

Figura 7.6: Abrir la terminal desde una carpeta en particular en Windows.

Desde la terminal podemos correr nuestros programas de R. Hacer esto es necesario cuando tenemos que programar alguna tarea de gran escala que se ejecutará de manera remota en algún servidor o cuando necesitamos encapsular nuestro programa para que otros lo puedan correr sin siquiera saber nada de R.

Veamos un ejemplo. En la carpeta C:\Users\Marcos\Trabajo\Ejemplos tengo guardado el siguiente script, en un archivo llamado mi_programa.R que tiene este contenido:

a <- "¡Hola, Mundo!"
b <- 3
d <- 5
cat("==========================================\n")
cat("                 RESULTADOS               \n")
cat("==========================================\n\n")
cat("El valor de b es ", b, ", mientras que d vale ", d, ".\n\n", sep = "")
cat("La suma entre ellos es igual a ", b + d, ".\n\n", sep = "")
cat("Este es un saludo:", a)

Para ejecutar este programa desde la terminal, sin abrir RStudio o R, utilizo el comando Rscript, que le indica a la computadora que el contenido del archivo mi_programa.R debe ser evaluado por R. Esto es lo que se observa en la consola:

Correr el programa de R desde la consola en Windows.

Figura 7.7: Correr el programa de R desde la consola en Windows.

Todo lo que en el programa estaba encerrado en una llamada a la función cat() es lo que se muestra como mensajes en la terminal. Notar que la instrucción RScript mi_programa.R funcione, debemos tener como directorio de trabajo aquella carpeta que aloja al archivo mi_programa.R, en caso contrario el sistema nos alertará que el mismo no está disponible.

Para que lo anterior funcione en Windows, hay que indicarle al sistema operativo que Rscript es un comando que se instaló con R y que lo puede encontrar en la carpeta de los archivos del programa R. Esto hay que hacerlo una sola vez editando las variables de entorno de Windows, que son cadenas de texto que contienen información acerca del sistema para determinar, por ejemplo, dónde buscar algunos archivos. Esto se logra siguiendo estos pasos:

  1. Fijarse en qué carpeta de la compu está instalado R. Seguramente lo encuentres si, abriendo el explorador de archivo, vas siguiendo este camino: Este equipo > Windows (C:) > Archivos de programa > R > R-version > bin. En esta carpeta tiene que haber dos archivos, llamados R.exe y Rscript.exe. Si es así, hacé clic con el botón derecho del mouse sobre cualquiera de ellos, luego en “Propiedades” y copiá el path que aparece en “Ubicación” (deberías copiar algo como C:\Program Files\R\R-3.6.0\bin).
  2. En Inicio, escribir “Entorno” y hacer clic en la opción “Editar las variables de entorno del sistema (panel de control)”.
  3. Hacer clic en el botón “Variables de entorno”.
  4. En el cuadro “Variables del sistema”, hacer clic en la variable “Path” y luego en “Editar”.
  5. Hacer clic en “Nuevo”, pegar la dirección C:\Program Files\R\R-3.6.0\bin y dar Enter. Luego, hacer clic en “Aceptar” tres veces para cerrar todo.
  6. ¡Listo! Ya podés correr tus programas desde la consola con el comando Rscript.

Lo bueno de esto es que si corremos nuestros programas desde la terminal, podemos hacer cosas interactivas. Por ejemplo, para todo lo que pusimos LEER en nuestros pseudocódigos, ahora podemos hacer verdaderamente que la persona usuaria del programa provea los valores correspondientes.

Veamos algunos ejemplos.

Práctica 2, Ejercicio 1: paridad de un número

La función scan() es la que permite escanear o leer valores que los usuarios ingresen por la terminal. Entre sus argumentos tenemos a file, que si lo seteamos como file = "stdin" indica que vamos a leer información desde la consola. Otros argumentos que son de utilidad incluyen a n = 1, que indica que sólo leeremos un valor y quiet = TRUE que le pide a esta función que no emita ningún mensaje. Por ejemplo, si el siguiente código se guarda en el archivo paridad.R y es ejecutado desde la consola, le va a pedir a la persona que lo esté usando que indique cualquier número y luego le va a comunicar si es par o impar:

cat("==========================================\n")
cat("           PARIDAD DE UN NÚMERO           \n")
cat("==========================================\n\n")
cat("Ingrese un número entero y presione enter:\n")
n <- scan(file = "stdin", n = 1, quiet = TRUE)
if (n %% 2 == 0) {
    cat(n, "es par\n")
} else {
    cat(n, "es impar\n")
}

Esto es lo que ocurre en la terminal:

Programa paridad.R.

Figura 7.8: Programa paridad.R.

Práctica 2, Ejercicio 3: salario

En este ejemplo, tenemos que leer tres valores, dos de lo cuales son de tipo carácter. Para esto tenemos que agregar en la función scan() el argumento what = "", que admite el ingreso de caracteres alfanuméricos (por default scan() sòlo espera recibir valores numéricos). Si el siguiente código se guarda en el archivo salario.R y se lo ejecuta desde la consola, produce el resultado que se muestra en la imagen:

cat("==========================================\n")
cat("           CÁLCULO DEL SALARIO            \n")
cat("==========================================\n\n")
cat("Ingrese la cantidad de horas trabajadas:\n")
horas <- scan("stdin", n = 1, quiet = TRUE)
cat("\nIngrese el día de la semana (DOM LUN MAR MIE JUE VIE SAB):\n")
dia <- scan("stdin", what = "", n = 1, quiet = TRUE)
cat("\nIngrese el turno (M T N):\n")
turno <- scan("stdin", what = "", n = 1, quiet = TRUE)

salario <- horas * 400
if (turno == "N") {
    salario <- salario + horas * 200
}
if (turno == "DOM") {
    salario <- salario + horas * 100
}
cat("\nEl salario que se debe abonar es $", salario, "\n", sep = "")
Programa salario.R.

Figura 7.9: Programa salario.R.

Práctica 4, Ejercicio 1: suma de elementos de un vector28

En este ejercicio escribimos una función para sumar los elementos de un vector. Vamos a ver cómo hacer para que un usuario nos diga cuáles son los valores que quiere sumar desde la consola. Primero preguntamos cuántos números se desean sumar y luego los recibimos en el vector v. Si el siguiente código queda guardado en el archivo suma.R y se lo corre desde la terminal, produce el resultado que se muestra en la imagen.

cat("==========================================\n")
cat("              SUMA DE NÚMEROS             \n")
cat("==========================================\n\n")
cat("¿Cuántos números va a ingresar?\n")
n <- scan("stdin", n = 1, quiet = TRUE)
cat("\nIngrese los números, presionando Enter luego de cada uno:\n")
v <- scan("stdin", n = n, quiet = TRUE)
suma <- 0
for (i in 1:length(v)) {
    suma <- suma + v[i]
}
cat("\nLa suma de los números es:", suma, "\n")
Programa suma.R.

Figura 7.10: Programa suma.R.

7.2 Uso de argumentos en la línea de comandos al ejecutar código de R

En ejemplos anteriores hemos visto cómo capturar distintas piezas de información de forma interactiva mediante la función scan() mientras estamos ejecutando un programa de R desde la línea de comandos.

En otras ocasiones, en lugar de pausar la ejecución del programa a la espera de que el usuario ingrese algún valor, es conveniente especificar algunas opciones directamente en la instrucción Rscript que ejecuta el código.

Por ejemplo, imaginemos que tenemos un programa llamado resumen.R que se encarga de hacer un análisis descriptivo de un conjunto de datos que están guardados en un archivo de texto de nombre 02_05_22.txt. Al ejecutar este programa desde la terminal, podemos indicar el nombre del archivo como un argumento adicional de esta forma:

Rscript resumen.R 02_05_22.txt

Ahora supongamos que este mismo tipo de análisis se repite todos los días con datos nuevos. En lugar de modificar nuestro script resumen.R, ejecutamos lo anterior con el nombre del archivo que corresponda y listo:

Rscript resumen.R 03_05_22.txt
Rscript resumen.R 04_05_22.txt
Rscript resumen.R 05_05_22.txt

Para que esto funcione, el programa que está guardado en resumen.R debe ser capaz de capturar el nombre del archivo que tiene leer y que el usuario se lo está pasando como un argumento adicional en la instrucción Rscript.

La función que se encarga de capturar los argumentos adicionales que enviamos desde la terminal es commandArgs(). Toma todos los elementos que escribamos y los reúne en un vector de tipo carácter. Por ejemplo, el archivo ejemplo1.R tiene el siguiente contenido:

# Capturar los argumentos pasados desde la terminal en un vector
args <- commandArgs(trailingOnly = TRUE)

# Contar cuántos argumentos nos pasaron
cat("Nos pasaron", length(args), "argumentos.\n\n")

# Mostrar los argumentos que nos pasaron
cat("Los argumentos que nos pasaron son:\n")
cat(args, "\n")

# Aunque los argumentos sean números, son tomados como carácter
cat("\nLos argumentos se toman como valores de tipo:\n")
class(args)

Al ejecutarlo desde la línea de comandos con los argumentos “hola”, “chau” y “4” obtenemos:

Rscript ejemplo1.R hola chau 4
Nos pasaron 3 argumentos.

Los argumentos que nos pasaron son:
hola chau 4 

Los argumentos se toman como valores de tipo:
[1] "character"

Si lo ejecutamos sin argumentos:

Rscript ejemplo1.R
Nos pasaron 0 argumentos.

Los argumentos que nos pasaron son:
 

Los argumentos se toman como valores de tipo:
[1] "character"

Ahora vamos a suponer que el programa ejemplo2.R tiene como objetivo contar un chiste o decir un refrán, según lo que se le pida en el único argumento que se le pasa al correrlo desde la terminal. Si el argumento es igual “chiste”, se cuenta el chiste; si es igual a “refran” se cuenta el refrán; y en otro caso no se hace nada. El contenido del archivo es:

# Capturar los argumentos pasados desde la terminal en un vector
args <- commandArgs(trailingOnly = TRUE)

if (args[1] == "chiste") {
    cat("- Juan, cómo has cambiado.\n- Yo no soy Juan.\n- Más a mi favor.\n\n")
} else if (args[1] == "refran") {
    cat("No por mucho madrugar amanece más temprano.\n\n")
} else {
    # Genero un error para que el programa se detenga, avisando lo que pasa
    stop("El argumento provisto debe ser igual a chiste o refran.\n")
}

Ejecutamos este archivo pasando distintos valores para su argumento:

Rscript ejemplo2.R refran
No por mucho madrugar amanece más temprano.
Rscript ejemplo2.R chiste
- Juan, cómo has cambiado.
- Yo no soy Juan.
- Más a mi favor.
Rscript ejemplo2.R hola
Error: El argumento provisto debe ser igual a chiste o refran.
Execution halted

Podemos controlar la cantidad de argumentos admitidos generando errores en el código para aquellas situaciones donde el usuario envíe menos o más que la cantidad deseada. Por ejemplo, en el caso anterior, es obligatorio enviar uno y sólo un argumento:

# Capturar los argumentos pasados desde la terminal en un vector
args <- commandArgs(trailingOnly = TRUE)

# Controlar la cantidad de argumentos
if (length(args) == 0 || length(args) > 1) {
    stop("Debe proveer exactamente un argumento, que debe ser igual a chiste o refran.\n")
}

if (args[1] == "chiste") {
    cat("- Juan, cómo has cambiado.\n- Yo no soy Juan.\n- Más a mi favor.\n\n")
} else if (args[1] == "refran") {
    cat("No por mucho madrugar amanece más temprano.\n\n")
} else {
    # Genero un error para que el programa se detenga, avisando lo que pasa
    stop("El argumento provisto debe ser igual a chiste o refran.\n")
}

Veamos lo que pasa si cumplimos o no con la cantidad exacta de argumentos que hay que pasarle al código de R:

Rscript ejemplo3.R
Error: Debe proveer exactamente un argumento, que debe ser igual a chiste o refran.
Execution halted
Rscript ejemplo3.R chiste refran
Error: Debe proveer exactamente un argumento, que debe ser igual a chiste o refran.
Execution halted
Rscript ejemplo3.R chiste
- Juan, cómo has cambiado.
- Yo no soy Juan.
- Más a mi favor.

Imaginemos por último que es obligatorio pasar un primer argumento (“chiste” o “refran”) y que opcionalmente se puede pasar un segundo argumento, que se va a tratar de un número para indicar cuántas veces queremos que el chiste o el refrán se repita. Como todos los argumentos se pasan como datos de tipo carácter, para poder usar el número tendremos que convertirlo a dato de tipo numérico.

# Capturar los argumentos pasados desde la terminal en un vector
args <- commandArgs(trailingOnly = TRUE)

# Si no proveyó argumentos, generar un error y que se detenga el programa
if (length(args) == 0) {
    stop("Debe proveer al menos un argumento (chiste o refran).")
}

# Si proveyó más de 2 argumentos, generar un error y que se detenga el programa
if (length(args) > 2) {
    stop("No debe proveer más de 2 argumentos. El primero es obligatorio (chiste o refran) y el segundo es opcional (un número que indica la cantidad de veces a repetir el chiste o el refrán).")
}

# Si no hay segundo argumento, args[2] es NA
if (is.na(args[2])) {
    n <- 1
} else {
    n <- as.numeric(args[2])
}

# Repetir n veces
for (i in 1:n) {
    if (args[1] == "chiste") {
        cat("- Juan, cómo has cambiado.\n- Yo no soy Juan.\n- Más a mi favor.\n\n")
    } else if (args[1] == "refran") {
        cat("No por mucho madrugar amanece más temprano.\n\n")
    } else {
        # Genero un error para que el programa se detenga, avisando lo que pasa
        stop("El argumento provisto debe ser igual a chiste o refran.\n")
    }
}

Veamos ahora cómo funciona:

Rscript ejemplo4.R refran 5
No por mucho madrugar amanece más temprano.

No por mucho madrugar amanece más temprano.

No por mucho madrugar amanece más temprano.

No por mucho madrugar amanece más temprano.

No por mucho madrugar amanece más temprano.
Rscript ejemplo4.R chiste 3
- Juan, cómo has cambiado.
- Yo no soy Juan.
- Más a mi favor.

- Juan, cómo has cambiado.
- Yo no soy Juan.
- Más a mi favor.

- Juan, cómo has cambiado.
- Yo no soy Juan.
- Más a mi favor.
Rscript ejemplo4.R refran
No por mucho madrugar amanece más temprano.
Rscript ejemplo4.R
Error: Debe proveer al menos un argumento (chiste o refran).
Execution halted

Si querés probar estos ejemplos, podés crear los archivos de código mencionados copiando y pegando las instrucciones o descargarlos de este enlace.

Además de la función commandArgs() existen paquetes de R para poder trabajar con argumentos y opciones de formas mucho más elaboradas, como los paquetes argparse y optparse, entre otros.