2009-03-03 15 views
5

He leído un montón de tutoriales sobre la forma correcta de generar una distribución logarítmica de los pesos de la nube de etiquetas. La mayoría de ellos agrupa las etiquetas en pasos. Esto me parece algo tonto, así que desarrollé mi propio algoritmo basado en lo que he leído para que distribuya dinámicamente el conteo de la etiqueta a lo largo de la curva logarítmica entre el umbral y el máximo. Aquí está la esencia de la misma en Python:¿Cuál es el algoritmo correcto para una curva de distribución logarmíntica entre dos puntos?

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 

Básicamente, sin el cálculo logarítmico de la cuenta individual, se generaría una línea recta entre los puntos, (mincount, minsize) y (MaxCount, maxsize).

El algoritmo hace una buena aproximación de la curva entre los dos puntos, pero adolece de un inconveniente. El minuto es un caso especial, y su logaritmo produce cero. Esto significa que el tamaño del mincount sería menor que el mínimo. Intenté cocinar los números para tratar de resolver este caso especial, pero parece que no puedo hacerlo bien. Actualmente solo trato el mincount como un caso especial y agrego "or 1" a la línea de logcount.

¿Hay un algoritmo más correcto para dibujar una curva entre los dos puntos?

Update Mar 3: Si no me equivoco, estoy tomando el registro del conteo y luego lo enchufo en una ecuación lineal. Para poner la descripción del caso especial en otras palabras, en y = lnx en x = 1, y = 0. Esto es lo que sucede en el minuto. Pero el mincount no puede ser cero, la etiqueta no se ha usado 0 veces.

Pruebe el código y conecte sus propios números para probar. Considero que tratar el mincount como caso especial está bien, tengo la sensación de que sería más fácil que cualquiera que sea la solución real a este problema. Siento que hay debe ser una solución a esto y que alguien probablemente ha encontrado una solución.

ACTUALIZACIÓN Abr 6: Una simple búsqueda google convierte un muchos de los tutoriales que he leído, pero this es probablemente el ejemplo más completo de las nubes de etiquetas escalonadas.

ACTUALIZACIÓN abr 28: En respuesta a la solución de antti.huima: Cuando se representa gráficamente, la curva que crea el algoritmo se encuentra por debajo de la línea entre los dos puntos. He estado tratando de hacer malabarismos con los números, pero todavía no se me ocurre una forma de pasar esa curva al otro lado de la línea. Supongo que si la función se cambiara a alguna forma de logaritmo en lugar de a un exponente, haría exactamente lo que necesitaría. ¿Es eso correcto? Si es así, ¿alguien puede explicar cómo lograr esto?

+0

Usted menciona tutoriales, yo ¿Pueden los enlaces de haz? – akuhn

+0

de acuerdo, sin más antecedentes es bastante difícil entender cuál es el problema real. – wds

Respuesta

2

Gracias a la ayuda de antti.huima, repensé lo que estaba tratando de hacer.

Tomando su método para resolver el problema, quiero una ecuación donde el logaritmo del mincount es igual a la ecuación lineal entre los dos puntos.

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

Si bien esto me da un buen punto de partida, necesito hacerlo pasar por el punto (MAX, max_weight). Se va a necesitar una constante:

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

Despejando K obtenemos:

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

lo tanto, para poner todo esto de nuevo en algún 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 
0

En una escala de registro, solo traza el registro de los números linealmente (en otras palabras, pretender que está trazando linealmente, pero tome el registro de los números que se trazarán primero).

El problema cero no puede resolverse analíticamente; debe elegir un orden de magnitud mínimo para su báscula, y no importa lo que nunca llegue a cero. Si quiere trazar algo a cero, sus opciones son darle arbitrariamente el orden mínimo de magnitud de la escala u omitirlo.

+0

Si te entiendo correctamente, creo que eso es lo que ya estoy haciendo. Estoy tomando el registro de los conteos y enchufándolo en la ecuación lineal. No estoy seguro de que esté entendiendo el problema del caso especial. No estoy tratando de encontrar el valor en cero, es que el valor en el mincount es 0. – dburke

0

No tengo la respuesta exacta, pero creo que quieres buscar Linealización de datos exponenciales. Comience por calcular la ecuación de la línea que pasa por los puntos y tome el registro de ambos lados de esa ecuación.

1

Comencemos con su mapeo desde el conteo registrado hasta el tamaño.Esa es la aplicación lineal que usted ha mencionado:

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

donde min y max son los valores mínimo y máximo tamaños, y una -b = log (MaxCount). La línea es de y = mx + c donde x = log (recuento) -b

En el gráfico, podemos ver que el gradiente, m, es (maxsize-minsize)/a.

Necesitamos x = 0 en y = minsize, por lo que log (mincount) -b = 0 -> b = log (mincount)

Esto nos deja con la siguiente pitón:

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 

Si desea asegurarse de que el conteo mínimo es siempre al menos 1, reemplace la primera línea con:

mincount = min(count+[1]) 

que añade 1 a la lista de recuento antes de hacer el min. Lo mismo vale para asegurarse de que el MaxCount siempre es al menos 1. Por lo tanto el código de final por arriba es:

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 
1

lo que tiene es que usted tiene etiquetas cuyos recuentos son del mínimo al máximo; el problema del umbral se puede ignorar aquí porque equivale a configurar cada conteo por debajo del umbral hasta el valor umbral y tomar el mínimo y el máximo solo después.

Desea asignar los recuentos de etiquetas a "pesos" pero de forma "logarítmica", lo que básicamente significa (según entiendo) lo siguiente. En primer lugar, las etiquetas con recuento MAX consiguen peso max_weight (en su ejemplo, 1,75):

weight(MAX) = max_weight 

En segundo lugar, las etiquetas con el recuento MIN consiguen peso min_weight (en su ejemplo, 0,75):

weight(MIN) = min_weight 

por último, se cumple que cuando su recuento disminuye en 1, el peso se multiplica por una constante K < 1, que indica la pendiente de la curva:

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

Resolviendo esto, obtenemos:

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

Tenga en cuenta que con x = MAX, el exponente es cero y el multiplicando a la derecha se convierte en 1.

Ahora tenemos el requisito adicional de que el peso (MIN) = min_weight, y podemos resolver:

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

de la que obtenemos

K^(MAX - MIN) = weight_min/weight_max 

y tomando logaritmos en ambos lados

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

es decir

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

El lado derecho es negativo como se desee, porque K < 1.Entonces

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

Así que ahora tiene la fórmula para calcular K. Después de esto sólo se aplican para cualquier recuento x entre MIN y MAX:

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

y ya está.

+0

Esto está muy cerca de lo que quiero. El único problema es que la curva está en el lado equivocado de la pendiente lineal. Usted asume que K debería ser menor que 1. Me gustaría que fuera un poco mayor que 1. ¿Cómo se puede lograr esto? – dburke

+0

Ah sí, lo siento, tienes razón --- en la última ecuación, cambia MAX - x a x - MIN y en el intercambio anterior ln weight_max y ln weight_min. –

+0

Cuando se grafica, la curva que crea su algoritmo se encuentra debajo de la línea entre los dos puntos. He estado tratando de hacer malabarismos con los números, pero todavía no se me ocurre una forma de pasar esa curva al otro lado de la línea. Supongo que si la función se cambiara a alguna forma de logaritmo en lugar de a un exponente, haría exactamente lo que necesitaría. ¿Es eso correcto? – dburke

Cuestiones relacionadas