Tema 3 Variables y expresiones

En este tema se estudia el concepto de variable. Las variables hacen referencia a datos. Los datos se pueden generar usando expresiones, que es el otro aspecto que se analiza en este tema.

3.1 Variables

Una variable permite asociar un valor u objeto a un identificador. En la memoria del ordenador habrá una zona donde se almacena el objeto asociado a la variable.

Para crear una variable en R hay que asignarle un valor mediante una operación llamada asignación. La asignación consta de tres elementos:

  • el identificador o nombre de la variable
  • el operador de asignación
  • una expresión que produce el valor a asignar

Vamos a ver un ejemplo:

x <- 7

Aquí la variable tiene el nombre x, el operador de asignación está compuesto por los caracteres < y - escritos juntos (<-). Por último, la expresión es el valor 7. A partir de ahora, la variable x tiene asociado el valor 7 y podemos usar la variable para consultar su valor. Por ejemplo:

y <- x * 6

Ahora, la variable x aparece en una expresión. Al evaluar la expresión la variable x se sustituye por su valor (7), de forma que x * 6 produce el valor 42, que es asignado a la variable y.

Como escribir los caracteres < y - resulta tedioso, RStudio tiene un atajo (una combinación de teclas) para teclearlo más rápidamente. Consiste en pulsar la tecla Alt junto a -.

Si tecleas una variable en la consola y pulsas Enter, R evalúa la expresión formada por la variable, lo cual equivale a consultar su valor.

y
## [1] 42

En la pestaña Environment, del panel superior derecho de RStudio, aparecen por defecto los objetos creados en el Entorno Global o espacio de trabajo, que incluyen las variables que se han creado en la sesión de trabajo. Ahí puedes consultar las variables creadas y su valor.

3.1.1 Nombres de variables

El nombre de una variable es un identificador. Las siguientes reglas se aplican a los identificadores:

  • Pueden ser una combinación de letras, dígitos, punto (.) y subrayado (_).
  • Deben comenzar por una letra o punto. Si comienzan con punto, el siguiente carácter no puede ser un dígito.
  • Las palabras reservadas de R, como if o for no pueden usarse como identificadores. (Ejecuta ?Reserved para consultar las palabras reservadas).

Ejemplos de identificadores válidos son: total, Suma, .bien, valor_total, nombre5. Ejemplos de identificadores inválidos son: alumn@, 7nombre, _bien, TRUE, .6nombre (TRUE no es válido porque es una palabra reservada de R).

R es sensible a las mayúsculas. Esto significa que los identificadores Nombre, nombre o NOMBRE son distintos. Aunque R lo permite, no uses identificadores que sólo se diferencian en que unas letras están en mayúsculas o minúsculas, pues puede causar confusión.

3.1.2 Asignando distintos valores a una variable

A una variable se le puede asignar distintos valores a lo largo del tiempo. Sin embargo, la variable sólo guarda el último valor asignado. Veamos un ejemplo (en el ejemplo las asignaciones se encierran entre paréntesis para que el resultado de la asignación aparezca en la pantalla):

(z <- 20)
## [1] 20
(z <- 6)
## [1] 6
z
## [1] 6

R es un lenguaje con tipado dinámico. Esto significa que una variable puede contener valores de distinto tipo a lo largo del tiempo (estudiaremos los distintos tipos de datos más adelante). Por ejemplo:

a <- 4.5     # a contiene un número
a <- "Juan"  # después una cadena de caracteres
a <- TRUE    # y ahora un valor lógico

3.1.3 Operadores de asignación alternativos

Aparte del operador de asignación: <-, existen otros dos operadores de asignación: = y ->:

b = 9
16 -> c

Como puede observase, cuando se usa el operador -> la expresión aparece a la izquierda del operador y la variable a la derecha. De momento, se recomienda usar el operador <-.

3.1.4 Consulta y borrado de variables

Para consultar las variables y, en general, los objetos que hemos definido, podemos usar las funciones ls y objects:

ls()
## [1] "a"    "b"    "c"    "mean" "pi"   "pred" "x"    "y"    "z"

También podemos consultar los objetos definidos en la pestaña Environment del panel superior derecho de RStudio. Para borrar variables del espacio de trabajo se puede usar la función rm. Por ejemplo, vamos a borrar las variables a y b:

rm(a, b)
ls()
## [1] "c"    "mean" "pi"   "pred" "x"    "y"    "z"

Si queremos borrar todos los objetos que hemos definido podemos usar la escoba que aparece en la pestaña Environment del panel superior derecho de RStudio. Otra posibilidad es esta:

rm(list = ls())
ls()
## character(0)

3.1.5 Guardar y recuperar objetos del entorno de trabajo

Normalmente, cuando terminamos una sesión de trabajo en R no estamos interesados en guardar los distintos objetos que hemos creado. Sin embargo, hay veces que algunas variables almacenan resultados que queremos mantener, porque para obtenerlos se precisa mucho trabajo y/o tiempo. En esos casos podemos recurrir a la función save:

a <- -5.5
b <- 22
c <- 0
save(a, b, file = "datos.RData")

La función save guarda una serie de variables en un archivo en memoria secundaria (cuyo contenido no se borra al apagar el ordenador). Como se puede ver, se listan los nombres de las variables que queremos guardar (hemos elegido guardar las variables a y b) y el nombre del archivo donde se guardarán los datos. Se suele usar la extensión RData para estos archivos. Para recuperar los datos almacenados con save se usa la función load:

rm(list = ls())  # borramos todas las variables
load("datos.RData")
ls()
## [1] "a" "b"

Hemos recuperado las variables a y b. Otra forma de cargar un archivo de datos es localizarlo en la pestaña Files en el panel inferior derecho de RStudio y hacer doble clic en él o usar la opción Session>Load Workspace ….

Si se quiere guardar todo el espacio de trabajo, se puede usar la función save.image:

rm(list = ls())  # borramos todas las variables
x <- 9
y <- 28
save.image(file = "todas.RData")

save.image usa por defecto el nombre de archivo .RData. Como curiosidad, cuando cerramos RStudio se nos preguntan si ejecutar save.image con sus parámetros por defecto. Si decimos que sí, automáticamente se guardan nuestros objetos en un archivo llamado .RData en el directorio de trabajo. Cuando se abre RStudio se recuperan los datos almacenados en un archivo .RData situado en la carpeta de inicio de RStudio.

rm(list = ls())  # borramos todas las variables
load("todas.RData")
ls()
## [1] "x" "y"

Otra posibilidad para guardar todo el espacio de trabajo es usar el botón con un disco del panel Environment. Las funciones savehistory y loadhistory también nos permiten guardar y recuperar el historial de órdenes tecleadas en la consola.

Cuando leemos datos de un archivo con load se pueden cargar varias variables. Podemos consultar los nombres de dichas variables así:

nombres <- load("todas.RData")
nombres
## [1] "x"            "y"            ".Random.seed"

Si sólo queremos guardar un objeto en un archivo, se puede usar la función saveRDS. Una pequeña ventaja de usar esta función es que al cargar el objeto (con readRDS) podemos elegir su nombre:

n <- "Julio"
saveRDS(n, file = "objeto.RDS")
nombre <- readRDS("objeto.RDS")
nombre
## [1] "Julio"

3.2 Expresiones aritméticas

Las variables son útiles, pues permiten almacenar valores y resultados de cálculos, resultados que podemos consultar o usar con posterioridad. Las expresiones sirven para expresar dichos cálculos. Las vamos a usar continuamente, pues el objeto de usar un ordenador suele ser realizar cálculos. A menudo, el resultado de evaluar expresiones se almacena en variables.

Las expresiones aritméticas expresan cálculos numéricos. Como hemos visto, pueden usarse en asignaciones. El resultado de evaluar la expresión es lo que se asigna a la variable. Una expresión aritmética puede contener:

  • valores literales: son valores concretos de un tipo de dato, como el valor 7.
  • variables: al evaluar la expresión una variable se sustituye por su valor asociado.
  • operadores aritméticos, como: +, -, *, /, ^
  • llamadas a funciones: las funciones producen valores en función de sus parámetros.

Veamos algunos ejemplos de expresiones aritméticas:

20            # literal
## [1] 20
1.8 * 2       # literales y operador de multiplicación
## [1] 3.6
x <- 9
x ^ 3         # variable, operador potencia y literal
## [1] 729
exp(x) - 1000 # función exponencial, operador y literal
## [1] 7103.084

