Atualmente, estou trabalhando em um algoritmo para implementar um filtro de média móvel (análogo a um filtro de média móvel) em C. De minha pesquisa na literatura, parece haver duas maneiras razoavelmente eficientes de fazer isso. A primeira é ordenar a janela inicial de valores e, em seguida, realizar uma pesquisa binária para inserir o novo valor e remover o existente a cada iteração.
O segundo (de Hardle e Steiger, 1995, JRSS-C, Algorithm 296) constrói uma estrutura de heap de duas extremidades, com um maxheap em uma extremidade, um minheap na outra e a mediana no meio. Isso produz um algoritmo de tempo linear em vez de um que é O (n log n).
Aqui está o meu problema: implementar o primeiro é possível, mas preciso executá-lo em milhões de séries temporais, portanto, a eficiência é muito importante. Este último está se mostrando muito difícil de implementar. Encontrei o código no arquivo Trunmed.c do código para o pacote de estatísticas do R, mas é indecifrável.
Alguém sabe de uma implementação de C bem escrita para o algoritmo de mediana de rolagem de tempo linear?
Editar: link para o código Trunmed.c http://google.com/codesearch/p?hl=en&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c
Respostas:
Eu olhei para R
src/library/stats/src/Trunmed.c
algumas vezes porque também queria algo semelhante em uma classe C ++ / sub-rotina C autônoma. Observe que, na verdade, são duas implementações em uma, consultesrc/library/stats/man/runmed.Rd
(a fonte do arquivo de ajuda) que dizSeria bom ver isso reutilizado de uma forma mais autônoma. Você é voluntário? Posso ajudar com alguns dos R bits.
Edição 1 : Além do link para a versão anterior do Trunmed.c acima, aqui estão as cópias atuais do SVN
Srunmed.c
(para a versão Stuetzle)Trunmed.c
(para a versão Turlach)runmed.R
para a função R chamando estesEdição 2 : Ryan Tibshirani tem algum código C e Fortran em binning mediano rápido que pode ser um ponto de partida adequado para uma abordagem em janela.
fonte
Não consegui encontrar uma implementação moderna de uma estrutura de dados c ++ com estatística de pedidos, então acabei implementando ambas as ideias no link dos principais codificadores sugerido por MAK ( Match Editorial : role para baixo até FloatingMedian).
Dois multisets
A primeira ideia particiona os dados em duas estruturas de dados (heaps, multisets etc) com O (ln N) por inserção / exclusão não permite que o quantil seja alterado dinamicamente sem um grande custo. Ou seja, podemos ter uma média móvel ou 75% móvel, mas não os dois ao mesmo tempo.
Árvore de segmentos
A segunda ideia usa uma árvore de segmento que é O (ln N) para inserções / exclusões / consultas, mas é mais flexível. O melhor de tudo o "N" é o tamanho do intervalo de dados. Portanto, se sua mediana móvel tem uma janela de um milhão de itens, mas seus dados variam de 1..65536, então apenas 16 operações são necessárias por movimento da janela rolante de 1 milhão !!
O código c ++ é semelhante ao que Denis postou acima ("Aqui está um algoritmo simples para dados quantizados")
Árvores de Estatística de Ordem GNU
Antes de desistir, descobri que stdlibc ++ contém árvores de estatísticas de pedidos !!!
Estes têm duas operações críticas:
Veja o manual libstdc ++ policy_based_data_structures_test (procure por "dividir e juntar").
Eu envolvi a árvore para uso em um cabeçalho de conveniência para compiladores que suportam typedefs parciais do estilo c ++ 0x / c ++ 11:
fonte
Fiz uma implementação C aqui . Mais alguns detalhes estão nesta pergunta: Mediana móvel na implementação C - Turlach .
Uso de amostra:
fonte
Eu uso este estimador mediano incremental:
que tem a mesma forma que o estimador médio mais comum:
Aqui, eta é um pequeno parâmetro de taxa de aprendizagem (por exemplo
0.001
), esgn()
é a função signum que retorna um de{-1, 0, 1}
. (Use uma constanteeta
como esta se os dados não forem estacionários e você quiser rastrear as mudanças ao longo do tempo; caso contrário, para fontes estacionárias, use algo comoeta = 1 / n
convergir, onden
está o número de amostras vistas até agora.)Além disso, modifiquei o estimador mediano para fazê-lo funcionar para quantis arbitrários. Em geral, uma função de quantil informa o valor que divide os dados em duas frações:
p
e1 - p
. O seguinte estima esse valor de forma incremental:O valor
p
deve estar dentro de[0, 1]
. Isso essencialmente muda asgn()
saída simétrica da função{-1, 0, 1}
para inclinar para um lado, particionando as amostras de dados em dois compartimentos de tamanhos desiguais (as fraçõesp
e1 - p
os dados são menores que / maiores que a estimativa de quantil, respectivamente). Observe que parap = 0.5
, isso se reduz ao estimador da mediana.fonte
Aqui está um algoritmo simples para dados quantizados (meses depois):
fonte
A mediana móvel pode ser encontrada mantendo duas partições de números.
Para manter partições, use Min Heap e Max Heap.
Max Heap conterá números menores que iguais à mediana.
Min Heap conterá números maiores que iguais à mediana.
Restrição de equilíbrio: se o número total de elementos for par, ambos os montes devem ter elementos iguais.
se o número total de elementos for ímpar, o Heap máximo terá um elemento a mais do que o Heap mínimo.
Elemento mediano: se ambas as partições tiverem o mesmo número de elementos, a mediana será a metade da soma do elemento máximo da primeira partição e do elemento mínimo da segunda partição.
Caso contrário, a mediana será o elemento máximo da primeira partição.
fonte
Talvez valha a pena apontar que existe um caso especial que tem uma solução exata simples: quando todos os valores no fluxo são inteiros dentro de um intervalo definido (relativamente) pequeno. Por exemplo, suponha que todos devem estar entre 0 e 1023. Nesse caso, apenas defina uma matriz de 1024 elementos e uma contagem e apague todos esses valores. Para cada valor no incremento de fluxo, o compartimento correspondente e a contagem. Depois que o fluxo termina, encontre o compartimento que contém o valor mais alto de contagem / 2 - facilmente realizado adicionando recipientes sucessivos a partir de 0. Usando o mesmo método, o valor de uma ordem de classificação arbitrária pode ser encontrado. (Há uma pequena complicação se for necessário detectar a saturação do compartimento e "atualizar" o tamanho dos compartimentos de armazenamento para um tipo maior durante uma execução.)
Este caso especial pode parecer artificial, mas na prática é muito comum. Também pode ser aplicado como uma aproximação para números reais se eles estiverem dentro de um intervalo e um nível "bom o suficiente" de precisão for conhecido. Isso valeria para praticamente qualquer conjunto de medições em um grupo de objetos do "mundo real". Por exemplo, a altura ou o peso de um grupo de pessoas. Não é um conjunto grande o suficiente? Funcionaria igualmente bem para os comprimentos ou pesos de todas as bactérias (individuais) do planeta - supondo que alguém pudesse fornecer os dados!
Parece que eu interpretei mal o original - que parece que ele quer uma mediana de janela deslizante em vez de apenas a mediana de um riacho muito longo. Essa abordagem ainda funciona para isso. Carregue os primeiros N valores de fluxo para a janela inicial e, em seguida, para o N + 1º valor de fluxo, incremente o compartimento correspondente enquanto diminui o compartimento correspondente ao 0º valor de fluxo. É necessário, neste caso, reter os últimos N valores para permitir o decréscimo, o que pode ser feito de forma eficiente endereçando ciclicamente uma matriz de tamanho N. Uma vez que a posição da mediana só pode mudar em -2, -1,0,1 , 2 em cada degrau da janela deslizante, não é necessário somar todos os escaninhos até a mediana de cada degrau, basta ajustar o "ponteiro mediano" dependendo de quais escaninhos laterais foram modificados. Por exemplo, se o novo valor e o que está sendo removido ficarem abaixo da mediana atual, ele não mudará (deslocamento = 0). O método falha quando N se torna muito grande para ser guardado convenientemente na memória.
fonte
Se você tiver a capacidade de referenciar valores como uma função de pontos no tempo, poderá amostrar valores com substituição, aplicando bootstrapping para gerar um valor mediano bootstrapped dentro de intervalos de confiança. Isso pode permitir que você calcule uma mediana aproximada com maior eficiência do que classificar constantemente os valores recebidos em uma estrutura de dados.
fonte
Para quem precisa de um mediano rodando em Java ... PriorityQueue é seu amigo. Inserção de O (log N), mediana de corrente de O (1) e remoção de O (N). Se você conhece a distribuição de seus dados, pode fazer muito melhor do que isso.
fonte
}), higher = new PriorityQueue<Integer>();
ounew PriorityQueue<Integer>(10,
. Não consegui executar o código.Aqui está um que pode ser usado quando a saída exata não é importante (para fins de exibição, etc.). Você precisa de totalcount e lastmedian, mais o novo valor.
Produz resultados bastante exatos para coisas como page_display_time.
Regras: o fluxo de entrada precisa ser regular na ordem do tempo de exibição da página, grande em contagem (> 30 etc) e ter uma mediana diferente de zero.
Exemplo: tempo de carregamento da página, 800 itens, 10ms ... 3000ms, média 90ms, mediana real: 11ms
Após 30 entradas, o erro médio é geralmente <= 20% (9ms..12ms) e fica cada vez menor. Após 800 entradas, o erro é + -2%.
Outro pensador com uma solução semelhante está aqui: Median Filter Implementação supereficiente
fonte
Aqui está a implementação java
fonte
Se você precisar apenas de uma média suavizada, uma maneira rápida / fácil é multiplicar o último valor por xe o valor médio por (1-x) e depois adicioná-los. Isso então se torna a nova média.
editar: Não é o que o usuário pediu e não é estatisticamente válido, mas bom o suficiente para muitos usos.
Vou deixar aqui (apesar dos votos negativos) para pesquisa!
fonte