data.table vs dplyr: ¿puede uno hacer algo bien y el otro no puede o lo hace mal?




(2)

En respuesta directa al Título de la Pregunta ...

dplyr definitivamente hace cosas que data.table no puede.

Tu punto # 3

dplyr abstrae (o hará) posibles interacciones de DB

es una respuesta directa a su propia pregunta, pero no se eleva a un nivel suficientemente alto. dplyr es verdaderamente un front-end extensible para múltiples mecanismos de almacenamiento de datos en el que, como data.table es una extensión para uno solo.

Considere a dplyr como una interfaz dplyr back-end, con todos los objetivos utilizando la misma gramática, donde puede extender los objetivos y los manejadores a voluntad. data.table es, desde la perspectiva de dplyr , uno de esos objetivos.

Nunca (espero) verá un día en que data.table intente traducir sus consultas para crear sentencias de SQL que funcionen con almacenes de datos en disco o en red.

dplyr posiblemente puede hacer cosas de data.table . data.table no funcionará o no.

Basado en el diseño de trabajo en memoria, data.tablepodría tener un tiempo mucho más difícil extenderse en el procesamiento paralelo de consultas que dplyr.

En respuesta a las preguntas en el cuerpo ...

Uso

¿Hay tareas analíticas que son mucho más fáciles de codificar con uno u otro paquete para personas familiarizadas con los paquetes (es decir, una combinación de pulsaciones de teclas requeridas contra el nivel requerido de esoterismo, donde menos de cada una es una buena cosa).

Esto puede parecer un despeje, pero la respuesta real es no. Las personas familiarizadas con las herramientas parecen utilizar la más familiar para ellas o la que en realidad es la adecuada para el trabajo en cuestión. Con eso dicho, a veces desea presentar una legibilidad particular, a veces un nivel de rendimiento, y cuando necesita un nivel suficientemente alto de ambos, es posible que necesite otra herramienta para ir con lo que ya tiene para hacer abstracciones más claras .

Actuación

¿Hay tareas analíticas que se realicen sustancialmente (es decir, más de 2x) de manera más eficiente en un paquete en comparación con otro?

De nuevo, no. data.tablesobresale por ser eficiente en todo lo que hace, y dplyren algunos aspectos tiene la carga de estar limitado al almacén de datos subyacente y a los manejadores registrados.

Esto significa que cuando se encuentra con un problema de rendimiento data.tablepuede estar bastante seguro de que está en la función de consulta y, si en realidad es un cuello de botella data.table, se habrá ganado la alegría de presentar un informe. Esto también es cierto cuando dplyrse usa data.tablecomo back-end; Es posible que veas algo de sobrecarga desde, dplyrpero lo más probable es que sea tu consulta.

Cuando dplyrtiene problemas de rendimiento con los back-ends, puede sortearlos al registrar una función para la evaluación híbrida o (en el caso de las bases de datos) manipular la consulta generada antes de la ejecución.

También vea la respuesta aceptada de cuándo es mejor plyr que data.table?

Visión general

Estoy relativamente familiarizado con data.table , no tanto con dplyr . He leído algunas viñetas y ejemplos de dplyr que han aparecido en SO, y hasta ahora mis conclusiones son que:

  1. data.table y dplyr son comparables en velocidad, excepto cuando hay muchos grupos (es decir,> 10-100K), y en algunas otras circunstancias (consulte los puntos de referencia a continuación)
  2. dplyr tiene una sintaxis más accesible
  3. dplyr abstrae (o hará) posibles interacciones de DB
  4. Hay algunas pequeñas diferencias de funcionalidad (ver "Ejemplos / Uso" más abajo)

En mi opinión 2. no tiene mucho peso porque estoy bastante familiarizado con su data.table , aunque entiendo que para los usuarios nuevos para ambos será un factor importante. Me gustaría evitar un argumento sobre cuál es más intuitivo, ya que es irrelevante para mi pregunta específica formulada desde la perspectiva de alguien que ya está familiarizado con data.table . También me gustaría evitar una discusión sobre cómo "más intuitivo" conduce a un análisis más rápido (ciertamente cierto, pero nuevamente, no es lo que más me interesa aquí).

Pregunta

