<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Weighted Slope One in Haskell: collaborative filtering in 29 lines of code</title>
	<atom:link href="http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/</link>
	<description>Bryan O&#039;Sullivan&#039;s blog</description>
	<lastBuildDate>Wed, 08 Feb 2012 06:41:38 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
	<item>
		<title>By: Functional Programming and Collective Intelligence - IV - Matthew Podwysocki's Blog</title>
		<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/comment-page-1/#comment-222578</link>
		<dc:creator>Functional Programming and Collective Intelligence - IV - Matthew Podwysocki's Blog</dc:creator>
		<pubDate>Sat, 11 Apr 2009 05:19:14 +0000</pubDate>
		<guid isPermaLink="false">http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/#comment-222578</guid>
		<description>[...] this implementation, Iâ€™m going to tackle the Weighted Slope One.&#160; Using the Haskell implementation by Bryan Oâ€™Sullivan as my source of inspiration.&#160; I find that going between Haskell and F# to be [...]</description>
		<content:encoded><![CDATA[<p>[...] this implementation, Iâ€™m going to tackle the Weighted Slope One.&nbsp; Using the Haskell implementation by Bryan Oâ€™Sullivan as my source of inspiration.&nbsp; I find that going between Haskell and F# to be [...]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Functional Programming and Collective Intelligence - IV - Matthew Podwysocki - CodeBetter.Com - Stuff you need to Code Better!</title>
		<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/comment-page-1/#comment-222577</link>
		<dc:creator>Functional Programming and Collective Intelligence - IV - Matthew Podwysocki - CodeBetter.Com - Stuff you need to Code Better!</dc:creator>
		<pubDate>Sat, 11 Apr 2009 05:11:45 +0000</pubDate>
		<guid isPermaLink="false">http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/#comment-222577</guid>
		<description>[...] this implementation, Iâ€™m going to tackle the Weighted Slope One.&#160; Using the Haskell implementation by Bryan Oâ€™Sullivan as my source of inspiration.&#160; I find that going between Haskell and F# to be [...]</description>
		<content:encoded><![CDATA[<p>[...] this implementation, Iâ€™m going to tackle the Weighted Slope One.&#160; Using the Haskell implementation by Bryan Oâ€™Sullivan as my source of inspiration.&#160; I find that going between Haskell and F# to be [...]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Chris K</title>
		<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/comment-page-1/#comment-75596</link>
		<dc:creator>Chris K</dc:creator>
		<pubDate>Tue, 28 Aug 2007 19:35:23 +0000</pubDate>
		<guid isPermaLink="false">http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/#comment-75596</guid>
		<description>And before I run for dinner I changed the code on the wiki so that update2 produces an un-normalized SlopeOne&#039; matrix and changed my predict&#039; to consume this instead.

You are right -- the sparse matrices as nested maps are easy to work with.  And I got to use Data.Map.mapMaybeWithKey and Data.Map.intersectionWith which I have not had call to employ before.

Cheers,
  Chris</description>
		<content:encoded><![CDATA[<p>And before I run for dinner I changed the code on the wiki so that update2 produces an un-normalized SlopeOne&#8217; matrix and changed my predict&#8217; to consume this instead.</p>
<p>You are right &#8212; the sparse matrices as nested maps are easy to work with.  And I got to use Data.Map.mapMaybeWithKey and Data.Map.intersectionWith which I have not had call to employ before.</p>
<p>Cheers,<br />
  Chris</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Chris K</title>
		<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/comment-page-1/#comment-75585</link>
		<dc:creator>Chris K</dc:creator>
		<pubDate>Tue, 28 Aug 2007 19:02:35 +0000</pubDate>
		<guid isPermaLink="false">http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/#comment-75585</guid>
		<description>The blog ate my code, so I posted all my code at http://haskell.org/haskellwiki/CollaborativeFiltering

The edited update function ensures there are no &quot;diagonal elements&quot;.

The predict&#039; function makes the relationship of the SlopeOne matrix to the predicted value a bit clearer.</description>
		<content:encoded><![CDATA[<p>The blog ate my code, so I posted all my code at <a href="http://haskell.org/haskellwiki/CollaborativeFiltering" rel="nofollow">http://haskell.org/haskellwiki/CollaborativeFiltering</a></p>
<p>The edited update function ensures there are no &#8220;diagonal elements&#8221;.</p>
<p>The predict&#8217; function makes the relationship of the SlopeOne matrix to the predicted value a bit clearer.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Chris K</title>
		<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/comment-page-1/#comment-75580</link>
		<dc:creator>Chris K</dc:creator>
		<pubDate>Tue, 28 Aug 2007 18:53:57 +0000</pubDate>
		<guid isPermaLink="false">http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/#comment-75580</guid>
		<description>Ah, I have now read your &quot;predict&quot; as well as the &quot;update&quot;. I had to rewrite your variable names to be sure I could read your code correctly.  Reading the original paper I see your algorithms look correct.  The default (0,0) does nothing since the first 0 multiples the rating and so the running total is unchanged.

A possibly clearer version of predict:

predict&#039; :: Ord a =&gt; SlopeOne a -&gt; Rating a -&gt; Rating a
predict&#039; (SlopeOne matrixIn) userRatings = M.mapMaybeWithKey calcItem matrixIn
  where calcItem item1 innerMap &#124; M.member item1 userRatings = Nothing
                                &#124; M.null combined = Nothing
                                &#124; norm_rating </description>
		<content:encoded><![CDATA[<p>Ah, I have now read your &#8220;predict&#8221; as well as the &#8220;update&#8221;. I had to rewrite your variable names to be sure I could read your code correctly.  Reading the original paper I see your algorithms look correct.  The default (0,0) does nothing since the first 0 multiples the rating and so the running total is unchanged.</p>
<p>A possibly clearer version of predict:</p>
<p>predict&#8217; :: Ord a =&gt; SlopeOne a -&gt; Rating a -&gt; Rating a<br />
predict&#8217; (SlopeOne matrixIn) userRatings = M.mapMaybeWithKey calcItem matrixIn<br />
  where calcItem item1 innerMap | M.member item1 userRatings = Nothing<br />
                                | M.null combined = Nothing<br />
                                | norm_rating</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Bryan O'Sullivan</title>
		<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/comment-page-1/#comment-75557</link>
		<dc:creator>Bryan O'Sullivan</dc:creator>
		<pubDate>Tue, 28 Aug 2007 17:58:00 +0000</pubDate>
		<guid isPermaLink="false">http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/#comment-75557</guid>
		<description>Hi, Chris. Regarding your suggested stylistic change, I originally wrote the Haskell code to use the kind of map you suggest, but it was both longer and less readable than the current code, the reason being that the left-hand item in a key pair was overrepresented in proportion to the number of times the right-hand item showed up. Accounting for this was annoying. Using nested maps avoids the problem.</description>
		<content:encoded><![CDATA[<p>Hi, Chris. Regarding your suggested stylistic change, I originally wrote the Haskell code to use the kind of map you suggest, but it was both longer and less readable than the current code, the reason being that the left-hand item in a key pair was overrepresented in proportion to the number of times the right-hand item showed up. Accounting for this was annoying. Using nested maps avoids the problem.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Chris K</title>
		<link>http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/comment-page-1/#comment-75536</link>
		<dc:creator>Chris K</dc:creator>
		<pubDate>Tue, 28 Aug 2007 16:52:47 +0000</pubDate>
		<guid isPermaLink="false">http://www.serpentine.com/blog/2007/08/27/weighted-slope-one-in-haskell-collaborative-filtering-in-29-lines-of-code/#comment-75536</guid>
		<description>Because of the &quot;norm&quot; operation, your &quot;update&quot; function only makes sense if applied to &quot;empty :: SlopeOne a&quot; as its first parameter.  So I would suggest that initial empty value by hard coded instead of a parameter to &quot;update&quot;.  And in this case &quot;update&quot; should be renamed to something more like &quot;slopeOneFromRatings&quot;

If you actually want &quot;updateSlopeOne&quot; that adds novel user preferences to a non-empty slopeOne value then you need to reverse the &quot;norm&quot; operation before adding in novel information (and finish with the &quot;norm&quot; operation).

Or you could never apply &quot;norm&quot; to the slopeOne information and always do the &quot;norm&quot; division when looking up values.

A more stylistic change would be to make slopeOne into the type  &quot;M.Map (a,a) (Int,Double)&quot; which takes the item pair as a single key.  Your current code with nested maps could be made slightly more efficient by grouping the insertions so that each insertion did not always involve a lookup into both the outer and inner map (by grouping by the outer Map&#039;s key either per user (easy given the way you construct prod) or sorted across all the users).  Also the (item1,item2)
 keys are irrelevant since the value is always zero.</description>
		<content:encoded><![CDATA[<p>Because of the &#8220;norm&#8221; operation, your &#8220;update&#8221; function only makes sense if applied to &#8220;empty :: SlopeOne a&#8221; as its first parameter.  So I would suggest that initial empty value by hard coded instead of a parameter to &#8220;update&#8221;.  And in this case &#8220;update&#8221; should be renamed to something more like &#8220;slopeOneFromRatings&#8221;</p>
<p>If you actually want &#8220;updateSlopeOne&#8221; that adds novel user preferences to a non-empty slopeOne value then you need to reverse the &#8220;norm&#8221; operation before adding in novel information (and finish with the &#8220;norm&#8221; operation).</p>
<p>Or you could never apply &#8220;norm&#8221; to the slopeOne information and always do the &#8220;norm&#8221; division when looking up values.</p>
<p>A more stylistic change would be to make slopeOne into the type  &#8220;M.Map (a,a) (Int,Double)&#8221; which takes the item pair as a single key.  Your current code with nested maps could be made slightly more efficient by grouping the insertions so that each insertion did not always involve a lookup into both the outer and inner map (by grouping by the outer Map&#8217;s key either per user (easy given the way you construct prod) or sorted across all the users).  Also the (item1,item2)<br />
 keys are irrelevant since the value is always zero.</p>
]]></content:encoded>
	</item>
</channel>
</rss>

