I've got a list of items. Users can occasionally select them. Now I want to order the items by their popularity. What's a good weighting function for that?
Constraints:
The weight should be in [0,1)
Recursive calculation is prefered (not required)
Newer events must have more influence than old ones.
I'd favor approved functions. As I've developped something like this once and it worked not as expected.
Now I want to order the items by their popularity.
So you order by the number of times some user selected the item.
The weight should be in [0,1).
Fine, divide by the total number of times some user selected some item plus one.
Recursive calculation is prefered
Why? Maybe I'm missing the point of what you're trying to do because otherwise this constraint is lost on me.
Edit:
Responding to your edit, try
sum ( 1 / age of vote ) / age of item
the sum being taken over all votes for a given item.
If you have a counter of votes per item, you can use a 'fading constant' in order to make older votes "fade away" with time. Something like:
Nvotes(i) = IsClicked(i) + Nvotes(i) * Kfade
where: 0 < Kfade < 1
Thus, whenever a new click is intercepted, all counters are advanced where only the selected item is incremented by 1.
EDIT: Since the total is less than 1, you may want to normalize Nvotes by the total number of clicks so far.
Keep a list items of the items that need sorting. Let each item have a score. Keep a list clicks of the N most recent clicks, in order of decreasing recency. Each item can appear more than once in the list. Choose a constant fade a little smaller than 1. Then do:
for item in items:
item.score = 0.0
bonus = 1.0
for item in clicks:
item.score += bonus
bonus *= fade
Now sort the items by score, highest first.
The score isn't in the range 0 to 1, but i don't see why you actually need that. It would be straightforward to normalise the scores afterwards.
This isn't recursive, but it's straightforward to put in recursive form.
This isn't a known algorithm. I don't know of any known algorithms for this except move-to-front, which is almost certainly more aggressive than you want.
Related
I need to keep track of and display how many times a price point is being used, e.g.: "$15 (2 options)" Given that i need to display the price and the count and increment the count each time a new option is added at the given price, should I go with:
or this:
Note: do not pay attention to the field names "array" and "object-priceCount", this is for testing purposes only.
I'd actually keep a map for these counters, so that you can update an individual counter with:
updateDoc(docRef, { 'priceCounts.USD15': increment(1));
We all know microscope from discovery meteor. The app is fine, it operates only with the number of upvotes. In the Best page it sorts posts by upvotes in descending order, upvote number is stored for each post and gets updated each time some user upvotes a post.
Imagine now we want to implement something like hacker news have - not only a click-based rating, but also a time-based rating. Lets now define that I will use word 'click' to describe an user action of clicking on post in the post list. This 'click' increases total number of clicks of this post by 1.
For thouse who do not knowe how hacker news algorithm work I will briefly explain. In common the total number of clicks of certain link (post) is divided by:
(T+2)^g
where T - total number of hours passed since post publishing time and now, and g is a "sensitivity" thing, lets call it that, which is just a number, 1.6, or 1.8, doesn't matter. This decrease influence of clicks as the time goes by. You can read more info (http://amix.dk/blog/post/19574)[here], for example.
Now, we want to have top-50 click&time-rated posts, so we need to query mongo to find all posts, sorted by score, calculated with formula from above.
I can see two major approaches to do so, and I find all of them quite bad.
First one, (the way I do now) subscribe to all posts, in template hepler prepare data for rendering by
rankedPosts: function() {
rawPosts = posts.find().map( function(item) { item.score = clicks/(T+2)^g; } ); // to add score for each post
rawPosts = _.sortBy( rawPosts, function(item) { return item.score*(-1); }) // to sort them by calculated score
rawPosts = _.first( rawPosts, 50 ); // to get only first 50
}
and then use rankedPosts for rendering. The bottleneck here is that each time I have to run through all posts.
Second one - somehow (I do not know how, or if it even possible) to subscribe for already scored/sorted/filtered collection, assuming meteor/mongodb can apply their magic to score/sort/filter (and recalculate score each new hour or new click) for me.
Now, obvious question, what will you recommend?
Thanks in advance.
Think about numbers. In a working page, you can have thousands of mosts, millions if the page is successful. Fetching all of them just to find the top 50 doesn't make sense.
I'd recommend storing the final calculated rating in a field. Then in subscription you apply sort by that field and desired limit. When post gain a new click, you simply recalculate the value and save it to db. Finally, in a cron job or meteor interval you update the rating of all items in the database.
I have to point out that I'm fairly new to reporting outside of Microsoft Access, and new to the site, so please bear with me!
Stripped down to essential items, my data object has:
CategoryID, ParentCategoryID, TransactionID, TransactionDate, SplitID, CurrencyID and Value.
I don't think this is relevant, but just in case -
A Split has a Category and a Value, with one to many belonging to a Transaction.
Multiple Splits may exist for the same Category & Transaction with
different, or the same, Value (to support different combinations of the other data
items I haven't listed).
A Transaction has a TransactionDate and a CurrencyID, so all Splits
belonging to a Transaction are for the same Currency.
A Category belongs to a Category recursively.
A Split may be assigned a Category at any level in the recursive hierarchy and the crux of my problem is to report Transaction / Split detail under the appropriate Category heading, with a sub-total to include all those details AND the totals of all child Categories.
So, I have a Detail row group holding all the ancilliary data items that aren't relevant and a TransactionIDGroup row group on the same row. I then have a CategoryGroup row group based on CategoryID with a Parent of ParentCategoryID to handle the recursive nature of the data and a CurrencyIDGroup column group to handle the possible multiple currencies involved.
Also in the CategoryIDGroup row group is a total row with the Value cell holding an expression.
If I leave that expression as =Sum(Fields!AccountValue.Value), the report quite nicely totals the Value for each Currency column for all the details specifically in each Category (the default scope), so I thought I needed to make the Sum 'Recursive'. However, you don't seem able to specify the optional Recursive parameter without specifying the scope as well.
If I specify scope as CategoryIDGroup, I get all zero sub-totals. If I use CurrencyIDGroup I get each one being the same report total for the Currency. Anything else either gets me a build error or a combined-currency report total.
The other issue I have is that the recursive child Category groups are reported sequentially underneath the parent Category group (so, outside the header row, detail rows and total row, and not within the group. However, if I can get the total to reflect the children as well as the details at that level, I'd be happy enough, even though it wouldn't seem to add up until you realised what was going on.
What I have in mind is something like:
Category A
Transaction 1 10/02/2011 ...................... £100.00
---------------------- £14.50
Transaction 2 18/03/2011 ...................... $159.34
Category Ai
Transaction 3 18/06/2011 ---------------------- £295.60
Total Category Ai £295.60
Total Category A £410.10 $159.34*
But what I get is this:
Category A
Transaction 1 10/02/2011 ...................... £100.00
---------------------- £14.50
Transaction 2 18/03/2011 ...................... $159.34
Total Category A £114.50 $159.34*
Category Ai
Transaction 3 18/06/2011 ---------------------- £295.60
Total Category Ai £295.60
I guess the fundamental question is - am I asking the impossible? Do I need to take a different approach, perhaps with sub-reports for the details? I've wondered about including a Sum of the values of the child Categories within the data object at each Category level, but is there something simple I'm missing?
Any pointers in the right direction would be greatly appreciated after several days tearing my hair out :)
I have no idea whether there was something simple I missed, but resolved the issue to my satisfaction by including another property in the data object, being the sum of all child categories for each currency, and including a new row to print the sum of that field. Just in case someone else hits just the same question!
I'm trying to do a formula in Infopath:
Say i have 3 fields called singles, 10's and 25's.
I also have another field where the user typed in a number.
If the user types in 15 for example, this should fill in the 10's filed with 1, and the singles field with 5.
If the user typed 49 into the field, then the 25's would be filled with 1, the 10's with 2, and the singles with 4.
If they type in 50, then the 25's field will be filled with 2.
Does anyone have any idea where I could start in doing this?
Many thanks in advance.
Jason
Here is the basics of the solution (which was quickly verified in IP). I leave the math specifics up to you just in case this is a HW problem. If you have specific troubles post back and we can help.
Set a rule on your input field with no conditions so it will fire anytime the field changes.
Use the number function on a field to be able to use it in calculations. Infopath doesn't have a mod function so do divide with floor and subtraction.
Create an action for each amount (making sure to order them by highest first).
So for example your 25s field action would be floor(number(inputfield) / 25).
Your 10s action would be floor(number(inputfield) - number(25sfield) * 25) / 10).
Etc for each one (and note that the ones field is just whatever is leftover - no divide or floor needed).
I've got a gridview with a list of categories. In the database (MSSQL2008), the category table has a SortOrder(INT) NULL field.
Currently categories are retrieved using:
Galleries.DataSource = From G In DB.GalleryCategories Order By G.SortOrder, G.Name
Now, what I need to be able to do is add "Move Up" and "Move Down" buttons to each row to allow the user to sort the items in an arbitrary way.
My initial thoughts are along the lines of:
Identify ID of selected item.
Identify ID of item before/after selected item.
Swap of identified items in the DB SortOrders.
I would then have make the sortorder NOT NULL and make sure it's initialised to a unique number
I'd appreciate any alternative suggestions / comments on this approach
Many thanks
I have generally seen it done this way, and have done it myself
SortOrder is an int
Each item increases by 10 (so, 10,20,30,40) or suitable increment
To move an item up, subtract 15
To move an item down, add 15
To insert an item, take the target and add/subtract 1
Apply a NormalizeSort() routine which resets the values to even intervals
10,20,25,30,40 => 10,20,30,40,50
That makes it all pretty simple, since inserting something above something else is just:
list.Add( New Item(..., target.SortOrder +1) )
list.NormalizeSort()
// or
item.SortOrder += 11, etc
If you want to make it a decimal, then you can just make it all sequential and just add .1, etc to the sort order and re-normalize again.
// Andrew
I believe
Galleries.AllowSorting = true;
would be far enough ;)