2009-03-03 12 views
5

Przeczytałem kilka tutoriali na temat prawidłowego sposobu generowania logarytmicznej dystrybucji wag tagcloud. Większość z nich grupuje znaczniki w etapy. Wydaje mi się to trochę głupie, więc opracowałem własny algorytm na podstawie tego, co przeczytałem, aby dynamicznie dystrybuował liczbę znaczników wzdłuż krzywej logistycznej między progiem a maksimum. Oto esencja w Pythonie:Jakie jest prawidłowe algandm dla logartycznej krzywej rozkładu pomiędzy dwoma punktami?

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 

Zasadniczo bez logarytmicznej obliczania indywidualnego zliczania, to generuje linię prostą między punktami (mincount, minSize) i (MAXCOUNT, maxSize).

Algorytm wykonuje dobre przybliżenie krzywej między dwoma punktami, ale ma jedną wadę. Mincount jest szczególnym przypadkiem, a jego logarytm daje zero. Oznacza to, że rozmiar mincount byłby mniejszy niż min. Próbowałem gotować numery, aby spróbować rozwiązać ten szczególny przypadek, ale nie wydaje się, aby to naprawić. Obecnie traktuję mincount jako specjalny przypadek i dodam "or 1" do wiersza logcount.

Czy istnieje poprawniejszy algorytm do narysowania krzywej między dwoma punktami?

Update Mar 3: Jeśli się nie mylę, przejmuję dziennik liczenia, a następnie podłączam go do równania liniowego. Aby opisać specjalny przypadek innymi słowy, w y = lnx przy x = 1, y = 0. Tak dzieje się w mincount. Ale mincount nie może wynosić zero, tag nie był używany 0 razy.

Wypróbuj kod i podłącz własne numery do przetestowania. Traktowanie mincount jako szczególnego przypadku jest w porządku przeze mnie, mam wrażenie, że byłoby to łatwiejsze niż jakiekolwiek rzeczywiste rozwiązanie tego problemu. Po prostu mam ochotę musi być rozwiązanie tego i że ktoś prawdopodobnie wymyślił rozwiązanie.

UPDATE 06 kwietnia: prosty google wyszukiwania pojawia się wiele tutoriali czytałem, ale this jest prawdopodobnie najbardziej kompletny przykład schodkowych chmury tagów.

UPDATE Apr 28: W odpowiedzi na rozwiązanie antti.huimy: Podczas tworzenia wykresu krzywa tworzona przez algorytm leży poniżej linii między dwoma punktami. Próbowałem żonglować liczbami, ale wciąż nie potrafię wymyślić sposobu na odwrócenie tej krzywej na drugą stronę linii. Zgaduję, że gdyby funkcja została zmieniona na jakąś formę logarytmu zamiast wykładnika, zrobiłaby dokładnie to, czego potrzebowałam. Czy to jest poprawne? Jeśli tak, czy ktoś może wyjaśnić, jak to osiągnąć?

+0

Możesz wspomnieć tutoriale, ja czy linki haz? – akuhn

+0

zgodzili się, bez większego tła trudno jest zorientować się, jaki jest faktyczny problem. – wds

Odpowiedz

2

Dzięki pomocy antti.huimy ponownie przemyślałem, co próbowałem zrobić.

Biorąc jego metodę rozwiązania problemu, chcę równanie, w którym logarytm z mincount jest równa liniowego równania między dwoma punktami.

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

Chociaż daje mi to dobry punkt wyjścia, muszę przejść przez punkt (MAX, max_weight). To będzie potrzebować stałej:

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

Rozwiązanie dla K otrzymujemy:

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

Tak więc, aby umieścić to wszystko z powrotem do jakiegoś kodu Pythona:

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

W skali logarytmicznej po prostu wykreśla się logarytmicznie liczby liniowe (innymi słowy, udawaj, że plotkujesz liniowo, ale weź logi liczb, które mają zostać naniesione jako pierwsze).

Zero problemu nie może być rozwiązany analitycznie - musisz wybrać minimalny rząd wielkości dla swojej skali i bez względu na to, czego nigdy nie osiągniesz zero. Jeśli chcesz wyrysować coś na poziomie zero, twoje wybory będą arbitralnie nadawać mu minimalny rząd wielkości skali lub go pomijać.

