Para recopilar los requisitos en un solo lugar y actualizar discusiones de ~ 4 años que crearon este problema para cubrir la función de _ funciones rodantes_ (también conocida como _ agregados rodantes_, _ ventana deslizante_ o _ promedio móvil _ / _ agregados móviles_).
Implementación rollmean
, simplificada.
x = data.table(v1=1:5, v2=1:5)
k = c(2, 3)
i - single column
j - single window
m - int referring to single row
w - current row's sum of rolling window
r - answer for each i, j
md5-be70673ef4a3bb883d4f334bd8fadec9
for i in x
for j in k
r = NA_real_
w = 0
for m in 1:length(i)
w = w + i[m]
w = w - i[m-j]
r[m] = w / j
sí, y muchas más funciones enrolladas siguen la misma idea básica (incluyendo
Desviación estándar rodante / cualquier momento basado en expectativas y cualquier función
como rollproduct que usa invertible * en lugar de + para agregar dentro
la ventana
Siempre imaginé la funcionalidad de la ventana rodante como agrupar el conjunto de datos en varios grupos superpuestos (ventanas). Entonces la API se vería así:
DT[i, j,
by = roll(width=5, align="center")]
Entonces, si j
contiene, digamos, mean(A)
, podemos reemplazarlo internamente con rollmean(A)
, exactamente como lo estamos haciendo con gmean()
este momento. O j
puede contener una funcionalidad arbitrariamente complicada (digamos, ejecutar una regresión para cada ventana), en cuyo caso le suministraríamos .SD
data.table, exactamente como lo hacemos con los grupos ahora.
De esta manera, no es necesario introducir más de 10 funciones nuevas, solo una. Y se siente data.table-y en espíritu también.
si de acuerdo
El sábado 21 de abril de 2018 a las 3:38 p.m. Pasha Stetsenko [email protected]
escribió:
Siempre imaginé la funcionalidad de la ventana rodante como agrupar los
conjunto de datos en varios grupos superpuestos (ventanas). Entonces la API miraría
algo como esto:DT [i, j,
por = rollo (ancho = 5, alinear = "centro")]Entonces, si j contiene, digamos, mean (A), podemos reemplazarlo internamente con
rollmean (A) - exactamente como lo estamos haciendo con gmean () en este momento. O puedo
contienen una funcionalidad arbitrariamente complicada (digamos, ejecute una regresión para
cada ventana), en cuyo caso le suministraríamos la tabla de datos .SD, exactamente
como lo hacemos con los grupos en este momento.De esta manera, no es necesario introducir más de 10 funciones nuevas, solo una. Y es
se siente data.table-y en espíritu también.-
Estás recibiendo esto porque hiciste un comentario.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/Rdatatable/data.table/issues/2778#issuecomment-383275134 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AHQQdbADiE4aAI1qPxPnFXUM5gR-0w2Tks5tquH8gaJpZM4TeTQf
.
@ st-pasha idea interesante, parece data.table-y spirit, pero impondrá muchas limitaciones y no es realmente apropiado para esta categoría de funciones.
DT[, rollmean(V1, 3), by=V2]
DT[, .(rollmean(V1, 3), rollmean(V2, 100))]
[.data.table
como ahora permitimos el turnorollmean(rnorm(10), 3)
DT[, .(rollmean(list(V1, V2), c(5, 20)), rollmean(list(V2, V3), c(10, 30)))]
mean
y rollmean
en la misma llamada j
DT[, .(rollmean(V1, 3), mean(V1)), by=V2]
Por lo general, cuando usamos by
, agregamos datos a un número menor de filas, mientras que las funciones móviles siempre devuelven un vector de la misma longitud que la entrada. Este tipo de funciones en SQL tienen API en el siguiente estilo:
SELECT AVG(value) OVER (ROWS BETWEEN 99 PRECEDING AND CURRENT ROW)
FROM tablename;
Aún puede combinarlo con GROUP BY de la siguiente manera:
SELECT AVG(value) OVER (ROWS BETWEEN 99 PRECEDING AND CURRENT ROW)
FROM tablename
GROUP BY group_columns;
Entonces, en SQL, esas funciones permanecen en SELECT
que se refiere a j
en DT.
En DT podríamos conseguir lo mismo con:
DT[, rollmean(value, 100)]
DT[, rollmean(value, 100), group_columns]
Las funciones sucesivas encajan en la misma categoría de funciones que shift
que también devuelve el mismo número de filas que obtuvo en la entrada.
Shift en SQL se ve así:
SELECT LAG(value, 1) OVER ()
FROM tablename;
mean
y rollmean
no son solo funciones diferentes, son categorías de funciones diferentes. Uno pretendía agregar según el grupo, otro no agregar en absoluto. Esto es fácilmente visible en SQL donde no usamos GROUP BY
para el tipo de funciones sucesivas, pero necesitamos usar GROUP BY
para agregados como mean
(eventualmente obteniendo el total de la subvención al agrupar cláusula no está presente).
No veo un razonamiento sólido para tratar de aplicar las mismas reglas de optimización que lo hacemos para mean
, especialmente cuando realmente no se ajusta al caso de uso, y todo eso solo por el bien de los datos.table-y espíritu. La propuesta actual también es data.table-y spirit, que se puede combinar fácilmente con :=
, igual que shift
. Simplemente agrega un conjunto de nuevas funciones, actualmente no disponibles en data.table.
@jangorecki Gracias, todas estas son consideraciones válidas. Por supuesto, diferentes personas tienen diferentes experiencias y diferentes puntos de vista sobre lo que debería considerarse "natural".
Es posible realizar rollmean por grupo: esto es solo una agrupación de 2 niveles: DT[, mean(V1), by=.(V2, roll(3))]
. Sin embargo, no veo cómo hacer diferentes tamaños de ventana en diferentes columnas con mi sintaxis ...
Debo admitir que nunca antes había visto la sintaxis SQL para uniones rodantes. Es interesante que usen un agregador estándar como AVG
pero le apliquen la especificación de ventanas. En cuanto a la documentación de Transact-SQL, hay algunas ideas interesantes, por ejemplo, la distinción entre selección de fila lógica / física. Permiten diferentes operadores "OVER" en diferentes columnas, sin embargo, en todos los ejemplos que dan, es la misma cláusula OVER repetida varias veces. Por lo tanto, sugiere que este caso de uso es mucho más común y, por lo tanto, usar un solo grupo roll()
resultaría en menos repetición.
Además, esta pregunta SO proporciona una idea interesante de por qué se introdujo la sintaxis OVER en SQL:
Puede utilizar GROUP BY SalesOrderID. La diferencia es que con GROUP BY solo puede tener los valores agregados para las columnas que no están incluidas en GROUP BY. Por el contrario, al usar funciones agregadas con ventana en lugar de GROUP BY, puede recuperar valores agregados y no agregados. Es decir, aunque no está haciendo eso en su consulta de ejemplo, puede recuperar tanto los valores de OrderQty individuales como sus sumas, recuentos, promedios, etc., en grupos de los mismos SalesOrderID.
Entonces, parece que la sintaxis está diseñada para eludir la limitación del SQL estándar donde los resultados agrupados no se pueden combinar con valores no agregados (es decir, seleccionando A
y mean(A)
en la misma expresión). Sin embargo, data.table
no tiene tal limitación, por lo que tiene más libertad en la elección de la sintaxis.
Ahora bien, si realmente queremos adelantarnos a la curva, debemos pensar en una perspectiva más amplia: cuáles son las funciones "rodantes", para qué se usan, cómo se pueden extender, etc. Aquí está mi opinión. desde el punto de vista de un estadístico:
La función "Media rodante" se utiliza para suavizar algunas entradas ruidosas. Digamos, si tiene observaciones a lo largo del tiempo y quiere tener alguna noción de "cantidad promedio", que sin embargo variaría con el tiempo, aunque muy lentamente. En este caso, se puede considerar la "media móvil de las últimas 100 observaciones" o la "media móvil de todas las observaciones anteriores". De manera similar, si observa cierta cantidad en un rango de entradas, puede suavizarla aplicando "media móvil sobre ± 50 observaciones".
Todos estos se pueden implementar como operadores de agrupación extendidos, siendo las ventanas móviles solo uno de los elementos de esta lista. Dicho esto, no entiendo por qué no podemos tener las dos cosas.
Debo admitir que nunca antes había visto la sintaxis SQL para uniones rodantes.
Supongo que te refieres a funciones rodantes, el problema no tiene nada que ver con las combinaciones rodantes.
Permiten diferentes operadores "OVER" en diferentes columnas, sin embargo, en todos los ejemplos que dan, es la misma cláusula OVER repetida varias veces. Por lo tanto, sugiere que este caso de uso es mucho más común y, por lo tanto, usar un solo grupo roll () resultaría en menos repetición.
Es solo una cuestión de caso de uso, si está llamando al mismo OVER () muchas veces, puede que le resulte más eficaz usar GROUP BY
, crear una tabla de búsqueda y reutilizarla en otras consultas. Independientemente de los ejemplos que haya, se requiere repetir OVER () para retener la característica de localidad para cada medida proporcionada. Mis casos de uso de Data Warehouses no eran tan simples como los de Microsoft docs.
Por el contrario, al usar funciones agregadas con ventana en lugar de GROUP BY, puede recuperar valores agregados y no agregados.
En data.table hacemos :=
y by
en una consulta para lograrlo.
Por lo tanto, parece que la sintaxis está diseñada para eludir la limitación del SQL estándar donde los resultados agrupados no se pueden combinar con valores no agregados (es decir, seleccionar tanto A como la media (A) en la misma expresión). Sin embargo, data.table no tiene tal limitación, por lo que tiene más libertad en la elección de la sintaxis.
No es una gran limitación de SQL, sino solo el diseño de GROUP BY, que se agregará, de la misma manera que nuestros agregados by
. Se requirió una nueva API para cubrir las nuevas funcionalidades de la ventana. La agrupación para la función de ventana SQL se puede proporcionar para cada llamada de función usando FUN() OVER (PARTITION BY ...)
donde _partición por_ es como agrupación local para medida única. Entonces, para lograr la flexibilidad de SQL, necesitaríamos usar j = mean(V1, roll=5)
o j = over(mean(V1), roll=5)
manteniendo esa API en j
. Aún así, este enfoque no permitirá admitir todos los casos de uso mencionados anteriormente.
puede suavizarlo aplicando "media móvil sobre ± 50 observaciones".
Esto es para lo que se usa el argumento align
.
Entonces, la primera extensión es mirar "ventanas suaves": imagine una media sobre las observaciones pasadas donde cuanto más una observación en el pasado es, menor es su contribución. O un promedio de observaciones cercanas sobre un núcleo gaussiano.
Hay muchas variantes (número virtualmente ilimitado) de promedios móviles, la función de ventana de suavizado más común (que no sea rollmean / SMA) es la media móvil exponencial (EMA). No es trivial decidir cuál debe incluirse y cuál no, y en realidad es mejor tomar esa decisión de acuerdo con las solicitudes de funciones que vendrán de los usuarios, hasta ahora no se solicitó ninguna como esta.
Todos estos se pueden implementar como operadores de agrupación extendidos, siendo las ventanas móviles solo uno de los elementos de esta lista.
Seguramente pueden, pero si observa SO y los problemas creados en nuestro repositorio, verá que esas pocas funciones móviles aquí son responsables del 95 +% de las solicitudes de los usuarios. Estoy feliz de trabajar en EMA y otros MA (aunque no estoy seguro de si data.table es el mejor lugar para ellos), pero como un tema separado. Algunos usuarios, incluido yo, están esperando un promedio móvil simple en data.table desde hace 4 años.
Aquí está mi opinión, desde el punto de vista de un estadístico.
Mi punto de vista proviene del almacenamiento de datos (donde utilicé la función de ventana, al menos una vez a la semana) y el análisis de tendencias de precios (donde utilicé decenas de promedios móviles diferentes).
rollmean
borrador de roll
. Encontré que la mayoría de los otros paquetes que implementan medias móviles no pueden manejar bien con na.rm=FALSE
y NA presentes en la entrada. Esta implementación maneja NA de manera consistente a mean
, lo que impone una sobrecarga adicional debido a las llamadas ISNAN
. Podríamos permitir una versión más rápida pero menos segura de la API si el usuario está seguro de que no hay NA en la entrada.
PR está en el n. ° 2795
@mattdowle respondiendo preguntas de relaciones públicas
¿Por qué estamos haciendo esto dentro de data.table? ¿Por qué lo estamos integrando en lugar de contribuir a los paquetes existentes y usarlos desde data.table?
mi conjetura es que se reduce a la sintaxis (características solo posibles o convenientes si están integradas en data.table; por ejemplo, [...] dentro y optimizado) y la construcción de elementos internos de data.table en la función rodante en el nivel C; por ejemplo, froll * debe ser consciente y utilizar índices y claves de data.table. Si es así, se necesitan más detalles al respecto; por ejemplo, un ejemplo breve y sencillo.
Para mí personalmente se trata de velocidad y falta de cadena de dependencias, hoy en día no es fácil de conseguir.
Los índices / clave podrían ser útiles para frollmin / frollmax, pero es poco probable que el usuario cree un índice en la variable de medida. Es poco probable que el usuario haga una variable de índice en la medida, además, aún no hemos realizado esta optimización para mínimo / máximo. No veo mucho sentido para la optimización de GForce porque la memoria asignada no se libera después de pasar lista * sino que se devuelve como respuesta (a diferencia de la media, suma, etc.).
Si no hay un argumento convincente para la integración, entonces deberíamos contribuir con los otros paquetes.
Enumeré algunos arriba, si no está convencido, le recomiendo que complete una pregunta a los usuarios de data.table, pregunte en twitter, etc. para verificar la respuesta. Esta función fue solicitada durante mucho tiempo y por muchos usuarios. Si la respuesta no lo convence, puede cerrar este problema.
Encontré que sparklyr
puede admitir funciones rodantes muy bien en un conjunto de datos a gran escala.
@harryprince podría poner un poco más de luz al proporcionar un código de ejemplo de cómo lo haces en sparklyr.
Según la viñeta dplyr de "Funciones de ventana"
Los agregados rodantes operan en una ventana de ancho fijo. No los encontrará en base R o en dplyr, pero hay muchas implementaciones en otros paquetes, como RcppRoll.
AFAIU, utiliza una API de chispa personalizada a través de Sparklyr para la que no se implementa la interfaz dplyr, ¿correcto?
Este problema se trata de agregados móviles, otros "tipos" de funciones de ventana ya están en data.table
durante mucho tiempo.
También sería útil proporcionar un ejemplo para que podamos comparar el rendimiento (en memoria) con el de sparklyr
/ SparkR
.
Se me acaba de ocurrir que esta pregunta:
¿Cómo calcular diferentes tamaños de ventana para diferentes columnas?
tiene, de hecho, un alcance más amplio y no se aplica únicamente a las funciones sucesivas.
Por ejemplo, parece perfectamente razonable preguntar cómo seleccionar el precio promedio del producto por fecha, luego por semana, y luego quizás por semana + categoría, todo dentro de la misma consulta. Si alguna vez implementamos dicha funcionalidad, la sintaxis natural para ella podría ser
DT[, .( mean(price, by=date),
mean(price, by=week),
mean(price, by=c(week, category)) )]
Ahora, si esta funcionalidad ya estuviera implementada, entonces habría sido un simple salto desde allí a los medios rodantes:
DT[, .( mean(price, roll=5),
mean(price, roll=20),
mean(price, roll=100) )]
No digo que esto sea inequívocamente mejor que rollmean(price, 5)
, simplemente agregando algunas alternativas para considerar ...
@ st-pasha
cómo seleccionar el precio promedio del producto por fecha, luego por semana, y luego quizás por semana + categoría, todo dentro de la misma consulta.
AFAIU, esto ya es posible usando ?groupingsets
, pero aún no está vinculado a [.data.table
.
@jangorecki , @ st-pasha, and Co. - ¡Gracias por todo su trabajo en esto! Tengo curiosidad por saber por qué se eliminó el soporte de ventana parcial del alcance, ¿existe alguna posibilidad de que esa funcionalidad vuelva a la hoja de ruta? Sería útil para mí a veces y llenaría un vacío de funcionalidad que, hasta donde yo sé, no se ha llenado ni en zoo
ni en RcppRoll
.
Esta pregunta de desbordamiento de pila es un buen ejemplo de una aplicación móvil que podría beneficiarse de un argumento partial = TRUE
.
@msummersgill Gracias por sus comentarios. En la primera publicación, vinculé explícitamente el compromiso sha donde se puede encontrar el código de característica de ventana parcial. La implementación que está allí se eliminó más tarde para reducir la complejidad del código. También imponía un bajo costo de rendimiento incluso cuando no se usaba esa función. Esta característica puede (y probablemente debería) implementarse de otra manera, primero complete como está, y luego simplemente complete la ventana parcial que falta usando un bucle adicional de 1:window_size
. Entonces, la sobrecarga de esa función solo se nota cuando la usa. Sin embargo, proporcionamos esa funcionalidad a través del argumento adaptive
, donde la función partial
es solo un caso especial de la media móvil adaptive
. Hay un ejemplo de cómo lograr partial
usando adaptive
en ?froll
manual . Pegándolo aquí:
d = as.data.table(list(1:6/2, 3:8/4))
an = function(n, len) c(seq.int(n), rep(n, len-n))
n = an(3, nrow(d))
frollmean(d, n, adaptive=TRUE)
Por supuesto, no será tan eficiente como la función de balanceo no adaptativa usando un bucle adicional para llenar solo una ventana parcial.
AFAIK zoo
tiene la función partial
.
¿Tienen algún plan para agregar funciones de regresión continua a data.table?
@waynelapierre, si habrá demanda para eso, entonces sí. Tienes mi +1
gracias esto es genial. Sin embargo, solo una pregunta. Solo veo agregados móviles simples, como una media móvil o una mediana móvil. ¿También está implementando funciones rodantes más refinadas, como marcos de datos DT rodantes? Digamos, cree un DT rodante usando las últimas 10 obs y ejecute una regresión lm
en él.
¡Gracias!
@randomgambit Yo diría que está fuera de alcance, a menos que haya una gran demanda para eso. No sería muy difícil hacerlo para ser más rápido que el R / zoo base simplemente manejando el bucle anidado en C. Pero deberíamos intentar implementarlo usando el algoritmo "en línea", para evitar el bucle anidado. Esto es más complicado y eventualmente podríamos hacerlo para cualquier estadística, por lo que tenemos que cortar esas estadísticas en algún momento.
@jangorecki interesante gracias. Eso significa que seguiré usando tsibble
para incrustar ... DATA.TABLES
en un tibble
! mente asombrada: D
Intenté usar frollmean
para calcular una "curva logística" no paramétrica que muestra P[y | x]
para y
binarios usando los vecinos más cercanos de x
. Me sorprendió que y
almacenado como logical
no se enviara automáticamente a integer
:
DT = data.table(x = rnorm(1000), y = runif(1000) > .5)
DT[order(x), .(x, p_y = frollmean(y, 50L))]
Error en froll (fun = "mean", x = x, n = n, fill = fill, algo = algo, align = align,:
x debe ser de tipo numérico
Un ejemplo de cómo los argumentos vectorizados x
/ n
pueden afectar el rendimiento.
https://github.com/AdrianAntico/RemixAutoML/commit/d8370712591323be01d0c66f34a70040e2867636#r34784427
menos bucles, código más fácil de leer, mucho más rápido (aceleración 10x-36x).
frollapply listo: https://github.com/Rdatatable/data.table/pull/3600
### fun mean sum median
# rollfun 8.815 5.151 60.175
# zoo::rollapply 34.373 27.837 88.552
# zoo::roll[fun] 0.215 0.185 NA
# frollapply 5.404 1.419 56.475
# froll[fun] 0.003 0.002 NA
hola chicos, DIVERSIÓN (definida por el usuario) pasada a frollapply se cambiará para devolver un objeto R o data.frame (data.table), x pasada a frollapply podría ser data.table de carácter no forzado a numérico, entonces FUN podría funcionar en etiquetas y frollapply devuelven una lista? luego podemos hacer una regresión progresiva o pruebas continuas como hacer las pruebas de Benford o el resumen en las etiquetas.
Siempre es útil proporcionar un ejemplo reproducible. Para aclarar ... en tal escenario, le gustaría frollapply(dt, 3, FUN)
devolver una lista de longitud nrow(dt)
donde cada elemento de la lista será data.table
devuelto por FUN(dt[window])
?
frollapply(x=dt, n=3, fun=FUN)[[3]]
es igual a FUN(dt[1:3])
frollapply(x=dt, n=3, FUN=FUN)[[4]]
es igual a FUN(dt[2:4])
¿Es eso correcto? @ jerryfuyu0104
Actualmente admitimos varias columnas pasadas al primer argumento, pero las procesamos por separado, en bucle. Probablemente necesitemos un argumento adicional multi.var=FALSE
, cuando se establece en verdadero, no se repite x
(como lo hace ahora: list(FUN(x[[1]]),FUN(x[[2]]))
) sino que pasa todas las columnas FUN(x)
.
alguna actualización para esto?
Secundo esa solicitud anterior.
Además, ¿sería posible admitir un argumento "parcial" para permitir ventanas parciales?
@eliocamp, ¿ podrías explicarnos qué es una ventana partial
?
@eliocamp sería posible apoyar el argumento "parcial". Es posible que ya lo sepa, pero el soporte para esta funcionalidad ya existe, usando el argumento adaptive=TRUE
, vea los ejemplos para obtener más detalles.
Significaría calcular la función desde el principio hasta el final en lugar de formar el punto de media ventana.
Por ejemplo, para una media móvil de 11 de ancho, el primer elemento devuelto sería la media de los elementos del 1 al 6. El segundo, la media del 1 al 7, y así sucesivamente.
@jangorecki oh, gracias, ¡no sabía eso! Lo comprobaré.
De acuerdo, necesitamos un argumento parcial, no solo por conveniencia sino también por velocidad. adaptive=TRUE
agrega una sobrecarga.
Y sí, también necesitamos una regresión progresiva, por lo que debemos suministrar múltiples variables y aplicarlas a la vez, no cada una por separado.
No hay ninguna actualización sobre el estado de esos.
Me encantaría ayudar, pero mis habilidades en C ++ son absolutamente inexistentes. : sweat: ¿Crees que podría ser adecuado para novatos?
No codificamos en C ++ sino en C. Sí, es un buen lugar para empezar. Hice exactamente eso en frollmean.
Miro el código y parece abrumador. Pero te actualizaré en cualquier caso.
Pero ahora, para otra solicitud: frollmean (.SD) debería conservar los nombres. De manera más general, froll * debería conservar los nombres si la entrada es similar a una lista con nombres.
Como usuario frecuente de data.table, encuentro extremadamente útil tener funciones de "tiempo consciente", como las que se ofrecen actualmente en el paquete tsibble
. Desafortunadamente, este paquete se desarrolla alrededor de dplyr
. Me pregunto si una implementación de data.table podría ser posible. Las funciones de ventana propuestas en este número son un subconjunto de esas características.
@ywhcuhk Gracias por sus comentarios, en realidad pensaba que este problema ya pedía demasiado. La mayor parte está bien cubierta por un rollo de paquete aún liviano que es muy rápido. En cuanto a las otras características, sugiero crear un nuevo número para cada característica que le interese, por lo que la discusión sobre si queremos implementar / mantener se puede decidir para cada una por separado. Solo con mirar el archivo Léame de tstibble, no veo nada nuevo que ofrezca ...
Su título es "Tidy Temporal Data Frames" pero ni siquiera parece ofrecer uniones temporales.
Gracias @jangorecki por la respuesta. Tal vez sea un problema que depende del contexto. La estructura de datos con la que trato con más frecuencia se conoce como "datos de panel", con un ID y una hora. Si el programa es "consciente" de esta característica de datos, muchas operaciones, especialmente las operaciones de series de tiempo, serán muy fáciles. Para alguien que conoce STATA, son las operaciones basadas en tsset
y xtset
, como adelanto, retraso, relleno de brecha, etc. Creo que el "índice" en la tabla de datos se puede mejorar de alguna manera para permitir tales operaciones.
Por supuesto, estas operaciones se pueden realizar en funciones data.table como shift
y by
. Solo pensé que index
en data.table tiene mucho potencial para ser explorado. Estoy de acuerdo en que esto debería pertenecer a un tema diferente. Pero no sé cómo moverlo sin perder las discusiones anteriores ...
Comentario más útil
@mattdowle respondiendo preguntas de relaciones públicas
Para mí personalmente se trata de velocidad y falta de cadena de dependencias, hoy en día no es fácil de conseguir.
Los índices / clave podrían ser útiles para frollmin / frollmax, pero es poco probable que el usuario cree un índice en la variable de medida. Es poco probable que el usuario haga una variable de índice en la medida, además, aún no hemos realizado esta optimización para mínimo / máximo. No veo mucho sentido para la optimización de GForce porque la memoria asignada no se libera después de pasar lista * sino que se devuelve como respuesta (a diferencia de la media, suma, etc.).
Enumeré algunos arriba, si no está convencido, le recomiendo que complete una pregunta a los usuarios de data.table, pregunte en twitter, etc. para verificar la respuesta. Esta función fue solicitada durante mucho tiempo y por muchos usuarios. Si la respuesta no lo convence, puede cerrar este problema.