Pandas: Ventana enrollable con tamaño de paso

Creado en 9 feb. 2017  ·  38Comentarios  ·  Fuente: pandas-dev/pandas

Solo una sugerencia: extienda rolling para admitir una ventana móvil con un tamaño de paso, como R's rollapply(by=X) .

Muestra de código

Pandas: solución ineficiente (aplique la función a cada ventana, luego corte para obtener cada segundo resultado)

import pandas
ts = pandas.Series(range(0, 40, 2))
ts.rolling(5).apply(max).dropna()[::2]

Sugerencia:

ts = pandas.Series(range(0, 40, 2))
ts.rolling(window=5, step=2).apply(max).dropna()

Inspirado por R (ver rollapply docs):

require(zoo)
TS <- zoo(seq(0, 40, 2))
rollapply(TS, 5, FUN=max, by=2)

8 12 16 20 24 28 32 36 40

Enhancement Needs Discussion Numeric Window

Comentario más útil

"Esto podría hacerse, pero me gustaría ver un caso de uso en el que esto sea importante".

Cualquiera que sea el proyecto en el que trabajé usando pandas, casi siempre me perdí esta función, es útil cada vez que necesita calcular la aplicación solo de vez en cuando, pero aún necesita una buena resolución dentro de cada ventana.

Todos 38 comentarios

Si está utilizando funciones 'estándar', estas están vectorizadas y, por lo tanto, son muy rápidas ( ts.rolling(5).max().dropna()[::2] ).

IIUC, el ahorro aquí provendría de aplicar la función solo una fracción del tiempo (por ejemplo, cada enésimo valor). Pero, ¿hay algún caso en el que eso marque una diferencia práctica?

esto se podría hacer, pero me gustaría ver un caso de uso en el que esto sea importante. Esto también rompería la API de 'retorno del mismo tamaño que la entrada'. Aunque no creo que esto sea realmente difícil de implementar (aunque implicaría una serie de cambios en la implementación). Usamos ventanas marginales (IOW, calcula la ventana y, a medida que avanza, elimine los puntos que se están yendo y agregue los puntos que está ganando). Así que todavía tendría que calcular todo, pero simplemente no lo generaría.

¡Gracias por tus respuestas!

IIUC, el ahorro aquí provendría de aplicar la función solo una fracción del tiempo (por ejemplo, cada enésimo valor). Pero, ¿hay algún caso en el que eso marque una diferencia práctica?

Mi caso de uso es ejecutar funciones de agregación (no solo el máximo) en algunos marcos de datos de series de tiempo grandes: 400 columnas, horas de datos a 5-25Hz. También he hecho algo similar (ingeniería de funciones en datos de sensores) en el pasado con datos de hasta 20 kHz. Ejecutar ventanas de 30 segundos con un paso de 5 segundos ahorra una gran parte del procesamiento; por ejemplo, a 25 Hz con un paso de 5 segundos es 1/125 del trabajo, lo que marca la diferencia entre ejecutarlo en 1 minuto o 2 horas.

Obviamente, puedo volver a numpy, pero sería bueno si hubiera una API de nivel superior para hacer esto. Pensé que valía la pena la sugerencia en caso de que otros también la encuentren útil. ¡No espero que construyas una función solo para mí!

puede intentar volver a muestrear a un intervalo de frecuencia más alto primero y luego rodar

algo como

df = df.resample ('30s')
df.rolling (..). max () (o cualquier función)

Hola @jreback , gracias por la sugerencia.

Esto funcionaría si solo estuviera ejecutando max en mis datos (el remuestreo necesita una función de reducción; de lo contrario, el valor predeterminado es mean , ¿verdad?):

df.resample('1s').max().rolling(30).max()

Sin embargo, me gustaría ejecutar mi función de reducción en 30 segundos de datos, luego avanzar 1 segundo y ejecutarla en los siguientes 30 segundos de datos, etc. El método anterior aplica una función en 1 segundo de datos y luego en otro función en 30 resultados de la primera función.

Aquí hay un ejemplo rápido: ejecutar un cálculo de pico a pico no funciona si se ejecuta dos veces (obviamente):

# 10 minutes of data at 5Hz
n = 5 * 60 * 10
rng = pandas.date_range('1/1/2017', periods=n, freq='200ms')
np.random.seed(0)
d = np.cumsum(np.random.randn(n), axis=0)
s = pandas.Series(d, index=rng)

# Peak to peak
def p2p(d):
    return d.max() - d.min()

