Pandas: Janela rolante com tamanho do passo

Criado em 9 fev. 2017  ·  38Comentários  ·  Fonte: pandas-dev/pandas

Apenas uma sugestão - estenda rolling para suportar uma janela rolante com um tamanho de passo, como R's rollapply(by=X) .

Amostra de Código

Pandas - solução ineficiente (aplique a função a todas as janelas e, em seguida, divida para obter resultados a cada segundo)

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

Sugestão:

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

Comentários muito úteis

"isso poderia ser feito, mas eu gostaria de ver um caso de uso em que isso seja importante."

Qualquer que seja o projeto em que trabalhei usando o pandas, quase sempre senti falta desse recurso, é útil sempre que você precisa calcular o aplicativo apenas de vez em quando, mas ainda precisa de uma boa resolução dentro de cada janela.

Todos 38 comentários

Se você estiver usando funções 'padrão', elas são vetorizadas e, portanto, v rápido ( ts.rolling(5).max().dropna()[::2] ).

IIUC a economia aqui viria de aplicar a função apenas uma fração do tempo (por exemplo, cada n-ésimo valor). Mas há algum caso em que isso faça uma diferença prática?

isso poderia ser feito, mas eu gostaria de ver um caso de uso em que isso seja importante. Isso também interromperia a API de 'retorno do mesmo tamanho da entrada'. Embora eu não ache que isso seja realmente difícil de implementar (embora envolva uma série de mudanças na implementação). Usamos janelas marginais (IOW, calcule a janela e conforme você avança, diminua os pontos que estão saindo e acrescente os pontos que está ganhando). Portanto, ainda teria que computar tudo, mas você simplesmente não iria imprimi-lo.

Obrigado por suas respostas!

IIUC a economia aqui viria de aplicar a função apenas uma fração do tempo (por exemplo, cada n-ésimo valor). Mas há algum caso em que isso faça uma diferença prática?

Meu caso de uso está executando funções de agregação (não apenas máx.) Em alguns grandes dataframes de série temporal - 400 colunas, horas de dados a 5-25 Hz. Eu também fiz algo semelhante (engenharia de recursos em dados de sensor) no passado com dados de até 20kHz. Executar janelas de 30 segundos com uma etapa de 5 segundos economiza uma grande parte do processamento - por exemplo, em 25 Hz com uma etapa de 5s é 1/125 do trabalho, o que faz a diferença entre executar em 1 minuto ou 2 horas.

Obviamente, posso voltar ao numpy, mas seria bom se houvesse uma API de nível superior para fazer isso. Achei que valia a pena a sugestão caso outros também a considerassem útil - não espero que você crie um recurso só para mim!

você pode tentar resamplimg para um intervalo de frequência mais alto primeiro, em seguida, rolar

algo como

df = df.resample ('30s')
df.rolling (..). max () (ou qualquer função)

Olá @jreback , obrigado pela sugestão.

Isso funcionaria se eu estivesse apenas executando max em meus dados (a reamostragem precisa de uma função de redução, caso contrário, o padrão é mean , certo?):

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

No entanto, gostaria de executar minha função de redução em 30 segundos de dados, avançar 1 segundo e executá-la nos próximos 30 segundos de dados, etc. O método acima aplica uma função em 1 segundo de dados e depois outro função em 30 resultados da primeira função.

