diff --git a/14-tabular/README.md b/14-tabular/README.md new file mode 100644 index 0000000..6b2522f --- /dev/null +++ b/14-tabular/README.md @@ -0,0 +1,18 @@ +Style #14 +============================== + +Constraints: + +- The input data of the problem is modeled as entities and relations between them + +- The data is placed in tables, with each row potentially cross-referencing rows in other tables + +- Existence of a relational query engine + +- The problem is solved by issuing queries over the tabular data + +Possible names: + +- Tabular +- Flatland +- Relational diff --git a/14-tabular/tf-14.py b/14-tabular/tf-14.py new file mode 100644 index 0000000..4633eac --- /dev/null +++ b/14-tabular/tf-14.py @@ -0,0 +1,89 @@ +import sys, re, string, sqlite3 + +# +# The relational database of this problem consists of 3 tables: +# documents, words, characters +# +def create_db_schema(connection): + c = connection.cursor() + c.execute('''CREATE TABLE documents (id INTEGER PRIMARY KEY AUTOINCREMENT, name)''') + c.execute('''CREATE TABLE words (id, doc_id, value)''') + c.execute('''CREATE TABLE characters (id, word_id, value)''') + connection.commit() + c.close() + +def load_file_into_database(path_to_file, connection): + """ Takes the path to a file and loads the contents into the database """ + def _read_file(path_to_file): + """ + Takes a path to a file and returns the entire contents of the + file as a string + """ + f = open(path_to_file) + data = f.read() + f.close() + return data + + def _filter_chars_and_normalize(str_data): + """ + Takes a string and returns a copy with all nonalphanumeric chars + replaced by white space, and all characters lower-cased + """ + pattern = re.compile('[\W_]+') + return pattern.sub(' ', str_data).lower() + + def _scan(str_data): + """ Takes a string and scans for words, returning a list of words. """ + return str_data.split() + + def _remove_stop_words(word_list): + f = open('../stop_words.txt') + stop_words = f.read().split(',') + f.close() + # add single-letter words + stop_words.extend(list(string.ascii_lowercase)) + return [w for w in word_list if not w in stop_words] + + # The actual work of splitting the input into words + words = _remove_stop_words(_scan(_filter_chars_and_normalize(_read_file(path_to_file)))) + + # Now let's add data to the database + # Add the document itself to the database + c = connection.cursor() + c.execute("INSERT INTO documents (name) VALUES (?)", (path_to_file,)) + c.execute("SELECT id from documents WHERE name=?", (path_to_file,)) + doc_id = c.fetchone()[0] + + # Add the words to the database + c.execute("SELECT MAX(id) FROM words") + row = c.fetchone() + word_id = row[0] + if word_id == None: + word_id = 0 + for w in words: + c.execute("INSERT INTO words VALUES (?, ?, ?)", (word_id, doc_id, w)) + # Add the characters to the database + char_id = 0 + for char in w: + c.execute("INSERT INTO characters VALUES (?, ?, ?)", (char_id, word_id, char)) + char_id += 1 + word_id += 1 + connection.commit() + c.close() + +# +# The main function +# +connection = sqlite3.connect(':memory:') +create_db_schema(connection) +load_file_into_database(sys.argv[1], connection) + +# Now, let's query +c = connection.cursor() +c.execute("SELECT value, COUNT(*) as C FROM words GROUP BY value ORDER BY C DESC") +for i in range(25): + row = c.fetchone() + if row != None: + print row[0] + ' - ' + str(row[1]) + +connection.close()