def p2p_arr(d):
    return d.max(axis=1) - d.min(axis=1)

def rolling_with_step(s, window, step, func):
    # See https://ga7g08.github.io/2015/01/30/Applying-python-functions-in-moving-windows/
    vert_idx_list = np.arange(0, s.size - window, step)
    hori_idx_list = np.arange(window)
    A, B = np.meshgrid(hori_idx_list, vert_idx_list)
    idx_array = A + B
    x_array = s.values[idx_array]
    idx = s.index[vert_idx_list + int(window/2.)]
    d = func(x_array)
    return pandas.Series(d, index=idx)

# Plot data
ax = s.plot(figsize=(12, 8), legend=True, label='Data')

# Plot resample then rolling (obviously does not work)
s.resample('1s').apply(p2p).rolling(window=30, center=True).apply(p2p).plot(ax=ax, label='1s p2p, roll 30 p2p', legend=True)

# Plot rolling window with step
rolling_with_step(s, window=30 * 5, step=5, func=p2p_arr).plot(ax=ax, label='Roll 30, step 1s', legend=True)

rolling window

@alexlouden de su descripción original, creo que algo como

df.resample('5s').max().rolling('30s').mean() (o cualquier reducción) está más en línea con lo que desea

IOW, tome lo que esté en un contenedor de 5 segundos, luego redúzcalo a un solo punto, luego pase por encima de esos contenedores. Esta idea general es que tiene una gran cantidad de datos que se pueden resumir en un corto período de tiempo, pero en realidad desea que esto pase a un nivel superior.

Hola @jreback , en realidad quiero ejecutar una función durante 30 segundos de datos, cada 5 segundos. Vea la función rolling_with_step en mi ejemplo anterior. El paso adicional de max / mean no funciona para mi caso de uso.

@jreback , existe una necesidad real de la función de paso que aún no se ha mencionado en esta discusión. Secundo todo lo que ha descrito

Suponga que estamos haciendo un análisis de series de tiempo con datos de entrada muestreados aproximadamente de 3 a 10 milisegundos. Estamos interesados ​​en las características del dominio de la frecuencia. El primer paso para construirlos sería averiguar la frecuencia de Nyquist. Suponga que por conocimiento del dominio sabemos que es 10 Hz (una vez cada 100 ms). Eso significa que necesitamos que los datos tengan una frecuencia de al menos 20 Hz (una vez cada 50 ms), si las características deben capturar bien la señal de entrada. No podemos volver a muestrear a una frecuencia más baja que esa. En última instancia, estos son los cálculos que hacemos:

df.resample('50ms').mean().rolling(window=32).aggregate(power_spectrum_coeff)

Aquí elegimos un tamaño de ventana en múltiplos de 8, y elegir 32 hace que el tamaño de la ventana sea de 1,6 segundos. La función agregada devuelve los coeficientes de dominio de frecuencia de un solo lado y sin el primer componente medio (la función fft es simétrica y con valor medio en el elemento 0). A continuación se muestra la función agregada de muestra:

def power_spectrum_coeff():
    def power_spectrum_coeff_(x):
        return np.fft.fft(x)[1 : int(len(x) / 2 + 1)]

    power_spectrum_coeff_.__name__ = 'power_spectrum_coeff'
    return power_spectrum_coeff_

Ahora, nos gustaría repetir esto en una ventana deslizante de, digamos, cada 0,4 segundos o cada 0,8 segundos. No tiene sentido desperdiciar cálculos y calcular FFT cada 50 ms y luego cortar más tarde. Además, volver a muestrear hasta 400 ms no es una opción, porque 400 ms son solo 2,5 Hz, que es mucho más baja que la frecuencia de Nyquist y, al hacerlo, se perderá toda la información de las funciones.

Se trata de características del dominio de la frecuencia, que tiene aplicaciones en muchos experimentos científicos relacionados con series de tiempo. Sin embargo, incluso las funciones agregadas en el dominio del tiempo más simples, como la desviación estándar, no pueden respaldarse eficazmente mediante el remuestreo.

Aunque no creo que esto sea realmente difícil de implementar (aunque implicaría una serie de cambios en la implementación). Usamos ventanas marginales (IOW, calcula la ventana y, a medida que avanza, elimina los puntos que se van y agrega los puntos que está ganando). Así que todavía tendría que calcular todo, pero simplemente no lo generaría.

