Current section

Files

Jump to
content_indexer lib tf_idf calculate.ex
Raw

lib/tf_idf/calculate.ex

defmodule ContentIndexer.TfIdf.Calculate do
@moduledoc """
Calculate the TF_IDF weights for a given document_name tokens
"""
alias ContentIndexer.TfIdf.{Corpus, DocCounts, DocTerms, TermCounts, WeightsIndexer}
@doc """
Retrieves the current set of weights i.e. the state
## Parameters
- document_name: String - document name
- tokens: List of tokens each being a String
## Example
iex> ContentIndexer.TfIdf.Calculate.tf_idf("test_file.md", ["bread","butter"])
{:ok, [
{"test_file_1.md", [{"butter", 0}, {"bread", -0.234}]},
]
}
"""
def tf_idf(document_name, tokens) do
#
# Parallelise (1) & (2) & once they are all completed
#
# Put (3-5) into a Task await/async - with a longish wait time!
#
# (1) Count the total number of terms in the doc & Add this to DocCounts
total_number_of_terms_in_document = Enum.count(tokens)
DocCounts.add_document(document_name, total_number_of_terms_in_document)
# (2) Increment the total number of documents in the Corpus Count
Corpus.increment()
# (3 & 4 & 5) Number of times each term appears in the document
{:ok, total_docs_in_corpus} = Corpus.count()
weights = process_document_terms(document_name, tokens, total_docs_in_corpus)
# Finally add the weights to the indexer for comparing & searching
{:ok, :added} = WeightsIndexer.add(document_name, weights)
end
defp process_document_terms(document_name, tokens, corpus_count) do
tokens
|> unique_term_count
|> Enum.map(fn(token) ->
term = elem(token, 0)
DocTerms.add_doc_term_count(document_name, term, elem(token, 1))
# (4) Increment number of documents with term
{:ok, {term, _term_count}} = TermCounts.increment_term(term)
# (5) Calculate TF_IDF on each term
calculate_tf_idf(term, document_name, corpus_count)
end)
end
defp calculate_tf_idf(term, document_name, corpus_count) do
term_tf = tf(term, document_name)
term_idf = idf(term, corpus_count)
{term, term_tf * term_idf}
end
# given a list of string tokens - this will return a list of tuples
# with each tuple containing unique terms & counts in the tokens list
defp unique_term_count(tokens) when is_list(tokens) do
tokens_stream = tokens
|> Enum.sort()
|> Stream.chunk_by(fn arg -> arg end)
|> Stream.map(fn(x) ->
{List.first(x), Enum.count(x)}
end)
Enum.to_list(tokens_stream)
end
defp idf(term, corpus_count) do
{:ok, number_of_docs_with_term} = TermCounts.term_count(term)
:math.log(corpus_count / (1 + number_of_docs_with_term))
end
defp tf(term, document_name) do
{:ok, doc_term_count} = DocTerms.get_doc_term_count(document_name, term)
{:ok, {_doc, total_terms_in_doc}} = DocCounts.document_term_count(document_name)
doc_term_count / total_terms_in_doc
end
end