Qual é a maneira mais simples (e espero que não seja muito lenta) de calcular a mediana com o MySQL? Eu usei AVG(x)
para encontrar a média, mas estou tendo dificuldades para encontrar uma maneira simples de calcular a mediana. Por enquanto, estou retornando todas as linhas para o PHP, fazendo uma classificação e depois escolhendo a linha do meio, mas certamente deve haver uma maneira simples de fazer isso em uma única consulta do MySQL.
Dados de exemplo:
id | val
--------
1 4
2 7
3 2
4 2
5 9
6 8
7 3
Classificar em val
dá 2 2 3 4 7 8 9
, então a mediana deve ser 4
, versus SELECT AVG(val)
qual == 5
.
sql
mysql
statistics
median
davr
fonte
fonte
Respostas:
No MariaDB / MySQL:
Steve Cohen ressalta que, após a primeira passagem, o @rownum conterá o número total de linhas. Isso pode ser usado para determinar a mediana, portanto, não é necessária nenhuma segunda passagem ou junção.
Também
AVG(dd.val)
edd.row_number IN(...)
é usado para produzir corretamente uma mediana quando há um número par de registros. Raciocínio:Finalmente, o MariaDB 10.3.3+ contém uma função MEDIAN
fonte
WHERE 1
paraWHERE d.val IS NOT NULL
para que excluaNULL
linhas para manter esse método alinhado com o nativoAVG
select avg(value) from (select value, row_number from (select a - b as value from a_table join b_table order by value))
Acabei de encontrar outra resposta online nos comentários :
Verifique se suas colunas estão bem indexadas e se o índice é usado para filtragem e classificação. Verifique com os planos de explicação.
Calcule o número da linha "mediana". Talvez usar:
median_row = floor(count / 2)
.Em seguida, escolha-o na lista:
Isso deve retornar uma linha com apenas o valor desejado.
Jacob
fonte
Descobri que a solução aceita não funcionava na minha instalação do MySQL, retornando um conjunto vazio, mas essa consulta funcionou para mim em todas as situações em que a testei:
fonte
data
e está sendo usado com dois nomes,x
ey
.Infelizmente, nem as respostas de TheJacobTaylor nem de velcrow retornam resultados precisos para as versões atuais do MySQL.
A resposta do Velcro acima é aproximada, mas não calcula corretamente os conjuntos de resultados com um número par de linhas. As medianas são definidas como 1) o número do meio em conjuntos de números ímpares ou 2) a média dos dois números do meio em conjuntos de números pares.
Então, aqui está a solução do velcro corrigida para lidar com conjuntos de números pares e ímpares:
Para usar isso, siga estas 3 etapas fáceis:
fonte
Eu proponho uma maneira mais rápida.
Obtenha a contagem de linhas:
SELECT CEIL(COUNT(*)/2) FROM data;
Em seguida, pegue o valor do meio em uma subconsulta classificada:
SELECT max(val) FROM (SELECT val FROM data ORDER BY val limit @middlevalue) x;
Testei isso com um conjunto de dados 5x10e6 de números aleatórios e ele encontrará a mediana em menos de 10 segundos.
fonte
Um comentário nesta página na documentação do MySQL tem a seguinte sugestão:
fonte
Instale e use estas funções estatísticas do mysql: http://www.xarg.org/2012/07/statistical-functions-in-mysql/
Depois disso, calcular a mediana é fácil:
fonte
A maioria das soluções acima funciona apenas para um campo da tabela; talvez seja necessário obter a mediana (percentil 50) de muitos campos na consulta.
Eu uso isso:
Você pode substituir o "50" no exemplo acima por qualquer percentil, é muito eficiente.
Apenas verifique se você tem memória suficiente para o GROUP_CONCAT, você pode alterá-lo com:
Mais detalhes: http://web.performancerasta.com/metrics-tips-calculating-95th-99th-or-any-percentile-with-single-mysql-query/
fonte
Eu tenho este código abaixo que eu encontrei no HackerRank e é bastante simples e funciona em todos os casos.
fonte
Com base na resposta do velcro, para aqueles que precisam fazer uma mediana de algo agrupado por outro parâmetro:
fonte
Você pode usar a função definida pelo usuário encontrada aqui .
fonte
Tome cuidado com uma contagem de valores ímpares - fornece a média dos dois valores no meio nesse caso.
fonte
Meu código, eficiente sem tabelas ou variáveis adicionais:
fonte
GROUP_CONCAT
é limitado a 1023 caracteres, mesmo quando usado em outra função como essa.Opcionalmente, você também pode fazer isso em um procedimento armazenado:
fonte
x IS NOT NULL
deve ser adicionada?CALL median("table","x","x IS NOT NULL")
.Minha solução apresentada abaixo funciona em apenas uma consulta sem criação de tabela, variável ou até subconsulta. Além disso, permite obter mediana para cada grupo em consultas agrupadas (é isso que eu precisava!):
Funciona devido ao uso inteligente de group_concat e substring_index.
Mas, para permitir grande group_concat, você deve definir group_concat_max_len para um valor mais alto (1024 caracteres por padrão). Você pode configurá-lo assim (para a sessão sql atual):
Mais informações sobre group_concat_max_len: https://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_group_concat_max_len
fonte
Outro riff na resposta de Velcrow, mas usa uma única tabela intermediária e aproveita a variável usada para numeração de linhas para obter a contagem, em vez de executar uma consulta extra para calculá-la. Também inicia a contagem, de modo que a primeira linha seja a linha 0, para permitir simplesmente usar Floor e Ceil para selecionar a (s) linha (s) mediana (s).
fonte
O exposto acima parece funcionar para mim.
fonte
{98,102,102,98}
é100
mas seu código fornece102
. Funcionou bem para números ímpares.Eu usei uma abordagem de duas consultas:
Eles são agrupados em uma função defn, para que todos os valores possam ser retornados de uma chamada.
Se seus intervalos forem estáticos e seus dados não forem alterados com frequência, pode ser mais eficiente pré-calcular / armazenar esses valores e usar os valores armazenados em vez de consultar do zero todas as vezes.
fonte
Como eu só precisava de uma solução mediana E percentil, criei uma função simples e bastante flexível com base nas descobertas neste tópico. Sei que me sinto feliz se encontrar funções "prontas" que sejam fáceis de incluir em meus projetos, por isso decidi compartilhar rapidamente:
O uso é muito fácil, exemplo do meu projeto atual:
fonte
Aqui está o meu caminho. Claro, você pode colocá-lo em um procedimento :-)
Você poderia evitar a variável
@median_counter
, se a subestimar:fonte
Desta maneira, parece incluir tanto a contagem par quanto a ímpar sem subconsulta.
fonte
Com base na resposta de @ bob, isso generaliza a consulta para poder retornar várias medianas, agrupadas por alguns critérios.
Pense, por exemplo, no preço médio de venda de carros usados em um lote de carros, agrupado por ano-mês.
fonte
Frequentemente, talvez seja necessário calcular a mediana não apenas para toda a tabela, mas também para agregados com relação ao nosso ID. Em outras palavras, calcule a mediana de cada ID em nossa tabela, onde cada ID possui muitos registros. (bom desempenho e funciona em muitos SQL + corrige o problema de pares e probabilidades, mais sobre o desempenho de diferentes métodos Medianos https://sqlperformance.com/2012/08/t-sql-queries/median )
Espero que ajude
fonte
O MySQL suporta funções de janela desde a versão 8.0, você pode usar
ROW_NUMBER
ouDENSE_RANK
( NÃO useRANK
, pois atribui a mesma classificação aos mesmos valores, como no ranking de esportes):fonte
Se o MySQL tiver ROW_NUMBER, o MEDIAN é (inspire-se nesta consulta do SQL Server):
A entrada é usada caso você tenha um número par de entradas.
Se você deseja encontrar a mediana por grupo, basta PARTITION BY group em suas cláusulas OVER.
Roubar
fonte
ROW_NUMBER OVER
, não há PARTIÇÃO POR, nada disso; este é o MySql, não um mecanismo de banco de dados real como o PostgreSQL, IBM DB2, MS SQL Server e assim por diante ;-).Depois de ler todos os anteriores, eles não correspondiam ao meu requisito real, então eu implementei o meu próprio que não precisa de nenhum procedimento ou instrução complicada, apenas
GROUP_CONCAT
todos os valores da coluna que eu queria obter a MEDIAN e aplicando uma COUNT DIV BY 2 Extraio o valor do meio da lista, como a seguinte consulta:(POS é o nome da coluna em que quero obter sua mediana)
Espero que isso possa ser útil para alguém da maneira que muitos outros comentários foram feitos para mim neste site.
fonte
Conhecendo a contagem exata de linhas, você pode usar esta consulta:
Onde
<half> = ceiling(<size> / 2.0) - 1
fonte
Eu tenho um banco de dados contendo cerca de 1 bilhão de linhas necessárias para determinar a idade média no conjunto. Classificar um bilhão de linhas é difícil, mas se você agregar os valores distintos que podem ser encontrados (as idades variam de 0 a 100), poderá classificar ESTA lista e usar alguma mágica aritmética para encontrar o percentil desejado da seguinte maneira:
Essa consulta depende das funções da janela de suporte do db (incluindo ROWS UNBOUNDED PRECEDING), mas se você não tiver, é simples associar o aggData CTE a si próprio e agregar todos os totais anteriores na coluna 'acumulada', usada para determinar quais O valor contém o precentil especificado. A amostra acima calcula p10, p25, p50 (mediana), p75 e p90.
-Chris
fonte
Retirado de: http://mdb-blog.blogspot.com/2015/06/mysql-find-median-nth-element-without.html
Eu sugeriria outra maneira, sem junção , mas trabalhando com strings
Eu não o verifiquei com tabelas com dados grandes, mas as tabelas pequenas / médias funcionam muito bem.
O bom aqui, que ele funciona também por GROUPING para que você possa retornar a mediana para vários itens.
aqui está o código de teste para a tabela de teste:
e o código para encontrar a mediana de cada grupo:
Resultado:
fonte
Em alguns casos, a mediana é calculada da seguinte forma:
A "mediana" é o valor "intermediário" na lista de números quando eles são ordenados por valor. Para conjuntos de contagens pares, a mediana é a média dos dois valores médios . Eu criei um código simples para isso:
A mediana $ retornada seria o resultado necessário :-)
fonte