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.
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.
Have a look at http://docs.python.org/library/functions.html#sorted.
(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
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
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
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!