3.2.1 Valores literales numéricos

Un valor literal numérico es un número concreto, también a veces llamado escalar. Existen varias formas de escribir un literal de tipo real:

  • En decimal, por ejemplo: 0.18.
  • En notación científica, por ejemplo: 1.23e4. Este número equivale a \(1.23 * 10^4\), o sea, 12300.
  • En hexadecimal, por ejemplo: 0x2f. Este número es el 47 en decimal (\(2 * 16^1 + 15 * 16^0\)).

Para escribir un literal entero se procede como con los reales, pero no se puede usar parte fraccional y hay que terminar el número con L. Por ejemplo: 12L, 123e2L, 0x2fL.

3.2.2 Precedencia y asociatividad de los operadores

Las expresiones de ejemplo previas incluyen un único operador. Sin embargo, una expresión puede incluir más de un operador. Las reglas de precedencia indican el orden en que se aplican los operadores. Por ejemplo, la multiplicación (operador *) tiene mayor precedencia que la suma (operador +):

2 + 3 * 5
## [1] 17

Como la multiplicación tiene mayor precedencia se ha calculado primero 3 * 5 (15) y despues 2 + 15. Como en matemáticas, los paréntesis sirven para alterar el orden de precedencia:

(2 + 3) * 5
## [1] 25

La asociatividad de un operador se usa cuando aparece más de una vez seguida un operador, y sirve para determinar si el operador se aplica de izquierda a derecha o de derecha a izquierda. Por ejemplo, el operador de división (/) se asocia de izquierda a derecha:

8 / 2 / 4
## [1] 1

Como la asociatividad es de izquierda a derecha, primero se calula 8 / 2 (4) y después 4 / 4 (1). De nuevo, los paréntesis sirven para alterar el orden de aplicación de los operadores:

8 / (2 / 4)
## [1] 16

A continuación se listan los distintos operadores aritméticos y lógicos de R ordenados de mayor a menor precedencia, en la última columna se indica su asociatividad.

Operador Significado Asociatividad
^ Potencia Derecha a izquierda
-x, +x menos unario, más unario Izquierda a derecha
%% Módulo Izquierda a derecha
*, / Multiplicación, división Izquierda a derecha
+, – Suma, resta Izquierda a derecha
<, >, <=, >=, ==, != Relacionales Izquierda a derecha
! NO lógico Izquierda a derecha
&, && Y lógico Izquierda a derecha
|, || O lógico Izquierda a derecha
->, ->> Asignación a la derecha Izquierda a derecha
<-, <<- Asignación a la izquierda Derecha a izquierda
= Asignación a la izquierda Derecha a izquierda

Todos los operadores de la lista son binarios, es decir, tiene dos operandos, salvo los operadores + y -. Estos tienen una versión binaria (para sumar y restar) y una versión unaria (para indicar el signo). Por ejemplo:

-2 + 5
## [1] 3

En esta expresión el - es el operador unario y el + es el operador binario. Observa que el resultado es 3, porque el operador unario tiene más precedencia que el binario. Si la precedencia fuera al revés el resultado sería -7. Otro ejemplo sería:

3 - -2
## [1] 5

En este caso el primer - es el operador binario y el segundo el unario.

Teniendo en cuenta la precedencia y asociatividad. ¿Qué crees que produce -2 ^ 2 (4 ó -4)?, ¿Y 2 ^ 3 ^ 3 (512 ó 134217728)? Prueba en la consola si has acertado. Usa paréntesis para obtener el otro resultado.

3.3 Expresiones lógicas o booleanas

En programación se usa con gran asiduidad las operaciones lógicas, también conocidas como booleanas. El término booleano viene del nombre del matemático George Boole. Una operación lógica es aquella que produce un valor de verdadero o falso. Existen dos literales booleanos, que son los valores TRUE y FALSE.

(casado <- TRUE)
## [1] TRUE
(americano <- FALSE)
## [1] FALSE

Una variable como casado o americano, que almacena un valor lógico, es llamada variable lógica o booleana. Se puede abreviar los valores TRUE y FALSE con T y F respectivamente, pero resulta más legible usar los primeros. Además, T y F son realmente variables que almacenan los valores lógicos TRUE y FALSE respectivamente. Al ser variables, sus valores pueden ser sobrescritos. No ocurre lo mismo con TRUE y FALSE que son palabras reservadas y no pueden ser sobrescritas.

