All posts by Telvis

Using Mahout to Group Religious Twitter Users

This post describes a method to group religious tweeters by their scripture reference patterns using the kmeans clustering algorithm. I use Apache Pig to process data retrieved by Habakkuk and Mahout to perform clustering.

Clustering Primer

Clustering consists of representing an entity (e.g. tweeter) as a feature vector, choosing a similarity metric and then applying a clustering algorithm to group the vectors based on similarity.

Feature Vector Example

Suppose we have two tweeters. Tweeter #1 tweets a reference to Exodus 1:1 and tweeter #2 tweets a reference to Genesis 1:1. Each tweeter can be represented as a "feature vector" that counts the number of references to a book by that tweeter.

Positions (Genesis Reference Count, Exodus Reference Count)
Tweeter #1: (0, 1)
Tweeter #2: (1, 0)

Each book has an assigned position within the feature vector. The first vector shows tweeter #1 referenced Genesis zero (0) times and Exodus one (1) time. The second vector shows tweeter #2 referenced Genesis one (1) time and Exodus zero (0) times. The vector can be extended to count every book or even every scripture reference. There are 66 books and 31102 bible verses in the KJV-based bible. Because the number is fixed, they can easily be mapped to integers used as vector position.

Similarity Metric

There are many methods for calculating the similarity between to vectors. Cosine similarity can measure the angle between vectors so smaller angles imply similar vectors. Euclidean distance measures the distance between vectors so "close" vectors are deemed similar. Other measures include Tanimoto and Manhattan distance. There are many techniques, consult the webernets for more details.

Kmeans Clustering

As the number of vectors grow, it becomes computationally expensive to calculate the similarity of all vectors in a data set. Kmeans clustering is an efficient algorithm to identify similar groups in large data sets.

The algorithm groups n vectors into k clusters of similar vectors. A cluster can be thought of as a geometric circle where the center of the circle defines a centroid point. The kmeans algorithm randomly picks k initial centroid points and assigns all n vectors to a cluster based on the nearest centroid. Next, a new round begins where new centroid points are calculated based on the mean of the vectors assigned to the k clusters in the previous round (hence k-means). The algorithm stops when the centroid points from subsequent rounds are 'close enough' or the maximum number of rounds have been reached.

Performing Kmeans clustering using Hadoop

Two Hadoop-based applications are used to perform this analysis. First, the raw tweets stored in JSON must be processed and converted into feature vectors using Apache Pig. Second, kmeans clustering is performed on the feature vectors using Apache Mahout.

Feature Extraction

Habakkuk tweets are "tagged" with book (e.g. genesis) and bibleverse (e.g genesis 1:1). The pig script below describes a data flow for transforming the habakkuk tweets into book feature vectors per tweeter.   The load statement uses elephant-bird to load the raw JSON from disk into PIG. The join statement uses the book name to correlate a book id with each tweet. The book id serves as the vector position. The foreach counts the book references per tweeter and the group by organizes the records by tweeter. Finally, the store uses elephant-bird to store the data in a format mahout can read.

This is just a code snippet. Check github for the full script.


-- load habakkuk json data, generate screenname and book reference
tweets = load '$data' using com.twitter.elephantbird.pig.load.JsonLoader();
filtered = foreach tweets generate
        (chararray)$0#'screenname' as screenname,
        (chararray)$0#'book' as book;

-- load book ids for join
bookids = load '$book_dict' as (book:chararray, docfreq:int, bookid:int);
filtered = join bookids by book, filtered by book;

-- group using tuple(screenname,book) as key
by_screen_book = group filtered by (screenname, bookids::bookid);

-- generate counts for each screenname, book
book_counts = foreach by_screen_book {
    generate group.screenname as screenname,
         group.bookids::bookid as bookid,
         COUNT(filtered) as count;
}

-- group by screenname: bag{(screenname, bookid, count)}
grpd = group book_counts by screenname;

-- nested projection to get: screenname: entries:bag{(bookid, count)}
-- uses ToTuple because SEQFILE_STORAGE expects bag to be in a tuple
vector_input = foreach grpd generate group,
       org.apache.pig.piggybank.evaluation.util.ToTuple(book_counts.(bookid, count));

-- store to sequence files
STORE vector_input INTO '$output' USING $SEQFILE_STORAGE (
  '-c $TEXT_CONVERTER', '-c $VECTOR_CONVERTER -- -cardinality 66'
);

Mahout in Action

The example mahout command below uses kmeans to generate 2 clusters (-k 2) and choose the initial clusters at random and place them in kmeans-initial-clusters. The maximum number of iterations is 10 (-x). Kmeans will use cosine distance measure (-dm) and a convergence threshold (-cd) of 0.1, instead of using the default value of 0.5 because cosine distances lie between 0 and 1.

$ mahout kmeans -i book_vectors-nv  \
-c kmeans-initial-clusters -k 2 -o clusters \
-x 10 -ow -cl -dm org.apache.mahout.common.distance.CosineDistanceMeasure \
-cd 0.1

Results

Finally, the clusterdump command can print information about the clusters such as the top books and the tweeters in the cluster. These clusters were generated with a small sample with only 10 tweets.

$ mahout clusterdump -d join_data/book.dictionary \
      -dt text -s clusters/clusters-1 \
      -p clusters/clusteredPoints -n 10 -o clusterdump.log