Aqui está um exemplo rápido - executar um cálculo de pico a pico não funciona executando duas vezes (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 da sua descrição original, acho algo como

df.resample('5s').max().rolling('30s').mean() (ou qualquer redução) está mais alinhado com o que você deseja

IOW, pegue o que quer que esteja em uma caixa de 5 segundos, reduza-o a um único ponto e role sobre essas caixas. Essa ideia geral é que você tem muitos dados que podem ser resumidos em uma escala de tempo curta, mas na verdade você quer que isso aconteça em um nível superior.

Ei @jreback , eu realmente quero executar uma função em 30 segundos de dados, a cada 5 segundos. Veja a função rolling_with_step em meu exemplo anterior. A etapa adicional de max / mean não funciona para meu caso de uso.

@jreback , há uma necessidade real da função step que ainda não foi mencionada nesta discussão. Concordo com tudo o que

Suponha que estejamos fazendo uma análise de série temporal com dados de entrada amostrados em aproximadamente 3 a 10 milissegundos. Estamos interessados ​​em recursos de domínio de frequência. O primeiro passo para construí-los seria descobrir a frequência de Nyquist. Suponha que, por conhecimento de domínio, saibamos que é 10 Hz (uma vez a cada 100 ms). Isso significa que precisamos que os dados tenham uma frequência de pelo menos 20 Hz (uma vez a cada 50 ms), se os recursos devem capturar bem o sinal de entrada. Não podemos reamostrar para uma frequência inferior. Em última análise, aqui estão os cálculos que fazemos:

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

Aqui, escolhemos um tamanho de janela em múltiplos de 8, e escolher 32 faz com que o tamanho da janela seja de 1,6 segundos. A função agregada retorna os coeficientes de domínio de frequência de um lado e sem o primeiro componente médio (a função fft é simétrica e com valor médio no 0º elemento). A seguir está a função de agregação de amostra:

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_

Agora, gostaríamos de repetir isso em uma janela deslizante de, digamos, a cada 0,4 segundos ou a cada 0,8 segundos. Não há nenhum ponto em desperdiçar cálculos e calcular FFT a cada 50 ms em vez disso e, em seguida, fatiar mais tarde. Além disso, reamostrar para 400 ms não é uma opção, porque 400 ms é apenas 2,5 Hz, que é muito mais baixo do que a frequência de Nyquist, e isso resultará na perda de todas as informações dos recursos.

Tratava-se de recursos de domínio de frequência, que têm aplicações em muitos experimentos científicos relacionados a séries temporais. No entanto, funções de agregação de domínio de tempo ainda mais simples, como desvio padrão, não podem ser suportadas de forma eficaz por reamostragem.

Embora eu não ache que isso seja realmente difícil de implementar (embora envolva uma série de mudanças na implementação). Usamos janelas marginais (IOW, calcule a janela e conforme você avança, diminua os pontos que estão saindo e adicione os pontos que está ganhando). Portanto, ainda teria que computar tudo, mas você simplesmente não iria imprimi-lo.

Ter o parâmetro 'step' e ser capaz de reduzir os cálculos reais usando-o deve ser o objetivo futuro do Pandas. Se o parâmetro step retornar apenas menos pontos, não vale a pena fazer, porque podemos dividir a saída de qualquer maneira. Talvez devido ao trabalho envolvido em fazer isso, podemos apenas recomendar todos os projetos com essas necessidades para usar o Numpy.

@Murmuria, você está convidado a enviar uma solicitação pull para fazer isso. Na verdade, não é tão difícil.

Enquanto eu apoio a solicitação de um parâmetro step em rolling() , gostaria de salientar que é possível obter o resultado desejado com o parâmetro base em resample() , se o tamanho do passo for uma fração inteira do tamanho da janela . Usando o exemplo 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:')

Obtemos o mesmo resultado (observe que a linha se estende por 30 segundos em ambos os lados):
rolling_with_step_using_resample

Isso ainda é um desperdício, dependendo do tipo de agregação. Para o caso particular de cálculo pico a pico, como no exemplo de @alexlouden , p2p_arr() é quase 200x mais rápido porque reorganiza a série em uma matriz 2-D e, em seguida, usa uma única chamada para max() e min() .

O parâmetro de etapa na rolagem também permitiria o uso desse recurso sem um índice de data e hora. Alguém já está trabalhando nisso?

@alexlouden acima disse o seguinte:

Obviamente, posso voltar ao numpy, mas seria bom se houvesse uma API de nível superior para fazer isso.

@Alexlouden ou qualquer outra pessoa que saiba, por favor, compartilhe algumas dicas sobre como fazer isso com o numpy? Pela minha pesquisa até agora, parece que não é trivial fazer isso também em estado de choque. Na verdade, há um problema em aberto sobre isso aqui https://github.com/numpy/numpy/issues/7753

Obrigado

Olá @tsando - a função rolling_with_step que usei acima não funcionou para você?

@alexlouden obrigado, acabei de verificar essa função e parece ainda depender do pandas (pega uma série como entrada e também usa o índice da série). Eu queria saber se há uma abordagem puramente entorpecente nisso. No tópico que mencionei https://github.com/numpy/numpy/issues/7753, eles propõem uma função que usa passos numpy, mas são difíceis de entender e traduzir para entradas de janela e etapa.

@tsando Aqui está um PDF do post do blog ao qual fiz link acima - parece que o autor mudou seu nome de usuário no Github e não colocou o site novamente. (Acabei de executá-lo localmente para convertê-lo em PDF).

Minha função acima foi apenas converter seu último exemplo para trabalhar com Pandas - se você quiser usar o numpy diretamente, pode fazer algo assim: https://gist.github.com/alexlouden/e42f1d96982f7f005e62ebb737dcd987

Espero que isto ajude!

@alexlouden obrigado! Eu apenas tentei em uma matriz de forma (13, 1313) mas me deu este erro:

image

"isso poderia ser feito, mas eu gostaria de ver um caso de uso em que isso seja importante."

Qualquer que seja o projeto em que trabalhei usando o pandas, quase sempre senti falta desse recurso, é útil sempre que você precisa calcular o aplicativo apenas de vez em quando, mas ainda precisa de uma boa resolução dentro de cada janela.

Eu concordo e apoio esse recurso também

Necessário quase sempre ao lidar com séries temporais, o recurso poderia fornecer um controle muito melhor para gerar recursos de séries temporais para visualização e análise. Apoie fortemente esta ideia!

concorde e apoie este recurso também

Isso seria muito útil para reduzir o tempo de computação ainda mantendo uma boa resolução de janela.

Eu forneço códigos de solução, que podem ser ajustados de acordo com seu objetivo específico.

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)