Tener el parámetro 'paso' y poder reducir los cálculos reales usándolo tiene que ser el objetivo futuro de Pandas. Si el parámetro de paso solo devuelve menos puntos, entonces no vale la pena hacerlo, porque podemos dividir la salida de todos modos. Quizás dado el trabajo involucrado en hacer esto, podríamos recomendar todos los proyectos con estas necesidades para usar Numpy.

@Murmuria, puede enviar una solicitud de extracción para hacer esto. En realidad, no es tan difícil.

Mientras secundo la solicitud de un parámetro step en rolling() , me gustaría señalar que es posible obtener el resultado deseado con el parámetro base en resample() , si el tamaño del paso es una fracción entera del tamaño de la ventana . Usando el ejemplo de @alexlouden :

pandas.concat([
    s.resample('30s', label='left', loffset=pandas.Timedelta(15, unit='s'), base=i).agg(p2p) 
    for i in range(30)
]).sort_index().plot(ax=ax, label='Solution with resample()', legend=True, style='k:')

Obtenemos el mismo resultado (tenga en cuenta que la línea se extiende 30 segundos en ambos lados):
rolling_with_step_using_resample

Esto todavía es un desperdicio, dependiendo del tipo de agregación. Para el caso particular de cálculo de pico a pico como en el ejemplo de @alexlouden , p2p_arr() es casi max() y min() .

El parámetro de paso en el balanceo también permitiría usar esta función sin un índice de fecha y hora. ¿Alguien ya está trabajando en eso?

@alexlouden arriba dijo esto:

Obviamente, puedo volver a numpy, pero sería bueno si hubiera una API de nivel superior para hacer esto.

¿Puede @alexlouden o cualquier otra persona que sepa compartir alguna idea sobre cómo hacer esto con numpy? De mi investigación hasta ahora, parece que tampoco es trivial hacer esto en numpy. De hecho, hay un problema abierto al respecto aquí https://github.com/numpy/numpy/issues/7753

Gracias

Hola @tsando , ¿la función rolling_with_step que usé arriba no te funcionó?

@alexlouden gracias, acabo de verificar esa función y parece que todavía depende de los pandas (toma una serie como entrada y también usa el índice de la serie). Me preguntaba si hay un enfoque puramente numpy en esto. En el hilo que mencioné https://github.com/numpy/numpy/issues/7753 , proponen una función que usa numerosos pasos, pero son difíciles de entender y traducir a entradas de ventana y paso.

@tsando Aquí hay un PDF de la publicación de blog que vinculé anteriormente: parece que el autor ha cambiado su nombre de usuario de Github y no ha vuelto a publicar su sitio . (Lo ejecuté localmente para convertirlo a PDF).

Mi función anterior fue yo simplemente convirtiendo su último ejemplo para trabajar con Pandas; si quisiera usar numpy directamente, podría hacer algo como esto: https://gist.github.com/alexlouden/e42f1d96982f7f005e62ebb737dcd987

¡Espero que esto ayude!

@alexlouden gracias! Lo probé en una matriz de forma (13, 1313) pero me dio este error:

image

"Esto podría hacerse, pero me gustaría ver un caso de uso en el que esto sea importante".

Cualquiera que sea el proyecto en el que trabajé usando pandas, casi siempre me perdí esta función, es útil cada vez que necesita calcular la aplicación solo de vez en cuando, pero aún necesita una buena resolución dentro de cada ventana.

Estoy de acuerdo y apoyo esta función también

Si lo necesita casi siempre cuando se trata de series de tiempo, la función podría proporcionar un control mucho mejor para generar funciones de series de tiempo tanto para visualización como para análisis. ¡Apoye firmemente esta idea!

estar de acuerdo y apoyar esta función también

Esto sería muy útil para reducir el tiempo de computación manteniendo una buena resolución de ventana.

Proporciono códigos de solución, que podrían ajustarse aún más de acuerdo con su objetivo particular.

def average_smoothing(signal, kernel_size, stride):
    sample = []
    start = 0
    end = kernel_size
    while end <= len(signal):
        start = start + stride
        end = end + stride
        sample.append(np.mean(signal[start:end]))
    return np.array(sample)

Estoy de acuerdo y apoyo esta característica. Veo que está en stop motion ahora mismo.

Calcular y luego reducir la resolución no es una opción cuando tiene TB de datos.

También sería muy útil en lo que hago. Tengo TB de datos en los que necesito varias estadísticas de ventanas que no se superponen para comprender las condiciones locales. Mi "solución" actual es simplemente crear un generador que corte los marcos de datos y las estadísticas de rendimiento. Sería muy útil tener esta función.