Lo que quiero saber es:

  1. ¿Hay tareas analíticas que son mucho más fáciles de codificar con uno u otro paquete para personas familiarizadas con los paquetes (es decir, una combinación de pulsaciones de teclas requeridas contra el nivel requerido de esoterismo, donde menos de cada una es una buena cosa).
  2. ¿Hay tareas analíticas que se realicen sustancialmente (es decir, más de 2x) de manera más eficiente en un paquete en comparación con otro?

Una pregunta reciente de SO me hizo pensar un poco más en esto, porque hasta ese momento no pensé que dplyr ofrecería mucho más de lo que ya puedo hacer en la data.table de data.table . Aquí está la solución dplyr (datos al final de Q):

dat %.%
  group_by(name, job) %.%
  filter(job != "Boss" | year == min(year)) %.%
  mutate(cumu_job2 = cumsum(job2))

Lo que fue mucho mejor que mi intento de hackear una solución data.table . Dicho esto, las buenas soluciones de data.table también son bastante buenas (gracias a Jean-Robert, Arun, y tenga en cuenta que estoy a favor de la declaración única sobre la solución estrictamente más óptima)

setDT(dat)[,
  .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], 
  by=list(id, job)
]

La sintaxis de este último puede parecer muy esotérica, pero en realidad es bastante sencilla si estás acostumbrado a la data.table de data.table (es decir, no utiliza algunos de los trucos más esotéricos).

Idealmente, lo que me gustaría ver son algunos buenos ejemplos en los que la forma dplyr o data.table es sustancialmente más concisa o tiene un rendimiento sustancialmente mejor.

Ejemplos

Uso
  • dplyr no permite operaciones agrupadas que devuelven un número arbitrario de filas (de la pregunta de eddi , nota: parece que se implementará en dplyr 0.5 , también, @beginneR muestra una solución temporal potencial utilizando do en la respuesta a la pregunta de @eddi) .
  • data.table compatible con las combinaciones rodantes (gracias @dholstius), así como las combinaciones superpuestas
  • data.table optimiza internamente expresiones de la forma DT[col == value] o DT[col %in% values] para la velocidad a través de la indexación automática que utiliza la búsqueda binaria mientras usa la misma sintaxis de la base R. Vea aquí para más detalles y un pequeño punto de referencia.
  • dplyr ofrece versiones de evaluación estándar de funciones (por ejemplo, regroup , summarize_each_ ) que pueden simplificar el uso programático de dplyr (tenga en cuenta que el uso programático de data.table es definitivamente posible, solo requiere una reflexión cuidadosa, sustitución / cotización, etc., al menos que yo sepa )
Puntos de referencia

Datos

Esto es para el primer ejemplo que mostré en la sección de preguntas.

dat <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L), name = c("Jane", "Jane", "Jane", "Jane", 
"Jane", "Jane", "Jane", "Jane", "Bob", "Bob", "Bob", "Bob", "Bob", 
"Bob", "Bob", "Bob"), year = c(1980L, 1981L, 1982L, 1983L, 1984L, 
1985L, 1986L, 1987L, 1985L, 1986L, 1987L, 1988L, 1989L, 1990L, 
1991L, 1992L), job = c("Manager", "Manager", "Manager", "Manager", 
"Manager", "Manager", "Boss", "Boss", "Manager", "Manager", "Manager", 
"Boss", "Boss", "Boss", "Boss", "Boss"), job2 = c(1L, 1L, 1L, 
1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 0L)), .Names = c("id", 
"name", "year", "job", "job2"), class = "data.frame", row.names = c(NA, 
-16L))

Este es mi intento de obtener una respuesta integral desde la perspectiva de dplyr, siguiendo el esquema general de la respuesta de Arun (pero algo reorganizado en función de las diferentes prioridades).

Sintaxis

Hay cierta subjetividad en la sintaxis, pero mantengo mi afirmación de que la concisión de data.table hace que sea más difícil de aprender y más difícil de leer. ¡Esto es en parte porque dplyr está resolviendo un problema mucho más fácil!

Una cosa realmente importante que dplyr hace por usted es que limita sus opciones. Afirmo que la mayoría de los problemas de una sola tabla se pueden resolver con solo cinco verbos clave que filtran, seleccionan, mutan, organizan y resumen, junto con un adverbio "por grupo". Esa restricción es de gran ayuda cuando está aprendiendo manipulación de datos, porque ayuda a ordenar su pensamiento sobre el problema. En dplyr, cada uno de estos verbos se asigna a una sola función. Cada función hace un trabajo y es fácil de entender de forma aislada.

