No hay una respuesta simple / directa porque las filosofías de ambos paquetes difieren en ciertos aspectos. Por eso, algunos compromisos son inevitables. Estas son algunas de las inquietudes que quizás deba abordar / considerar.
Operaciones que involucran i
(== filter()
y slice()
en dplyr)
Suponga DT
con, digamos, 10 columnas. Considere estas expresiones de data.table:
DT[a > 1, .N]
DT[a > 1, mean(b), by=.(c, d)]
(1) da el número de filas en la DT
columna where a > 1
. (2) devuelve mean(b)
agrupados por c,d
para la misma expresión en i
que (1).
Las dplyr
expresiones de uso común serían:
DT %>% filter(a > 1) %>% summarise(n())
DT %>% filter(a > 1) %>% group_by(c, d) %>% summarise(mean(b))
Claramente, los códigos de data.table son más cortos. Además, también son más eficientes en memoria 1 . ¿Por qué? Porque tanto en (3) como en (4), filter()
devuelve filas para las 10 columnas primero, cuando en (3) solo necesitamos el número de filas, y en (4) solo necesitamos columnas b, c, d
para las operaciones sucesivas. Para superar esto, tenemos select()
columnas a priori:
DT %>% select(a) %>% filter(a > 1) %>% summarise(n())
DT %>% select(a,b,c,d) %>% filter(a > 1) %>% group_by(c,d) %>% summarise(mean(b))
Es esencial resaltar una gran diferencia filosófica entre los dos paquetes:
En data.table
, nos gusta mantener juntas estas operaciones relacionadas, y eso permite mirar j-expression
(desde la misma llamada de función) y darnos cuenta de que no hay necesidad de columnas en (1). La expresión en i
se calcula y .N
es solo la suma de ese vector lógico que da el número de filas; el subconjunto completo nunca se realiza. En (2), solo las columnas b,c,d
se materializan en el subconjunto, las demás columnas se ignoran.
Pero en dplyr
, la filosofía es tener una función que haga precisamente una cosa bien . No hay (al menos actualmente) forma de saber si la operación posterior filter()
necesita todas esas columnas que filtramos. Deberá pensar en el futuro si desea realizar estas tareas de manera eficiente. Personalmente, lo encuentro contra-intuitivo en este caso.
Tenga en cuenta que en (5) y (6), todavía subconjuntamos columnas a
que no requerimos. Pero no estoy seguro de cómo evitarlo. Si la filter()
función tuviera un argumento para seleccionar las columnas a devolver, podríamos evitar este problema, pero entonces la función no hará una sola tarea (que también es una elección de diseño de dplyr).
Subasignar por referencia
dplyr nunca se actualizará por referencia. Esta es otra gran diferencia (filosófica) entre los dos paquetes.
Por ejemplo, en data.table puede hacer:
DT[a %in% some_vals, a := NA]
que actualiza columna a
por referencia solo en aquellas filas que satisfacen la condición. Por el momento, dplyr copia en profundidad toda la tabla de datos internamente para agregar una nueva columna. @BrodieG ya mencionó esto en su respuesta.
Pero la copia profunda se puede reemplazar por una copia superficial cuando se implementa FR # 617 . También relevante: dplyr: FR # 614 . Tenga en cuenta que aún así, la columna que modifique siempre se copiará (por lo tanto, un poco más lenta / menos eficiente en memoria). No habrá forma de actualizar columnas por referencia.
Otras funcionalidades
En data.table, puede agregar mientras se une, y esto es más fácil de entender y es eficiente en la memoria, ya que el resultado de la unión intermedia nunca se materializa. Consulte esta publicación para ver un ejemplo. No puede (¿en este momento?) Hacer eso usando la sintaxis data.table / data.frame de dplyr.
La función de combinaciones sucesivas de data.table tampoco es compatible con la sintaxis de dplyr.
Recientemente implementamos uniones superpuestas en data.table para unir rangos de intervalo ( aquí hay un ejemplo ), que es una función separadafoverlaps()
en este momento y, por lo tanto, podría usarse con los operadores de tubería (magrittr / pipeR? - nunca lo probé yo mismo).
Pero en última instancia, nuestro objetivo es integrarlo [.data.table
para que podamos recopilar las otras características como agrupar, agregar al unirse, etc., que tendrán las mismas limitaciones descritas anteriormente.
Desde 1.9.4, data.table implementa la indexación automática utilizando claves secundarias para subconjuntos basados en búsquedas binarias rápidas en la sintaxis R normal. Por ejemplo: DT[x == 1]
y DT[x %in% some_vals]
creará automáticamente un índice en la primera ejecución, que luego se utilizará en subconjuntos sucesivos de la misma columna para un subconjunto rápido mediante la búsqueda binaria. Esta característica seguirá evolucionando. Consulte esta esencia para obtener una breve descripción general de esta función.
Por la forma en que filter()
se implementa para data.tables, no aprovecha esta característica.
Una característica de dplyr es que también proporciona una interfaz a las bases de datos que utilizan la misma sintaxis, que data.table no hace en este momento.
Por lo tanto, tendrá que sopesar estos (y probablemente otros puntos) y decidir en función de si estas compensaciones son aceptables para usted.
HTH
(1) Tenga en cuenta que la eficiencia de la memoria afecta directamente la velocidad (especialmente a medida que los datos aumentan de tamaño), ya que el cuello de botella en la mayoría de los casos es mover los datos de la memoria principal a la caché (y hacer uso de los datos en la caché tanto como sea posible, reducir las pérdidas de caché - para reducir el acceso a la memoria principal). Sin entrar en detalles aquí.
dplyr
métodos para las tablas de datos, pero la tabla de datos también tiene sus propios métodos comparables