¡Esta característica es de hecho imprescindible cuando se trata de series de tiempo!

De acuerdo, ciertamente necesito agregar esta función. Intento hacer correlaciones de ventana en ejecución entre los precios de las acciones y tengo que crear mi propia función para ello.

¡No puedo creer que una característica tan básica no esté disponible todavía!
¿Cuándo se resolverá este problema?
Gracias

Para contribuir a una 'discusión adicional':
Mi caso de uso es calcular un valor mínimo / máximo / mediano por hora para un mes de datos con una resolución de 1 segundo. Son datos de uso de energía y hay picos durante 1-2 segundos que perdería con el remuestreo. Aparte de eso, remuestrear a, por ejemplo, 5 segundos / 1 minuto no cambiaría el hecho de que todavía tengo que calcular 4k / 1k ventanas por día que deben desecharse, en lugar de solo poder calcular las 24 ventanas necesarias por día .

Sería posible solucionar esto usando groupby aso, pero eso no parece ser ni intuitivo ni tan rápido como la implementación continua (2 segundos para ventanas de 2.5mil horas con clasificación). Es impresionantemente rápido y útil, pero realmente necesitamos un argumento de zancada para utilizar completamente su poder.

Eché un vistazo al problema. Esto es relativamente trivial, sin embargo, la forma en que se implementa el código, desde una mirada superficial, creo que requerirá que alguien se esfuerce por editar manualmente todas las rutinas rodantes. Ninguno de ellos respeta los límites de ventana dados por las clases indexadoras. Si lo hicieran, tanto esta solicitud como la # 11704 serían fácilmente solucionables. En cualquier caso, creo que es manejable para cualquiera que quiera dedicar un tiempo a arreglar las cosas. Inicié un PR a medias (se espera que sea rechazado, solo para un MVP) para demostrar cómo abordaría el problema.

Corriendo:

import numpy as np
import pandas as pd

data = pd.Series(
    np.arange(100),
    index=pd.date_range('2020/05/12 12:00:00', '2020/05/12 12:00:10', periods=100))

print('1s rolling window every 2s')
print(data.rolling('1s', step='2s').apply(np.mean))

data.sort_index(ascending=False, inplace=True)

print('1s rolling window every 500ms (and reversed)')
print(data.rolling('1s', step='500ms').apply(np.mean))

rendimientos

1s rolling window every 2s
2020-05-12 12:00:00.000000000     4.5
2020-05-12 12:00:02.020202020    24.5
2020-05-12 12:00:04.040404040    44.5
2020-05-12 12:00:06.060606060    64.5
2020-05-12 12:00:08.080808080    84.5
dtype: float64
1s rolling window every 500ms (and reversed)
2020-05-12 12:00:10.000000000    94.5
2020-05-12 12:00:09.494949494    89.5
2020-05-12 12:00:08.989898989    84.5
2020-05-12 12:00:08.484848484    79.5
2020-05-12 12:00:07.979797979    74.5
2020-05-12 12:00:07.474747474    69.5
2020-05-12 12:00:06.969696969    64.5
2020-05-12 12:00:06.464646464    59.5
2020-05-12 12:00:05.959595959    54.5
2020-05-12 12:00:05.454545454    49.5
2020-05-12 12:00:04.949494949    44.5
2020-05-12 12:00:04.444444444    39.5
2020-05-12 12:00:03.939393939    34.5
2020-05-12 12:00:03.434343434    29.5
2020-05-12 12:00:02.929292929    24.5
2020-05-12 12:00:02.424242424    19.5
2020-05-12 12:00:01.919191919    14.5
2020-05-12 12:00:01.414141414     9.5
2020-05-12 12:00:00.909090909     4.5
dtype: float64

