¿Debo usar un data.frame o una matriz?

152

¿Cuándo se debe usar a data.frame, y cuándo es mejor usar a matrix?

Ambos mantienen los datos en un formato rectangular, por lo que a veces no está claro.

¿Hay alguna regla general sobre cuándo usar qué tipo de datos?

microbio
fuente
A menudo, una matriz puede adaptarse mejor a un tipo particular de datos, pero si el paquete que desea usar para analizar dicha matriz espera un marco de datos, siempre tendrá que convertirlo innecesariamente. Creo que no hay forma de evitar recordar qué paquete usa cuál.
xApple

Respuestas:

176

Parte de la respuesta ya está contenida en su pregunta: utiliza marcos de datos si se puede esperar que las columnas (variables) sean de diferentes tipos (numérico / carácter / lógico, etc.). Las matrices son para datos del mismo tipo.

En consecuencia, la opción matrix / data.frame solo es problemática si tiene datos del mismo tipo.

La respuesta depende de lo que va a hacer con los datos en data.frame / matrix. Si se va a pasar a otras funciones, el tipo esperado de los argumentos de estas funciones determina la elección.

También:

Las matrices son más eficientes en memoria:

m = matrix(1:4, 2, 2)
d = as.data.frame(m)
object.size(m)
# 216 bytes
object.size(d)
# 792 bytes

Las matrices son necesarias si planea realizar operaciones de tipo álgebra lineal.

Los marcos de datos son más convenientes si con frecuencia hace referencia a sus columnas por nombre (a través del operador $ compacto).

Los marcos de datos también son mejores en mi humilde opinión para informar (imprimir) información tabular, ya que puede aplicar el formato a cada columna por separado.

Michał
fuente
55
Una cosa que agregaría a esta respuesta es que si planea usar el paquete ggplot2 para hacer gráficos, ggplot2 solo funciona con data.frames y no con matrices. ¡Solo algo a tener en cuenta!
Bajcz
77

Algo no mencionado por @Michal es que no solo es una matriz más pequeña que el marco de datos equivalente, el uso de matrices puede hacer que su código sea mucho más eficiente que el uso de marcos de datos, a menudo considerablemente. Esa es una razón por la cual internamente, muchas funciones R obligarán a matrices de datos que están en marcos de datos.

Los marcos de datos son a menudo mucho más convenientes; uno no siempre tiene únicamente fragmentos atómicos de datos por ahí.

Tenga en cuenta que puede tener una matriz de caracteres; no solo tiene que tener datos numéricos para construir una matriz en R.

Al convertir un marco de datos en una matriz, tenga en cuenta que existe una data.matrix()función que maneja los factores de manera apropiada al convertirlos en valores numéricos basados ​​en los niveles internos. La coerción a través de as.matrix()dará como resultado una matriz de caracteres si alguna de las etiquetas de factores no es numérica. Comparar:

> head(as.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a   B  
[1,] "a" "A"
[2,] "b" "B"
[3,] "c" "C"
[4,] "d" "D"
[5,] "e" "E"
[6,] "f" "F"
> head(data.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a B
[1,] 1 1
[2,] 2 2
[3,] 3 3
[4,] 4 4
[5,] 5 5
[6,] 6 6

Casi siempre uso un marco de datos para mis tareas de análisis de datos, ya que a menudo tengo más que solo variables numéricas. Cuando codifico funciones para paquetes, casi siempre forzo la matriz y luego formateo los resultados como un marco de datos. Esto se debe a que los marcos de datos son convenientes.

Gavin Simpson
fuente
Me he estado preguntando la diferencia entre data.matrix () y as.matrix (), también. Gracias por aclararlos y sus consejos en programación.
microbio
¡Gracias por compartir @Gavin Simpson! ¿Podría presentarnos un poco más sobre cómo volver de 1-6 a af?
YJZ
1
@YZhang Debería almacenar las etiquetas para cada factor y un vector lógico que indique qué columnas de la matriz eran factores. Entonces sería relativamente trivial convertir solo aquellas columnas que fueron factores nuevamente en factores con las etiquetas correctas. Los comentarios no son buenos lugares para el código, así que vea si la Q se ha preguntado y respondido antes y, si no, haga una nueva pregunta.
Gavin Simpson
47

@Michal: las matrices no son realmente más eficientes en cuanto a memoria:

m <- matrix(1:400000, 200000, 2)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 1600776 bytes

... a menos que tenga una gran cantidad de columnas:

m <- matrix(1:400000, 2, 200000)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 22400568 bytes
petrelharp
fuente
El argumento de la eficiencia de la memoria se trata realmente de data.framesofrecer más flexibilidad sobre los tipos de columna. data.frame(a = rnorm(1e6), b = sample(letters, 1e6, TRUE))será mucho más pequeño (6 veces según mi cálculo rápido) en memoria que la matrixversión debido a la coerción de tipo.
MichaelChirico
9

La matriz es en realidad un vector con métodos adicionales. mientras data.frame es una lista. La diferencia se reduce a la lista de vectores vs. para la eficiencia del cálculo, quédese con la matriz. Usando data.frame si es necesario.

usuario8341
fuente
3
Hmm, una matriz es un vector con dimensiones, no veo dónde entran los métodos.
Gavin Simpson
0

Las matrices y los marcos de datos son matrices rectangulares 2D y pueden ser heterogéneas por filas y columnas . Comparten algunos métodos y propiedades, pero no todos.

Ejemplos:

M <- list(3.14,TRUE,5L,c(2,3,5),"dog",1i)  # a list
dim(M) <- c(2,3)                           # set dimensions
print(M)                                   # print result

#      [,1]  [,2]      [,3]
# [1,] 3.14  5         "dog"
# [2,] TRUE  Numeric,3 0+1i

DF <- data.frame(M)                   # a data frame
print(DF)                             # print result

#      X1      X2   X3
#  1 3.14       5  dog
#  2 TRUE 2, 3, 5 0+1i

M <- matrix(c(1,1,1,1,2,3,1,3,6),3)   # a numeric matrix
DF <- data.frame(M)                   # a all numeric data frame

solve(M)                              # obtains inverse matrix
solve(DF)                             # obtains inverse matrix
det(M)                                # obtains determinant
det(DF)                               # error
Trisquel
fuente
0

¡No puedo enfatizar más la diferencia de eficiencia entre los dos! Si bien es cierto que los DF son más convenientes en algunos casos de análisis de datos, también permiten datos heterogéneos, y algunas bibliotecas los aceptan solo, todo esto es realmente secundario a menos que escriba un código único para una tarea específica.

Dejame darte un ejemplo. Había una función que calcularía la ruta 2D del método MCMC. Básicamente, esto significa que tomamos un punto inicial (x, y) e iteramos un cierto algoritmo para encontrar un nuevo punto (x, y) en cada paso, construyendo así la ruta completa. El algoritmo implica el cálculo de una función bastante compleja y la generación de alguna variable aleatoria en cada iteración, por lo que cuando se ejecutó durante 12 segundos pensé que estaba bien, dada la cantidad de cosas que hace en cada paso. Dicho esto, la función reunió todos los puntos en la ruta construida junto con el valor de una función objetivo en un marco de datos de 3 columnas. Entonces, 3 columnas no son tan grandes, y el número de pasos también fue más de 10,000 (en este tipo de problemas, las rutas de 1,000,000 de longitud son típicas, por lo que 10,000 no es nada). Entonces, pensé que un DF 10, 000x3 definitivamente no es un problema. La razón por la que se usó un DF es simple. Después de llamar a la función, se llamó a ggplot () para dibujar la ruta (x, y) resultante. Y ggplot () no acepta una matriz.

Luego, en algún momento por curiosidad, decidí cambiar la función para recopilar el camino en una matriz. Con mucho gusto, la sintaxis de los DF y las matrices es similar, todo lo que hice fue cambiar la línea especificando df como data.frame a una inicializándola como una matriz. Aquí también debo mencionar que en el código inicial, el DF se inicializó para tener el tamaño final, por lo que más adelante en el código de la función solo se registraron nuevos valores en espacios ya asignados, y no hubo sobrecarga de agregar nuevas filas al DF. Esto hace que la comparación sea aún más justa, y también simplificó mi trabajo ya que no necesitaba reescribir nada más en la función. Solo un cambio de línea desde la asignación inicial de un marco de datos del tamaño requerido a una matriz del mismo tamaño. Para adaptar la nueva versión de la función a ggplot (), convertí la matriz ahora devuelta a datos.

Después de volver a ejecutar el código, no podía creer el resultado. ¡El código se ejecuta en una fracción de segundo! En lugar de unos 12 segundos. Y nuevamente, la función durante las 10,000 iteraciones solo leyó y escribió valores en espacios ya asignados en un DF (y ahora en una matriz). Y esta diferencia también es para el tamaño razonable (o más bien pequeño) de 10000x3.

Entonces, si su única razón para usar un DF es hacerlo compatible con una función de biblioteca como ggplot (), siempre puede convertirlo a un DF en el último momento: trabaje con matrices en la medida en que lo considere conveniente. Si, por otro lado, hay una razón más sustancial para usar un DF, como si usa algún paquete de análisis de datos que requeriría una transformación constante de matrices a DF y viceversa, o no hace cálculos intensivos usted mismo y solo usa el estándar paquetes (muchos de ellos en realidad transforman internamente un DF en una matriz, hacen su trabajo y luego transforman el resultado, para que hagan todo el trabajo de eficiencia por usted), o hacen un trabajo único para que no le importe y sienta más cómodo con los DF, entonces no debería preocuparse por la eficiencia.

O una regla más práctica y diferente: si tiene una pregunta como en el OP, use matrices, por lo que usaría los DF solo cuando no tenga esa pregunta (porque ya sabe que tiene que usar los DF, o porque sí Realmente no me importa ya que el código es de una sola vez, etc.).

Pero, en general, tenga siempre presente este punto de eficiencia como una prioridad.

Vadim
fuente