Usted crea complejidad al canalizar estas operaciones simples junto con %>% . Aquí hay un ejemplo de una de las publicaciones a las que Arun ha vinculado :

diamonds %>%
  filter(cut != "Fair") %>%
  group_by(cut) %>%
  summarize(
    AvgPrice = mean(price),
    MedianPrice = as.numeric(median(price)),
    Count = n()
  ) %>%
  arrange(desc(Count))

Incluso si nunca antes has visto dplyr (¡o incluso R!), Puedes entender lo que está sucediendo porque las funciones son verbos en inglés. La desventaja de los verbos en inglés es que requieren más mecanografía que [ , pero creo que eso se puede mitigar en gran medida con un autocompletado mejor.

Aquí está el código equivalente data.table:

diamondsDT <- data.table(diamonds)
diamondsDT[
  cut != "Fair", 
  .(AvgPrice = mean(price),
    MedianPrice = as.numeric(median(price)),
    Count = .N
  ), 
  by = cut
][ 
  order(-Count) 
]

Es más difícil seguir este código a menos que ya esté familiarizado con data.table. (Tampoco pude averiguar cómo sangrar lo repetido [ de una manera que se vea bien a mis ojos). Personalmente, cuando miro el código que escribí hace 6 meses, es como mirar un código escrito por un extraño, por lo que prefiero el código directo, aunque detallado.

Otros dos factores menores que creo que disminuyen ligeramente la legibilidad:

  • Dado que casi todas las operaciones de la tabla de datos utilizan [ se necesita un contexto adicional para descubrir qué está sucediendo. Por ejemplo, ¿ x[y] une dos tablas de datos o extrae columnas de un marco de datos? Esto es solo un pequeño problema, porque en un código bien escrito los nombres de las variables deberían sugerir lo que está sucediendo.

  • Me gusta que group_by() es una operación separada en dplyr. Cambia fundamentalmente el cálculo, por lo que creo que debería ser obvio cuando se hojea el código, y es más fácil detectar group_by() que el argumento by a [.data.table .

También me gusta que la tubería no se limite a un solo paquete. Puede comenzar por ordenar sus datos con tidyr y terminar con un gráfico en ggvis . Y no está limitado a los paquetes que escribo, cualquiera puede escribir una función que forme una parte perfecta de una tubería de manipulación de datos. De hecho, prefiero el código anterior de data.table reescrito con %>% :

diamonds %>% 
  data.table() %>% 
  .[cut != "Fair", 
    .(AvgPrice = mean(price),
      MedianPrice = as.numeric(median(price)),
      Count = .N
    ), 
    by = cut
  ] %>% 
  .[order(-Count)]

Y la idea de canalizar con %>% no se limita solo a marcos de datos y se generaliza fácilmente a otros contextos: gráficos web interactivos , raspado web , gists , contratos de tiempo de ejecución , ...)

Memoria y rendimiento

Los he agrupado juntos porque, para mí, no son tan importantes. La mayoría de los usuarios de R trabajan con menos de 1 millón de filas de datos, y dplyr es lo suficientemente rápido para ese tamaño de datos que no conoce el tiempo de procesamiento. Optimizamos dplyr para expresividad en datos medianos; siéntase libre de usar data.table para velocidad sin procesar en datos más grandes.

La flexibilidad de dplyr también significa que puede modificar fácilmente las características de rendimiento utilizando la misma sintaxis. Si el rendimiento de dplyr con el backend del marco de datos no es lo suficientemente bueno para usted, puede usar el backend data.table (aunque con un conjunto de funciones algo restringido). Si los datos con los que está trabajando no caben en la memoria, entonces puede usar una base de datos backend.

Dicho todo esto, el rendimiento de dplyr mejorará a largo plazo. Definitivamente implementaremos algunas de las grandes ideas de data.table como el ordenamiento de radix y el uso del mismo índice para uniones y filtros. También estamos trabajando en la paralelización para poder aprovechar los múltiples núcleos.

Caracteristicas

Algunas cosas en las que planeamos trabajar en 2015:

  • el paquete de readr , para facilitar la obtención de archivos del disco y en la memoria, de forma análoga a fread() .

  • Uniones más flexibles, incluido el soporte para uniones no equitativas.

  • Agrupación más flexible como muestras de bootstrap, rollups y más

También estoy invirtiendo tiempo en mejorar los conectores de la base de datos de R, la capacidad de hablar con las API web y facilitar el raspado de páginas HTML .





dplyr