Para obtener detalles de implementación, consulte el PR (o aquí: https://github.com/anthonytw/pandas/tree/rolling-window-step)

Si bien me hubiera gustado dedicar más tiempo a terminarlo, desafortunadamente no me queda nada para abordar el duro trabajo de reelaborar todas las funciones de balanceo. Mi recomendación para cualquiera que quiera abordar esto sería hacer cumplir los límites de ventana generados por las clases del indexador y unificar las funciones fijas / variables _ * _ móviles. Con los límites de inicio y fin, no veo ninguna razón por la que deban ser diferentes, a menos que tenga una función que haga algo especial con datos muestreados de manera no uniforme (en cuyo caso esa función específica podría manejar mejor el matiz, así que tal vez poner una bandera o algo).

¿Esto también funcionará para una ventana personalizada usando el enfoque get_window_bounds() ?

Hola, secundo también la sugerencia, por favor. Esta sería una característica realmente útil.

Si está utilizando funciones 'estándar', estas están vectorizadas y, por lo tanto, son muy rápidas ( ts.rolling(5).max().dropna()[::2] ).

IIUC, el ahorro aquí provendría de aplicar la función solo una fracción del tiempo (por ejemplo, cada enésimo valor). Pero, ¿hay algún caso en el que eso marque una diferencia práctica?

Tengo un ejemplo de este tipo aquí: https://stackoverflow.com/questions/63729190/pandas-resample-daily-data-to-annual-data-with-overlap-and-offset

Cada N sería cada 365. El tamaño de la ventana es variable durante la vida útil del programa y no se garantiza que el paso sea una fracción entera del tamaño de la ventana.

Básicamente, necesito un tamaño de ventana establecido que pase por "# de días en el año que está mirando", lo cual es imposible con todas las soluciones que he encontrado para este problema hasta ahora.

También tengo una necesidad similar con el siguiente contexto (adaptado de una necesidad real y profesional):

  • Tengo un marco de datos cronológico con una columna de marca de tiempo y una columna de valor, que representa eventos irregulares. Como la marca de tiempo de cuando un perro pasó por debajo de mi ventana y cuántos segundos le tomó pasar. Puedo tener 6 eventos para un día determinado y luego ningún evento durante los próximos 2 días
  • Me gustaría calcular una métrica (digamos el tiempo medio que pasan los perros frente a mi ventana) con una ventana móvil de 365 días, que se distribuiría cada 30 días.

Por lo que tengo entendido, la API dataframe.rolling () me permite especificar la duración de 365 días, pero no la necesidad de omitir 30 días de valores (que es un número no constante de filas) para calcular la siguiente media sobre otra selección de 365 días de valores.

Obviamente, el marco de datos resultante que espero tendrá un número (mucho) menor de filas que el marco de datos inicial de 'eventos de perros'.

Solo para obtener más claridad sobre esta solicitud con un ejemplo simple.

Si tenemos esta Serie:

In [1]: s = pd.Series(range(5))

In [2]: s
Out[2]:
0    0
1    1
2    2
3    3
4    4
dtype: int64

y tenemos un tamaño de ventana de 2 y un tamaño de paso de 1 . Se evaluaría esta primera ventana en el índice 0 , pase sobre la ventana en el índice 1 , evalúe la ventana en el índice 2 , etc.

In [3]: s.rolling(2, step=1, min_periods=0).max()

Out[3]:
0    0.0
1    NaN # step over this observation
2    2.0
3    NaN # step over this observation
4    4.0
dtype: float64

Del mismo modo, si tenemos esta serie basada en el tiempo

In [1]: s = pd.Series(range(5), index=pd.DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06', '2020-01-09']))

In [2]: s
Out[2]:
2020-01-01    0
2020-01-02    1
2020-01-03    2
2020-01-06    3
2020-01-09    4
dtype: int64

y tenemos un tamaño de ventana de '3D' y un tamaño de paso de '3D' . ¿Sería este el resultado correcto?

In [3]: s.rolling('3D', step='3D', min_periods=0).max()

Out[3]:
2020-01-01    0.0       # evaluate this window
2020-01-02    NaN    # step over this observation (2020-01-01 + 3 days > 2020-01-02)
2020-01-03    NaN    # step over this observation (2020-01-01 + 3 days > 2020-01-03)
2020-01-06    3.0      # evaluate this window ("snap back" to this observation)
2020-01-09    4.0      # evaluate this window (2020-01-06 + 3 days = 2020-01-09)
dtype: float64

@mroeschke wrt al primer ejemplo ([3]), los resultados no son los que esperaba. Supongo que esta es una ventana final (por ejemplo, en el índice = 0 sería el máximo de elementos en -1 y 0, así que solo el máximo ([0]), entonces debería avanzar hacia el índice "1", al índice = 0 + paso = 1, y el siguiente cálculo sería max ([0,1]), luego max ([1,2]), etc. Lo que parece que querías tener era un tamaño de paso de dos, por lo que pasar del índice = 0 al índice = 0 + 2 = 2 (omitiendo el índice 1) y continuar así. En este caso, es casi correcto, pero no debería haber NaN. Si bien puede ser "solo" el doble del tamaño en este caso, en otros casos es sustancial. Por ejemplo, tengo aproximadamente una hora de datos de ECG de 500 Hz para un paciente, eso es 1.8 millones de muestras. Si quisiera un promedio móvil de 5 minutos cada dos minutos, eso sería una matriz de 1,8 millones de elementos con 30 cálculos válidos y un poco menos de 1,8 millones de NaN. :-)

Para la indexación, el tamaño del paso = 1 es el comportamiento actual, es decir, calcular la característica de interés utilizando los datos de la ventana, desplazar la ventana en uno y luego repetir. En este ejemplo, quiero calcular la característica de interés usando los datos en la ventana, luego cambiar en 60,000 índices y luego repetir.

Comentarios similares para la época. En este caso, puede haber algún desacuerdo sobre la forma correcta de implementar este tipo de ventana, pero en mi opinión, la "mejor" (TM) forma es comenzar desde el tiempo t0, encontrar todos los elementos en el rango (t0-window , t0], calcule la función y muévala según el tamaño del paso. Deseche las ventanas que tengan menos del número mínimo de elementos (puede ser configurable, el valor predeterminado es 1). Ese ejemplo es para una ventana final, pero puede modificar para adaptarse a cualquier configuración de ventana. Esto tiene la desventaja de perder tiempo en espacios grandes, pero los espacios se pueden manejar de manera inteligente e incluso si calcula de manera ingenua (porque es un vago como yo), todavía tengo que ver este asunto en la práctica , ya que las brechas no suelen ser lo suficientemente grandes como para importar en datos reales.

¿Quizás eso sea más claro? Eche un vistazo a mi ejemplo + código anterior, que podría explicarlo mejor.

Gracias por la aclaración @anthonytw. De hecho, parece que necesitaba interpretar step como "paso a punto".

En cuanto a los NaN, entiendo los sentimientos de eliminar los NaN en el resultado de salida automáticamente, pero como se menciona en https://github.com/pandas-dev/pandas/issues/15354#issuecomment -278676420 por @jreback , hay una consideración de coherencia API para que la salida tenga la misma longitud que la entrada. Puede haber un usuario al que también le gustaría conservar los NaN (¿tal vez?), Y dropna aún estaría disponible después de la operación rolling(..., step=...).func() .

@mroeschke Creo que se deben hacer excepciones. Siempre que ponga una nota explícita en la documentación y el comportamiento no sea el predeterminado, nadie se verá afectado negativamente por no devolver un vector lleno de basura. Mantener NaNs frustra la mitad del propósito. Un objetivo es limitar el número de veces que realizamos un cálculo costoso. El otro objetivo es minimizar el conjunto de características a algo manejable. El ejemplo que le di es real, y no tantos datos como los que realmente se tienen que procesar en una aplicación de monitorización de pacientes. ¿Es realmente necesario asignar 60000x el espacio necesario y luego buscar en la matriz para eliminar los NaN? ¿Para cada característica que queremos calcular?

Tenga en cuenta que un cálculo puede producir una matriz de valores. ¿Qué quiero hacer con una forma de onda de ECG? Bueno, calcule el espectro de potencia, ¡por supuesto! Entonces, necesito asignar suficiente espacio para 1 vector PSD completo (150,000 elementos) 1.8 millones de veces (2 TB de datos) y luego filtrar para obtener las piezas que me importan (34 MB). Para toda la serie. Para todos los pacientes. ¡Supongo que necesito comprar más RAM!

También vale la pena mencionar que NaN, para algunas funciones, podría ser un resultado significativo. En cuyo caso, ya no puedo distinguir la diferencia entre un NaN significativo y los NaN basura que rellenan los datos.

Si bien entiendo el deseo de mantener la API, esta no es una característica que rompa ningún código existente (porque es una característica nueva que no existía antes), y dada la funcionalidad, no hay razón para que alguien espere que produzca un salida del mismo tamaño. E incluso si lo hicieran, una nota en la documentación para el tamaño del paso sería suficiente. Las desventajas superan con creces cualquier beneficio de tener una API "consistente" (para una característica que no existía anteriormente, fíjate). No proceder de esta manera paralizará la función, casi ni siquiera vale la pena implementarla en ese caso (en mi experiencia, el costo de espacio es casi siempre el factor más importante).

¿Fue útil esta página
0 / 5 - 0 calificaciones