Thoughts on computation, social science, and lifehacking
from an up-and-coming data scientist.
Showing posts with label text-analysis. Show all posts
Showing posts with label text-analysis. Show all posts
Friday, March 15, 2013
Hacking Scrabble with Cascalog
We've barely met, but I am in love with Cascalog. So elegant. So powerful. So easy to ship from testing to staging to production. So perfect for the workflow abstraction where most data science happens. Let me count the ways...
The only downside is the shortage of documentation and examples. There's an active google group, yes, but only a handful of cascalog questions on StackOverflow. So I thought I'd pay things forward by tossing out a bunch of toy examples that I used in my early experiments with the language. These examples cover clojure's basic syntax and regular expressions, plus cascalog's filtering and basic aggregations. I'll save tests, joins, etc. for a later post.
All these examples use the Scrabble Word dictionary, which provides a nice bite-sized playground for lots of MapReduced fun. The Tournament Word List is a list of all the legal words for Scrabble in U.S. tournament play. Words are in all caps and separated by line breaks. I downloaded and saved the file as /Users/agong/Data/scrabble-list-twl06.txt. To speed up testing, I also sampled rows at random to create a 10% sample (/Users/agong/Data/scrabble-list-twl06-1pct.txt) and a 1% sample (/Users/agong/Data/scrabble-list-twl06-1pct.txt).
First, let's try a vanilla query: just print all the lines in the file. This will help make sure clojure and hadoop are set up properly.
; vanilla query: print all lines
(?<- (stdout) [?word]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") ?word))
If that works, we can do some basic filtering.
; basic filter: print lines starting with 'z'
(?<- (stdout) [?word]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") :> ?word)
(clojure.core/re-matches #"Z.*" ?word) ))
So far so good. We have Hello world, and basic filters check out. Let's use the count aggregation to count the number of words in the sample:
; count words
(?<- (stdout) [?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") ?word)
(cascalog.ops/count ?count) ))
Now let's build up to letter counts (a twist on the classic MapReduce word count example.) To get there, we need to define a map cat operator:
; split words into letters
; https://github.com/sritchie/cascalog-class/blob/master/src/cascalog_class/core.clj
(defmapcatop split
"Accepts a word and emits a single 1-tuple for each letter."
[word]
(clojure.core/re-seq #"." word))
; count letters
(require `cascalog.ops)
(?<- (stdout) [?letter ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") ?word)
(split ?word :> ?letter) (cascalog.ops/count ?count))
With these very simple tools, we can do a surprising number of interesting things. Let's create a function to do n-gram counting, modified slightly from this example.
; count ngrams
(defmapcatop ngrams
"Accepts a word and n-parameter and emits a single 1-tuple for each n-gram."
[word n]
(map my-join (partition n 1 word)))
Now we can count bigrams and trigrams:
; Character bigrams
(?<- (stdout) [?letter ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") ?word)
(ngrams ?word 2 :> ?letter) (cascalog.ops/count ?count))
; Character trigrams - no sampling
(?<- (stdout) [?letter ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06.txt") ?word)
(ngrams ?word 3 :> ?letter) (cascalog.ops/count ?count))
Let's get word lengths...
; get word lengths
(defmapcatop get-len
"Accepts a word and emits a single 1-tuple with its length."
[word]
[(.length word)])
; distribution of word lengths
(?<- (stdout) [?length ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") ?word)
(get-len ?word :> ?length)
(cascalog.ops/count ?count))
I guess it makes sense that the longest words in scrabble are 15 letters long...
Now let's combine filters and aggregators. We need to create a filter operation for this...
(deffilterop len-n? [word n]
"Keep only words not of length n"
(= (.length word) n))
; distribution of lengths for 7-letter words (a silly example to make sure it worked)
(?<- (stdout) [?length ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") :> ?word)
(len-n? ?word 7)
(get-len ?word :> ?length)
(cascalog.ops/count ?count))
Vowel dumps are an important part of scrabble tactics: words that let you get rid of extra vowels without wasting a turn to exchange your hand. First, we can do pure vowel dumps -- words that include no consonants at all. (We'll grant Y vowel status, even though it only qualifies sometimes.)
; vowel dumps
(deffilterop pure-vowel-dump? [word]
"Keep only words containing only vowels (and sometimes y)"
(every?
(into #{} (clojure.core/re-seq #"." "AEIOUY"))
(clojure.core/re-seq #"." word)))
(?<- (stdout) [?word]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06.txt") :> ?word)
(pure-vowel-dump? ?word))
Hm. There aren't very many of these pure vowel dumps. How about a more flexible function that calculates the proportion of vowels in the word?
(defmapop vowel-ratio [word]
(/
(.length (clojure.string/replace word #"[AEIOUY]" ""))
(.length word)))
Now we can look up words with 70% or more vowels. For good measure, let's show the ratio of vowels in each.
;Ratios for vowel dumps
(?<- (stdout) [?word ?ratio]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-1pct.txt") :> ?word)
(vowel-ratio ?word :> ?ratio )
(> ?ratio 7/10))
Among these vowel dumps, what's the distribution of lengths? The distribution of letters?
;Length distribution for vowel dumps
(?<- (stdout) [?length ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06.txt") :> ?word)
(vowel-ratio ?word :> ?ratio )
(> ?ratio 7/10)
(get-len ?word :> ?length)
(cascalog.ops/count :> ?count))
;Letter distribution for vowel dumps
(?<- (stdout) [?letter ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06.txt") :> ?word)
(vowel-ratio ?word :> ?ratio )
(> ?ratio 7/10)
(split ?word :> ?letter)
(cascalog.ops/count :> ?count))
Okay, a few more fun examples. Palindromes...
;Palindromes
(deffilterop palindrome? [word]
"Return true for palindromes"
(= word (clojure.string/reverse word)) )
(?<- (stdout) [?word]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06.txt") :> ?word)
(palindrome? ?word))
;Distribution of letters in palindromes
(?<- (stdout) [?letter ?count]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06.txt") :> ?word)
(palindrome? ?word)
(split ?word :> ?letter)
(cascalog.ops/count ?count))
Not a whole lot of these either. What about vowel-consonant palindromes? That is, words where the back-to-front and front-to-back ordering of vowel consonants is the same?
;Vowel-consonants palindromes
(deffilterop vc-palindrome? [word]
"Return true for vowel-consonant palindromes"
(let [vc-word (clojure.string/replace (clojure.string/replace word #"[AEIOUY]" "A") #"[^A]" "B")]
(= vc-word (clojure.string/reverse vc-word)) ))
(?<- (stdout) [?word]
((lfs-textline "/Users/agong/Data/scrabble-list-twl06-10pct.txt") :> ?word)
(vc-palindrome? ?word))
There we go! From ANALYZE to ZYMOSIS. (Your words will probably be different, because of the sampling. But they will still be v/c palindromes. :) )
I had fun spending an afternoon putting together these examples. It was a great, well-bounded way to get my feet wet with clojure and cascalog.
HTH
Thursday, May 17, 2012
Nifty tools for playing with words
Here are a bunch of sites I use to play with words -- whether brainstorming or trying to accomplish something specific with text analysis.
A rhyming dictionary. Helpfully splits up the word list by syllables, so you can finish that sonnet you've been working on.
Here's a nifty little site for generating portmanteaus (word splices): http://www.werdmerge.com/
http://www.leandomainsearch.com: generates themed domain names, and checks to make sure they're unclaimed by URL squatters.
Online lorem generator. Here's the same thing in python.
Markov text generation: http://www.beetleinabox.com/markov.html.
Permute words and letters. This seems less useful to me... It gives all the combinations, not just the ones that make some kind of sense.
Lavarand used to do random haikus and corporate memos, but it looks like they've broken down.
Google ngrams on AWS public data sets. These are combinations of words that commonly co-occur in English.
Yes, yes. And then there's wordle. Too pretty for the rest of us.
What else belong on this list?
A rhyming dictionary. Helpfully splits up the word list by syllables, so you can finish that sonnet you've been working on.
Here's a nifty little site for generating portmanteaus (word splices): http://www.werdmerge.com/
http://www.leandomainsearch.com: generates themed domain names, and checks to make sure they're unclaimed by URL squatters.
Online lorem generator. Here's the same thing in python.
Markov text generation: http://www.beetleinabox.com/markov.html.
Permute words and letters. This seems less useful to me... It gives all the combinations, not just the ones that make some kind of sense.
Lavarand used to do random haikus and corporate memos, but it looks like they've broken down.
Google ngrams on AWS public data sets. These are combinations of words that commonly co-occur in English.
Yes, yes. And then there's wordle. Too pretty for the rest of us.
What else belong on this list?
Friday, December 30, 2011
Authorship of Ron Paul newsletters -- statistical text analysis and Bayesianism
In the midst of all the hullabaloo about Ron Paul's decades-old racists newsletters, and his denial that he "never read that stuff," I ran across an interesting attempt to adding some data to the discussion. Filed under "pointless data exercises" and "politics," blogger Peter Larson has used text analysis to compare his blog, Ron Paul's recent speeches, and the original newsletters. He calls his results a smoking gun, with a question mark tacked on, and argues that Ron Paul wrote most of the original newsletters.
Before I go on, let me say that this is a really cool application. Instead of the he-said-she-said debate that's running in the media, this piece brings some actual data to bear on the conversation. Bravo!
That said, now I'm going to harp on statistics and inference. The problem with Larson's analysis is that he never addresses the question, "If Ron Paul didn't write the newsletters, who did?" Without answering that question, and putting some probabilities behind it, it's going to be very hard for text analysis to prove the issue one way or another. (Larson admits this on his blog.) Right now, his analysis proves that between himself and Ron Paul, Paul is much more likely to have written the letters. Not exactly a smoking gun.
That said, it's interesting data. For the record, I'm mostly convinced. In my mind, the statistics are flawed, but they still lend some weight against Paul. Oddly enough, Larson's finding that several of the letters were probably *not* written by Ron Paul was particularly persuasive. It feels human and messy, the way I would expect this kind of thing to be.
Final thoughts, mainly intended for my statistically minded friends: yes, this is an unabashedly Bayesian perspective. I'm demanding priors, which probably can't be specified to anyone's satisfaction.
IMHO, the frequentist approach has even deeper problems. From a frequentist perspective (which is where Larson's original, and deeply confusing p-values come from.) we use Paul's recent speeches and writings to estimate some parameters of his current text-generating process. We then compare the newsletters to that process and estimate the probability that the older text was generated by the same process.
Problem: we *know* the old text was not generated by the same process. It was written (allegedly) by a younger Ron Paul, on different topics, speaking into a different political climate. Without a broader framework, it's impossible to determine whether the differences are important. The Bayesian approach provides a direct way of assessing that framework. The frequentist approach doesn't -- at least not that I can see, without jumping through a lot of hoops -- and in the meantime, it obscures the test that's actually being conducted.
Before I go on, let me say that this is a really cool application. Instead of the he-said-she-said debate that's running in the media, this piece brings some actual data to bear on the conversation. Bravo!
That said, now I'm going to harp on statistics and inference. The problem with Larson's analysis is that he never addresses the question, "If Ron Paul didn't write the newsletters, who did?" Without answering that question, and putting some probabilities behind it, it's going to be very hard for text analysis to prove the issue one way or another. (Larson admits this on his blog.) Right now, his analysis proves that between himself and Ron Paul, Paul is much more likely to have written the letters. Not exactly a smoking gun.
That said, it's interesting data. For the record, I'm mostly convinced. In my mind, the statistics are flawed, but they still lend some weight against Paul. Oddly enough, Larson's finding that several of the letters were probably *not* written by Ron Paul was particularly persuasive. It feels human and messy, the way I would expect this kind of thing to be.
Final thoughts, mainly intended for my statistically minded friends: yes, this is an unabashedly Bayesian perspective. I'm demanding priors, which probably can't be specified to anyone's satisfaction.
IMHO, the frequentist approach has even deeper problems. From a frequentist perspective (which is where Larson's original, and deeply confusing p-values come from.) we use Paul's recent speeches and writings to estimate some parameters of his current text-generating process. We then compare the newsletters to that process and estimate the probability that the older text was generated by the same process.
Problem: we *know* the old text was not generated by the same process. It was written (allegedly) by a younger Ron Paul, on different topics, speaking into a different political climate. Without a broader framework, it's impossible to determine whether the differences are important. The Bayesian approach provides a direct way of assessing that framework. The frequentist approach doesn't -- at least not that I can see, without jumping through a lot of hoops -- and in the meantime, it obscures the test that's actually being conducted.
Thursday, December 29, 2011
Job posting on Polmeth: Research Officer in Quantitative Text Analysis
This came across the polmeth mailing list a couple hours ago. Looks interesting, but the pay isn't exactly competitive for candidates with "a postgraduate degree in computer science, computational linguistics, or possibly a cognate social science discipline."
Job opening:
Research Officer in Quantitative Text Analysis
Duration: 24 months
Start Date: 1 March 2012 or as soon as possible thereafter
Salary: £31,998 – £38,737 p.a. incl.
Applications are invited for the post of Research Officer, to assist with a principal research officer for the European Research Council funded grant Quantitative Text Analysis for the Social Sciences (QUANTESS), working with Professor Kenneth Benoit (PI).
The research officer’s general duties and responsibilities will be to work with text and the computer organization, storage, processing, and analysis of text. These tasks will involve a combination of programming, database work, and statistical computation. The texts will be drawn from social, political, legal, and commercial examples, and will have a primarily social science focus. The research officer will be expected to work with existing tools for text analysis, actively participate in the development of new tools, and participate in the application of these tools to the social scientific analysis of textual data.
The successful applicant will be expected to possess advanced skills and experience with computer programming, especially the ability use a language used in text processing such as Python; familiarity with SQL; and experience with the R statistical package or the ability to learn R.
The successful candidate should have a postgraduate degree in computer science, computational linguistics, or possibly a cognate social science discipline and have an interest in text analysis and quantitative linguistics, have a knowledge of social science statistics and have worked in a research environment previously.
To apply for this post please go to http://www.lse.ac.uk/JobsatLSE and select “Visit the ONLINE RECRUITMENT SYSTEM web page”. If you have any queries about applying on the online system, please call 020 7955 6656 or email hr.jobs@lse.ac.uk quoting reference 1223243.
Closing date for receipt of applications is: 31 January 2012 by 23.59 (UK time).
Please access the attached hyperlink for an important electronic communications disclaimer: http://lse.ac.uk/emailDisclaimer
******************************
****************************
Political Methodology E-Mail List
Editors: Diana O'Brien <dzobrien@wustl.edu>
Jon C. Rogowski <rogowski.jon@gmail.com>
**********************************************************
Send messages to polmeth@artsci.wustl.edu
To join the list, cancel your subscription, or modify
your subscription settings visit:
http://polmeth.wustl.edu/polmeth.php
**********************************************************
Monday, November 7, 2011
Resources on NLP sentence diagramming
Here are some notes from a recent search for resources on automatic sentence diagramming. I was looking for code/software to diagram sentences automatically, ideally in python.
Vocab
Also, as far as I can tell, linguists, grammarians, and English majors call sentence diagrams "Reed-Kellogg" diagrams. NLP and computer science types call the diagrams "parse trees" or "concrete syntax trees," and produce them (usually) using probabalistic context free grammars (PCFGs).
Search result
If you want code that just works, the Stanford Parser looks like the place to start. It's in java, but I'm sure that won't be too much of a problem, because you can call it from the command line.
Python's NLTK might also work, but there are lots of different parsers, and it's not clear whether and how much training they require.
There are various other software packages out there -- some of them online -- but I doubt they'd support much volume, and batching would be a pain.
Background
http://en.wikipedia.org/wiki/Sentence_diagram
http://en.wikipedia.org/wiki/Parse_tree
http://en.wikipedia.org/wiki/Context-free_grammar
NLTK
http://nltk.googlecode.com/svn/trunk/doc/book/ch08.html
http://www.cs.bgu.ac.il/~elhadad/nlp11/nltk-pcfg.html
http://www.ibm.com/developerworks/linux/library/l-cpnltk/index.html
Stanford Parser
http://nlp.stanford.edu/downloads/lex-parser.shtml
http://nlp.stanford.edu:8080/parser/index.jsp
http://projects.csail.mit.edu/spatial/Stanford_Parser
Other Software & Code
http://www.sendraw.ucf.edu/
http://1aiway.com/nlp4net/services/enparser/reedkelloggtree.aspx
http://faculty.washington.edu/dillon/GramResources/
Funny blog posts about politics and grammar, or the lack thereof
http://boingboing.net/2009/02/17/how-obamas-sentences.html
http://www.slate.com/articles/life/the_good_word/2008/10/diagramming_sarah.html
Vocab
Also, as far as I can tell, linguists, grammarians, and English majors call sentence diagrams "Reed-Kellogg" diagrams. NLP and computer science types call the diagrams "parse trees" or "concrete syntax trees," and produce them (usually) using probabalistic context free grammars (PCFGs).
Search result
If you want code that just works, the Stanford Parser looks like the place to start. It's in java, but I'm sure that won't be too much of a problem, because you can call it from the command line.
Python's NLTK might also work, but there are lots of different parsers, and it's not clear whether and how much training they require.
There are various other software packages out there -- some of them online -- but I doubt they'd support much volume, and batching would be a pain.
Background
http://en.wikipedia.org/wiki/Sentence_diagram
http://en.wikipedia.org/wiki/Parse_tree
http://en.wikipedia.org/wiki/Context-free_grammar
NLTK
http://nltk.googlecode.com/svn/trunk/doc/book/ch08.html
http://www.cs.bgu.ac.il/~elhadad/nlp11/nltk-pcfg.html
http://www.ibm.com/developerworks/linux/library/l-cpnltk/index.html
Stanford Parser
http://nlp.stanford.edu/downloads/lex-parser.shtml
http://nlp.stanford.edu:8080/parser/index.jsp
http://projects.csail.mit.edu/spatial/Stanford_Parser
Other Software & Code
http://www.sendraw.ucf.edu/
http://1aiway.com/nlp4net/services/enparser/reedkelloggtree.aspx
http://faculty.washington.edu/dillon/GramResources/
Funny blog posts about politics and grammar, or the lack thereof
http://boingboing.net/2009/02/17/how-obamas-sentences.html
http://www.slate.com/articles/life/the_good_word/2008/10/diagramming_sarah.html
Saturday, November 5, 2011
What ideas do twitter users invoke to support/oppose the #OWS protests?
I've been getting used to the twitter API. As a quick test, I downloaded a bunch of tweets about #OWS. Then I uploaded 1,000 of them to mturk and asked turkers to classify them as supportive or opposed to the #OWS movement.
Now I'm running some text analysis on the tweets. I'll be posting code and writing up results here over the next few days. Questions are welcome!
For starters, here are words people use in support/opposition to the #OWS movement.
Here's the same data, rendered as word clouds, so it looks artsy. This really is the same data: sizes in the wordcloud are determined by the weights of the classifier -- regression betas, for you mathy people out there. Color and coordinates are arbitrary. So these wordclouds are exactly the same info as the tables above, just presented in a more visually appealing format.
In support:
Opposed:
As I peer at these tea leaves, I see a solidarity-oriented "stand together against brutal capitalist injustice" theme in the support words, and a libertarian "quit your whining and get to work" theme in the oppose words. What do you make of it?
Caveats and details of the method
This analysis is based on 1,000 tweets drawn from Monday, Tuesday and Wednesday of this week, so some of the themes might be specific to the events of those days. Also, there was quite a bit of noise in the sentiment coding. That will probably wash out in a large enough sample, but I don't know if 1,000 if large enough. Finally, support on twitter was running about 85% in favor of the protests, so the assessments of opposing words are probably less robust.
Now I'm running some text analysis on the tweets. I'll be posting code and writing up results here over the next few days. Questions are welcome!
For starters, here are words people use in support/opposition to the #OWS movement.
| Support | Oppose | |||
| love | 0.48 | homeless | -0.93 | |
| politics | 0.46 | focus | -0.9 | |
| opwallstreet | 0.43 | crowd | -0.75 | |
| congress | 0.43 | handouts | -0.74 | |
| stand | 0.4 | capitalist | -0.71 | |
| bank | 0.39 | irony | -0.59 | |
| brutal | 0.36 | called | -0.51 | |
| class | 0.32 | act | -0.49 | |
| strike | 0.31 | scanner | -0.46 | |
| poll | 0.31 | happened | -0.44 | |
| evict | 0.31 | received | -0.36 | |
| p21 | 0.31 | getting | -0.33 | |
| stay | 0.29 | quotwe | -0.32 | |
| global | 0.29 | dont | -0.32 | |
| help | 0.25 | cont | -0.31 | |
| justice | 0.23 | john | -0.3 | |
| income | 0.23 | home | -0.29 | |
| senatorsanders | 0.23 | paul | -0.28 | |
| moveon | 0.22 | hear | -0.24 | |
| occupywallst | 0.21 | weoccupyamerica | -0.23 | |
| solidarity | 0.2 | protests | -0.22 | |
| call | 0.2 | free | -0.19 | |
| cop | 0.2 | tents | -0.19 | |
| allowed | 0.17 | protesting | -0.14 | |
| peaceful | 0.16 | occupylsx | -0.13 |
Here's the same data, rendered as word clouds, so it looks artsy. This really is the same data: sizes in the wordcloud are determined by the weights of the classifier -- regression betas, for you mathy people out there. Color and coordinates are arbitrary. So these wordclouds are exactly the same info as the tables above, just presented in a more visually appealing format.
In support:
Opposed:
As I peer at these tea leaves, I see a solidarity-oriented "stand together against brutal capitalist injustice" theme in the support words, and a libertarian "quit your whining and get to work" theme in the oppose words. What do you make of it?
Caveats and details of the method
This analysis is based on 1,000 tweets drawn from Monday, Tuesday and Wednesday of this week, so some of the themes might be specific to the events of those days. Also, there was quite a bit of noise in the sentiment coding. That will probably wash out in a large enough sample, but I don't know if 1,000 if large enough. Finally, support on twitter was running about 85% in favor of the protests, so the assessments of opposing words are probably less robust.
Tuesday, October 25, 2011
Repost: Analysis of Steve Jobs tribute messages.
Here. The analysis is interesting, even if the presentation is kind of slow. Full source code (python and nltk, natch) is included.
HT FlowingData
HT FlowingData
Subscribe to:
Posts (Atom)



