Los data.frames son una clase de objetos especial en R. Normalmente, cuando se realiza un estudio estadístico sobre los sujetos u objetos de una muestra, la información se organiza precisamente en un dataframe: una hoja de datos, en los que cada fila corresponde a un sujeto y cada columna a una variable. La estructura de un data.frame es muy similar a la de una matriz. La diferencia es que una matriz sólo admite valores numéricos, mientras que en un dataframe podemos incluir también datos alfanuméricos.
El siguiente ejemplo muestra como crear un data.frame a partir de los datos recogidos sobre una muestra de 10 personas, para cada una de las cuales se ha registrado su edad, sexo y tiempo en minutos que estuvo hablando por teléfono el día antes de la encuesta:
edad <- c(22, 34, 29, 25, 30, 33, 31, 27, 25, 25)
tiempo <- c(14.21, 10.36, 11.89, 13.81, 12.03, 10.99, 12.48, 13.37, 12.29, 11.92)
sexo <- c("M","H","H","M","M","H","M","M","H","H")
misDatos <- data.frame(edad,tiempo,sexo)
misDatos
## edad tiempo sexo
## 1 22 14.21 M
## 2 34 10.36 H
## 3 29 11.89 H
## 4 25 13.81 M
## 5 30 12.03 M
## 6 33 10.99 H
## 7 31 12.48 M
## 8 27 13.37 M
## 9 25 12.29 H
## 10 25 11.92 H
str(misDatos) # Estructura de 'misDatos'
## 'data.frame': 10 obs. of 3 variables:
## $ edad : num 22 34 29 25 30 33 31 27 25 25
## $ tiempo: num 14.2 10.4 11.9 13.8 12 ...
## $ sexo : chr "M" "H" "H" "M" ...
names(misDatos) # Nombre de las variables contenidas en 'misDatos'
## [1] "edad" "tiempo" "sexo"
En este ejemplo se ha creado un data.frame llamado misDatos que contiene a las tres variables edad, tiempo y sexo. La función str() nos muestra la estructura de este objeto, confirmándonos que es un data.frame de tres variables con 10 observaciones cada una. Donde, las dos primeras variables son numéricas y la tercera, el sexo, es un factor con dos valores, “H” y “M”. La función names() por su parte, nos devuelve los nombres de las variables contenidas en misDatos.
Cuando desde R se leen datos situados en un fichero externo (un fichero de texto, una hoja excel, un archivo de datos de SPSS,…), estos datos se importan en un dataframe.
El acceso a los datos que se encuentran en un data.frame es muy similar al acceso a los datos de una matriz que ya vimos en la sección anterior. Sin embargo, para los data.frames R dispone de algunas funciones que facilitan la tarea de seleccionar o filtrar datos. Así por ejemplo, si queremos ver sólo los datos de los sujetos (filas) 3 a 6, escribiríamos:
misDatos[3:6,]
## edad tiempo sexo
## 3 29 11.89 H
## 4 25 13.81 M
## 5 30 12.03 M
## 6 33 10.99 H
Si queremos seleccionar los datos de edad (primera columna), podemos tratar a misDatos igual que si fuese una matriz:
misDatos[,1]
## [1] 22 34 29 25 30 33 31 27 25 25
Aunque también podemos referirnos a la columna por su nombre:
misDatos$edad
## [1] 22 34 29 25 30 33 31 27 25 25
Nótese que en este caso hemos de utilizar el nombre del data.frame (misDatos) seguido del símbolo $ y del nombre de la variable que nos interesa (edad). De manera equivalente, la selección de esa variable puede realizarse mediante:
misDatos[,"edad"]
## [1] 22 34 29 25 30 33 31 27 25 25
o poniendo el nombre de la variable entre dobles corchetes y entre comillas:
misDatos[["edad"]]
## [1] 22 34 29 25 30 33 31 27 25 25
Así pues, los siguientes comandos son equivalentes y dan el mismo resultado:
mean(misDatos[,1])
## [1] 28.1
mean(misDatos$edad)
## [1] 28.1
mean(misDatos[,"edad"])
## [1] 28.1
mean(misDatos[["edad"]])
## [1] 28.1
El acceso a las variables dentro de un dataframe puede hacerse engorroso cuando hemos de escribir constantemente el nombre del dataframe (en particular si éste es muy largo). Imaginemos, por ejemplo, que para el conjunto misDatos deseamos construir tablas de frecuencias de cada una de las variables que contiene, una tabla de frecuencias cruzadas para el nivel de estudios por sexo, y que además queremos calcular la edad media de los individuos de cada sexo. La sintaxis a utilizar sería la siguiente:
table(misDatos$estudios)
## < table of extent 0 >
table(misDatos$sexo)
##
## H M
## 5 5
table(misDatos$edad)
##
## 22 25 27 29 30 31 33 34
## 1 3 1 1 1 1 1 1
table(misDatos$sexo,misDatos$edad)
##
## 22 25 27 29 30 31 33 34
## H 0 2 0 1 0 0 1 1
## M 1 1 1 0 1 1 0 0
mean(misDatos$edad[misDatos$sexo=="M"])
## [1] 27
mean(misDatos$edad[misDatos$sexo=="H"])
## [1] 29.2
Obviamente, escribir tantas veces misDatos resulta tedioso, al margen de que se multiplica el riesgo de cometer errores en la redacción de los comandos. Para evitar este problema podemos utilizar el comando attach(), cuyo objetivo consiste básicamente en “enganchar” el contenido del dataframe al entorno donde R busca los nombres de variable; de esta forma se puede acceder directamente a las variables del dataframe por su nombre, sin necesidad de que éste tenga que ser precedido con el nombre del dataframe y el símbolo $; una vez que hayamos acabado nuestra tarea “desenganchamos” el dataframe con detach(). La tarea anterior, utilizando estos comandos, se puede llevar a cabo mediante:
attach(misDatos)
## The following objects are masked _by_ .GlobalEnv:
##
## edad, sexo, tiempo
#table(estudios)
table(sexo)
## sexo
## H M
## 5 5
table(edad)
## edad
## 22 25 27 29 30 31 33 34
## 1 3 1 1 1 1 1 1
table(sexo,edad)
## edad
## sexo 22 25 27 29 30 31 33 34
## H 0 2 0 1 0 0 1 1
## M 1 1 1 0 1 1 0 0
mean(edad[sexo=="M"])
## [1] 27
mean(edad[sexo=="H"])
## [1] 29.2
detach(misDatos)
lo cual es notablemente más simple. No obstante hay que ser extremadamente precavido al utilizar estos comandos ya que cuando se utilizan varios dataframes simultáneamente es muy fácil hacer attachs y detachs en lugares incorrectos, lo que puede conducir a mezclar datos de distinta procedencia y cometer errores inadvertidos. Obsérvese el siguiente ejemplo:
# Creamos el dataframe 'divisas' con la tasa de cambio de algunas monedas
divisas = data.frame(moneda=c("Libra", "Euro", "Rublo"), cambio=c(1.2, 1, 0.02))
divisas
## moneda cambio
## 1 Libra 1.20
## 2 Euro 1.00
## 3 Rublo 0.02
# Creamos un dataframe con el nombre de algunos paises y su moneda nacional
paises = data.frame(pais=c("EEUU", "Venezuela", "Japón"),moneda=c("Dólar", "Bolívar", "Yen"))
paises
## pais moneda
## 1 EEUU Dólar
## 2 Venezuela Bolívar
## 3 Japón Yen
attach(divisas) # Enganchamos 'divisas' al entorno de búsqueda de nombres de variables.
moneda # Se muestra la variable 'moneda' del dataframe 'divisas'
## [1] "Libra" "Euro" "Rublo"
attach(paises) # ¡CUIDADO! no se ha hecho el 'detach' del datafame 'divisas'
## The following object is masked from divisas:
##
## moneda
moneda # 'moneda' se lee del último dataframe "enganchado", que es 'paises'
## [1] "Dólar" "Bolívar" "Yen"
cambio # ¡Pero esta variable se sigue leyendo del dataframe 'divisas'!
## [1] 1.20 1.00 0.02
# Si, por ejemplo, pegamos los valores de ambas variables pensando que corresponden al mismo dataframe estaríamos metiendo la pata.
paste(moneda,cambio, sep=": ")
## [1] "Dólar: 1.2" "Bolívar: 1" "Yen: 0.02"
detach(paises) # Desenganchamos el dataframe 'paises'
moneda # Recuperamos la variable 'moneda' que estaba en 'divisas'
## [1] "Libra" "Euro" "Rublo"
detach(divisas) # Desenganchamos 'divisas
¿Que pasa si al hacer el detach(divisas) llamamos de nuevo la variable moneda?
Se Crea una variable llamada longitud, con 6 valores, y a continuación un dataframe llamado medidas que contiene tres variables con tres valores cada una; en el dataframe una de las variables también se llama longitud:
longitud=c(12,10,11,13,14,17)
medidas=data.frame(longitud=c(6,4,7), peso=c(240,326,315), diametro=c(8,9,9))
Calculamos el valor medio de estas cuatro variables:
mean(longitud)
## [1] 12.83333
mean(medidas$longitud)
## [1] 5.666667
mean(medidas$peso)
## [1] 293.6667
mean(medidas$diametro)
## [1] 8.666667
Efectuamos el attach del dataframe medidas para acceder a sus variables y calculamos de nuevo la media de cada una:
attach(medidas)
## The following object is masked _by_ .GlobalEnv:
##
## longitud
mean(peso)
## [1] 293.6667
mean(diametro)
## [1] 8.666667
mean(longitud)
## [1] 12.83333
Obsérvese de que R nos advierte de que el objeto longitud ha quedado enmascarado. Vemos que, si bien R ha calculado los valores medios del diámetro y peso correspondientes a las variables del dataframe medidas, la longitud sigue siendo la de la variable longitud previa a hacer el attach.
Lo anterior ocurre pues ya existe una variable de enterno con el mismo nombre de alguna variable del data.frame y al hacer uso del attach esta se engancha al data.frame. Esto deja innacesible la variable la variable del data.frame.
El comando with permite ejecutar una o varias instrucciones sobre las variables de un dataframe accediendo a ellas solamente por su nombre, sin necesidad de utilizar attach. Resulta particularmente útil para realizar cálculos con las variables dentro de un dataframe.
Si, por ejemplo, queremos calcular la densidad de los objetos cuyas medidas figuran en el dataframe anterior, podemos utilizar la siguiente sintaxis
with(medidas,{
volumen=longitud*pi*(diametro/2)^2 # Calcula el volumen de los objetos
densidad=peso/volumen # Calcula su densidad
densidad # Muestra los valores de densidad
})
## [1] 0.7957747 1.2810990 0.7073553
La estructura que se acabo de definir no modifica la el contenido del data.frame medidas.
medidas
## longitud peso diametro
## 1 6 240 8
## 2 4 326 9
## 3 7 315 9
Si lo que se quiere es incluir la densidad dentro del data.frame medidas deberíamos proceder del siguiente modo:
medidas$densidad=with(medidas,{
volumen=longitud*pi*(diametro/2)^2 # Calcula el volumen de los objetos
densidad=peso/volumen # Calcula su densidad
densidad # Muestra los valores de densidad
})
medidas # Mostramos el dataframe 'medidas'. Ahora sí que contiene la densidad
## longitud peso diametro densidad
## 1 6 240 8 0.7957747
## 2 4 326 9 1.2810990
## 3 7 315 9 0.7073553
La función subset() nos permite seleccionar una parte del data.frame. Por ejemplo, si deseamos crear dos dataframes nuevos, uno solo con los hombres y otro con las mujeres utilizaríamos:
hombres=subset(misDatos,sexo=="H")
hombres
## edad tiempo sexo
## 2 34 10.36 H
## 3 29 11.89 H
## 6 33 10.99 H
## 9 25 12.29 H
## 10 25 11.92 H
mujeres=subset(misDatos,sexo=="M")
mujeres
## edad tiempo sexo
## 1 22 14.21 M
## 4 25 13.81 M
## 5 30 12.03 M
## 7 31 12.48 M
## 8 27 13.37 M
Sujetos que sean hombres y tengan más de 30 años (la condición “ y ” se especifica mediante el símbolo “ & ”):
mayores=subset(misDatos,sexo=="H" & edad>30)
mayores
## edad tiempo sexo
## 2 34 10.36 H
## 6 33 10.99 H
Sujetos que tengan menos de 25 o más 30 años (la condición “ o ” se expresa mediante la línea vertical “ | ”):
extremos=subset(misDatos,edad<25|edad>30)
extremos
## edad tiempo sexo
## 1 22 14.21 M
## 2 34 10.36 H
## 6 33 10.99 H
## 7 31 12.48 M
Podemos seleccionar además un subconjunto de variables del data.frame. Por ejemplo, si nos interesan solo la edad y el tiempo de uso del móvil de los hombres de la muestra:
hombres=subset(misDatos,sexo=="H", select=c(edad, tiempo))
hombres
## edad tiempo
## 2 34 10.36
## 3 29 11.89
## 6 33 10.99
## 9 25 12.29
## 10 25 11.92
Si tenemos dos dataframes con la misma estructura (idénticas variables), pero distintos datos, podemos combinarlos pegando uno a continuación del otro mediante rbind (acrónimo de rowbind, pegar por filas):
animales1 = data.frame(animal=c("vaca","perro","rana","lagarto","mosca","jilguero"),
clase=c("mamífero","mamífero","anfibio","reptil","insecto","ave"))
animales1
## animal clase
## 1 vaca mamífero
## 2 perro mamífero
## 3 rana anfibio
## 4 lagarto reptil
## 5 mosca insecto
## 6 jilguero ave
animales2 = data.frame(animal=c("atún", "cocodrilo", "gato","rana"), clase=c("pez", "reptil", "mamífero","anfibio"))
animales2
## animal clase
## 1 atún pez
## 2 cocodrilo reptil
## 3 gato mamífero
## 4 rana anfibio
animales3 = rbind(animales1,animales2)
animales3
## animal clase
## 1 vaca mamífero
## 2 perro mamífero
## 3 rana anfibio
## 4 lagarto reptil
## 5 mosca insecto
## 6 jilguero ave
## 7 atún pez
## 8 cocodrilo reptil
## 9 gato mamífero
## 10 rana anfibio
El comando rbind no controla la posible aparición de casos repetidos en los dos dataframes (podemos comprobar que la rana está repetida en el dataframe ‘animales3’). La función merge() evita este problema; utilizando la opción all=TRUE ó all=FALSE (valor por defecto) se consigue que se muestren todos los datos de ambos dataframes, o solo aquellos que son comunes a ambos
animales4=merge(animales1,animales2)
animales4
## animal clase
## 1 rana anfibio
animales5=merge(animales1,animales2,all=TRUE)
animales5
## animal clase
## 1 atún pez
## 2 cocodrilo reptil
## 3 gato mamífero
## 4 jilguero ave
## 5 lagarto reptil
## 6 mosca insecto
## 7 perro mamífero
## 8 rana anfibio
## 9 vaca mamífero
Si los dataframes tienen estructura distinta, pero contienen variables en común que permiten identificar unívocamente a los mismos objetos en ambos conjuntos, también podemos combinarlos mediante merge():
superficieAnimales=data.frame(animal=c("perro","tortuga","jilguero",
"cocodrilo","vaca","lagarto","sardina"),
superficie=c("pelo","placas óseas","plumas",
"escamas","pelo","escamas","escamas"))
superficieAnimales
## animal superficie
## 1 perro pelo
## 2 tortuga placas óseas
## 3 jilguero plumas
## 4 cocodrilo escamas
## 5 vaca pelo
## 6 lagarto escamas
## 7 sardina escamas
merge(animales3,superficieAnimales) # Muestra sólo los animales comunes a ambos dataframes
## animal clase superficie
## 1 cocodrilo reptil escamas
## 2 jilguero ave plumas
## 3 lagarto reptil escamas
## 4 perro mamífero pelo
## 5 vaca mamífero pelo
merge(animales3,superficieAnimales, all.x=TRUE) # Muestra todos los animales del primer dataframe.
## animal clase superficie
## 1 atún pez <NA>
## 2 cocodrilo reptil escamas
## 3 gato mamífero <NA>
## 4 jilguero ave plumas
## 5 lagarto reptil escamas
## 6 mosca insecto <NA>
## 7 perro mamífero pelo
## 8 rana anfibio <NA>
## 9 rana anfibio <NA>
## 10 vaca mamífero pelo
merge(animales3,superficieAnimales, all.y=TRUE) # Muestra todos los animales del segundo dataframe.
## animal clase superficie
## 1 cocodrilo reptil escamas
## 2 jilguero ave plumas
## 3 lagarto reptil escamas
## 4 perro mamífero pelo
## 5 sardina <NA> escamas
## 6 tortuga <NA> placas óseas
## 7 vaca mamífero pelo
merge(animales3,superficieAnimales, all=TRUE) # Muestra todos los animales de ambos dataframes.
## animal clase superficie
## 1 atún pez <NA>
## 2 cocodrilo reptil escamas
## 3 gato mamífero <NA>
## 4 jilguero ave plumas
## 5 lagarto reptil escamas
## 6 mosca insecto <NA>
## 7 perro mamífero pelo
## 8 rana anfibio <NA>
## 9 rana anfibio <NA>
## 10 sardina <NA> escamas
## 11 tortuga <NA> placas óseas
## 12 vaca mamífero pelo
Como vemos, cuando en la combinación de dataframes faltan datos, se rellenan los huecos con valores perdidos (NA)
Para ordenar un dataframe hemos de aplicar la función order() al elemento o elementos por el que queramos ordenar, y utilizar el resultado de esta función como índice del dataframe.
Por ejemplo, si queremos ordenar el data.frame animales1 por orden alfabético de animales, haríamos:
ordenacion=order(animales1$animal)
ordenacion # Posiciones dentro del dataframe 'animales1' de los animales ordenados alfabéticamente
## [1] 6 4 5 2 3 1
animales1=animales1[ordenacion,] # Se reordenan las filas del dataframe animales1
animales1
## animal clase
## 6 jilguero ave
## 4 lagarto reptil
## 5 mosca insecto
## 2 perro mamífero
## 3 rana anfibio
## 1 vaca mamífero
de modo equivalente, en una línea de código:
animales1=animales1[order(animales1$animal),]