$ cat clusterdump.log
CL-0 ...
    Top Terms: 
        luke                                    =>  0.4444444444444444
        matthew                                 =>  0.3333333333333333
        john                                    =>  0.2222222222222222
        galatians                               =>  0.1111111111111111
        philippians                             =>  0.1111111111111111
    Weight:  Point:
    1.0: Zigs26 = [luke:1.000]
    1.0: da_nellie = [john:1.000]
    1.0: austinn_21 = [luke:1.000]
    1.0: YUMADison22 = [luke:1.000]
    1.0: chap_stique = [galatians:1.000]
    1.0: ApesWhitelaw = [matthew:2.000, john:1.000]
    1.0: alexxrenee22 = [luke:1.000]
    1.0: AbigailObregon3 = [philippians:1.000]
    1.0: thezealofisrael = [matthew:1.000]
VL-7 ...
    Top Terms: 
        ephesians                               =>                 1.0
    Weight:  Point:
    1.0: Affirm_Success = [ephesians:1.000]

The results show 2 clusters. 1 cluster has 9 tweeters with the top books as luke, matthew, john, galations and phillippians. The second cluster has 1 tweeter with ephesians as a top book. Obviously, YMMV with different convergence thresholds, data and distance metrics.

References

I recommend the following books to anyone learning Pig and Mahout.

The storm is over

I hope we stay positive when times get tough - because the storms of life don't last forever.

(Genesis 8:10-11 NIV) [Noah] waited seven more days and again sent out the dove from the ark. When the dove returned to him in the evening, there in its beak was a freshly plucked olive leaf! Then Noah knew that the water had receded from the earth.

grow where we are planted

I hope we grow where we are planted - even unexpected setbacks can work for our benefit.

(Jeremiah 29:7 KJV) And seek the peace of the city whither I have caused you to be carried away captives, and pray unto the Lord for it: for in the peace thereof shall ye have peace.

Installing WPScan

WPScan is a useful tool for finding vulnerabilities in your WordPress blog. Here are the steps I followed to install wpscan on centos 6.3.

1. Install Ruby 1.9.x and RubyGems 1.8.24 from the source: http://www.rosehosting.com/blog/how-to-install-ruby-1-9-3-and-rubygems-1-8-24-on-centos-6-2/

2. I also needed to install 'readline'

 sudo gem install rb-readline

3. Steps to install and run WPScan: https://github.com/wpscanteam/wpscan/blob/master/README.md

Valentine’s Day Scripture Usage on Twitter

heartsI'd like to know which bible verses were popular on twitter on Valentine's Day - 2013. I've added elasticsearch support to my project habakkuk to store religious tweets for analysis. It turns out to be a collection of love scriptures. Very Nice!

Results

  1. John 3:16  "For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life."
  2. 1  John 4:19 "We love because he first loved us."
  3. 1 Corinthians 13:4 "Love is patient, love is kind. It does not envy, it does not boast, it is not proud."
  4. 1 Corinthians 13:13 "And now these three remain: faith, hope and love. But the greatest of these is love."
  5. John 14:23  "Jesus replied, “Anyone who loves me will obey my teaching. My Father will love them, and we will come to them and make our home with them."
  6. Psalm 37:23 "The steps of a good man are ordered by the Lord: and he delighteth in his way."
  7. John 15:13 "Greater love has no one than this: to lay down one’s life for one’s friends."
  8. 1 Corinthians 13:7  "[Love]  always protects, always trusts, always hopes, always perseveres."
  9. Philippians 4:13 "I can do all this through him who gives me strength."
  10. Romans 5:8 "But God demonstrates his own love for us in this: While we were still sinners, Christ died for us."

Technical Details

To get the data for Valentines day 2013, I execute the following

$ python bible_facet.py -s 2013-02-14 -e 2013-02-15

It shows the raw query json for the Elasticsearch Query DSL. Below that it shows the top 10 bible references. Its basically just a faceted search on the bibleverse field.

    {
      "query": {
        "filtered": {
          "filter": {
            "range": {
              "created_at_date": {
                "to": "2013-02-15T00:00:00",
                "include_upper": false,
                "from": "2013-02-14T00:00:00"
              }
            }
          },
          "query": {
            "match_all": {}
          }
        }
      },
      "facets": {
        "bibleverse": {
          "terms": {
            "field": "bibleverse",
            "order": "count",
            "size": 10
          }
        }
      },
      "size": 0
    }

Total bibleverse 1568
  john 3:16 85
  i_john 4:19 42
  i_corinthians 13:4 31
  i_corinthians 13:13 24
  john 14:23 19
  psalm 37:23 18
  john 15:13 18
  i_corinthians 13:7 18
  philippians 4:13 17
  romans 5:8 15

 

Project Details

Please reference the elasticsearch readme in habakkuk for details regarding the query that obtained this data. Special thanks to http://www.biblegateway.com for the NIV Bible text. I should also shout out to openbible.info, his twitter Lent blog is awesome.

hot tea

We are like tea bags, we don't know what we're capable of until we get into hot water.

(Daniel 3:26, 27 NIV) "Nebuchadnezzar then approached the opening of the blazing furnace and shouted, “Shadrach, Meshach and Abednego, servants of the Most High God, come out! Come here!” So Shadrach, Meshach and Abednego came out of the fire, and the satraps, prefects, governors and royal advisers crowded around them. They saw that the fire had not harmed their bodies, nor was a hair of their heads singed; their robes were not scorched, and there was no smell of fire on them."

no gain

Some say "no pain, to gain". We can also say "no love, no gain"

1 Corinthians 13:3 NIV "If I give all I possess to the poor and give over my body to hardship that I may boast, but do not have love, I gain nothing."

knowledge

How much we love is more important than how much we know.

1 Corinthians 13:2 NIV "If I have the gift of prophecy and can fathom all mysteries and all knowledge, and if I have a faith that can move mountains, but do not have love, I am nothing."

gong

A life without love is like "The Gong Show" with no Show.

1 Corinthians 13:1 (NIV) If I speak in the tongues of men or of angels, but do not have love, I am only a resounding gong or a clanging cymbal.