Eu concordo e apoio esse recurso. Vejo que está em stop motion agora.

Calcular e reduzir a resolução não é uma opção quando você tem TBs de dados.

Seria muito útil no que eu faço também. Tenho TB de dados em que preciso de várias estatísticas de janelas não sobrepostas para entender as condições locais. Minha "correção" atual é apenas criar um gerador que divide os frames de dados e as estatísticas de rendimento. Seria muito útil ter esse recurso.

Esse recurso é realmente obrigatório quando há séries temporais envolvidas!

Concordo, certamente preciso adicionar esse recurso. Estou tentando fazer correlações de janela em execução entre os preços das ações e tenho que criar minha própria função para isso

Não posso acreditar que um recurso tão básico não existe ainda!
Quando esse problema será resolvido?
Obrigado

Para contribuir para uma 'discussão adicional':
Meu caso de uso é calcular um valor mínimo / máximo / mediano por hora para um mês de dados com uma resolução de 1 segundo. São dados de uso de energia e há picos de 1 a 2 segundos que eu perderia com a reamostragem. Fora isso, a reamostragem para, por exemplo, 5 segundos / 1 minuto não mudaria o fato de que ainda tenho que calcular 4k / 1k janelas por dia que precisam ser descartadas, em vez de apenas ser capaz de calcular as 24 janelas necessárias por dia .

Seria possível contornar isso usando groupby aso, mas isso não parece ser intuitivo nem tão rápido quanto a implementação contínua (2 segundos para janelas de 2,5 milhões de horas com classificação). É impressionantemente rápido e útil, mas realmente precisamos de um argumento avançado para utilizar totalmente seu poder.

Eu dei uma olhada no problema. Isso é relativamente trivial, no entanto, a maneira como o código é implementado, de uma olhada superficial, acho que vai exigir que alguém trabalhe na edição manual de todas as rotinas de rolagem. Nenhum deles respeita os limites da janela fornecidos pelas classes do indexador. Se o fizessem, tanto este pedido quanto o # 11704 seriam facilmente resolvidos. Em todo caso, acho que é administrável para quem quer passar algum tempo enfeitando as coisas. Eu iniciei um PR incompleto (esperado para ser rejeitado, apenas por um MVP) para demonstrar como eu enfrentaria o problema.

Correndo:

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))

