<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	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/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>2Paths &#187; Featured</title>
	<atom:link href="http://www.2paths.com/category/featured/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.2paths.com</link>
	<description>Custom Software Technical Architecture, Design and Development in Vancouver, BC, Canada</description>
	<lastBuildDate>Mon, 27 Sep 2010 01:15:46 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Interesting Vancouver on Oct 8th 2010</title>
		<link>http://www.2paths.com/2010/09/26/interesting-vancouver-on-oct-8th-2010/</link>
		<comments>http://www.2paths.com/2010/09/26/interesting-vancouver-on-oct-8th-2010/#comments</comments>
		<pubDate>Mon, 27 Sep 2010 01:15:46 +0000</pubDate>
		<dc:creator>Aaron</dc:creator>
				<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Events]]></category>
		<category><![CDATA[Featured]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1328</guid>
		<description><![CDATA[One of our colleagues, Mark Busse from Industrial Brand Creative, is helping to organize what looks to be a very&#8230;.interesting event:

	
“The plan is to have all sorts of speakers speak about all sorts of stuff. Not brands, advertising, blogging and twitter but interesting, unexpected, original things. I’m hoping to find fascinating people and to just [...]]]></description>
			<content:encoded><![CDATA[<p>One of our colleagues, <a href="http://twitter.com/markbusse">Mark Busse</a> from <a href="http://www.industrialbrand.com/">Industrial Brand Creative</a>, is helping to organize what looks to be a very&#8230;.interesting event:</p>
<blockquote><p>
	<a href="http://interestingvancouver.com/"><img src="http://evbdn.eventbrite.com/s3-s3/eventlogos/5479334/795576591.gif" alt="Interesting Vancouver" /></a></p>
<p>“The plan is to have all sorts of speakers speak about all sorts of stuff. Not brands, advertising, blogging and twitter but interesting, unexpected, original things. I’m hoping to find fascinating people and to just ask them to speak about something they care about. I want to replicate the experience of clicking from one really good blog to another, ranging across sciences, arts, musics, jokes and whatever.”</p>
<p>—Russell Davies, founder of Interesting</p>
</blockquote>
<p>The format is quite cool &#8211; a smallish number of speakers give 5 minute talks, and the rest of the night is unstructured.  The speakers are from quite the range of life and the topics look extremely thought provoking.  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2010/09/26/interesting-vancouver-on-oct-8th-2010/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cassandra Key-Value Store Primer</title>
		<link>http://www.2paths.com/2010/06/07/cassandra-key-value-store-primer/</link>
		<comments>http://www.2paths.com/2010/06/07/cassandra-key-value-store-primer/#comments</comments>
		<pubDate>Mon, 07 Jun 2010 17:56:55 +0000</pubDate>
		<dc:creator>Omar</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[cassandra]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[key-value store]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[thrift]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1286</guid>
		<description><![CDATA[The &#8220;no-sql&#8221; movement has been gaining strength over recent years. There is no denying that relational databases have their role and are highly effective in many cases. However, there are times when the scalability and availability of a relational database can become an issue. The &#8220;no-sql&#8221; movement is hoping to provide an alternative option to [...]]]></description>
			<content:encoded><![CDATA[<p>The &#8220;no-sql&#8221; movement has been gaining strength over recent years. There is no denying that relational databases have their role and are highly effective in many cases. However, there are times when the scalability and availability of a relational database can become an issue. The &#8220;no-sql&#8221; movement is hoping to provide an alternative option to relational databases. These options allow for the creation of data stores that do not necessarily require the creation of fixed schemas or the joining of tables and tend to focus on scaling horizontally. </p>
<p><strong>Apache Cassandra</strong><br />
<a href="http://cassandra.apache.org/">Cassandra</a> is a fairly recent addition to the &#8220;no-sql&#8221; movement. Initially developed by Facebook, Cassandra was open sourced in 2008 and is now housed and maintained by Apache.</p>
<blockquote><p>Cassandra is a highly scalable, eventually consistent, distributed, structured key-value store. Cassandra brings together the distributed systems technologies from Dynamo  and the data model from Google&#8217;s BigTable. Like Dynamo, Cassandra is eventually consistent. Like BigTable, Cassandra provides a ColumnFamily-based data model richer than typical key/value systems.</p>
<p>Cassandra was open sourced by Facebook in 2008, where it was designed by Avinash Lakshman (one of the authors of Amazon&#8217;s Dynamo) and Prashant Malik ( Facebook Engineer ). In a lot of ways you can think of Cassandra as Dynamo 2.0 or a marriage of Dynamo and BigTable. Cassandra is in production use at Facebook but is still under heavy development.</p></blockquote>
<p><strong>Data Model</strong><br />
The key difference between Cassandra and many of the other key-value stores is that it possesses the concept of a &#8220;Super Column.&#8221; For more information on what exactly a super column is (and an overview of the Cassandra data model in general) read the following blog post &#8220;<a href="http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model">WTF is a SuperColumn? An Intro to the Cassandra Data Model</a>&#8221;</p>
<p><strong>Performance</strong><br />
Many may be curious about the performance of these key value stores. We found an article that compared <a href="http://blog.medallia.com/2010/05/choosing_a_keyvalue_storage_sy.html">Cassandra to Voldemort</a></p>
<p><strong>Interacting with Cassandra</strong><br />
Cassandra is developed in java however it does not allow access via JDBC. Instead it provides access to it&#8217;s repository via the &#8220;<a href="http://incubator.apache.org/thrift/">Apache Thrift</a>&#8221; framework which basically allows developers in any major language to gain access to a given service (in this case the Cassandra data store). For those like me who are used to JDBC, the thrift interface can be a bit confusing given that there isn&#8217;t much documentation for them. The Java client made available on the Cassandra website is rather rudimentary with no support for things like transactions, therefore many people choose to make use of a library called <a href="http://prettyprint.me/2010/02/23/hector-a-java-cassandra-client/">Hector</a> that provides many of the features necessary for any production worthy application making use of Cassandra.</p>
<p><strong>The Project </strong><br />
What I wanted to to do was use the Cassandra data store on a pet project that I&#8217;m working on in my spare time. I intend to use the datastore to do the following: </p>
<ol>
<li>Store messages being sent from one user to another</li>
<li>Track queries made by a given user</li>
<li>Store user complaints</li>
</ol>
<p><strong>Installation</strong><br />
Installation of the server is easy enough. Install Java 1.6 if you haven&#8217;t already. Edit <code>/bin/cassandra.in.sh</code> and change <code>JAVA_HOME</code> if you run a different version of java as your default<code> JAVA_HOME</code>. By default the server has an RMI port set to 8080, you can change the value in the <code>/bin/cassandra.in.sh</code> file as well.</p>
<p><strong>Configuration</strong><br />
Edit <code>/bin/storage-conf.xml</code> to define various key spaces and column families. The concept of a Keyspace is similar to the schema in a relational database, where as the ColumnFamily is analogous to a table. In defining the ColumnFamilies you are defining the key groupings and how they are sorted. Therefore, I defined the following:</p>
<pre class="brush: xml">
  &lt;Keyspaces&gt;
    &lt;Keyspace Name=&quot;Nikahfied&quot;&gt;

      &lt;ColumnFamily Name=&quot;Queries&quot;
                    Comment=&quot;User queries&quot;/&gt;

      &lt;ColumnFamily Name=&quot;Complaints&quot;
                    Comment=&quot;User complaints&quot;/&gt;

      &lt;ColumnFamily Name=&quot;Messages&quot;
                    ColumnType=&quot;Super&quot;
                    CompareWith=&quot;UTF8Type&quot;
                    CompareSubcolumnsWith=&quot;UTF8Type&quot;
                    RowsCached=&quot;10000&quot;
                    KeysCached=&quot;50%&quot;
                    Comment=&quot;Message threads between users of the system&quot;/&gt;

    &lt;/Keyspace&gt;
  &lt;/Keyspaces&gt;
</pre>
<p>I would be creating a schema named Nikahfied and three table are Queries, Complaints and Messages. As you will notice, there is no real structure to the ColumnFamilies other than what type of column and the CompareWith strategy as outlined in the &#8220;<a href="http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model">WTF is a SuperColumn? An Intro to the Cassandra Data Model</a>&#8221; article. In this case we have one super column and two regular columns. In code I will ensure that each of the rows fore each ColumnFamily contain the same content however there is nothing forcing you to do so. As you will notice, we can configure each of the ColumnFamiles individually. Messages will need to handle heavy reads and writes where as Queries and Complaints will be more write intensive. Messages therefore have a caching scheme defined where as the others do not as they will primarily handle writes rather than reads.</p>
<p>When initially defining the column structure I was a bit confused. I was taking the definition of Cassandra as a &#8220;key value store&#8221; literally and was confused about there being a primary key and the secondary keys. When defining a basic Column I though it would simple have a key and a value. So things would be defined as <code>Nikahfied.&lt;ColumnFamily&gt;.key = value</code> where this is actually not the case. For a basic ColumnFamily, the structure is as follows: <code>Nikahfied.&lt;ColumnFamily&gt;.&lt;Primary key&gt;.&lt;Secondary Key&gt; = value</code>. In the case of a super column, this goes one level further <code>Nikahfied.&lt;ColumnFamily&gt;.&lt;Primary key&gt;.&lt;Secondary Key&gt;.&lt;Tertiary Key&gt; = value</code>. That had me puzzled for some time&#8230;upon re-reading the &#8220;<a href="http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model">WTF is a SuperColumn? An Intro to the Cassandra Data Model</a>&#8221; article I realized my mistake so I though I&#8217;d point it out to anyone else who was confused about this construct.</p>
<p><strong>Data Structures:</strong><br />
<span style="text-decoration:underline">Queries:</span><br />
The structure of queries would be simple enough. I would store each query by date/time. To track which queries the user made when.</p>
<p><code>Nikahfied.Queries.UserId.&lt;Date/Time&gt; = &lt;Query&gt;</code></p>
<p><span style="text-decoration:underline">Complaints:</span><br />
There is only one type of complaint wherein a user can complain about another user. So I wanted to track who received the complaint, who complained and when. The structure of a complaint would be defined as follows:<br />
<code>Nikahfied.Complaints.&lt;Violator Id&gt;.&lt;Complainant Id&gt; = &lt;Date/Time&gt;</code></p>
<p><span style="text-decoration:underline">Messages:</span><br />
If there was ever a case to use a super column, a messages would be it. A user has a number of threads that contain one or more message. A user can receive and send messages. Messages themselves are the same, I simply need to track which threads contain messages that a user sent. I also need to keep track of which threads have unread messages within them. My initial design was the following:<br />
<code>Nikahfied.Messages.RecipientId.threads = A JSON Object that contains Threads defined in JSON Objects themselves. A thread being a group of<br />
                              .sent_ids = An array of thread id's that contain messages that the user has sent<br />
                              .unread_ids = An array of thread ids that contain unread messages</code></p>
<p>However, with this structure there was no need for a super column and I was determined on making use of one <img src='http://www.2paths.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . So I expanded out the thread structure as follows:</p>
<p><code>Nikahfied.Messages.RecipientId.threads.&lt;thread id&gt; = A JSON Object that contains an individual Thread<br />
                              .sent_ids = An array of thread id's that contain messages that the user has sent<br />
                              .unread_ids = An array of thread ids that contain unread messages</code></p>
<p>Now that we have the content structure defined I now need to create a hook between my application and the Cassandra key value store. For development I have decided to use the Grails Framework to speed up development. I have included the Hector library to access the framework but needed to define a CassandraService to be the central point of contact between various controllers and other services to the Cassandra key store. Both Cassandra&#8217;s Thrift interface and Hector have pretty poor documentation so I created a Grails service that makes the interaction with Cassandra a bit clearer (at least to me). <span style="text-decoration:underline;font-weight:bold">Note:</span> You will need to be using Java 1.6.</p>
<pre class="brush: groovy">
import me.prettyprint.cassandra.service.CassandraClientPool
import me.prettyprint.cassandra.service.CassandraClientPoolFactory
import me.prettyprint.cassandra.service.CassandraClient
import me.prettyprint.cassandra.service.Keyspace
import org.apache.cassandra.thrift.Column;
import org.apache.cassandra.thrift.ColumnPath;
import org.apache.cassandra.thrift.NotFoundException;

import org.apache.cassandra.thrift.SuperColumn

class CassandraService {

    boolean transactional = true

    def servers=[&quot;localhost:9160&quot;]
    def defaultKeyspace=&quot;Nikahfied&quot;
    private static final String NOT_FOUND = &quot;&quot;

    private execute(keyspaceName=defaultKeyspace,block){
        CassandraClientPool pool = CassandraClientPoolFactory.INSTANCE.get();
        CassandraClient client = pool.borrowClient(servers);

        try {
            Keyspace keyspace = client.getKeyspace(keyspaceName)
            return block(keyspace)
        } finally {
            pool.releaseClient(client);
        }
    }

    /**
     * Get a single super column
     * @param columnFamily
     * @param secondaryKey
     * @param key primary key
     * @return Matching super column
     */
    public SuperColumn getSuperColumn(String columnFamily, String key, String secondaryKey) {
        ColumnPath cp = new ColumnPath(columnFamily)
        cp.setSuper_column(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            SuperColumn sc = null;
            try {
                sc = keyspace.getSuperColumn(key, cp)
            } catch (NotFoundException nfe) {
                sc = null;
            }
            return sc
        }
    }

    /**
     * Get multiple super columns
     * @param columnFamily
     * @param secondaryKey
     * @param keys primary keys
     * @return matching super columns
     */
    public Map multigetSuperColumn(String columnFamily, List keys, String secondaryKey) {
        ColumnPath cp = new ColumnPath(columnFamily)
        cp.setSuper_column(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            Map scMap = keyspace.multigetSuperColumn(keys, cp)
            return scMap
        }
    }

    /**
     * Get multiple columns
     * @param secondaryKey secondaryKey
     * @param keys primary keys
     * @param columnFamily column family
     * @return results if any
     */
    public Map multigetColumn(List keys, String secondaryKey, String columnFamily) {
    	ColumnPath cp = new ColumnPath(columnFamily)
        cp.setColumn(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            Map cMap = keyspace.multigetColumn(keys, cp)
            return cMap
        }
    }

    /**
     * Get a single column and it&#039;s values
     * @param columnFamily
     * @param key primary key
     * @return matching column
     */
    public Column getColumn(String columnFamily, String key, String secondaryKey) {
        def cp = new ColumnPath(columnFamily)
        cp.setColumn(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            Column c = null;
            try {
                c = keyspace.getColumn(key, cp)
            } catch (NotFoundException nfe) {
                c = null;
            }
            return c
        }
    }

    /**
     * Sets the new value for this column path
     * @param cf Column family
     * @param secondaryKey secondary key
     * @param key primary key
     * @param value value
     */
    def setColumnPathValue(String cf, String key, String secondaryKey, String value){
        def cp = new ColumnPath(cf)
        cp.setColumn(secondaryKey.bytes)
        return setColumnValue(cp, key, value)
    }

    /**
     * Sets the new super column value for this column path
     * @param cf Column Family
     * @param sc Super Key Id
     * @param secondaryKey Secondary Key
     * @param key primary key
     * @param value value
     * @return the value of the Column requested
     */
    def setColumnPathValue(String cf, String sc, String key, String secondaryKey, String value){
        def cp = new ColumnPath(cf)
        cp.setSuper_column(sc.bytes)
        cp.setColumn(secondaryKey.bytes)
        return setColumnValue(cp, key, value)
    }

    /**
     * Set a regular column
     * @param cp Column path
     * @param key primary key
     * @param value value
     */
    private setColumnValue(ColumnPath cp, String key, String value){
        return execute{ Keyspace keyspace -&amp;gt;
            keyspace.insert(key, cp, value.bytes)
        }
    }

    /**
     * Batch insert content for a give key and it&#039;s values
     * @param key primary key
     * @param columnMap column key values
     * @param superColumnMap super column key values
     */
    public batchInsert(String key, Map&amp;lt;String, List&amp;gt; columnMap, Map&amp;lt;String, List&amp;gt; superColumnMap) {
    	return execute{ Keyspace keyspace -&amp;gt;
        	keyspace.batchInsert(key, columnMap, superColumnMap)
    	}
    }

}
</pre>
<p>Using this class I have created a MessageService, ComplaintsService and a QueriesService that interact with the CassandraService to retrieve and store data. There is an adjoining Message and Complaints controller where as the QueriesService is referenced by the SearchController. Hopefully this blog post will provide you with a better understanding of how to get started with Cassandra the CassandraService class should provide you with a jump start on integrating Cassandra into an existing Grails or Java project.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2010/06/07/cassandra-key-value-store-primer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>one-to-many relationships in Grails forms</title>
		<link>http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/</link>
		<comments>http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/#comments</comments>
		<pubDate>Fri, 02 Oct 2009 01:35:25 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Featured]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1228</guid>
		<description><![CDATA[Here&#8217;s a scenario we see fairly often in our Grails applications.

 Parent object has a collection of Child objects
We want the Parent&#8217;s create and edit GSPs to allow us to add/remove/update associated Child objects
The controller should correctly persist changes to the collection of Child objects, including maintaining Child object ids so any other objects referencing [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a scenario we see fairly often in our <a href="http://www.grails.org/">Grails</a> applications.</p>
<ul>
<li> Parent object has a collection of Child objects</li>
<li>We want the Parent&#8217;s create and edit GSPs to allow us to add/remove/update associated Child objects</li>
<li>The controller should correctly persist changes to the collection of Child objects, including maintaining Child object ids so any other objects referencing them don&#8217;t get screwed up</li>
</ul>
<p>I found a really nice solution that avoids adding a lot of code to the controller to sift out added/changed/deleted collection members. The original page seems to have disappeared, so here are copies from <a href="http://web.archive.org/web/20080822094606/http://www.lxisoft.com/web/guest/grails">archive.org</a> (easier to read) and <a href="http://docs.google.com/gview?a=v&amp;q=cache:yNp_JSw97_gJ:www.ambersand.org/c/document_library/get_file%3Fp_l_id%3D10913%26folderId%3D11055%26name%3DDLFE-101.pdf">Google cache</a> (PDF).</p>
<p>I was disappointed that the original page is gone, and I found some small errors in the sample code, so I thought it would be nice to document here.</p>
<p>Here&#8217;s a sample project I created to go through this. Source code: <a href="http://www.2paths.com/wp-content/uploads/2009/09/one-many.tar.gz">one-many.tar.gz</a></p>
<p>The original example used Quest objects that can hold many Task objects. I&#8217;ll follow the <a href="http://www.grails.org/Quick+Start">Grails docs</a> and use Author objects that can hold many Book objects.</p>
<p>First, create the Author class.</p>
<pre class="brush: java">
    import org.apache.commons.collections.list.LazyList;
    import org.apache.commons.collections.FactoryUtils;

    class Author {

        static constraints = {
        }

        String name
        List books = new ArrayList()
        static hasMany = [ books:Book ]

        static mapping = {
            books cascade:&quot;all,delete-orphan&quot;
        }

        def getExpandableBookList() {
            return LazyList.decorate(books,FactoryUtils.instantiateFactory(Book.class))
        }

    }
</pre>
<p>(Here&#8217;s a minor correction I had to make to the original document&#8217;s code. They declared getExpandableBookList as returning a List, but that gave unknown property errors. Using a plain def fixed that.)</p>
<p>This adds a bunch of useful behaviour right away. The mapping block declares that books will be deleted when they&#8217;re removed from the Author.books collection, so we don&#8217;t need to clean up anything manually. By initializing books to an empty ArrayList when an Author object is created, and by using the getExpandableBookList() method, we can easily add and remove Book objects to the Author.books collection.</p>
<p>Next, the Book class is pretty simple.</p>
<pre class="brush: java">
    class Book {

        static constraints = {
        }

        String title
        boolean _deleted

        static transients = [ &#039;_deleted&#039; ]

        static belongsTo = [ author:Author ]

        def String toString() {
            return title
        }

    }
</pre>
<p>Nothing too fancy here, but pay attention to the _deleted property. That&#8217;s what we&#8217;ll be using to filter out Book objects that need to be removed from the Author.book collection on updates.</p>
<p>For the views, I like to combine the guts of the create and edit GSPs into a template that they can both render.</p>
<pre class="brush: html">
    &lt;div class=&quot;dialog&quot;&gt;
        &lt;table&gt;
            &lt;tbody&gt;
                &lt;tr class=&quot;prop&quot;&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;name&quot;&gt;&lt;label for=&quot;name&quot;&gt;Name:&lt;/label&gt;&lt;/td&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;value ${hasErrors(bean:authorInstance,field:&#039;name&#039;,&#039;errors&#039;)}&quot;&gt;
                        &lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; value=&quot;${fieldValue(bean:authorInstance,field:&#039;name&#039;)}&quot;/&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr class=&quot;prop&quot;&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;name&quot;&gt;&lt;label for=&quot;books&quot;&gt;Books:&lt;/label&gt;&lt;/td&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;value ${hasErrors(bean:authorInstance,field:&#039;books&#039;,&#039;errors&#039;)}&quot;&gt;
                        &lt;g:render template=&quot;books&quot; model=&quot;[&#039;authorInstance&#039;:authorInstance]&quot; /&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
    &lt;/div&gt;
</pre>
<p>That uses _books.gsp to render the editable list of books.</p>
<pre class="brush: html">
&lt;script type=&quot;text/javascript&quot;&gt;
    var childCount = ${authorInstance?.books.size()} + 0;

    function addChild() {
        var htmlId = &quot;book&quot; + childCount;
        var deleteIcon = &quot;${resource(dir:&#039;images/skin&#039;, file:&#039;database_delete.png&#039;)}&quot;;
        var templateHtml = &quot;&lt;div id=&#039;&quot; + htmlId + &quot;&#039; name=&#039;&quot; + htmlId + &quot;&#039;&gt;\n&quot;;
        templateHtml += &quot;&lt;input type=&#039;text&#039; id=&#039;expandableBookList[&quot; + childCount + &quot;].title&#039; name=&#039;expandableBookList[&quot; + childCount + &quot;].title&#039; /&gt;\n&quot;;
        templateHtml += &quot;&lt;span onClick=&#039;$(\&quot;#&quot; + htmlId + &quot;\&quot;).remove();&#039;&gt;&lt;img src=&#039;&quot; + deleteIcon + &quot;&#039; /&gt;&lt;/span&gt;\n&quot;;
        templateHtml += &quot;&lt;/div&gt;\n&quot;;
        $(&quot;#childList&quot;).append(templateHtml);
        childCount++;
    }
&lt;/script&gt;

&lt;div id=&quot;childList&quot;&gt;
    &lt;g:each var=&quot;book&quot; in=&quot;${authorInstance.books}&quot; status=&quot;i&quot;&gt;
        &lt;g:render template=&#039;book&#039; model=&quot;[&#039;book&#039;:book,&#039;i&#039;:i]&quot;/&gt;
    &lt;/g:each&gt;
&lt;/div&gt;
&lt;input type=&quot;button&quot; value=&quot;Add Book&quot; onclick=&quot;addChild();&quot; /&gt;
</pre>
<p>And that uses _book.gsp to render the individual records. It&#8217;s a bit overkill to call out to another template for only a few lines of HTML, but that&#8217;s how the original example did it and I&#8217;ll do the same for consistency.</p>
<pre class="brush: html">
    &lt;div id=&quot;book${i}&quot;&gt;
        &lt;g:hiddenField name=&#039;expandableBookList[${i}].id&#039; value=&#039;${book.id}&#039;/&gt;
        &lt;g:textField name=&#039;expandableBookList[${i}].title&#039; value=&#039;${book.title}&#039;/&gt;
        &lt;input type=&quot;hidden&quot; name=&#039;expandableBookList[${i}]._deleted&#039; id=&#039;expandableBookList[${i}]._deleted&#039; value=&#039;false&#039;/&gt;
        &lt;span onClick=&quot;$(&#039;#expandableBookList\\[${i}\\]\\._deleted&#039;).val(&#039;true&#039;); $(&#039;#book${i}&#039;).hide()&quot;&gt;&lt;img src=&quot;${resource(dir:&#039;images/skin&#039;, file:&#039;database_delete.png&#039;)}&quot; /&gt;&lt;/span&gt;
    &lt;/div&gt;
</pre>
<p>Here&#8217;s where I changed a bit more from the original example. I used <a href="http://jquery.com/">jQuery</a> because the selectors make things easy. Basically we render the books from the already-persisted author object, and keep track (using the _deleted field) of any that the user wants to remove. We also keep track of new objects to add.</p>
<p>One of the reasons I really liked this technique was how little impact there is on the controller. We just need to add this to the update method in AuthorController.</p>
<pre class="brush: java">
    def update = {
        def authorInstance = Author.get( params.id )
        if(authorInstance) {
            if(params.version) {
                // ... version locking stuff
            }
        authorInstance.properties = params
        def _toBeDeleted = authorInstance.books.findAll {it._deleted}
        if (_toBeDeleted) {
            authorInstance.books.removeAll(_toBeDeleted)
        }
        // ... etc.
</pre>
<p>The original example added similar code to the save method, but I don&#8217;t think it&#8217;s required for new objects (since they don&#8217;t have any already-persisted books to delete, only new books to create) so I only put it in the update method. I also changed it from find{} to findAll{} to guarantee that we get a list, and checked that we have objects to remove before calling the removeAll().</p>
<p>And it works great! Let&#8217;s look at some screenshots of the application in action.</p>
<p>First, we can create a new author and add some books right here instead of creating them separately and then matching them up.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/1.png" alt="Create Author" width="285" height="383" class="alignnone size-full wp-image-1232" /></p>
<p>Hit &#8220;Create&#8221; and it creates the Author and Book objects.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/2.png" alt="Show Author" width="281" height="354" class="alignnone size-full wp-image-1232" /></p>
<p>Edit the author we just created and see how we get a form that looks the same.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/3.png" alt="Edit Author" width="285" height="373" class="alignnone size-full wp-image-1232" /></p>
<p>However, it&#8217;s worth noting that the books displayed here are the already-persisted ones, so the form is keeping track of their ids and whether we should keep them or delete them on update. Let&#8217;s delete the first one and add two more new books.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/5.png" alt="Edit Author" width="279" height="400" class="alignnone size-full wp-image-1232" /></p>
<p>Now when we hit &#8220;Update&#8221; the controller has to be smart enough to remove that first book from the Author.books collection, then create two new Book objects and add them to the collection. And naturally, it is.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/6.png" alt="Show Author" width="286" height="376" class="alignnone size-full wp-image-1232" /></p>
<p>In addition to creating and destroying Book objects, we can update them. For example, let&#8217;s change the title of that first book to be the long version.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/7.png" alt="Edit Author" width="291" height="402" class="alignnone size-full wp-image-1232" /></p>
<p>No problem!</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/8.png" alt="Edit Author" width="553" height="386" class="alignnone size-full wp-image-1232" /></p>
<p>So that&#8217;s one-to-many relationships in Grails forms. I hope it&#8217;s useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>Show me the data!  Seminar on Innovative Approaches to Turn Statistics into Knowledge 2009 (OECD, US Census Bureau, World Bank)</title>
		<link>http://www.2paths.com/2009/07/28/show-me-the-data-seminar-on-innovative-approaches-to-turn-statistics-into-knowledge-2009-oecd-us-census-bureau-world-bank/</link>
		<comments>http://www.2paths.com/2009/07/28/show-me-the-data-seminar-on-innovative-approaches-to-turn-statistics-into-knowledge-2009-oecd-us-census-bureau-world-bank/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 00:37:33 +0000</pubDate>
		<dc:creator>Aaron</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[aaron.gladders]]></category>
		<category><![CDATA[barcamp.vancouver]]></category>
		<category><![CDATA[bcv09]]></category>
		<category><![CDATA[bls]]></category>
		<category><![CDATA[linked.data]]></category>
		<category><![CDATA[michal.urbanski]]></category>
		<category><![CDATA[OECD]]></category>
		<category><![CDATA[semantic web]]></category>
		<category><![CDATA[us.bureau.of.labor.statistics]]></category>
		<category><![CDATA[us.census.bureau]]></category>
		<category><![CDATA[world.bank]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1162</guid>
		<description><![CDATA[(Update 20091005 &#8211; I gave the same talk at the US Bureau of Labor Statistics and Barcamp Vancouver 2009, in case you end up on this page from either source)
We recently presented at the Turning Statistics Into Knowledge conference, jointly organized by the US Census Bureau, the OECD and the World Bank, on July 15-16 [...]]]></description>
			<content:encoded><![CDATA[<p>(Update 20091005 &#8211; I gave the same talk at the US Bureau of Labor Statistics and Barcamp Vancouver 2009, in case you end up on this page from either source)</p>
<p>We recently presented at the <a href="http://www.oecd.org/progress/ict/statknowledge">Turning Statistics Into Knowledge</a> conference, jointly organized by the <a href="http://www.census.gov/">US Census Bureau</a>, the <a href="http://www.oecd.org/">OECD</a> and the <a href="http://www.worldbank.org/">World Bank</a>, on July 15-16 in DC at the Census Bureau.  Here is a <a href='http://www.2paths.com/wp-content/uploads/2009/07/tsik.zip'>pdf</a> of it including our speaking notes, or you can click through it below, sadly without notes (goto the slideshare site to see the notes).</p>
<div style="width:425px;text-align:left" id="__ss_1776637"><a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/2Paths/show-me-the-data-turning-statistics-into-knowledge-2009" title="Show me the Data!  Turning Statistics Into Knowledge 2009">Show me the Data!  Turning Statistics Into Knowledge 2009</a><object style="margin:0px" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=20090715-turningstatisticsintoknowledgerevised-090727160953-phpapp01&#038;stripped_title=show-me-the-data-turning-statistics-into-knowledge-2009" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=20090715-turningstatisticsintoknowledgerevised-090727160953-phpapp01&#038;stripped_title=show-me-the-data-turning-statistics-into-knowledge-2009" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;">View more <a style="text-decoration:underline;" href="http://www.slideshare.net/">presentations</a> from <a style="text-decoration:underline;" href="http://www.slideshare.net/2Paths">2Paths</a>.</div>
</div>
<p>I promised a number of people links to non-technical to technical primers, here we go:</p>
<p><strong>Non-technical</strong></p>
<ol>
<li>Very straight-forward overview, very good for sharing with non-technical people:<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/OGg8A2zfWKg&#038;hl=en&#038;fs=1&#038;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/OGg8A2zfWKg&#038;hl=en&#038;fs=1&#038;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object></li>
<li>Very quick one-pager, &#8220;<a href="http://ajbadd.wordpress.com/2009/06/09/the-semantic-web-a-low-fat-no-nonsense-introduction/">The “Semantic Web”: a low-fat, no-nonsense introduction</a>&#8221; by <a href="http://twitter.com/ajubdeen">@ajubdeen</a></li>
<li><a href="http://www.sciam.com/article.cfm?id=the-semantic-web&#038;print=true">Original article</a> from <a href="http://www.w3.org/People/Berners-Lee/">Tim Berners-Lee</a> showing his vision.</li>
<li>CNN <a href="http://www.cnn.com/2008/TECH/12/17/db.semanticweb/index.html?eref=rss_tech">Making sense of the &#8217;semantic Web&#8217;</a></li>
<li><a href="http://www.ted.com/talks/tim_berners_lee_on_the_next_web.html">Tim Berners-Lee 2009 TED talk on Linked Data</a></li>
</ol>
<p><strong>Semi-technical</strong></p>
<ol>
<li>Largely non-technical, injects a few buzzwords, &#8220;<a href="http://www.slideshare.net/mediasemanticweb/quick-linked-data-introduction">Quick Linked Data Introduction</a>&#8221; by <a href="http://mhausenblas.blogr.com/stories/">Michael Hausenblas</a></li>
<li><a href="http://www.readwriteweb.com/archives/understanding_the_new_web_era_web_30_linked_data_s.php">Understanding the New Web Era: Web 3.0, Linked Data, Semantic Web</a> by <a href="http://www.readwriteweb.com/archives/author/richard-macmanus-1.php">Richard MacManus</a>, summarizing a multi-part overview by <a href="http://twitter.com/gregboutin">Greg Boutin</a></li>
</ol>
<p><strong>Technical</strong></p>
<ol>
<li>An <strong>excellent</strong> overview &#8220;<a href="http://www.slideshare.net/mediasemanticweb/linked-data-michael-hausenblas-2009-03-05">Linked Data Tutorial</a>&#8221; by <a href="http://mhausenblas.blogr.com/stories/">Michael Hausenblas</a></li>
<li><a href="http://www.slideshare.net/iandavis/30-minute-guide-to-rdf-and-linked-data">30 Minute Guide to RDF and Linked Data</a> by <a href="http://twitter.com/iand">Ian Davis</a> and <a href="http://twitter.com/tommyh">Tom Heath</a></li>
</ol>
<p>After that, you&#8217;re off to the races!</p>
<p>(but as I said, just send us an email if you have questions or wanted to talk about it some more)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/07/28/show-me-the-data-seminar-on-innovative-approaches-to-turn-statistics-into-knowledge-2009-oecd-us-census-bureau-world-bank/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Visualizing Semantic Data using Javascript and the HTML Canvas Element</title>
		<link>http://www.2paths.com/2009/06/22/visualizing-semantic-data-using-javascript-and-the-html-canvas-element/</link>
		<comments>http://www.2paths.com/2009/06/22/visualizing-semantic-data-using-javascript-and-the-html-canvas-element/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 21:45:37 +0000</pubDate>
		<dc:creator>gord</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[graph]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[rdf]]></category>
		<category><![CDATA[semantic]]></category>
		<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1014</guid>
		<description><![CDATA[A work in progress visualization of freedb (RDF version of wikipedia).]]></description>
			<content:encoded><![CDATA[<p>So after trying a few existing javascript/canvas graph visualization libraries, I decided to try my hand at writing my own. We were doing a lot of stuff with RDF in the office at the time, so I decided that my visualization would be a graph of rdf data and relationships. Since I&#8217;d never done anything with the HTML canvas element before, I thought I&#8217;d give that a shot too.</p>
<p><strong>Graph Layout</strong></p>
<p>After looking at various existing graph layout algorithms, I decided to reinvent the wheel and developed my own approach. Each node has a mass based on its size, and I simply used gravity calculations to determine the (inverse, so they repel each other) force each node apples to each other node. You can see this in action when you mouse over a node, it gets bigger and other nodes retreat from it as its mass increases.</p>
<p>This allows each node to position itself in a way so it is visible. It&#8217;s not perfect, but it works okay and makes the graph feel responsive to the user.</p>
<p><strong>How to use it</strong></p>
<p>Double click on a node to load its relationships and related nodes. A node must be an rdf node to load properly, I haven&#8217;t implemented any way to handle normal web links yet. Basically, only click on links that look like <strong>h</strong><strong>ttp://dbpedia.org/resource/<em>something</em><span style="font-weight: normal;">, or that you know are rdf.</span></strong></p>
<div id="attachment_1016" class="wp-caption alignnone" style="width: 521px"><a href="http://staging.2paths.com:8080/deepsnow/" target="_self"><img class="size-full wp-image-1016" src="http://www.2paths.com/wp-content/uploads/2009/06/deepsnow1.png" alt="image of visualization in action, click for demo" width="511" height="300" /></a><p class="wp-caption-text">click for demo of the wikipedia article for baseball</p></div>
<p>If you&#8217;re just interested in the pretty pictures, stop here, if you want more technical information read on.</p>
<p><strong>Implementation</strong></p>
<p>I just used javascript and followed the <a href="http://www.whatwg.org/specs/web-apps/current-work/#canvasrenderingcontext2d" target="_blank">HTML5 docs for using the canvas element</a>. One thing I ran into was the fact that Firefox doesn&#8217;t yet have a full canvas implementation, specifically it doesn&#8217;t yet render text on the canvas. I tried doing some voodoo with absolute positioning divs containing text over the canvas, and after a brief descent into insanity, I found the excellent <a href="http://typeface.neocracy.org/">typeface.js</a> library which emulates the missing canvas calls for text.</p>
<p>As for the javascript code itself, I implemented a simple object graph that can contain vertices or edges, then a render loop just advances the object graph by a given amount of time and renders the scene to the canvas.</p>
<p>The back end serving up rdf was done quickly in grails using the <a href="http://jena.sourceforge.net/" target="_blank">Jena</a> framework to do all the heavy lifting. There is some minimal inference going on using the RDF data to determine if something is an image.</p>
<p><strong><strong>Disclaimer</strong></strong></p>
<p><strong><span style="font-weight: normal;">Keep in mind that this is a work of progress that I have not spent much time with. It has bugs, and it&#8217;s nowhere near usable for anything useful, but it serves its purpose as a proof of concept. I&#8217;m not sure if it will even run under IE, but it should run on any browser that has a decent javascript + canvas implementation.</span></strong></p>
<p><strong> </strong></p>
<p><strong>Conclusions</strong></p>
<p><strong><span style="font-weight: normal;">The canvas element is cool, and works well.</span></strong></p>
<p>Basically I hope you like it. Please leave any suggestions or feedback as comments. I realize it&#8217;s probably full of bugs, but that&#8217;s the nature of the beast.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/06/22/visualizing-semantic-data-using-javascript-and-the-html-canvas-element/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>SolutionsIQ&#8217;s Certified Product Owner Course</title>
		<link>http://www.2paths.com/2009/06/19/solutionsiqs-certified-product-owner-course/</link>
		<comments>http://www.2paths.com/2009/06/19/solutionsiqs-certified-product-owner-course/#comments</comments>
		<pubDate>Fri, 19 Jun 2009 16:17:59 +0000</pubDate>
		<dc:creator>Lorill</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[certification]]></category>
		<category><![CDATA[product owner]]></category>
		<category><![CDATA[scrum]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1003</guid>
		<description><![CDATA[Working as both a Developer and as a ScrumMaster has skewed my perspective a bit to be dev-team centric, so I recently took SolutionsIQ&#8217;s Certified Product Owner course to learn more about the scrum software development process through the eyes of a Product Owner.
The course was laid out in a novel way &#8211; as a [...]]]></description>
			<content:encoded><![CDATA[<p>Working as both a Developer and as a ScrumMaster has skewed my perspective a bit to be dev-team centric, so I recently took <a href="http://www.solutionsiq.com/services/course-catalog.php#cspo">SolutionsIQ&#8217;s Certified Product Owner course</a> to learn more about the scrum software development process through the eyes of a Product Owner.</p>
<p>The course was laid out in a novel way &#8211; as a scrum project itself! As Product Owners we were asked to compose User Stories capturing what we hoped to learn from the course, and at the end of each &#8220;iteration&#8221; within the two-day course, were asked to re-prioritize these, knowing time was tight and we may not get to all topics. This helped us to have hands-on experience with Story creation and re-prioritization, with the added bonus of us each being personally invested in the outcome instead of it being a theoretical scenario.</p>
<p>One of the topics I was interested in was how to adapt the scrum process for a Product Owner that is not only not on site, but is overseas and in a different time zone. At 2Paths, one of our larger projects that was recently completed involved a client overseas. This project was very successful, and we accredited much of its success to our client&#8217;s buy-in and participation to our scrum process. We had, however, modified the traditional scrum process to accommodate our client&#8217;s schedule and time availability. Because they were quite busy with other projects, and due to the large time difference, the client didn&#8217;t attend our daily scrum as is recommended. Instead we had weekly phone check-ins which augmented our regular email communication to discuss issues as needed.</p>
<p>I was hoping in the course to see if there would be a more recommended way for this sort of Product Owner to be involved in a project, and how we could try to more closely follow the traditional scrum process. However, the course was more geared towards an internal Product Owner. This type of Product Owner would be a lot more hands on, would physically attend the daily scrum, and spend time sitting within the dev team to facilitate clearer communication. It also seemed that many of the other course attendees had similar project roles where the Product Owner was in-house. From what I could gather, I think we handled our process as well as we could given the circumstances.</p>
<p>The course was well organized, and the two presenters Bryan Stallings and Skip Angel were quite knowledgeable and helpful. We had many good group discussions and participants actively participated, sharing their own war stories. I&#8217;d recommend this course for other ScrumMasters wanting to get a better sense of what is required by a Product Owner, and how they fit into the scrum process.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/06/19/solutionsiqs-certified-product-owner-course/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>machine learning</title>
		<link>http://www.2paths.com/2009/05/13/machine-learning/</link>
		<comments>http://www.2paths.com/2009/05/13/machine-learning/#comments</comments>
		<pubDate>Thu, 14 May 2009 00:18:32 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[People]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[tech talks]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=953</guid>
		<description><![CDATA[Bethany Leffler stopped by last week to deliver a talk about machine learning. We&#8217;re all pretty comfortable with systems where you predefine a (possibly very complex) set of rules to handle whatever data will be thrown at it. Bethany&#8217;s area of expertise is in situations where you want the machine to figure things out for [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://antiflux.org/~bethany/">Bethany Leffler</a> stopped by last week to deliver a talk about <a href="http://en.wikipedia.org/wiki/Machine_learning">machine learning</a>. We&#8217;re all pretty comfortable with systems where you predefine a (possibly very complex) set of rules to handle whatever data will be thrown at it. Bethany&#8217;s area of expertise is in situations where you want the machine to figure things out for itself instead of just following explicit orders. We&#8217;ve been getting more and more interested in that around here, for completely legitimate purposes and not at all for dubious <a href="http://en.wikipedia.org/wiki/Skynet_(Terminator)">SkyNet</a>-style applications.</p>
<p>My primary goal was to get a handle on the vocabulary so that I could ask semi-intelligent questions to get more information. That worked out well, because Bethany gave us an excellent high-level introduction to the field.</p>
<ul>
<li>supervised learning &#8211; the system uses a reference set of labelled data to classify new data points</li>
<li>unsupervised learning &#8211; no reference data set</li>
<li>reinforcement learning &#8211; the system gets feedback, positive or negative, from its decisions and optimizes its behaviour accordingly</li>
</ul>
<p>We talked about classification problems a lot. Imagine an 2D space like the one below with a bunch of data points that we can intuitively group into clusters. This could represent some set of objects you&#8217;re trying to group into categories based on two measured properties, say a voice recognition system that figures out words based on pitch and length (a bad example). The question is how to draw boundaries around those clusters, especially when some points are reasonably close to two or more clusters.</p>
<p><img class="alignnone size-full wp-image-970" title="data clusters" src="http://www.2paths.com/wp-content/uploads/2009/05/clusters.png" alt="data clusters" width="489" height="477" /></p>
<p>Some unsupervised learning algorithms:</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Hierarchical_clustering">hierarchical clustering</a></li>
<li><a href="http://en.wikipedia.org/wiki/K-means">k-means</a></li>
<li><a href="http://en.wikipedia.org/wiki/Expectation-maximization_algorithm">expectation maximization</a></li>
</ul>
<p>The k-Means algorithm made the most sense to me. It works by first  picking cluster centres at random and associating each data point with a cluster according to the nearest centre. It then refines the cluster centres by computing the centroid of the points within each cluster. These two steps are repeated until the solution converges (i.e. the centres stop moving around). Unfortunately, you need to tell the algorithm how many clusters there are at the beginning and the system doesn&#8217;t always converge. These problems are general to unsupervised learning. K-means is cheap enough that you can run it multiple times with random starting centre points and compare the outcomes to increase confidence in your solution.</p>
<p>Some supervised learning algorithms:</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/K-nearest_neighbor_algorithm">k-nearest neighbours</a></li>
<li><a href="http://en.wikipedia.org/wiki/Naive_Bayes_classifier">bayesian classification</a></li>
<li><a href="http://en.wikipedia.org/wiki/Decision_tree_learning">decision trees</a></li>
<li><a href="http://en.wikipedia.org/wiki/Artificial_neural_network">neural networks</a>/<a href="http://en.wikipedia.org/wiki/Support_vector_machine">support vector machines</a></li>
</ul>
<p>The k-nearest neighbours algorithm seems pretty straightforward here. The training data are a bunch of labelled data points. For each test input, you find the k nearest labelled neighbours. The test input is then assigned to the same cluster as the majority of those k nearest neighbours. If you put the newly labelled input into the pile of reference data (i.e. let the system learn as it goes), this can make your clusters drift over time. I suspect this can get dangerous if you&#8217;re not careful.</p>
<p>Bayesian classification uses P(C|x)= P(x|C)P(C)/P(x) which sounds vaguely familiar from the old stats and physics courses. Best to think about that some more before discussing it. Decision trees looked more straightforward to read, but I think I&#8217;d have a hard time implementing something that creates its own. I&#8217;ve had very mild introductions to neural networks a few times and they always seem like a whole mess of complexity that I don&#8217;t want to deal with.</p>
<p>Bethany talked about some of the difficulties with supervised learning, such as the amount of training data and time required, along with the associated problems of overfitting or underfitting the data. If your training data are too closely coupled, you risk misclassifying input data that&#8217;s a bit further out. Similarly, if your training data are too loose, you risk problems of cluster boundaries overlapping. On the topic of the amount of data and time required, Bethany made the excellent point that people don&#8217;t like the effort required for supervised machine learning, but we spend orders of magnitude more on training babies and then marvel at how much they learn. That&#8217;s not to say that we should spend more time with machines instead of babies; rather, it&#8217;s just important to realize that even an exquisitely refined and capable machine like a human brain still needs a lot of training input before it can do anything useful.</p>
<p><a href="http://en.wikipedia.org/wiki/Reinforcement_learning">Reinforcement learning</a> algorithms fall into two categories:</p>
<ul>
<li>model-based</li>
<li>model-free</li>
</ul>
<p>Those reminded me of <a href="http://en.wikipedia.org/wiki/Model_predictive_control">model predictive control</a> and other feedback systems like <a href="http://en.wikipedia.org/wiki/PID_controller">PID</a> from the control theory courses I took in school. In the machine learning context, you also want the system to guess when it encounters unknown data. In reinforcement learning, the machine gets a particular reward or punishment for classifying/misclassifying each new input, and it classifies new input based on its own self-interest. In the model-free algorithms (e.g. <a href="http://en.wikipedia.org/wiki/Q-learning">Q-learning</a>, <a href="http://en.wikipedia.org/wiki/Function_approximation">function approximation</a>, <a href="http://en.wikipedia.org/wiki/Markov_decision_process#Policy_iteration">policy iteration</a>), the machine has some way of learning based only on the rewards and punishments of its classifications. For unknown data, they do things like guess randomly (Q-learning) or work out probabilities of reward/punishment based on past decisions (policy iteration). In the model-based  algorithms (<a href="http://jmlr.csail.mit.edu/papers/volume3/brafman02a/brafman02a.pdf">R-max</a>, <a href="http://icml2008.cs.helsinki.fi/papers/627.pdf">KWIK</a>), the machine has some model of the environment that gets refined based on input. This can take a lot of work from the programmer to determine a good model, but it sounds like a properly tuned algorithm can classify very reliably.</p>
<p>I hope I&#8217;ve described that reasonably accurately. I did a bunch of probability in various math and physics courses back in university, so while reading about these topics, they dance between understandable and just plain Greek. I also built an autonomous robot for a <a href="http://www.engphys.ubc.ca/projectlab/phys253/">course</a>, so I appreciate just how hard it is to get machines to do anything remotely intelligent-looking in the real world.</p>
<p>A big thanks to Bethany for her informative talk! I&#8217;m sure it will fuel many arguments around the water cooler.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/05/13/machine-learning/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2008 Retrospective &#8211; Our Year Remembered</title>
		<link>http://www.2paths.com/2008/12/12/2008-retrospective-our-year-remembered/</link>
		<comments>http://www.2paths.com/2008/12/12/2008-retrospective-our-year-remembered/#comments</comments>
		<pubDate>Fri, 12 Dec 2008 23:13:50 +0000</pubDate>
		<dc:creator>Judi</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Culture]]></category>
		<category><![CDATA[Events]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[People]]></category>
		<category><![CDATA[curling]]></category>
		<category><![CDATA[social]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=362</guid>
		<description><![CDATA[This year we made a point of making time to reflect on things, not just at the end of the year, but all the way through it.  At the End of a project, the End of a quarter, sometimes even the end of a conversation.  We adopted a new routine of hosting Clearinghouse meetings to [...]]]></description>
			<content:encoded><![CDATA[<p>This year we made a point of making time to reflect on things, not just at the end of the year, but all the way through it.  At the End of a project, the End of a quarter, sometimes even the end of a conversation.  We adopted a new routine of hosting Clearinghouse meetings to reflect on how we are functioning on a team.  And we continued validating our past efforts and identifying new goals through our quarterly meetings, governed by the Rockefeller Habits.  We dedicated ourselves to project retrospectives, rather than calling them post-mortems.  Always the goal being to improve.   So naturally, the year would not be complete with a 2008 retrospective.  To reflect on what can be improved, and more importantly, to celebrate our successes and blessings!</p>
<p>We experienced what could best be described as an illuminating and transformational year at 2Paths.   From changes to our processes and tools to the diversification of our team demographic, to teaming up with some fabulous new valued partners and clients, change abounded.  And there’s not a soul among us who would argue that it wasn’t for the better.  As our year concludes, my 2008 retrospective blog finds no shortage of things to reminisce about.   Sit back and enjoy.</p>
<p>From a people perspective, the team welcomed 2 new babies into our extended family.  Congratulations to Michal and Lori on the birth of baby Cohen, and congratulations to Ricky and Lin on their second addition to the family, Lucas.  I personally bought a house and got married to the love of my life, Willy the caveman, in a small Whistler wedding. Garret married the love of his life, the beautiful Priscille, in grand fashion Winnipeg-style.  2Paths gladly welcomed new members to the team Omar Khan, Sandra Widjaja, Gary Meehan, Tim Oxenford, and. Kari Braaten.  And last, and very important &#8211; we celebrated Michal Urbanski’s 5-year anniversary with the company by sending him on kite-board lessons in Squamish.  He returned exhilarted and intact, thank goodness.</p>
<p>Processes and tools were areas that 2Paths focused heavily on in 2008, recognizing that these will differentiate us from our competition in the future.  We honed in on our existing skills by cultivating 3 certified scrum-masters (Lorill, Geoff and Omar).   Scrum (a brief daily morning developer meeting) has been pivotal to our successful software projects.   Someone likened it to inviting your mother-in-law to live with you – she may not fix everything, but she will most certainly point our everything that needs to be fixed.  Then it’s just about the fixing.    Next, the team continued to examine open source software tools and added some more to our repertoire, including Grails, Hudson, and Groovy, to name a few.   To ensure we got the latest on emerging technologies and uses of technologies, we also sent 2Pathsians to attend conferences on Geo-spatial technology, the Semantic Web, QCon (a conference of technical depth and enterprise focus), No Fluff Just Stuff, and Agile Methodologies.</p>
<p>In the spring, we moved our office to the Arthur Erikson Waterfall building by Granville Island from Gastown.   In addition to loving the view, we have been enjoying lunch at Go Fish, the NoodleBox, and Sushi Go, not to mention whipping up concoctions in our gourmet kitchen.  After our first pay cheque in the new location, our Synesso Cyncra coffee machine made its proud appearance in our kitchen providing java nirvana for everyone to enjoy.  There has been talk of a group membership to Caffeine Anonymous, but so far we’ve been holding out alright.</p>
<p>From a team-building perspective, we did a TON of fun things together this year.  Our long-standing tradition of playing together, letting off some steam, and laughing is our real secret to strong teamwork.  Here’s the illustrious list of our 2008 fun:</p>
<p>-    We bonded over some spring-skiing/riding at Grouse mountain, followed by beer on the patio in the glorious sun;<br />
-    We learned to Curl at Marpole Curling;<br />
-    We tested our trust in each other during rock-climbing;<br />
-    We golfed at Gleneagles and BBQed at Judi’s new home;<br />
-    We enjoyed some fine-dining at Chambar;<br />
-    We participated in Bike to Work week<br />
-    We enlisted a yoga instructor to lead us through weekly hatha yoga practices on the new roof-top patio in the summer, and in our boardroom in the winter.</p>
<p>One of our major projects has been with the &#8220;Insert name of international organization which we will shortly be revealing&#8221; on an application that has global impact and countless social good benefits.  It gave us an opportunity to go globetrotting to the likes of Paris and London.  Better yet, it presented a chance to work in concert with partners, Gagan Diesh and Kaaren Schulz of DesignStamp, <a title="DesignStamp" href="http://www.designstamp.com/" target="_blank">http://www.designstamp.com/,</a>whom we’ve grown to love and cannot do without.  We were also glad to continue our solid working relationship with our valued client, Carlson Wagonlit Travel, <a class="wp-caption-dd" title="CWT" href="http://www.carlsonwagonlit.com/en" target="_blank">http://www.carlsonwagonlit.com/en</a>.  And, we played pretend, and acted like a client to ourselves, dictating our own requirements towards an internal initiative which we will may be talking about a lot more in 2009.</p>
<p>So, as you can see we have been very busy this year.  Not just building web applications that make complicated problems palatable and bite-size, but also in being good to ourselves and to each other.   And we look forward to an exciting 2009, filled with market improvements, more work with our valued partners and clients, and at the very least, some good conversations, hopefully with you, over a fancy-pants coffee from our cool espresso machine.  Have a safe and happy holiday season, everyone!</p>
<div id="attachment_374" class="wp-caption alignnone" style="width: 486px"><a href="http://www.2paths.com/wp-content/uploads/2008/12/2paths_curling_2008_low.jpg"><img class="size-medium wp-image-374" title="2paths_curling_2008_low" src="http://www.2paths.com/wp-content/uploads/2008/12/2paths_curling_2008_low.jpg" alt="2Paths Team" width="476" height="317" /></a><p class="wp-caption-text">2Paths Team</p></div>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2008/12/12/2008-retrospective-our-year-remembered/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

