Qual é o algoritmo correto para uma curva de distribuição logarítmica entre dois pontos?

StackOverflow https://stackoverflow.com/questions/604953

  •  03-07-2019
  •  | 
  •  

Pergunta

Eu li um monte de tutoriais sobre a maneira correta para gerar uma distribuição logarítmica de pesos tagcloud. A maioria deles grupo as tags em etapas. Isto parece um pouco bobo para mim, então eu desenvolvi meu próprio algoritmo baseado no que eu li até que dinamicamente distribui contagem do tag ao longo da curva logarthmic entre o limiar eo máximo. Aqui está a essência do que em python:

from math import log
count = [1, 3, 5, 4, 7, 5, 10, 6]
def logdist(count, threshold=0, maxsize=1.75, minsize=.75):
    countdist = []
    # mincount is either the threshold or the minimum if it's over the threshold
    mincount = threshold<min(count) and min(count) or threshold
    maxcount = max(count)
    spread = maxcount - mincount
    # the slope of the line (rise over run) between (mincount, minsize) and ( maxcount, maxsize)
    delta = (maxsize - minsize) / float(spread)
    for c in count:
        logcount = log(c - (mincount - 1)) * (spread + 1) / log(spread + 1)
        size = delta * logcount - (delta - minsize)
        countdist.append({'count': c, 'size': round(size, 3)})
    return countdist

Basicamente, sem o cálculo logarítmica da contagem individual, ele geraria uma linha reta entre os pontos, (mincount, minsize) e (maxcount, maxsize).

O algoritmo faz uma boa aproximação da curva entre os dois pontos, mas sofre de uma desvantagem. O mincount é um caso especial, e o logaritmo do que produz zero. Isso significa que o tamanho do mincount seria inferior a minsize. Tentei cozinhar até números para tentar resolver este caso especial, mas parece que não consegue acertar. Atualmente eu só tratar a mincount como um caso especial e acrescentar "or 1" para a linha logcount.

Existe um algoritmo mais correta para desenhar uma curva entre os dois pontos?

Atualização de 03 de março : Se não estou enganado, eu estou tomando o log da contagem e, em seguida, colocá-lo em uma equação linear. Para colocar a descrição do caso especial em outras palavras, em y = lnx em x = 1, y = 0. Isto é o que acontece no mincount. Mas o mincount não pode ser zero, a marca não tem sido utilizado 0 vezes.

Tente o código e conectar seus próprios números para teste. Tratar a mincount como um caso especial é bem por mim, tenho a sensação de que seria mais fácil do que qualquer que seja a solução real para esse problema é. Eu apenas sinto que há deve ser uma solução para este e que alguém provavelmente surgiu com uma solução.

Atualização 06 de abril : Um simples google procurar voltas até um muitos dos tutoriais que eu li, mas este é provavelmente o exemplo completo maioria de nuvens de tags em degraus.

Atualização 28 de abril : Em resposta à solução da antti.huima: Quando graficamente, a curva que seu algoritmo cria mentiras abaixo da linha entre os dois pontos. Eu estive tentando conciliar os números ao redor, mas ainda não consigo chegar a uma maneira de inverter essa curva para o outro lado da linha. Eu estou supondo que se a função foi mudado para alguma forma de logaritmo, em vez de um expoente que faria exatamente o que eu preciso. Isso está correto? Se assim for, alguém pode explicar como conseguir isso?

Foi útil?

Solução

Graças à ajuda de antti.huima, eu re-pensado que eu estava tentando fazer.

Tomar seu método de resolver o problema, eu quero uma equação onde o logaritmo da mincount é igual à equação linear entre os dois pontos.

weight(MIN) = ln(MIN-(MIN-1)) + min_weight
min_weight = ln(1) + min_weight

Enquanto isso me dá um bom ponto de partida, eu preciso fazê-lo passar pelo ponto (MAX, max_weight). Vai precisar de uma constante:

weight(x) = ln(x-(MIN-1))/K + min_weight

Resolvendo para K obtemos:

K = ln(MAX-(MIN-1))/(max_weight - min_weight)

Assim, para colocar tudo isso para trás em algum código python:

from math import log
count = [1, 3, 5, 4, 7, 5, 10, 6]
def logdist(count, threshold=0, maxsize=1.75, minsize=.75):
    countdist = []
    # mincount is either the threshold or the minimum if it's over the threshold
    mincount = threshold<min(count) and min(count) or threshold
    maxcount = max(count)
    constant = log(maxcount - (mincount - 1)) / (maxsize - minsize)
    for c in count:
        size = log(c - (mincount - 1)) / constant + minsize
        countdist.append({'count': c, 'size': round(size, 3)})
    return countdist