rendimentos

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 obter detalhes de implementação, dê uma olhada no PR (ou aqui: https://github.com/anthonytw/pandas/tree/rolling-window-step)

Embora eu tivesse gostado de gastar mais tempo para finalizá-lo, infelizmente não tenho mais nenhum para lidar com o trabalho árduo de retrabalhar todas as funções de rolagem. Minha recomendação para quem deseja resolver isso seria impor os limites da janela gerados pelas classes do indexador e unificar as funções _ * _ fixas / variáveis ​​contínuas. Com limites de início e fim, não vejo nenhuma razão para que sejam diferentes, a menos que você tenha uma função que faça algo especial com dados amostrados não uniformemente (nesse caso, essa função específica seria mais capaz de lidar com a nuance, então talvez definir uma bandeira ou algo assim).

Isso também funcionará para uma janela personalizada usando a abordagem get_window_bounds() ?

Olá, também apoio a sugestão, por favor. Este seria um recurso realmente útil.

Se você estiver usando funções 'padrão', elas são vetorizadas e, portanto, v rápido ( ts.rolling(5).max().dropna()[::2] ).

IIUC a economia aqui viria de aplicar a função apenas uma fração do tempo (por exemplo, cada n-ésimo valor). Mas há algum caso em que isso faça uma diferença prática?

Eu tenho um exemplo assim aqui: https://stackoverflow.com/questions/63729190/pandas-resample-daily-data-to-annual-data-with-overlap-and-offset

Cada enésima seria a cada 365. O tamanho da janela é variável ao longo da vida do programa e a etapa não é garantida como uma fração inteira do tamanho da janela.

Eu basicamente preciso de um tamanho de janela definido que classifique "# de dias no ano que está olhando", o que é impossível com todas as soluções que encontrei para esse problema até agora.

Eu também tenho uma necessidade semelhante com o seguinte contexto (adaptado de uma necessidade real e profissional):

  • Eu tenho um dataframe cronológico com uma coluna de carimbo de data / hora e uma coluna de valor, que representa eventos irregulares. Como a data e hora de quando um cachorro passou por baixo da minha janela e quantos segundos ela levou para passar. Posso ter 6 eventos em um determinado dia e nenhum evento nos próximos 2 dias
  • Gostaria de calcular uma métrica (digamos, o tempo médio gasto por cães na frente da minha janela) com uma janela rolante de 365 dias, que rolaria a cada 30 dias

Pelo que entendi, a API dataframe.rolling () me permite especificar a duração de 365 dias, mas não a necessidade de pular 30 dias de valores (que é um número não constante de linhas) para calcular a próxima média sobre outra seleção de 365 dias de valores.

Obviamente, o dataframe resultante, espero, terá um número (muito) menor de linhas do que o dataframe inicial de 'eventos do cão'.

Só para ter mais clareza sobre esse pedido com um exemplo simples.

Se tivermos esta série:

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

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

e temos um tamanho de janela de 2 e um tamanho de passo de 1 . Esta primeira janela no índice 0 seria avaliada, passe por cima da janela no índice 1 , avalie a janela no í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

Da mesma forma, se tivermos esta série baseada no tempo

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

e temos um tamanho de janela de '3D' e um tamanho de passo de '3D' . Este seria o resultado correto?

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 para o primeiro exemplo ([3]), os resultados não são o que eu esperava. Presumo que esta seja uma janela final (por exemplo, no índice = 0 seria o máximo de elementos em -1 e 0, então apenas max ([0]), então deveria avançar índice "1", para índice = 0 + step = 1, e o próximo cálculo seria max ([0,1]), então max ([1,2]), etc. O que parecia que você pretendia ter era um tamanho de etapa de dois, então você mova do índice = 0 para o índice = 0 + 2 = 2 (pulando o índice 1) e continuando assim. Neste caso, é quase correto, mas não deve haver NaNs. Embora possa ser "apenas" o dobro do tamanho neste caso, em outros casos, é substancial. Por exemplo, eu tenho cerca de uma hora de dados de ECG de 500 Hz para um paciente, isso são 1,8 milhões de amostras. Se eu quisesse uma média móvel de 5 minutos a cada dois minutos, isso seria uma matriz de 1,8 milhões de elementos com 30 cálculos válidos e um pouco menos de 1,8 milhões de NaNs. :-)

Para indexação, step size = 1 é o comportamento atual, ou seja, calcule o recurso de interesse usando dados na janela, mude a janela em um e repita. Neste exemplo, quero calcular o recurso de interesse usando os dados da janela, deslocar 60.000 índices e repetir.

Observações semelhantes para a época. Neste caso, pode haver alguma discordância quanto à maneira correta de implementar este tipo de janela, mas na minha opinião a "melhor" (TM) maneira é começar no tempo t0, encontrar todos os elementos no intervalo (t0-window , t0], calcule o recurso e, em seguida, mova pelo tamanho do passo. Jogue fora todas as janelas que tenham menos do que o número mínimo de elementos (pode ser configurável, o padrão é 1). Esse exemplo é para uma janela à direita, mas você pode modificar para caber em qualquer configuração de janela. Isso tem a desvantagem de perder tempo em grandes lacunas, mas as lacunas podem ser tratadas de forma inteligente e mesmo se você calcular de maneira ingênua (porque você é preguiçoso como eu). Ainda estou para ver esse assunto na prática , uma vez que as lacunas geralmente não são grandes o suficiente para ter importância em dados reais.

Talvez isso seja mais claro? Dê uma olhada no meu exemplo + código acima, que pode explicar melhor.

Obrigado pelo esclarecimento @anthonytw. Na verdade, parece que eu precisava interpretar step como "passo a ponto".

Quanto aos NaNs, entendo o sentimento de descartar os NaNs no resultado de saída automaticamente, mas conforme mencionado em https://github.com/pandas-dev/pandas/issues/15354#issuecomment -278676420 por @jreback , há uma consideração de consistência da API para que a saída tenha o mesmo comprimento que a entrada. Pode haver um usuário que gostaria de manter os NaNs também (talvez?), E dropna ainda estaria disponível após a operação rolling(..., step=...).func() .

@mroeschke Acho que devem ser feitas exceções. Contanto que você coloque uma nota explícita na documentação e o comportamento não seja o padrão, ninguém será afetado negativamente por não retornar um vetor cheio de lixo. Manter os NaNs acaba com metade do propósito. Um objetivo é limitar o número de vezes que executamos um cálculo caro. O outro objetivo é minimizar o conjunto de recursos para algo gerenciável. O exemplo que dei a você é real, e não quase tantos dados quanto alguém realmente precisa processar em um aplicativo de monitoramento de pacientes. É realmente necessário alocar 60000x o espaço necessário e, em seguida, pesquisar na matriz para excluir os NaNs? Para cada recurso que queremos calcular?

Observe que um cálculo pode produzir uma matriz de valores. O que eu quero fazer com uma forma de onda de ECG? Bem, calcule o espectro de potência, é claro! Portanto, preciso alocar espaço suficiente para 1 vetor PSD completo (150.000 elementos) 1,8 milhão de vezes (2 TB de dados) e filtrar para obter as partes que me interessam (34 MB). Para todas as séries. Para todos os pacientes. Acho que preciso comprar mais RAM!

Também vale a pena mencionar que NaN, para alguns recursos, pode ser uma saída significativa. Nesse caso, não consigo mais distinguir entre um NaN significativo e os NaNs inúteis que preenchem os dados.

Embora eu compreenda o desejo de manter a API, este não é um recurso que quebrará qualquer código existente (porque é um novo recurso que não existia antes), e dada a funcionalidade, não há razão para alguém esperar que produza um saída do mesmo tamanho. E mesmo que o fizessem, uma nota na documentação para o tamanho do passo seria suficiente. As desvantagens superam em muito qualquer benefício de ter uma API "consistente" (para um recurso que não existia anteriormente, veja bem). Não proceder dessa forma irá paralisar o recurso, quase nem vale a pena implementá-lo nesse caso (em minha experiência, o custo de espaço é quase sempre o fator maior).

Esta página foi útil?
0 / 5 - 0 avaliações