library(rstan)
library(reshape2)
##### Import data files
Search.Term.Counts <- read.csv("string_title_year_count.csv")
Industry.Terms <- read.csv("search_terms_and_industries.csv")
# data cleaning: restrict to year 2012 and remove a few negative counts (probably due to a coding mistake upstream)
Search.Term.Counts.t <- subset(Search.Term.Counts,year==2012)
Search.Term.Counts.t$count <- ifelse(Search.Term.Counts.t$count < 0, 0, Search.Term.Counts.t$count)
# extract Num.Words.In.Vocabulary and Num.Docs from Search.Term.Counts
Num.Words.In.Vocabulary <- length(levels(Search.Term.Counts.t$string))
Num.Docs <- length(levels(as.factor(Search.Term.Counts.t$title)))
# extract Num.Topics [= Num.Industries + 1] from Industry.Terms
Num.Topics <- length(levels(as.factor(Industry.Terms$code)))+1
# extract Search.Term.Counts
melted.Search.Term.Counts <- melt(Search.Term.Counts.t[,c("title","string","count")], id=c("title", "string"))
Search.Term.Counts.Array <- acast(melted.Search.Term.Counts, string ~ title)
# Create the simply flat topic prior
# alpha here represents the virtual count of times we've seen this topic for this doc (akin to the beta distribution's parameters so that 1 is a flat uniform)
# Steyvers n Griffiths (2007) suggest 50/T, which is actually around 1 here anyway
Topic.Prior <- matrix(1,nrow=Num.Topics, ncol=Num.Docs)
rownames(Topic.Prior) <- c(levels(as.factor(Industry.Terms$code)),"000")
colnames(Topic.Prior) <- levels(as.factor(Search.Term.Counts.t$title))
# Create the topic prior which is flat but to which I'll add additional weight allocated for search terms known to be associated with particular industries
# beta here represents the virtual count of times we've seen this word used in this topic
# Steyvers n Griffiths (2007) suggest 0.01, which is improper, but they acknowledge that it should depend on vocabulary, num docs, etc... so I'll stick with 1 that corresponds to a flat uniform
Word.Prior <- matrix(1,nrow=Num.Words.In.Vocabulary, ncol=Num.Topics)
rownames(Word.Prior) <- levels(Search.Term.Counts.t$string)
colnames(Word.Prior) <- c(levels(as.factor(Industry.Terms$code)),"000")
# add information to the word prior
# loop thru each industry (not including the misc topic category)
# find the maximum number of words in that industry's search terms
# loop thru that industry's search terms to appropriately increment the prior for cases of individual terms in a search string of multiple words:
# the individual word should be more associated with misc and the whole search string of multiple words should be more associated with the industry
for (Current.Industry.Index in 1:(ncol(Word.Prior)-1)) {
Industry.Code <- colnames(Word.Prior)[Current.Industry.Index]
Current.Industry.Terms <- subset(Industry.Terms,code == Industry.Code)
Current.Industry.Terms$Num.Terms <- sapply(gregexpr("\\W+", Current.Industry.Terms$string), function(x) sum(x>0) ) + 1
Max.Num.Terms <- max(Current.Industry.Terms$Num.Terms)
for (Current.String.Index in 1:nrow(Current.Industry.Terms)) {
if (as.character(Current.Industry.Terms$string[Current.String.Index]) %in% rownames(Word.Prior)) {
Word.Prior[as.character(Current.Industry.Terms$string[Current.String.Index]),Industry.Code] <- Word.Prior[as.character(Current.Industry.Terms$string[Current.String.Index]),Industry.Code] + Current.Industry.Terms$Num.Terms[Current.String.Index]
Word.Prior[as.character(Current.Industry.Terms$string[Current.String.Index]),"000"] <- Word.Prior[as.character(Current.Industry.Terms$string[Current.String.Index]),"000"] + (Max.Num.Terms - Current.Industry.Terms$Num.Terms[Current.String.Index])
}
}
}
##########################################################################################
# Analyze with Stan
##########################################################################################
# reshaping the data as required by Stan -- packed into a list: counts as a VxM array, topic prior as a TxM array, and word prior as a VxM array
StanInputData <- list(T = Num.Topics, V = Num.Words.In.Vocabulary, M = Num.Docs, K = Search.Term.Counts.Array, alpha = t(Topic.Prior), beta = t(Word.Prior))
# run stan on the file, compile to C [stanc()], and iteratively draw samples from the posterior [sampling()]
stan.fit <- stan(file="LDA.stan", data=StanInputData, seed = 12345, iter=1000, chains=4)