Existe uma função SciPy ou a função ou módulo NumPy para Python que calcula a média de execução de uma matriz 1D, dada uma janela específica?
python
numpy
scipy
moving-average
Shejo284
fonte
fonte
UPD: soluções mais eficientes foram propostas por Alleo e jasaarim .
Você pode usar
np.convolve
para isso:Explicação
A média corrente é um caso da operação matemática da convolução . Para a média em execução, você desliza uma janela ao longo da entrada e calcula a média do conteúdo da janela. Para sinais 1D discretos, convolução é a mesma coisa, exceto que, em vez da média, você calcula uma combinação linear arbitrária, ou seja, multiplica cada elemento por um coeficiente correspondente e soma os resultados. Esses coeficientes, um para cada posição na janela, às vezes são chamados de kernel de convolução . Agora, a média aritmética de N valores é
(x_1 + x_2 + ... + x_N) / N
, então o kernel correspondente é(1/N, 1/N, ..., 1/N)
, e é exatamente isso que obtemos usandonp.ones((N,))/N
.Arestas
O
mode
argumento denp.convolve
especifica como lidar com as arestas. Eu escolhi ovalid
modo aqui porque acho que é assim que a maioria das pessoas espera que a corrida funcione, mas você pode ter outras prioridades. Aqui está um gráfico que ilustra a diferença entre os modos:fonte
numpy.cumsum
tem uma complexidade melhor.Solução eficiente
A convolução é muito melhor do que a abordagem direta, mas (eu acho) ela usa FFT e, portanto, bastante lenta. No entanto, especialmente para calcular a corrida, a seguinte abordagem funciona bem
O código para verificar
Note-se que
numpy.allclose(result1, result2)
éTrue
, dois métodos são equivalentes. Quanto maior o N, maior a diferença no tempo.aviso: embora o cumsum seja mais rápido, haverá um aumento no erro de ponto flutuante que pode fazer com que seus resultados sejam inválidos / incorretos / inaceitáveis
os comentários apontaram esse problema de erro de ponto flutuante aqui, mas estou tornando mais óbvio aqui na resposta. .
np.longdouble
mas seu erro de ponto flutuante ainda será significativo para um número relativamente grande de pontos (em torno de> 1e5, mas depende dos seus dados)fonte
numpy.convolve
O (mn); seus documentos mencionam quescipy.signal.fftconvolve
usa FFT.running_mean([1,2,3], 2)
givesarray([1, 2])
. Substituirx
por[float(value) for value in x]
faz o truque.x
contiver flutuadores. Exemplo:running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
retorna0.003125
enquanto se espera0.0
. Mais informações: en.wikipedia.org/wiki/Loss_of_significanceAtualização: O exemplo abaixo mostra a
pandas.rolling_mean
função antiga que foi removida nas versões recentes do pandas. Um equivalente moderno da chamada de função abaixo seriapandas é mais adequado para isso do que o NumPy ou SciPy. Sua função rolling_mean faz o trabalho de maneira conveniente. Ele também retorna uma matriz NumPy quando a entrada é uma matriz.
É difícil superar o
rolling_mean
desempenho com qualquer implementação Python pura e personalizada. Aqui está um exemplo de desempenho em relação a duas das soluções propostas:Também existem boas opções de como lidar com os valores das arestas.
fonte
df.rolling(windowsize).mean()
agora funciona (em vez disso, devo acrescentar muito rapidamente). para 6.000 séries de linhas%timeit test1.rolling(20).mean()
retornou 1000 loops, o melhor de 3: 1,16 ms por loopdf.rolling()
funciona bem o suficiente, o problema é que mesmo esse formulário não suportará ndarrays no futuro. Para usá-lo, teremos que carregar nossos dados em um Dataframe do Pandas primeiro. Eu adoraria ver essa função adicionada a umnumpy
ou a outroscipy.signal
.%timeit bottleneck.move_mean(x, N)
é de 3 a 15 vezes mais rápido que os métodos cumsum e pandas no meu pc. Dê uma olhada em sua referência no README do repo .Você pode calcular uma média de execução com:
Mas é lento.
Felizmente, o numpy inclui uma função convolve que podemos usar para acelerar as coisas. A média de execução é equivalente a convolver
x
com um vetor que éN
longo, com todos os membros iguais a1/N
. A implementação numpy do convolve inclui o transiente inicial, portanto, você deve remover os primeiros pontos N-1:Na minha máquina, a versão rápida é 20 a 30 vezes mais rápida, dependendo do comprimento do vetor de entrada e do tamanho da janela de média.
Observe que o convolve inclui um
'same'
modo que parece que deveria resolver o problema transitório inicial, mas o divide entre o começo e o fim.fonte
mode='valid'
emconvolve
que não requer qualquer pós-processamento.mode='valid'
remove o transitório de ambas as extremidades, certo? Selen(x)=10
eN=4
, para uma corrida significa que eu gostaria 10 resultados, masvalid
retorna 7.modes = ('full', 'same', 'valid'); [plot(convolve(ones((200,)), ones((50,))/50, mode=m)) for m in modes]; axis([-10, 251, -.1, 1.1]); legend(modes, loc='lower center')
(com pyplot e numpy importados).runningMean
Tenho o efeito colateral da média com zeros, quando você sai da matriz comx[ctr:(ctr+N)]
o lado direito da matriz.runningMeanFast
também tem esse problema de efeito de fronteira.nos meus testes no Tradewave.net, o TA-lib sempre vence:
resultados:
fonte
NameError: name 'info' is not defined
. Estou recebendo esse erro, senhor.Para uma solução pronta para uso, consulte https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Ele fornece média de execução com o
flat
tipo de janela. Observe que isso é um pouco mais sofisticado do que o método simples de convolução do tipo faça você mesmo, pois ele tenta lidar com os problemas no início e no final dos dados refletindo-os (o que pode ou não funcionar no seu caso). ..)Para começar, você pode tentar:
fonte
numpy.convolve
, a diferença apenas na alteração da sequência.w
o tamanho da janela es
os dados?Você pode usar scipy.ndimage.filters.uniform_filter1d :
uniform_filter1d
:'reflect'
está o padrão, mas no meu caso, eu queria'nearest'
Também é bastante rápido (quase 50 vezes mais rápido
np.convolve
e 2-5 vezes mais rápido que a abordagem de cumsum fornecida acima ):aqui estão três funções que permitem comparar erros / velocidades de diferentes implementações:
fonte
uniform_filter1d
,np.convolve
com um retângulo, enp.cumsum
seguido pornp.subtract
. meus resultados: (1.) convolve é o mais lento. (2.) cumsum / subtrair é cerca de 20 a 30 vezes mais rápido. (3.) uniform_filter1d é cerca de 2-3 vezes mais rápido que cumsum / subtrair. vencedor é definitivamente uniform_filter1d.uniform_filter1d
é mais rápido que acumsum
solução (em cerca de 2-5x). euniform_filter1d
não obtém erro maciço de ponto flutuante como acumsum
solução.Sei que essa é uma pergunta antiga, mas aqui está uma solução que não usa nenhuma estrutura ou biblioteca de dados extra. É linear no número de elementos da lista de entrada e não consigo pensar em outra maneira de torná-la mais eficiente (na verdade, se alguém souber uma maneira melhor de alocar o resultado, informe-me).
NOTA: isso seria muito mais rápido usando uma matriz numpy em vez de uma lista, mas eu queria eliminar todas as dependências. Também seria possível melhorar o desempenho através da execução multithread
A função assume que a lista de entrada é unidimensional, portanto, tenha cuidado.
Exemplo
Suponha que temos uma lista
data = [ 1, 2, 3, 4, 5, 6 ]
na qual queremos calcular uma média móvel com período de 3 e que você também deseja uma lista de saída com o mesmo tamanho da entrada (na maioria das vezes).O primeiro elemento possui o índice 0, portanto a média móvel deve ser calculada nos elementos do índice -2, -1 e 0. Obviamente, não temos dados [-2] e dados [-1] (a menos que você queira usar métodos especiais). condições de contorno), então assumimos que esses elementos são 0. Isso equivale a preencher com zero a lista, exceto que na verdade não a preenchemos, basta acompanhar os índices que exigem preenchimento (de 0 a N-1).
Assim, para os primeiros N elementos, continuamos somando os elementos em um acumulador.
A partir dos elementos N + 1, a acumulação simples não funciona. esperamos,
result[3] = (2 + 3 + 4)/3 = 3
mas isso é diferente de(sum + 4)/3 = 3.333
.A maneira de calcular o valor correto é subtrair
data[0] = 1
desum+4
, dando assimsum + 4 - 1 = 9
.Isso acontece porque atualmente
sum = data[0] + data[1] + data[2]
, mas também é verdade para todosi >= N
porque, antes da subtração,sum
édata[i-N] + ... + data[i-2] + data[i-1]
.fonte
Eu sinto que isso pode ser resolvido de forma elegante usando gargalo
Veja a amostra básica abaixo:
"mm" é a média móvel de "a".
"window" é o número máximo de entradas a serem consideradas para a média móvel.
"min_count" é o número mínimo de entradas a serem consideradas para a média móvel (por exemplo, para os primeiros elementos ou se a matriz possui valores nan).
A parte boa é que o gargalo ajuda a lidar com os valores nan e também é muito eficiente.
fonte
Ainda não verifiquei o quão rápido isso é, mas você pode tentar:
fonte
Esta resposta contém soluções usando a biblioteca padrão do Python para três cenários diferentes.
Média de execução com
itertools.accumulate
Esta é uma solução Python 3.2+ com eficiência de memória que calcula a média de execução em uma quantidade iterável de valores ao aproveitar
itertools.accumulate
.Observe que
values
pode ser iterável, incluindo geradores ou qualquer outro objeto que produza valores em tempo real.Primeiro, construa preguiçosamente a soma cumulativa dos valores.
Em seguida,
enumerate
a soma cumulativa (começando em 1) e construa um gerador que produz a fração dos valores acumulados e o índice de enumeração atual.Você pode emitir
means = list(rolling_avg)
se precisar de todos os valores na memória de uma vez ou ligar de formanext
incremental.(Obviamente, você também pode iterar
rolling_avg
com umfor
loop, que será chamadonext
implicitamente.)Esta solução pode ser escrita como uma função da seguinte maneira.
Uma corrotina para a qual você pode enviar valores a qualquer momento
Essa rotina consome os valores que você envia e mantém uma média contínua dos valores vistos até o momento.
É útil quando você não possui valores iteráveis, mas adquire os valores a serem calculados em média um a um em momentos diferentes ao longo da vida do programa.
A corotina funciona assim:
Computando a média em uma janela deslizante de tamanho
N
Essa função de gerador pega um iterável e um tamanho de janela
N
e gera a média sobre os valores atuais dentro da janela. Ele usa adeque
, que é uma estrutura de dados semelhante a uma lista, mas otimizada para modificações rápidas (pop
,append
) nos dois pontos de extremidade .Aqui está a função em ação:
fonte
Um pouco atrasado para a festa, mas criei minha própria função que NÃO envolve as extremidades ou os zeros com zeros que são usados para encontrar a média também. Como um tratamento adicional, é que ele também faz nova amostragem do sinal em pontos espaçados linearmente. Personalize o código à vontade para obter outros recursos.
O método é uma multiplicação simples de matriz com um kernel Gaussiano normalizado.
Um uso simples em um sinal sinusoidal com ruído distribuído normal adicionado:
fonte
sum
, usando emnp.sum
vez disso 2 O@
operador (não faz ideia do que é isso) gera um erro. Eu posso olhar para ele mais tarde, mas eu estou faltando o tempo agora@
é o operador de multiplicação de matrizes que implementa np.matmul . Verifique se suay_in
matriz é uma matriz numpy, esse pode ser o problema.Em vez de entorpecido ou covarde, eu recomendaria que os pandas fizessem isso mais rapidamente:
Isso leva a média móvel (MA) de 3 períodos da coluna "dados". Você também pode calcular as versões deslocadas, por exemplo, a que exclui a célula atual (deslocada uma para trás) pode ser calculada facilmente como:
fonte
pandas.rolling_mean
enquanto a mina usapandas.DataFrame.rolling
. Você também pode calcular o movimento,min(), max(), sum()
etc., bem comomean()
com esse método facilmente.pandas.rolling_min, pandas.rolling_max
etc. Eles são semelhantes, mas diferentes.Há um comentário de mab enterrado em uma das respostas acima, que possui esse método.
bottleneck
temmove_mean
uma média móvel simples:min_count
é um parâmetro útil que basicamente levará a média móvel até esse ponto em sua matriz. Se você não definirmin_count
, ele irá igualarwindow
, e tudo atéwindow
pontos seránan
.fonte
Outra abordagem para encontrar a média móvel sem usar o numpy, o panda
imprimirá [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]
fonte
Agora, essa pergunta é ainda mais antiga do que quando o NeXuS escreveu sobre isso no mês passado, mas eu gosto de como o código dele lida com casos extremos. No entanto, por ser uma "média móvel simples", seus resultados ficam aquém dos dados aos quais se aplicam. Pensei que lidar com casos extremos de uma forma mais satisfatória do que os modos de Numpy
valid
,same
efull
poderia ser alcançado através da aplicação de uma abordagem similar a umconvolution()
método baseado.Minha contribuição usa uma média de execução central para alinhar seus resultados com seus dados. Quando há muito poucos pontos disponíveis para a janela de tamanho completo a ser usada, as médias de execução são calculadas a partir de janelas sucessivamente menores nas bordas da matriz. [Na verdade, a partir de janelas sucessivamente maiores, mas esse é um detalhe da implementação.]
É relativamente lento porque usa
convolve()
, e provavelmente poderia ser bastante estimulado por um verdadeiro Pythonista, no entanto, acredito que a idéia se mantém.fonte
Há muitas respostas acima sobre o cálculo de uma média corrente. Minha resposta adiciona dois recursos extras:
Esse segundo recurso é particularmente útil para determinar quais valores diferem da tendência geral em um determinado valor.
Uso numpy.cumsum, pois é o método mais econômico em termos de tempo ( consulte a resposta de Alleo acima ).
Esse código funciona apenas para Ns. Pode ser ajustado para números ímpares, alterando o np.insert de padded_x e n_nan.
Exemplo de saída (bruto em preto, movavg em azul):
Esse código pode ser facilmente adaptado para remover todos os valores médios móveis calculados com menos que cutoff = 3 valores não nan.
fonte
Use apenas a biblioteca padrão do Python (memória eficiente)
Apenas dê outra versão do uso
deque
apenas da biblioteca padrão . É uma surpresa para mim que a maioria das respostas esteja usandopandas
ounumpy
.Na verdade, eu encontrei outra implementação em documentos python
No entanto, a implementação me parece um pouco mais complexa do que deveria ser. Mas deve estar nos documentos python padrão por um motivo: alguém poderia comentar sobre a implementação do meu e do documento padrão?
fonte
O(n*d)
cálculos (d
sendo o tamanho da janela,n
tamanho do iterable) e eles estão fazendoO(n)
Com as variáveis do @ Aikude, escrevi uma linha.
fonte
Embora existam soluções para esta pergunta aqui, dê uma olhada na minha solução. É muito simples e está funcionando bem.
fonte
Ao ler as outras respostas, acho que não é isso que a pergunta pediu, mas cheguei aqui com a necessidade de manter uma média constante de uma lista de valores que cresciam em tamanho.
Portanto, se você quiser manter uma lista dos valores que está adquirindo de algum lugar (um site, um dispositivo de medição etc.) e a média dos últimos
n
valores atualizados, use o código abaixo, para minimizar o esforço de adicionar novos elementos:E você pode testá-lo com, por exemplo:
Que dá:
fonte
Outra solução usando apenas uma biblioteca padrão e deque:
fonte
Para fins educacionais, deixe-me adicionar mais duas soluções Numpy (que são mais lentas que a solução cumsum):
Funções usadas: as_strided , add.reduceat
fonte
Todas as soluções acima mencionadas são pobres porque não possuem
numpy.cumsum
ouO(len(x) * w)
implementações como convoluções.Dado
Note que
x_[:w].sum()
é igualx[:w-1].sum()
. Portanto, para a primeira média, asnumpy.cumsum(...)
adiçõesx[w] / w
(viax_[w+1] / w
) e subtrações0
(dex_[0] / w
). Isto resulta emx[0:w].mean()
Por meio do cumsum, você atualizará a segunda média adicionando
x[w+1] / w
e subtraindo adicionalmentex[0] / w
, resultando emx[1:w+1].mean()
.Isso continua até que
x[-w:].mean()
seja alcançado.Esta solução é vetorizada
O(m)
, legível e numericamente estável.fonte
Que tal um filtro de média móvel ? É também uma linha e tem a vantagem de poder manipular facilmente o tipo de janela se precisar de algo além do retângulo, por exemplo. uma média móvel simples N-longa de uma matriz a:
E com a janela triangular aplicada:
Nota: Normalmente, descarto as primeiras N amostras como falsas, portanto,
[N:]
no final, mas não é necessário e é apenas uma questão de escolha pessoal.fonte
Se você optar por criar o seu próprio, em vez de usar uma biblioteca existente, esteja ciente do erro de ponto flutuante e tente minimizar seus efeitos:
Se todos os seus valores forem aproximadamente da mesma ordem de magnitude, isso ajudará a preservar a precisão, sempre adicionando valores de magnitudes aproximadamente semelhantes.
fonte