Thursday 17 August 2017

Filtro De Média Em Movimento Eficiente


Eu essencialmente tenho uma série de valores como este: a matriz acima é simplificada demais, estou coletando 1 valor por milissegundo em meu código real e preciso processar a saída em um algoritmo que escrevi para encontrar o pico mais próximo antes de um ponto no tempo. Minha lógica falha porque no meu exemplo acima, 0.36 é o pico real, mas meu algoritmo olhava para trás e veria o último número 0.25 como o pico, pois há uma diminuição para 0,24 antes dele. O objetivo é levar esses valores e aplicar um algoritmo para eles, que os suavizará um pouco para que eu tenha mais valores lineares. (Ie: Id como os meus resultados serem curvy, não jaggedy) Eu fui dito para aplicar um filtro exponencial de média móvel aos meus valores. Como posso fazer isso. É muito difícil para mim ler equações matemáticas, eu ligo muito melhor com o código. Como faço para processar valores na minha matriz, aplicando um cálculo exponencial da média móvel para os fazer sair 8 de fevereiro 12 às 20:27 Para calcular uma média móvel exponencial. Você precisa manter algum estado ao redor e você precisa de um parâmetro de ajuste. Isso exige uma pequena classe (supondo que você esteja usando o Java 5 ou posterior): Instantiate com o parâmetro de decaimento que você deseja (pode ter uma afinação deve estar entre 0 e 1) e depois use a média () para filtrar. Ao ler uma página sobre alguma recorrência matemática, tudo o que você realmente precisa saber ao transformá-lo em código é que os matemáticos gostam de escrever índices em matrizes e seqüências com subíndices. (Contudo, algumas outras notações, o que não ajuda.) No entanto, o EMA é bastante simples, pois você só precisa se lembrar de um valor antigo, não é necessário nenhum arrays de estados complicados. Respondeu 8 de fevereiro às 20:42 TKKocheran: praticamente. Não é bom quando as coisas podem ser simples (Se começar com uma nova seqüência, obtenha uma nova média). Observe que os primeiros termos na seqüência média saltarão em torno de um bit devido a efeitos de limites, mas você obtém aqueles com outras médias móveis também. No entanto, uma boa vantagem é que você pode envolver a lógica média móvel na média e experimentar sem perturbar demais o seu programa. Ndash Donal Fellows 9 de fevereiro às 0:06 Estou tendo dificuldade em entender suas perguntas, mas vou tentar responder de qualquer maneira. 1) Se o seu algoritmo encontrou 0,25 em vez de 0,36, então está errado. É errado porque assume um aumento ou diminuição monotônico (que sempre está subindo ou sempre está descendo). A menos que você tenha TODOS OS seus dados, seus pontos de dados --- como você os apresenta --- são não-lineares. Se você realmente quer encontrar o valor máximo entre dois pontos no tempo, então corte sua matriz de tmin para tmax e encontre o máximo desse subarray. 2) Agora, o conceito de médias móveis é muito simples: imagine que eu tenho a seguinte lista: 1.4, 1.5, 1.4, 1.5, 1.5. Eu posso suavizá-lo tomando a média de dois números: 1.45, 1.45, 1.45, 1.5. Observe que o primeiro número é a média de 1,5 e 1,4 (segundo e primeiro número), a segunda (nova lista) é a média de 1,4 e 1,5 (terceira e segunda lista antiga) a terceira (nova lista) a média de 1,5 e 1,4 (Quarto e terceiro), e assim por diante. Eu poderia ter feito período três ou quatro, ou n. Observe como os dados são muito mais suaves. Uma boa maneira de ver as médias móveis no trabalho é ir para o Google Finance, selecionar um estoque (tente Tesla Motors bastante volátil (TSLA)) e clique em técnicas na parte inferior do gráfico. Selecione a média móvel com um período determinado e uma média móvel exponencial para comparar suas diferenças. A média móvel exponencial é apenas uma outra elaboração deste, mas considera os dados anteriores menos do que os novos dados, é uma maneira de polarizar o alisamento na parte de trás. Leia a entrada da Wikipedia. Então, isso é mais um comentário do que uma resposta, mas a pequena caixa de comentários foi apenas pequena. Boa sorte. Se você estiver tendo problemas com a matemática, você poderia ir com uma média móvel simples em vez de exponencial. Então, a saída que você obtém seria os últimos x termos divididos por x. Pseudocódigo não testado: note que você precisará lidar com as partes de início e fim dos dados, pois claramente você não pode usar os 5 últimos termos quando estiver no seu segundo ponto de dados. Além disso, existem formas mais eficientes de calcular essa média móvel (soma sumária - a mais nova), mas é para obter o conceito do que está acontecendo. Respondeu 8 de fevereiro às 20:41 Sua resposta 2017 Stack Exchange, IncI recentemente aprendi sobre avanços na resposta a esta postagem. E estava me perguntando como eu poderia usá-los para calcular um filtro médio móvel de forma mais eficiente do que o que eu propus nesta publicação (usando filtros de convolução). Isto é o que eu tenho até agora. Ele leva uma visão da matriz original, depois rola pela quantidade necessária e resume os valores do kernel para calcular a média. Estou ciente de que as bordas não são tratadas corretamente, mas posso cuidar disso depois. Existe uma maneira melhor e mais rápida O objetivo é filtrar grandes arrays de ponto flutuante até 5000x5000 x 16 camadas em tamanho, uma tarefa que scipy. ndimage. filters. convolve é bastante lenta em. Note que estou procurando conectividade de 8 vizinhos, que é um filtro 3x3 que leva a média de 9 pixels (8 em torno do pixel focal) e atribui esse valor ao pixel na nova imagem. EDITAR esclarecimentos sobre como vejo isso funcionando: use stridetricks para gerar uma matriz como 0,1,2,1,2,3,2,3,4. Que corresponde à linha superior do kernel de filtro. Role ao longo do eixo vertical para obter a linha do meio do grão 10,11,12,11,12,13,13,14,15. E adicione-o à matriz que recebi em 1) Repita para obter a linha inferior do kernel 20,21,22,21,22,23,22,23,24. . Neste ponto, tomo a soma de cada linha e dividi-la pelo número de elementos no filtro, me dando a média para cada pixel, (deslocado por 1 linha e 1 col, e com algumas curtições em torno de bordas, mas eu posso Cuide disso mais tarde). O que eu esperava era um melhor uso de stridetricks para obter os 9 valores ou a soma dos elementos do kernel diretamente, para toda a matriz, ou que alguém possa me convencer de outro método mais eficiente. Perguntou 8 de fevereiro às 18:05 Para o que vale a pena, Heres como você o faria usando truques extravagantes. Eu ia publicar isso ontem, mas me distraí com o trabalho real. ) Paul amp comer ambos têm implementações agradáveis ​​usando várias outras maneiras de fazer isso. Apenas para continuar com as coisas da pergunta anterior, imaginei que a Id posteasse o equivalente N-dimensional. Você não conseguirá bater significativamente as funções scipy. ndimage para matrizes 1D. (Scipy. ndimage. uniformfilter deve vencer scipy. ndimage. convolve.) Além disso, se você estiver tentando obter uma janela em movimento multidimensional, você arrisca-se a usar o uso de memória sempre que você inadvertidamente faça uma cópia da sua matriz. Embora a matriz de rolamento inicial seja apenas uma visão da memória de sua matriz original, quaisquer etapas intermediárias que copiem a matriz fará uma cópia que é ordens de magnitude maiores que sua matriz original (ou seja, digamos que você está trabalhando com uma matriz original de 100x100 . A visão nele (para um tamanho de filtro de (3,3)) será 98x98x3x3, mas usará a mesma memória que o original. No entanto, qualquer cópia usará a quantidade de memória que uma matriz completa 98x98x3x3 seria) Basicamente, usando louco Truques de estrondo são ótimos para quando você quer vetualizar operações de janela em movimento em um único eixo de um ndarray. Isso facilita o cálculo de coisas como um desvio padrão móvel, etc, com uma pequena sobrecarga. Quando você quer começar a fazer isso ao longo de múltiplos eixos, é possível, mas você geralmente está melhor com funções mais especializadas. (Por exemplo, scipy. ndimage, etc.) De qualquer forma, como você faz isso: então, o que nós obtemos quando fazemos b rollingwindow (a, filtsize) é uma matriz 8x8x3x3, isso é realmente uma visão da mesma memória que o original 10x10 Matriz. Poderíamos ter tão facilmente usado tamanho de filtro diferente ao longo de diferentes eixos ou operados somente ao longo de eixos selecionados de uma matriz N-dimensional (ou seja, filtsize (0,3,0,3) em uma matriz 4-dimensional nos daria uma visão de 6 dimensões ). Podemos então aplicar uma função arbitrária ao último eixo repetidamente para efetivamente calcular as coisas em uma janela em movimento. No entanto, porque estavam armazenando matrizes temporárias que são muito maiores do que a nossa matriz original em cada passo do meio (ou std ou o que quer que seja), isso não é eficiente em termos de memória. Também não será terrivelmente rápido. O equivalente para ndimage é apenas: isso irá lidar com uma variedade de condições de contorno, faça a desfocagem no local sem requerer uma cópia temporária da matriz e seja muito rápido. Striding tricks é uma boa maneira de aplicar uma função a uma janela em movimento ao longo de um eixo, mas eles não são uma boa maneira de fazê-lo ao longo de múltiplos eixos, geralmente. Apenas meu 0.02, pelo menos. Muito bem posto: truques de correr são uma boa maneira de aplicar uma função a uma janela em movimento ao longo de um eixo, mas eles não são uma boa maneira de fazê-lo ao longo de múltiplos eixos, geralmente. E é claro que sua explicação sobre a memória 39 é importante. Um tipo de resumo da sua resposta (pelo menos para mim) é: 39don39t ir muito longe pescando, a captura quarenteed é allready em scipy39. Obrigado ndash coma 9 de fevereiro às 16:37 Obrigado, Joe, por esta resposta. Em rollingwindow deve o ifat hasattr (.): Retornar rollingwindowlastaxis (.) Em vez de rollingwindow. Ndash unutbu 12 de fevereiro às 16:47 Não estou familiarizado com o Python para escrever código para isso, mas as duas melhores maneiras de acelerar as convoluções são separar o filtro ou usar a transformada de Fourier. Filtro separado. Convolução é O (MN), onde M e N são o número de pixels na imagem e o filtro, respectivamente. Uma vez que a filtragem média com um kernel de 3 por 3 é equivalente a filtrar primeiro com um kernel de 3 por 1 e, em seguida, um kernel de 1 por 3, você pode obter uma melhoria de 33 (33) 30 velocidades por convolução consecutiva com Dois kernels 1-d (obviamente isso fica melhor à medida que o kernel aumenta). Você ainda pode usar truques de passo aqui, é claro. Transformada de Fourier . Conv (A, B) é equivalente a ifft (fft (A) fft (B)). Isto é, uma convolução no espaço direto torna-se uma multiplicação no espaço de Fourier, onde A é sua imagem e B é seu filtro. Uma vez que a multiplicação (do elemento) das transformações de Fourier exige que A e B sejam do mesmo tamanho, B é uma matriz de tamanho (A) com o kernel no centro da imagem e zeros em qualquer outro lugar. Para colocar um kernel 3-por-3 no centro de uma matriz, você pode ter que atacar A para o tamanho ímpar. Dependendo da sua implementação da transformada de Fourier, isso pode ser muito mais rápido do que a convolução (e se você aplicar o mesmo filtro várias vezes, você pode pré-calcular fft (B). Salvar outros 30 de tempo de computação). Respondeu 9 de fevereiro às 15:27 Para o que vale a pena, em python, estas são implementadas em scipy. ndimage. uniformfilter e scipy. signal. fftconvolve. respectivamente. Ndash Joe Kington 9 de fevereiro às 15:44 Jonas: Cool A abordagem do filtro separado funciona bem, como você diz, economiza mais tempo à medida que o tamanho do kernel aumenta. Para uma matriz de 5000x5000, com um tamanho de kernel 11x11, eu estou obtendo 7,7s para convolução 2d usando ndimage. convolve e 2.0s para duas convulsões 1d usando ndimage. convolve1d. Para a sua segunda solução, o que é B ndash Benjamin 9 de fevereiro às 16:02 Uma coisa que eu tenho certeza precisa ser corrigida é a sua matriz de visão b. Tem alguns itens da memória não alocada, então você terá problemas. Dada a sua nova descrição do seu algoritmo, a primeira coisa que precisa ser corrigida é o fato de que você está caminhando para fora da alocação de: Porque eu ainda não estou entendendo o método e parece haver maneiras mais simples de resolver o problema. Para colocar isso aqui: o que parece ser a abordagem direta. A única operação estranha é que ele aloca e povoa B apenas uma vez. Toda a adição, divisão e indexação deve ser feita independentemente. Se você está fazendo 16 bandas, você ainda precisa apenas alocar B uma vez se sua intenção for salvar uma imagem. Mesmo que isso não seja útil, pode esclarecer por que não entendo o problema, ou pelo menos servir como referência para acelerar os outros métodos. Isso é executado em 2.6 seg no meu laptop em uma série de 5k x 5k de float64s, 0,5 da qual é a criação de B respondida em 8 de fevereiro às 19:31. Não é tão clara sua pergunta, mas estou assumindo agora que você gostaria de melhorar Significativamente esse tipo de média. Agora, que tipo de melhorias de desempenho você realmente esperaria Atualização: Antes de tudo, um aviso: o código em seu estado atual não se adapta adequadamente à forma do kernel. No entanto, essa não é a minha principal preocupação agora (de qualquer forma, a idéia é como se adaptar adequadamente). Acabei de escolher a nova forma de um 4D A intuitivamente, para mim, realmente faz sentido pensar em um centro de núcleo 2D a ser centrado em cada posição de grade do original 2D A. Mas essa modelagem 4D pode não ser realmente a melhor. Penso que o problema real aqui é o desempenho da soma. Um deve ser capaz de encontrar a melhor ordem (do 4D A) para utilizar completamente a arquitetura do cache de suas máquinas. No entanto, essa ordem pode não ser a mesma para pequenos arrays que tipo de cooperação com o cache de suas máquinas e aqueles maiores, que não (pelo menos não de maneira direta). Atualização 2: Aqui está uma versão ligeiramente modificada do mf. Claramente, é melhor remodelar para uma matriz 3D primeiro e, em seguida, em vez de somar apenas o produto ponto (isso tem a vantagem de tudo, esse kernel pode ser arbitrário). No entanto, ainda é um pouco mais lento (na minha máquina) do que a função atualizada Pauls. Respondeu 8 de fevereiro às 19:33

No comments:

Post a Comment