3.3.1 Operadores relacionales

La mayor parte de las expresiones lógicas incluyen operadores relacionales. Los operadores relacionales son binarios y sirven para comparar dos valores. Los operadores relacionales son:

  • <: menor
  • <=: menor o igual
  • >: mayor
  • >=: mayor o igual
  • ==: igual (observa que son dos símbolos de =)
  • !=: distinto

Veamos un ejemplo de uso de algunos de estos operadores:

x <- 10
x > 0      # ¿es x mayor que 0?
## [1] TRUE
x != 10    # ¿es x distinto de 10?
## [1] FALSE

3.3.2 Operaciones lógicas compuestas

Los operadores lógicos permiten generar expresiones lógicas compuestas a partir de expresiones lógicas simples. Los principales operadores lógicos son el operador Y, el O y el NO.

3.3.2.1 Operador Y (and): &&

Es un operador binario. Produce el valor verdadero si ambos operandos (que son de tipo lógico) son verdaderos, en otro caso produce el valor falso. Ejemplo:

x <- 10
y <- -5
x > 0 && y < 0    # ¿es x mayor que cero e y menor que cero?
## [1] TRUE
x == 10 && y == 0 # ¿es x igual a 10 e y igual a 0?
## [1] FALSE

El operador es &&. El operador & es equivalente, pero se usa para colecciones de datos, como vectores.

3.3.2.2 Operador O (or): ||

Es un operador binario. Produce un valor verdadero si al menos uno de los operandos es verdadero, en otro caso (ambos operandos son falsos) produce el valor falso. Ejemplo:

x <- 10
y <- -5
x > 0 || y > 0
## [1] TRUE
x == 0 || y == 0
## [1] FALSE
x > 0 || y < 0
## [1] TRUE

El operador es ||. El operador | es equivalente, pero se usa para colecciones de datos, como vectores.

3.3.2.3 Operador NO (not)

Es un operador unario, es decir, tiene un único operando. Invierte el valor de su operando. Es decir, si el operando vale TRUE produce FALSE, y si el operando vale FALSE produce TRUE. Ejemplo:

casado <- TRUE
! casado
## [1] FALSE

El operador de negación tiene el símbolo !.

3.3.3 Ejemplos de expresiones lógicas

Escribir la expresión lógica oportuna para expresar si se verifica cierta condición lógica cuesta al principio. A continuación vamos a estudiar algunos ejemplos. Antes de ver la solución intenta pensar la expresión lógica adecuada, después consulta el código con la solución. Ten en cuenta que una condición lógica se puede expresar con distintas expresiones lógicas.

3.3.3.1 ¿Pertenece una variable a un intervalo?

Se trata de ver si el valor almacenado en una variable está comprendido en un intervalo, por ejemplo, \([5, 10]\):

# Solución
x <- 7
x >= 5 && x <= 10 # ¿pertenece x al intervalo [5, 10]
## [1] TRUE
x <- 12
x >= 5 && x <= 10 # ¿pertenece x al intervalo [5, 10]
## [1] FALSE

3.3.3.2 ¿No pertenece una variable a un intervalo?

Se trata de ver si el valor almacenado en una variable está fuera de un intervalo, por ejemplo, \([5, 10]\):

# Solución
x <- 7
x < 5 || x > 10
## [1] FALSE
x <- 12
x < 5 || x > 10
## [1] TRUE

Otra posibilidad es usar la expresión que comprueba si la variable pertenece al intervalo y negarla.

# Solución alternativa
x <- 7
!(x >= 5 && x <= 10)
## [1] FALSE
x <- 12
!(x >= 5 && x <= 10)
## [1] TRUE

3.3.3.3 ¿Toma una variable uno de varios posibles valores?

Se trata de ver si el valor almacenado en una variable coincide con alguno de una serie de valores, por ejemplo 2, 7 y 9.

x <- 7
x == 2 || x == 7 || x == 9 # ¿vale x 2, 7 o 9?
## [1] TRUE
x <- 12
x == 2 || x == 7 || x == 9 # ¿vale x 2, 7 o 9?
## [1] FALSE

