Estoy usando data.table y hay muchas funciones que requieren que establezca una clave (por ejemplo X[Y]
). Como tal, deseo comprender qué hace una clave para configurar correctamente las claves en mis tablas de datos.
Una fuente que leí fue ?setkey
.
setkey()
ordena undata.table
y lo marca como ordenado. Las columnas ordenadas son la clave. La clave puede ser cualquier columna en cualquier orden. Las columnas se ordenan siempre en orden ascendente. La tabla se cambia por referencia. No se realiza ninguna copia, salvo la memoria de trabajo temporal de una columna.
Mi conclusión aquí es que una clave "ordenaría" la tabla de datos, lo que resultaría en un efecto muy similar a order()
. Sin embargo, no explica el propósito de tener una clave.
Las preguntas frecuentes 3.2 y 3.3 de data.table explica:
3.2 No tengo una clave en una mesa grande, pero la agrupación es muy rápida. ¿Porqué es eso?
data.table utiliza la clasificación de radix. Esto es significativamente más rápido que otros algoritmos de clasificación. Radix es específicamente solo para enteros, consulte
?base::sort.list(x,method="radix")
. Esta es también una de las razones por las quesetkey()
es rápido. Cuando no se establece ninguna clave, o lo agrupamos en un orden diferente al de la clave, lo llamamos ad hoc by.3.3 ¿Por qué el agrupamiento por columnas en la clave es más rápido que un ad hoc por?
Debido a que cada grupo es contiguo en la RAM, lo que minimiza las búsquedas de páginas, y la memoria se puede copiar a granel (
memcpy
en C) en lugar de realizar un bucle en C.
A partir de aquí, supongo que establecer una clave de alguna manera le permite a R usar "ordenación de radix" sobre otros algoritmos, y por eso es más rápido.
La guía de inicio rápido de 10 minutos también tiene una guía sobre las teclas.
- Llaves
Comencemos considerando data.frame, específicamente nombres de filas (o en inglés, nombres de filas). Es decir, los múltiples nombres que pertenecen a una sola fila. ¿Los múltiples nombres que pertenecen a una sola fila? Eso no es a lo que estamos acostumbrados en un data.frame. Sabemos que cada fila tiene como máximo un nombre. Una persona tiene al menos dos nombres, un primer nombre y un segundo nombre. Esto es útil para organizar un directorio telefónico, por ejemplo, que se ordena por apellido y luego por nombre. Sin embargo, cada fila de un data.frame solo puede tener un nombre.
Una clave consta de una o más columnas de nombres de filas, que pueden ser números enteros, factores, caracteres o alguna otra clase, no simplemente caracteres. Además, las filas están ordenadas por clave. Por lo tanto, una tabla de datos puede tener como máximo una clave, porque no se puede ordenar de más de una forma.
La unicidad no se aplica, es decir, se permiten valores de clave duplicados. Dado que las filas están ordenadas por clave, cualquier duplicado en la clave aparecerá consecutivamente
El directorio telefónico fue útil para comprender qué es una clave, pero parece que una clave no es diferente en comparación con tener una columna de factores. Además, no explica por qué se necesita una clave (especialmente para usar ciertas funciones) y cómo elegir la columna para establecer como clave. Además, parece que en una tabla de datos con el tiempo como columna, establecer cualquier otra columna como clave probablemente también estropearía la columna de tiempo, lo que lo hace aún más confuso ya que no sé si se me permite establecer cualquier otra columna como llave. Alguien puede iluminarme, por favor?
fuente
Respuestas:
Actualización menor: consulte también las nuevas viñetas HTML . Este número destaca las otras viñetas que planeamos.
Actualicé esta respuesta nuevamente (febrero de 2016) a la luz de la nueva
on=
función que también permite uniones ad-hoc . Consulte el historial para obtener respuestas anteriores (desactualizadas).¿Qué hace exactamente
setkey(DT, a, b)
?Hace dos cosas:
DT
de datos por la (s) columna (s) proporcionada ( a , b ) por referencia , siempre en orden creciente .sorted
aDT
.El reordenamiento es rápido (debido a la ordenación de base interna de data.table ) y eficiente en memoria (solo se asigna una columna adicional de tipo double ).
¿Cuándo se
setkey()
requiere?Para las operaciones de agrupación,
setkey()
nunca fue un requisito absoluto. Es decir, podemos realizar un cold-by o adhoc-by .Sin embargo, antes de
v1.9.6
, las uniones del formulariox[i]
debenkey
configurarsex
. Con el nuevoon=
argumento de v1.9.6 + , esto ya no es cierto y, por lo tanto, la configuración de claves tampoco es un requisito absoluto aquí.Tenga en cuenta que el
on=
argumento también se puede especificar explícitamente incluso para laskeyed
combinaciones.Entonces, ¿cuál es la razón para implementar el
on=
argumento?Hay bastantes razones.
Permite distinguir claramente la operación como una operación que involucra dos tablas de datos . El solo hecho
X[Y]
no distingue esto también, aunque podría quedar claro al nombrar las variables de manera apropiada.También permite comprender las columnas en las que se está realizando la combinación / subconjunto inmediatamente al mirar esa línea de código (y no tener que rastrear hasta la
setkey()
línea correspondiente ).En las operaciones en las que las columnas se agregan o actualizan por referencia , las
on=
operaciones son mucho más eficaces, ya que no es necesario reordenar toda la tabla de datos solo para agregar / actualizar columnas. Por ejemplo,En el segundo caso, no tuvimos que reordenar. No es calcular el orden lo que lleva mucho tiempo, sino reordenar físicamente la tabla de datos en la RAM y, al evitarlo, mantenemos el orden original y también funciona.
Incluso de lo contrario, a menos que esté realizando uniones repetitivamente, no debería haber una diferencia de rendimiento notable entre uniones con clave y ad-hoc .
Esto lleva a la pregunta, ¿qué ventaja tiene ya la introducción de datos en una tabla de datos ?
¿Hay alguna ventaja en teclear una tabla de datos?
Al escribir una tabla de datos, se reordena físicamente en función de esas columnas en la RAM. Calcular el pedido no suele ser la parte que requiere mucho tiempo, sino el reordenamiento en sí. Sin embargo, una vez que tenemos los datos ordenados en la RAM, las filas que pertenecen al mismo grupo son todas contiguas en la RAM y, por lo tanto, es muy eficiente en caché. Es la ordenación lo que acelera las operaciones en tablas de datos con clave.
Por lo tanto, es esencial averiguar si el tiempo dedicado a reordenar toda la tabla de datos vale la pena para realizar una combinación / agregación eficiente en caché. Por lo general, a menos que se realicen operaciones repetitivas de agrupación / unión en la misma tabla de datos con clave, no debería haber una diferencia notable.
Pregunta: ¿Cómo crees que sería el rendimiento en comparación con una combinación con clave , si utilizas
setorder()
para reordenar el data.table y el usoon=
? Si lo ha seguido hasta ahora, debería poder averiguarlo :-).fuente
DT[J(1e4:1e5)]
realmente equivalente aDF[DF$x > 1e4 & DF$x < 1e5, ]
? ¿Podrías indicarme quéJ
significa? Además, esa búsqueda no devolvería ninguna fila, yasample(1e4, 1e7, TRUE)
que no incluye números superiores a 1e4.>=
y<=
- arreglado.J
(y.
) son alias delist
(es decir, son equivalentes). Internamente, cuandoi
es una lista, se convierte en una tabla de datos después de la cual se usa la búsqueda binaria para calcular índices de fila. Fijo1e4
para1e5
evitar confusiones. Gracias por avistar. Tenga en cuenta que ahora podemos usar elon=
argumento directamente para realizar subconjuntos binarios en lugar de configurar la clave. Lea más sobre las nuevas viñetas HTML . Y esté atento a esa página para ver las viñetas de las uniones.Una clave es básicamente un índice en un conjunto de datos, que permite operaciones de clasificación, filtrado y unión muy rápidas y eficientes. Estas son probablemente las mejores razones para usar tablas de datos en lugar de marcos de datos (la sintaxis para usar tablas de datos también es mucho más fácil de usar, pero eso no tiene nada que ver con las claves).
Si no comprende los índices, considere lo siguiente: una guía telefónica se "indexa" por nombre. Entonces, si quiero buscar el número de teléfono de alguien, es bastante sencillo. Pero supongamos que quiero buscar por número de teléfono (por ejemplo, buscar quién tiene un número de teléfono en particular). A menos que pueda "volver a indexar" la guía telefónica por número de teléfono, tomará mucho tiempo.
Considere el siguiente ejemplo: suponga que tengo una tabla, ZIP, de todos los códigos postales en los EE. UU. (> 33,000) junto con la información asociada (ciudad, estado, población, ingreso medio, etc.). Si quiero buscar la información de un código postal específico, la búsqueda (filtro) es aproximadamente 1000 veces más rápida si
setkey(ZIP,zipcode)
primero.Otro beneficio tiene que ver con las combinaciones. Suponga que tiene una lista de personas y sus códigos postales en una tabla de datos (llámelo "PPL"), y quiero agregar información de la tabla ZIP (por ejemplo, ciudad, estado, etc.). El siguiente código lo hará:
Esta es una "combinación" en el sentido de que estoy uniendo la información de 2 tablas basadas en un campo común (código postal). Uniones como esta en tablas muy grandes son extremadamente lentas con marcos de datos y extremadamente rápidas con tablas de datos. En un ejemplo de la vida real, tuve que hacer más de 20.000 uniones como esta en una tabla completa de códigos postales. Con las tablas de datos, el guión tardó unos 20 minutos. correr. Ni siquiera lo probé con marcos de datos porque me habría llevado más de 2 semanas.
En mi humilde opinión, no solo deberías leer sino estudiar las preguntas frecuentes y el material de introducción. Es más fácil de entender si tiene un problema real al que aplicar esto.
[Respuesta al comentario de @ Frank]
Re: clasificación vs. indexación - Basado en la respuesta a esta pregunta , parece que
setkey(...)
de hecho reordena las columnas en la tabla (por ejemplo, una clasificación física) y no crea un índice en el sentido de la base de datos. Esto tiene algunas implicaciones prácticas: por un lado, si configura la clave en una tabla consetkey(...)
y luego cambia cualquiera de los valores en la columna clave, data.table simplemente declara que la tabla ya no está ordenada (desactivando elsorted
atributo); lo hace no dinámicamente re-índice para mantener el orden adecuado (como sucedería en una base de datos). Además, "la eliminación de la clave" utilizandosetky(DT,NULL)
no no restaurar la tabla a la misma del orden original, sin clasificar.Re: filtrar frente a unir : la diferencia práctica es que el filtrado extrae un subconjunto de un solo conjunto de datos, mientras que unir combina datos de dos conjuntos de datos basados en un campo común. Hay muchos tipos diferentes de combinación (interna, externa, izquierda). El ejemplo anterior es una combinación interna (solo se devuelven los registros con claves comunes a ambas tablas), y esto tiene muchas similitudes con el filtrado.
fuente
setkey
realidad reordena las filas de manera irreversible. Si es solo para fines de visualización, ¿cómo imprimo las primeras diez filas de acuerdo con el orden "verdadero" (que habría visto antes de setkey)? Estoy bastante seguro desetkey(DT,NULL)
que no hace esto ... (cont.)X[Y,...]
, necesita "filtrar" las filas de X usando la clave. Por supuesto, otras cosas suceden después de eso (las columnas de Y están disponibles y hay un by-without-by implícito), pero todavía no veo eso como un beneficio conceptualmente distinto. Sin embargo, supongo que su respuesta se expresa en términos de operaciones que podría querer hacer, donde la distinción puede ser útil.setkey(DT,NULL)
elimina la clave pero no afecta el orden de clasificación. Hizo una pregunta sobre esto aquí . Veamos.