+0

Jeśli dobrze cię rozumiem, myślę, że to już robię. Biorę dziennik zliczeń i podłączam go do równania liniowego. Nie jestem pewien, czy rozumiesz specjalny problem z przypadkiem. Nie próbuję znaleźć wartości na poziomie zero, jest to, że wartość na mincount wynosi 0. – dburke

0

Nie mam dokładnej odpowiedzi, ale myślę, że chcesz sprawdzić Linearizing Exponential Data. Zacznij od obliczenia równania linii przechodzącej przez punkty i pobierz log obu stron tego równania.

1

Zacznijmy od mapowania z zalogowanej liczby do rozmiaru.To odwzorowanie liniowe wspomniałeś:

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

gdzie min i max są minimalną i maksymalną rozmiary i = log (MAXCOUNT) -b. Linia ma postać y = mx + c, gdzie x = log (liczba) -b

Na wykresie widać, że gradient m to (maxsize-minsize)/a.

Musimy x = y = 0 w minSize, więc log (mincount) -b = 0 -> b = log (mincount)

To pozostawia nas z następującym pytona:

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 

Jeśli chcesz się upewnić, że minimalna liczba jest zawsze co najmniej 1, należy wymienić pierwszą linię z:

mincount = min(count+[1]) 

który dołącza do listy 1 count przed wykonaniem min. To samo dotyczy upewniając się MAXCOUNT jest zawsze co najmniej 1. Zatem twój ostateczny kod za powyżej jest:

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

co masz jest, że masz tagów których liczy się od MIN do MAX; problem z progiem można tutaj zignorować, ponieważ oznacza to ustawienie każdego zliczeń poniżej progu do wartości progowej i przyjęcie minimum i maksimum dopiero później.

Chcesz zmapować liczbę znaczników do "wag", ale w "sposób logarytmiczny", który zasadniczo oznacza (jak rozumiem) następujące. Po pierwsze, tagi z count MAX uzyskać masę max_weight (w przykładzie 1.75):

weight(MAX) = max_weight 

drugie, znaczniki z hrabią MIN uzyskać masę min_weight (w przykładzie 0.75):

weight(MIN) = min_weight 

Wreszcie stwierdził, że gdy liczba zmniejsza się o 1, waga jest mnożona przez stałą K < 1, co wskazuje na stromość krzywej:

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

rozwiązywania tego, otrzymujemy:

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

Należy zauważyć, że z x = MAX, wykładnik jest zero i mnożna na prawo staje 1.

Teraz mamy dodatkowy wymóg, że waga (MIN) = min_weight i możemy rozwiązać:

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

z którego otrzymujemy

K^(MAX - MIN) = weight_min/weight_max 

i biorąc logarytm obustronnie

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

tj

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

z prawej strony jest negatywny, jak jest to pożądane, ponieważ K < 1.Następnie

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

Więc teraz mają wzór do obliczenia K. Po to właśnie zastosowanie do wszelkich zliczania x pomiędzy MIN i MAX:

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

i gotowe.

+0

Jest to bardzo blisko tego, co chcę. Jedynym problemem jest to, że krzywa znajduje się po niewłaściwej stronie nachylenia liniowego. Zakładasz, że K powinno być mniejsze niż 1. Chciałbym, żeby było nieco większe niż 1. Jak to osiągnąć? – dburke

+0

Ach tak, przepraszam, masz rację --- w ostatnim równaniu, zmień MAX - x na x - MIN, a na poprzedniej zamień ln weight_max i ln weight_min. –

+0

Podczas tworzenia wykresu krzywa tworzona przez algorytm leży poniżej linii między dwoma punktami. Próbowałem żonglować liczbami, ale wciąż nie potrafię wymyślić sposobu na odwrócenie tej krzywej na drugą stronę linii. Zgaduję, że gdyby funkcja została zmieniona na jakąś formę logarytmu zamiast wykładnika, zrobiłaby dokładnie to, czego potrzebowałam. Czy to jest poprawne? – dburke

Powiązane problemy