Observa que x == 2 || x == 7 || x == 9 es equivalente a (x == 2 || x == 7) || x == 9, porque el operador O es binario y se asocia de izquierda a derecha. Una expresión que tiene varias condiciones O encadenadas es verdadera si, y sólo si, al menos una condición es verdadera (piensa por qué).

Esta condición se puede expresar más fácilmente usando vectores y el operador %in%, que comprueba si un elemento está en un vector:

x <- 7
x %in% c(2, 7, 9) # ¿está x en el vector [2, 7, 9]?
## [1] TRUE
x <- 12
x %in% c(2, 7, 9) # ¿está x en el vector [2, 7, 9]?
## [1] FALSE

3.3.3.4 ¿No toma una variable uno de varios posibles valores?

Se trata de ver si el valor almacenado en una variable no coincide con ninguno de una serie de valores, por ejemplo 2, 7 y 9.

x <- 7
x != 2 && x != 7 && x != 9
## [1] FALSE
x <- 12
x != 2 && x != 7 && x != 9
## [1] TRUE

Observa que x != 2 && x != 7 && x != 9 es equivalente a (x != 2 && x != 7) && x != 9, porque el operador Y es binario y se asocia de izquierda a derecha. Una expresión que tiene varias condiciones Y encadenadas es verdadera si, y sólo si, todas las condiciones son verdaderas.

Una forma alternativa de expresar la condición es:

x <- 7
!(x %in% c(2, 7, 9)) # ¿no está x en el vector [2, 7, 9]?
## [1] FALSE
x <- 12
!(x %in% c(2, 7, 9)) # ¿no está x en el vector [2, 7, 9]?
## [1] TRUE

3.4 Invocación a funciones

Como hemos comentado, las funciones sirven para realizar cálculos. Una función es invocada facilitándole unos argumentos. En función de los argumentos realiza sus cálculos y devuelve un resultado. Por ejemplo, la función cos toma como argumento un ángulo en radianes y devuelve su coseno:

cos(0)
## [1] 1
cos(pi / 2)
## [1] 6.123032e-17

R tiene varias formas de especificar los argumentos de una función al ser invocada. Vamos a estudiarlo con la función punif que devuelve un valor de la función de distribución uniforme. Esta función tiene la siguiente cabecera:

  • punif(q, min = 0, max = 1, lower.tail = TRUE, log.p = FALSE)

Consulta la ayuda de punif para una descripción detallada de su funcionamiento. Veamos un ejemplo de invocación:

punif(0.75, 0, 1, TRUE, FALSE)
## [1] 0.75

Con esta invocación queremos ver la probabilidad de que una variable aleatoria de una distribución uniforme \(U(0, 1)\) sea menor o igual que 0.75. Este tipo de invocación es posicional: cada argumento se corresponde con el parámetro que ocupa la misma posición en la cabecera de la función. En el ejemplo, 0.75 se corresponde con q, 0 con min, 1 con max y así sucesivamente. Veamos otro ejemplo con una \(U(0, 2)\):

punif(0.75, 0, 2, TRUE, FALSE)
## [1] 0.375

Si miramos la cabecera de la función veremos que todos los parámetros, salvo el primero, tienen el formato x = valor. Esto quiere decir que el parámetro x toma el valor por defecto valor. El valor por defecto de un parámetro significa que si no se especifica ese argumento en la invocación a la función el parámetro tomará dicho valor por defecto. Por ejemplo:

punif(0.75) # equivale a punif(0.75, 0, 1, TRUE, FALSE)
## [1] 0.75
punif(0.75, 0, 2) # equivale a punif(0.75, 0, 2, TRUE, FALSE)
## [1] 0.375

Un valor por defecto se elige de forma que sea el valor más común posible. De este modo, en muchas ocasiones no habrá que especificar el argumento.

Observa la segunda llamada: punif(0.75, 0, 2). Aunque 0 es el valor por defecto para el límite inferior de la distribución uniforme, ha habido que especificar dicho argumento porque estamos invocando a la función con argumentos posicionales: punif(0.75, 2) equivale a punif(0.75, 2, 1, TRUE, FALSE), que produce un error porque no tiene sentido una distribución \(U(2, 1)\).

Afortunadamente, R no obliga a invocar a las funciones con parámetros posicionales. La alternativa es realizar una invocación por nombre, en la que los argumentos se especifican mediante su nombre y el valor que toman:

punif(q = 0.75, max = 2) # equivale a punif(0.75, 0, 2, TRUE, FALSE)
## [1] 0.375
punif(max = 2, q = 0.75) # equivale a punif(0.75, 0, 2, TRUE, FALSE)
## [1] 0.375

Se puede mezclar invocación posicional con invocación por nombre. En dicho caso, los argumentos posicionales irán primero y se corresponden con sus posiciones en la cabecera. Este tipo de invocación, dada su versatilidad, se usa asiduamente:

punif(0.75, max = 2) # uniforme [0, 2]
## [1] 0.375
punif(0.75, lower.tail = FALSE) # equivale a punif(0.75, 0, 1, FALSE, FALSE)
## [1] 0.25

La última invocación produce la probabilidad de que una variable aleatoria de una distribución \(U(0, 1)\) sea mayor que 0.75.

3.5 Ejercicios

  1. Almacena en una variable un precio (por ejemplo, 100) y en otra variable el IVA (por ejemplo, un 21%). Escribe una expresión que calcule el precio al aplicarle el IVA.

  2. Escribe las siguientes expresiones aritméticas en R:

    • \(\frac{a}{b} + 1\)

    • \(\frac{a+b}{c+d}\)

    • \(\frac{a+\frac{b}{c}}{d+\frac{c}{b}}\)

    • \(a+\frac{b}{c-d}\)

    • \((a+b)\frac{c}{d}\)

    Prueba que las expresiones son correctas. Para ello asigna a las variables a, b, c y d los valores 2, 1, -1 y 4 respectivamente. El resultado de evaluar las distintas expresiones es 3, 1, 0.333, 1.8 y -0.75 respectivamente.

  3. Intenta predecir el resultado de evaluar las siguientes expresiones lógicas. Piensa en el orden en que se evalúan las subexpresiones dentro de cada expresión. Los valores de las variables son: a <- TRUE, b <- TRUE, c <- FALSE, d <- FALSE.

    • c || !a && b
    • ! (a || c) || b && !c
    • ! (! (! (a && c || d)))
    • !(5<3) && a || !(d || c)

  4. Escribe una expresión lógica que compruebe si la variable x pertenece al rango \([0, 5]\) o al rango \([10, 15]\).

  5. Escribe una expresión lógica que compruebe si una variable contiene un valor par. Nota: el operador %% devuelve el resto de la división entera.

  6. Utiliza la función pnorm para calcular la probabilidad de que una variable de una distribución normal \(N(0, 2)\) sea mayor que 3.

  7. Calcula algunos datos sobre una circunferencia a partir de su radio.

    • Guarda en la variable radio el valor del radio de la circunferencia con la que vamos a trabajar.
    • Calcula la longitud de la circunferencia de dicho radio.
    • Calcula el área del círculo delimitado por dicha circunferencia.

  8. Queremos conocer los datos estadísticos de una asignatura. Para ello necesitamos definir variables que almacenen el número de suspensos, aprobados, notables y sobresalientes de una asignatura (asigna los valores que prefieras). Usa expresiones para calcular:

    • El tanto por ciento de alumnos que han superado la asignatura.
    • El tanto por ciento de suspensos, aprobados, notables y sobresalientes de la asignatura.

  9. El código siguiente genera un punto \((x, y)\) aleatorio en el cuadrado de esquinas [-1, -1] y [1, 1]. Además, el punto es visualizado para comprobar si cae dentro del círculo unidad (centro (0, 0) y radio 1):

    x <- runif(1, min = -1, max = 1)
    y <- runif(1, min = -1, max = 1)
    rad <- seq(0, 2*pi, length.out = 100)
    plot(cos(rad), sin(rad), type = "l", asp = 1)
    points(x, y, pch = 19)

    Escribe una expresión lógica que permita comprobar si el punto generado cae en el círculo unidad.

  10. Utiliza expresiones para calcular:

    • La división entera 23 entre 5.
    • El resto de la división entera 23 entre 5.
    • \(\frac{\sqrt{4.5^2 + 5^2}}{100 - 22. 3}\)
    • Si la variable x contiene un valor en el intervalo [-5, 5], pero distinto del cero.
    • Las variables x e y contienen valores enteros positivos. Escribe una expresión lógica que indique si al menos una contiene un valor impar.