Weight checkbutton in SearchBrowser

25 views
Skip to first unread message

Dave

unread,
Feb 14, 2010, 11:30:03 PM2/14/10
to Quod Libet Development
I'm trying to understand how the weigth checkbutton works in the
search brwoser, but am coming up empty.

quodlibet/browsers/search.py contains a limit function trying to
perform 2 functions - limit the search results (works great) and apply
a ratingbased weight (can't determine if it works or not). The
relevant code (line 127) in search.py looks like this:

if self.__weight.get_active():
def choose(r1, r2):
if r1 or r2: return cmp(random.random(), r1/
(r1+r2))
else: return random.randint(-1, 1)
def rating(song):
return song.get("~#rating", 0.5)
songs.sort(cmp=choose, key=rating)
else: random.shuffle(songs)
return songs[:limit]

Not a python programmer, but I don't see how r1 or r2 would ever get
set. The resulting weight appears to be always random. Looked for a
test case that covered this, but did not find one.

Thanks in advance.

lazka

unread,
Feb 15, 2010, 10:20:28 AM2/15/10
to Quod Libet Development

On Feb 15, 5:30 am, Dave <davepari...@gmail.com> wrote:
> Not a python programmer, but I don't see how r1 or r2 would ever get
> set.  The resulting weight appears to be always random.  Looked for a
> test case that covered this, but did not find one.

http://pastie.org/825561

Have a look at http://docs.python.org/library/functions.html#sorted.

Josh Lee

unread,
Feb 15, 2010, 10:57:18 AM2/15/10
to quod-libet-...@googlegroups.com
On Sun, Feb 14, 2010 at 23:30, Dave <davep...@gmail.com> wrote:
> I'm trying to understand how the weigth checkbutton works in the
> search brwoser, but am coming up empty.

(I am the author of these lines.)

The basic idea is to compare songs based on the number of stars: If
one has 2 and the other has 3, then we should pick the first over the
second 2 times out of 5. Expressing this as a comparison function and
passing it to the sort function gives the desired result. This
comparison function works the same way as the "weighted shuffle" mode
(even though it's not really obvious).

To be honest, I doubt the sort function was designed to be used in
this manner — [HowTo/Sorting][1] in the Python wiki says "During the
course of the sort the relationships must stay the same for the final
list to make sense." Presumably, it won't compare every song with
every other song, and I didn't do the probability, but it appears to
work in practice. :)

[1]: http://wiki.python.org/moin/HowTo/Sorting

> quodlibet/browsers/search.py contains a limit function trying to
> perform 2 functions - limit the search results (works great) and apply
> a ratingbased weight (can't determine if it works or not).  The
> relevant code (line 127) in search.py looks like this:

Some comments on these lines:

A 0-star song will always lose the comparison. If both songs have a
rating of 0, then they just get compared randomly.

Back to the example of "2 stars vs 3 stars", we'll have r1=0.5,
r2=0.75, so r1/(r1+r2) = 0.4, which is the same as the 2/5 we need, so
the comparison function works.

>            if self.__weight.get_active():
>                def choose(r1, r2):
>                    if r1 or r2: return cmp(random.random(), r1/
> (r1+r2))
>                    else: return random.randint(-1, 1)
>                def rating(song):
>                    return song.get("~#rating", 0.5)
>                songs.sort(cmp=choose, key=rating)
>            else: random.shuffle(songs)
>            return songs[:limit]

> Not a python programmer, but I don't see how r1 or r2 would ever get
> set.  The resulting weight appears to be always random.  Looked for a
> test case that covered this, but did not find one.

The list.sort function passes the two keys to the comparison function,
so r1 and r2 are the "~#rating" of the two songs currently being
compared.

Josh Lee

eric casteleijn

unread,
Feb 16, 2010, 4:54:53 PM2/16/10
to quod-libet-...@googlegroups.com
> To be honest, I doubt the sort function was designed to be used in
> this manner — [HowTo/Sorting][1] in the Python wiki says "During the
> course of the sort the relationships must stay the same for the final
> list to make sense." Presumably, it won't compare every song with
> every other song, and I didn't do the probability, but it appears to
> work in practice. :)

I think this is a cool hack, but still a hack, which, depending on how
sort is implemented, may lead to errors, or other unpredictable
behaviors. It violates this principle that if a > b, and b > c then a >
c, since there is a small chance that c actually sorts before a. I could
think of theoretical cases where a sort would never finish, for instance.

What I think would be better is to go through the list of songs, and
generate some weighted random values for each item, and then sort on
those values, and forget them. Of course that doesn't fit neatly in a
comparison function that you can pass into sort.

eric

Steven Robertson

unread,
Feb 16, 2010, 6:18:11 PM2/16/10
to quod-libet-...@googlegroups.com
On Tue, Feb 16, 2010 at 4:54 PM, eric casteleijn <this...@gmail.com> wrote:
> What I think would be better is to go through the list of songs, and
> generate some weighted random values for each item, and then sort on those
> values, and forget them. Of course that doesn't fit neatly in a comparison
> function that you can pass into sort.

I'm fond of tournament selection for weighted random sort. See
plugins/events/randomalbum.py for a full implementation with multiple
keys, but the basic idea is

songs = random.sample(library.values(), M)
return sorted(songs, key=lambda s: s('~#rating', 0.5))[:N]

where N is the number of songs desired and M [N<=M<=len(library)]
controls the weight. If M==N, the sample is entirely random, and if
M==len(library), the sample is entirely deterministic. It has the
advantage of being very fast (not that it matters in this case) and
over a few rounds becomes essentially indistinguishable from a full
weighted random sort.

Hastily,
Steven

Dave

unread,
Feb 16, 2010, 8:01:34 PM2/16/10
to Quod Libet Development
Thanks everyone for the great feedback. So for purposes of explaining
this to a user, it sounds as if this checkbox a) works and b) is
effectively a "play higher rated songs more often" feature.

I ran lazka's example a bit and it's clear that songs with rating <
0.5 are returned, but they tend towards getting sorted to the end of
the list. Tried hacking Steven's code in, to that example, but
lazka's 'Song' class is not 'callable'. A lesson for another day!

Reply all
Reply to author
Forward
0 new messages