Outras dicas

Vamos começar com o mapeamento da contagem registrados para o tamanho. Esse é o mapeamento linear você mencionou:

   size
    |
max |_____
    |   /
    |  /|
    | / |
min |/  |
    |   |
   /|   |
0 /_|___|____
    0   a

onde MIN e MAX são o mínimo e máximo tamanhos , e a = log (maxcount) -b. A linha é de y = mx + c em que X = log (contagem) -b

A partir do gráfico, podemos ver que o gradiente, m, é (maxsize-minsize) / a.

Precisamos x = 0 em y = minsize, então log (mincount) -b = 0 -> b = log (mincount)

Isso nos deixa com o seguinte python:

mincount = min(count)
maxcount = max(count)
xoffset = log(mincount)
gradient = (maxsize-minsize)/(log(maxcount)-log(mincount))
for c in count:
    x = log(c)-xoffset
    size = gradient * x + minsize

Se você quiser ter certeza de que a contagem mínima é sempre pelo menos 1, substituir a primeira linha com:

mincount = min(count+[1])

que anexa 1 à lista de contagem antes de fazer o min. O mesmo vale para ter certeza que o maxcount é sempre pelo menos 1. Assim, seu código final per acima é:

from math import log
count = [1, 3, 5, 4, 7, 5, 10, 6]
def logdist(count, maxsize=1.75, minsize=.75):
    countdist = []
    mincount = min(count+[1])
    maxcount = max(count+[1])
    xoffset = log(mincount)
    gradient = (maxsize-minsize)/(log(maxcount)-log(mincount))
    for c in count:
        x = log(c)-xoffset
        size = gradient * x + minsize
        countdist.append({'count': c, 'size': round(size, 3)})
    return countdist

o que você tem é que você tem marcas cujas contagens são de MIN a MAX; a questão limiar pode ser ignorada aqui, porque isso equivale a definição de cada contagem abaixo do limiar para o valor limite e tendo o mínimo eo máximo só depois.

Você deseja mapear as contagens de tag de "pesos", mas de uma "moda logarítmica", que basicamente significa que (como eu o entendo) o seguinte. Em primeiro lugar, as marcas com contagem MAX obter peso max_weight (no seu exemplo, 1,75):

weight(MAX) = max_weight

Em segundo lugar, as etiquetas com a contagem MIN obter peso min_weight (no seu exemplo, 0,75):

weight(MIN) = min_weight

Finalmente, sustenta que, quando a sua contagem diminui em 1, o peso é multiplicado com uma constante K <1, o que indica a inclinação da curva:

weight(x) = weight(x + 1) * K

Resolver este, temos:

weight(x) = weight_max * (K ^ (MAX - x))

Note que, com x = MAX, o expoente é zero e o multiplicando no direito torna-se 1.

Agora, temos a exigência extra que peso (MIN) = min_weight, e podemos resolver:

weight_min = weight_max * (K ^ (MAX - MIN))

a partir do qual obtemos

K ^ (MAX - MIN) = weight_min / weight_max

e tendo logaritmo em ambos os lados

(MAX - MIN) ln K = ln weight_min - ln weight_max

i.

ln K = (ln weight_min - ln weight_max) / (MAX - MIN)

O lado direito é negativa se o desejar, porque K <1. Em seguida,

K = exp((ln weight_min - ln weight_max) / (MAX - MIN))

Então, agora você tem a fórmula para calcular K. Após isso, você simplesmente aplicar para qualquer contagem x entre MIN e MAX:

weight(x) = max_weight * (K ^ (MAX - x))

E está feito.

Em uma escala log, você só traçar o registro dos números de forma linear (em outras palavras, fingir que está tramando linearmente, mas ter o registro dos números a serem plotados primeiro).

O problema de zero não pode ser resolvido analiticamente - você tem que escolher um mínimo de ordem de magnitude para sua escala, e não importa o que você não pode nunca chegar a zero. Se você quiser trama algo em zero, suas escolhas são para dar-lhe arbitrariamente a ordem mínima de magnitude da escala, ou omiti-lo.

Eu não tenho a resposta exata, mas eu acho que você quer olhar para cima linearização exponencial de dados. Comece por calcular a equação da linha passando pelos pontos e tomar o log de ambos os lados dessa equação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top