From a13de4a461b345b1fe12eb578632c6241c30d4cd Mon Sep 17 00:00:00 2001 From: eugene wu Date: Fri, 13 Jan 2012 16:41:29 -0500 Subject: [PATCH] fleshed out day4 --- Makefile | 3 ++- day4/README.md | 20 ++++++++++++++------ lectures/day4.pptx | Bin 2163391 -> 2163738 bytes 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index f43f967..f2fa3a0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ all: labs labs: day0/README.md day1/README.md day2/README.md day3/regression.py day3/hypothesis_testing.py - cp -r ./day0 ./day1 ./day2 ./day3 ./day4 /tmp/dataiap_html/ + cp -r ./day0 ./day1 ./day2 ./day3 ./day4 ./day5 /tmp/dataiap_html/ python resources/markdown/markdown_headers.py day0/README.md /tmp/dataiap_html/day0/index.html python resources/markdown/markdown_headers.py day1/README.md /tmp/dataiap_html/day1/index.html python resources/markdown/markdown_headers.py day2/README.md /tmp/dataiap_html/day2/index.html @@ -9,4 +9,5 @@ labs: day0/README.md day1/README.md day2/README.md day3/regression.py day3/hypot python resources/markdown/markdown_headers.py day4/README.md /tmp/dataiap_html/day4/index.html python resources/hacco/hacco.py day3/regression.py -d /tmp/dataiap_html/day3/ #/tmp/dataiap_html python resources/hacco/hacco.py day3/hypothesis_testing.py -d /tmp/dataiap_html/day3/ #/tmp/dataiap_html + python resources/hacco/hacco.py day5/mapreduce.py -d /tmp/dataiap_html/day5/ #/tmp/dataiap_html echo "\n\nnow do: \n\tgit checkout gh-pages\n\tcp -r /tmp/dataiap_html/* .\n" diff --git a/day4/README.md b/day4/README.md index 446e127..b6c915f 100644 --- a/day4/README.md +++ b/day4/README.md @@ -84,7 +84,13 @@ One intuition is that if a term is relevant to a folder, then the emails in the terms_in_email = e['text'].split() # split the email text using whitespaces folder_tf[e['folder']].update(terms_in_email) -The above code iterates over all the emails and splits the message bodies. It then retrieves the `Counter` for the email's folder (`folder_tf[e['folder']]`), and increments the counter for each term in the email. By the end of this loop, we should have term frequency values for each folder. +The above code iterates over all the emails and splits the message bodies. It then retrieves the `Counter` for the email's folder (`folder_tf[e['folder']]`), and increments the counter for each term in the email. By the end of this loop, we should have term frequency values for each folder. Something similar to (ignore the actual values): + + 'inbox' --> { 'conference': 10, 'to': 40, 'call': 20, …} + 'sec_panel' --> { 'meeting': 10, 'sec': 20, … } + +Now we can iterate through each of the items in `folder_tf`, sort the counter, and print the top 20 terms for each folder. + for folder, counter in folder_tf.items(): print folder @@ -137,14 +143,17 @@ The first thing we want to do is compute the number of folders that contain each # this collects all of the terms in each folder terms_per_folder[e['folder']].update(terms_in_email) -The above code reads each email dictionary, extracts the words using `e['text'].split()`, and adds it to the per-folder set (`terms_per_folder[e['folder']]`). We used a `set` to remove duplicate terms. Now our job is to count the number of folders that contain each term. `Counter` is similar to a `dict` but keeps track of how many times a key is added and stores it as the key's value. Each iteration retrieves the terms for a given folder, and adds them all to the counter. +The above code reads each email dictionary, extracts the words using `e['text'].split()`, and adds it to the per-folder set (`terms_per_folder[e['folder']]`). We used a `set` to remove duplicate terms. Now our job is to count the number of folders that contain each term. + + +Each iteration retrieves the terms for a given folder, and adds them all to the counter. allterms = Counter() for folder, terms in terms_per_folder.iteritems(): # this will increment the counter value for each term in `terms` allterms.update(terms) -Great, now we have a dictionary, `all terms`, that maps each term to the number of folders it's in. Now let's actually compute the idf. +Great, now we have a dictionary, `allterms`, that maps each term to the number of folders it's in. Now let's actually compute the idf. Notice that we add `1.0` to the denominator to avoid divide by zero errors and so that the denominator is a float. Python truncates integers by rounding down, so if the numerator and denominator are both `int`s, you could end up with a lot of zeros (e.g., 1/2 = 0). The log of 0 is undefined. idfs = {} nfolders = len(terms_per_folder) # the number of keys should be the number of folders @@ -303,10 +312,9 @@ The main idea is that folders that share terms with high tf-idf values are proba Let's say we have a total of 1000 terms across all of the email senders. Every folder has a tf-idf score for each of the 1000 terms (some may be 0). We could model all of the scores of a folder as a 1000-dimensional vector, where each dimension corresponds to a term, and the distance along the dimension is the term's tf-idf value. The cosine of the two email senders' vectors measures the similarity between them. Suppose the vectors were A and B. Then the cosine would be: - cos(A,B) = (A·B) / ((||A|| * ||B||) + 1) - -The numerator is the sum of all the tf-idf terms the senders have in common. The denominator is the product of the [vector norms](https://en.wikipedia.org/wiki/Magnitude_(mathematics)#Euclidean_vectors). We typically add `1` in case the vectors are both 0. + cos(A,B) = (A·B) / ((||A|| * ||B||) + 1.0) +The numerator is the sum of all the tf-idf terms the senders have in common. The denominator is the product of the [vector norms](https://en.wikipedia.org/wiki/Magnitude_(mathematics)#Euclidean_vectors). Once again, we add `1` in case either vector is 0. A `cos(A,B)` of 1 means they are identical and 0 means the senders are independent from each other (the vectors are orthogonal). Here is how we would calculate the cosine similarity of two folders, using the `tfidfs` dictionary you computed in the previous section. We assume that `tfidfs` is a dictionary where each value is a list of `(term, tfidf-score)` pairs diff --git a/lectures/day4.pptx b/lectures/day4.pptx index d7789b85c712f457273a2cd1ab66c098ea9a6f1b..9cb3d22809cf3941352c9e58031628b215d2aba2 100644 GIT binary patch delta 9093 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