I am working on the file "A Noob's guide to text classification [Unrendered].ipynb".
The first task is:
To stream the input corpus we store the absolute path of the files as a list and pass that to the MyCorpus get_texts function. This function implements the streaming interface.
NOTE
I am going to run it only for the first 50 files in the test_data to show you the output. Iterating through the entire test set may take quite sometime.
Can you write Python code for it?

import sys
accCount=0 # Keep track of the accuracy
# Iterate over the test documents
# to iterate over the entire test_data replace range(0,30) with (0,len(test_data.filenames))
for i in range(0,50):
    print "CLASSIFICATION " + str(i)
    # Root path where training data exists
    root_path='/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/training/'
    filename=[]  # list containing absolute path name of all the filenames
    targetvar=[] # list that contains target label

    for j in NBPredictions[i]: # pull up the top5 predictions
        container_path=join(root_path,j)  # container path is root_path/class_name
        for root, dirs, files in os.walk(container_path, topdown=True):
            for name in files:
                targetvar.append(root[root.rfind('/')+1:])
                filename.append(join(root,name))

    # import gensim here to carry out LSI

    # serialize the corpus and store it as a .mm file

    mycorpus= MyCorpus(filename)
    corpora.MmCorpus.serialize('/home/arvindramesh/Desktop/Internship/Experimental/corpus.mm', mycorpus)
    corpus= corpora.MmCorpus('/home/arvindramesh/Desktop/Internship/Experimental/corpus.mm')
    dictionary = mycorpus.dictionary

    # apply tfidf to the corpus
    tfidf = models.TfidfModel(corpus,id2word=dictionary)
    # Apply LSI to tfidf of corpus
    lsi = models.LsiModel(tfidf[corpus],id2word=dictionary)
    # create a index for fast lookup
    index = similarities.SparseMatrixSimilarity(lsi[tfidf[corpus]],corpus.num_terms)

    # Testing
    tokens= tokenizer.tokenize(test_data.data[i])# tokenize the data
    vec_bow = dictionary.doc2bow(tokens) # do a bag of words represenation on the data
    sims = index[lsi[tfidf[vec_bow]]] # compute the lsi similarity measure
    a= (sorted(enumerate(sims),key=lambda item: -item[1])) # sort the similarity measure
    # a now contains a list of tuples that contains the (document id,similarity score)

   ...


I am working on the file "A Noob's guide to text classification [Unrendered].ipynb".
The first task is:
VOILA!
Now we have the input data in the required structure ready. Let us go ahead and port it with
sklearn.datasets.load_files.
Can you write Python code for it?

from sklearn.datasets import load_files
training_data = load_files('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/training/')
print "Loaded " + str(len(training_data.filenames)) + " Training Documents "


I am working on the file "A Noob's guide to text classification [Unrendered].ipynb".
The first task is:
WOAH!
We see here that the accuracy of the classifier is now 82.67% for the top 5 predictions. However in real text classification system, you would need human intervention to choose one target label out of the top 5 predictions which may be cumbersome. Our idea here is to automate the decision making, hence printing the top 5 predictions may not be a good idea.

 What we will do now is apply the  Latent Semantic Indexing(LSI)  similarity measure between the test document and training document that belong to classes that were predicted in the Naive Bayes Model (which is the top 5 predictions that we made earlier) .
LSI is an indexing and retrieval method that uses a mathematical technique called singular value decomposition (SVD) to identify patterns in the relationships between the terms and concepts contained in an unstructured collection of text. LSI is based on the principle that words that are used in the same contexts tend to have similar meanings. A key feature of LSI is its ability to extract the conceptual content of a body of text by establishing associations between those terms that occur in similar contexts. It is called Latent Semantic Indexing because of its ability to correlate semantically related terms that are latent in a collection of text. The method, also called latent semantic analysis (LSA), uncovers the underlying latent semantic structure in the usage of words in a body of text and how it can be used to extract the meaning of the text in response to user queries, commonly referred to as concept searches. Queries, or concept searches, against a set of documents that have undergone LSI will return results that are conceptually similar in meaning to the search criteria even if the results don’t share a specific word or words with the search criteria. *Source

Fortunately Gensim comes to our rescue here. Gensim is a python package by Radim...
Can you write Python code for it?

# Print the top 5 predictions made for the first document in the test set by MultinomialNB model
NBPredictions[0]


I am working on the file "A Noob's guide to text classification [Unrendered].ipynb".
The first task is:
We can see the default arguments of CountVectorizer by printing the CountVectorizer instance as shown above. The first and foremost parameter that we must pay attention to is lowercase

This is a very important argument for dimensionality reduction. Setting lowercase=False will treat a word Cat and cat differently while they may mean the same thing. Thus it is advisable to either lowercase the input data or set lowercase=True in CountVectorizer. CountVectorizer sets this parameter as True by default and hence we may not have to explicitly set is while intializing.

 The  max_df and min_df help filter out the most common and rare  words respectively. We avoid using a word that is present in almost every document as they may not be very indicative of the class that the text belongs to. The same argument can be applied to words that are present very rarely in the input corpus. Thus filtering them out leads to a better set of features which could help us better categorize a text.

 min_df can take integer or float values. setting min_df=3 tells the CountVectorizer to ignore words that exits in strictly less than or equal to 3 documents. If float, the parameter represents a a percentage of documents, integer absolute counts.

 max_df ignores terms that have a document frequency strictly higher than the given threshold (corpus-specific stop words). If float, the parameter represents a proportion of documents, integer absolute counts.

 ngram is a contiguous sequence of n items from a given set of texts. ngram representation can sometimes make more sense when we are analyzing a large chunk of texts as it spits out Collocation. Using a bigram or tri-gram feature may give us features such as "United States" and "US Vice...
Can you write Python code for it?

from sklearn.feature_extraction.text import CountVectorizer
import datetime,re

print ' [process  started: ' + str(datetime.datetime.now()) + ']'
# Initialize the "CountVectorizer" object, which is scikit-learn's bag of words tool.
count_vect = CountVectorizer(analyzer = "word", stop_words= set(stopwords_list), min_df=3, max_df=0.5, lowercase=True,
                            ngram_range=(1,3))
# fit_transform() does two functions: First, it fits the model
# and learns the vocabulary; second, it transforms our training data
# into feature vectors. The input to fit_transform should be a list of
# strings.
X_train_count= count_vect.fit_transform(training_data.data)
print ' [process  ended: ' + str(datetime.datetime.now()) + ']'
print "Created a Sparse Matrix with " + str(X_train_count.shape[0]) + " Documents and "+ str(X_train_count.shape[1]) + " Features"


I am working on the file "A Noob's guide to text classification [Unrendered].ipynb".
The first task is:
Wohoooo!! We have a winner!!

Other metrics for classifier performance
Accuracy is a good measure but there are few other metrics such the f1 score  that help us visualize the performance of a classifier better.

 F1 Score can be interpreted as a weighted average of the precision and recall, where an F1 score reaches its best value at 1 and worst score at 0. The relative contribution of precision and recall to the F1 score are equal.

 Precision  measures whether predictions are correct while  Recall  measures whether everything that should be predicted, is predicted. Precision and Recall are inversely proportional and hence f1 scores balances these two. For us, Scikit-learn has an implementation to compute the f1 score where the f1 score formula is given by

F1 = 2*(precision * recall) / (precision + recall)
Can you write Python code for it?

from __future__ import division
#load test data
test_data= load_files('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/test/',shuffle=True, encoding='ISO-8859-2')
# variable to track the accuracy
Logisticount=0
SGDcount=0
truelabel=[]# list to hold true label
Logistic=[] # list to hold predicted label of LogisticRegression Classifier
Sgd=[]# list to hold predicted label of SGDClassifier

print ' [Classification Started: ' + str(datetime.datetime.now()) + ']'
# Iterate over the test document file by file
for i in range(0,len(test_data.filenames)):
    docs_test = [test_data.data[i]]
    # Apply the count vectorizer we used to fit the training data on the test data
    doc_test_counts = count_vect.transform(docs_test)
    # apply the tfidf transformation
    doc_test_tfidf = tfidf_transformer.transform(doc_test_counts)
    predicted1 = clf1.predict(doc_test_tfidf)
    predicted2 = clf2.predict(doc_test_tfidf)
    # Predicted Target label
    predicted_label1=training_data.target_names[predicted1]
    Logistic.append(predicted_label1)
    predicted_label2 = training_data.target_names[predicted2]
    Sgd.append(predicted_label2)
    # True Target label of test document
    true_label=test_data.target_names[test_data.target[i]]
    truelabel.append(true_label)
    # calculate the accuracy
    if predicted_label1==true_label:
        Logisticount+=1
    if predicted_label2==true_label:
        SGDcount+=1
print ' [Classification Ended: ' + str(datetime.datetime.now()) + ']'

# create a dictionary that maps class label with interger
# this will help us while calculating f1 scores
dictmap={}
for i in range(0,len(training_data.target_names)):
    dictmap[str(training_data.target_names[i])]=i

# Create a vector of target for true and predicted label
y_true = [dictmap[str(x)] if dictmap.has_key(x) else 84 for x in truelabel]
y_pred_logistic = [dictmap[str(x)] for x in Logistic]
y_pred_SGD = [dictmap[str(x)] for x in Sgd]
print "Created Predicted Target labels"


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
As discussed earlier, the load_files function returns a data bunch which consists of {target_names,data,target, DESCR, filenames}. We can access a particular file from the training example as follows:
Can you write Python code for it?

# category of first document in the buch
print "TARGET NAME : " + training_data.target_names[training_data.target[0]]
# data of the first document in the bunch
print "DATA : " + training_data.data[0][:500]
# Target value of the first document in the bunch
print "TARGET : " + str(training_data.target[0])
# filename of the first document in the bunch
print "FILENAME: " + training_data.filenames[0]


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
Classification Report for LogisticRegression
Can you write Python code for it?

from sklearn.metrics import classification_report
print(classification_report(y_true, y_pred_logistic, target_names=training_data.target_names))


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
Did you just say  64.392182842 % Accuracy?

The good news is we could do much better than this!!!!

The bad performance of Naive Bayes is a clear indication of the feature selection method that we chose. Hence in this section we will spend some time tweaking the features to see if we can improve the accuracy of the classifier.
Fortunately the CountVectorizer provides arguments that we can tweak. Let us dive right in.
Can you write Python code for it?

from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
print count_vect


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
Feature Extraction
Key thing to realizing a machine learning system is feature extraction. Often times 80% of your time and effort in a Machine Learning project is spent on realizing technique that give us good features to work with. Even an effective algorithm becomes useless when we use bad feature selection. The most popular technique that is used in text classification is the  Bag of Words  representation which converts the input text data into its numeric represenation. The Bag of Words model learns a vocabulary from all of the documents, then models each document by counting the number of times each word appears. *For example, consider the following two sentences:

Sentence 1: "The cat sat on the hat"

Sentence 2: "The dog ate the cat and the hat"

From these two sentences, our vocabulary is as follows:

{ the, cat, sat, on, hat, dog, ate, and }

"
To get our bags of words, we count the number of times each word occurs in each sentence. In Sentence 1, "the" appears twice, and "cat", "sat", "on", and "hat" each appear once, so the feature vector for Sentence 1 is:

{ the, cat, sat, on, hat, dog, ate, and }

Sentence 1: { 2, 1, 1, 1, 1, 0, 0, 0 }

Similarly, the features for Sentence 2 are: { 3, 1, 0, 0, 1, 1, 1, 1}

*Example and explanation taken from Kaggle
Let us fire up the  sklearn.feature_extraction.text and start building the bag of words representation for the training data.

 While constructing the bag of words representation, we come across words such as "the", "a", "am".. which do not add any meaning. So these words are filtered out before processing the input text. This is primarily done to reduce the dimensionality of the data. We begin by importing the stopwords.txt file from the reuters corpus into a set and passing that as a argument to CountVectorizer.
Can you write Python code for it?

stopwords_list = []
with open('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/reuters/stopwords') as f:
    for line in f:
        stopwords_list.append(line.strip())

print "Stop Words List :"
print stopwords_list[:10]
print "...."

from sklearn.feature_extraction.text import CountVectorizer
import datetime,re

print ' [process  started: ' + str(datetime.datetime.now()) + ']'
# Initialize the "CountVectorizer" object, which is scikit-learn's bag of words tool.
count_vect = CountVectorizer(analyzer = "word", stop_words= set(stopwords_list))

# fit_transform() does two functions: First, it fits the model
# and learns the vocabulary; second, it transforms our training data
# into feature vectors. The input to fit_transform should be a list of
# strings.
X_train_count= count_vect.fit_transform(training_data.data)
print ' [process  ended: ' + str(datetime.datetime.now()) + ']'
print "Created a Sparse Matrix with " + str(X_train_count.shape[0]) + " Documents and "+ str(X_train_count.shape[1]) + " Features"


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
From 64 to 67 % Accuracy

Now there isn't much tweak that you can do further to improve the accuracy of Naive Bayes. Though using alternate models such as Logistic Regression and Stochastic Gradient Descent may give better results, i want see if i can maybe improve the accuracy of the exisiting model by using some simple techniques.

 Instead of predicting one target label, we can tweak the program to maybe output the top 5 (say) predictions for every test document based on the probability value. This is quite easy to implement on the existing code and requires us to use the predict_proba function instead of the predict function in MultinomialNB. I'll show you in a second how to do this.
Can you write Python code for it?

from __future__ import division
#load test data
test_data= load_files('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/test/',shuffle=True, encoding='ISO-8859-2')
# variable to track the accuracy
count=0
print ' [Classification Started: ' + str(datetime.datetime.now()) + ']'
# Let's create a dictionary that stores the top 5 predictions for every test document
# Hence the key will be the document number and the value will be a list containing top 5 predictions
NBPredictions={}

# Iterate over the test document file by file
for i in range(0,len(test_data.filenames)):
    docs_test = [test_data.data[i]]
    # Apply the count vectorizer we used to fit the training data on the test data
    doc_test_counts = count_vect.transform(docs_test)
    # apply the tfidf transformation
    doc_test_tfidf = tfidf_transformer.transform(doc_test_counts)
    probability = clf.predict_proba(doc_test_tfidf).flatten()
    #print probability
    # sort according to the probability value in descending order
    a= (-probability).argsort()[:5]
    #create a list of predicted labels
    predicted=[]
    for target in a:
        predicted.append(training_data.target_names[target])

    true_label = test_data.target_names[test_data.target[i]]
    NBPredictions[i]=predicted
    if true_label in predicted:
        count+=1

print ' [Classification Ended: ' + str(datetime.datetime.now()) + ']'
print "ACCURACY : " + str(count/len(test_data.filenames))


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
Notice how the number of features has gone up from 25834 in the previous case to 55469. This is because of using ngram features. We now use TFIDF on the word counts.
Can you write Python code for it?

from sklearn.feature_extraction.text import TfidfTransformer
print '[process started: ' + str(datetime.datetime.now()) + ']'
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_count)
print '[process ended: ' + str(datetime.datetime.now()) + ']'
print X_train_tfidf.shape

from sklearn.naive_bayes import MultinomialNB
print ' [Classification Started: ' + str(datetime.datetime.now()) + ']'
# you fit the NB model
clf = MultinomialNB().fit(X_train_tfidf, training_data.target)
print ' [Classification ended: ' + str(datetime.datetime.now()) + ']'

from __future__ import division

#load test data
test_data= load_files('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/test/',shuffle=True, encoding='ISO-8859-2')
# variable to track the accuracy
count=0
print ' [Classification Started: ' + str(datetime.datetime.now()) + ']'
# Iterate over the test document file by file
for i in range(0,len(test_data.filenames)):
    docs_test = [test_data.data[i]]
    # Apply the count vectorizer we used to fit the training data on the test data
    doc_test_counts = count_vect.transform(docs_test)
    # apply the tfidf transformation
    doc_test_tfidf = tfidf_transformer.transform(doc_test_counts)
    predicted = clf.predict(doc_test_tfidf)
    # Predicted Target label
    predicted_label=training_data.target_names[predicted]
    # True Target label of test document
    true_label=test_data.target_names[test_data.target[i]]
    # calculate the accuracy
    if predicted_label==true_label:
        count+=1
print ' [Classification Ended: ' + str(datetime.datetime.now()) + ']'
print "ACCURACY : " + str(count/len(test_data.filenames))


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
Now let us put it all together.
Gensim
Note that corpus that we loaded using load_files above resides fully in memory, as a plain Python Bunch. When the input corpus becomes very big with millions of documents , storing all of them in RAM may be a bad design decision and could crash the system. Gensim overcomes this by using a technique called streaming. While streaming a document is loaded and processed in memory one by one. Although the output is the same as for the plain Python bunch, the corpus is now much more memory friendly, because at most one vector resides in RAM at a time. The implementation of a gensim corpus class is as shown below:
Can you write Python code for it?

# to print the gensim logging in ipython notebook
import logging
logging.basicConfig(format='%(levelname)s : %(message)s', level=logging.INFO)
logging.root.level = logging.INFO  # ipython sometimes messes up the logging setup; restore

import gensim
import os
from os.path import join
from gensim import utils
import datetime, re, sys
from nltk.tokenize import RegexpTokenizer
from gensim import similarities,models, matutils
from gensim.similarities import MatrixSimilarity, SparseMatrixSimilarity, Similarity
from gensim import corpora
tokenizer = RegexpTokenizer(r'[a-zA-Z0-9]+')

# Tokenizer
def text_tokenize(text):
    tokens = tokenizer.tokenize(text.lower())
    # return the transformed corpus
    out=[]
    for token in tokens:
        # we filter out tokens that exist in stopwords list
        if token not in stopwords_list:
            out.append(token)

    return out  # returns a list

class MyCorpus(gensim.corpora.TextCorpus):
    def get_texts(self):
        for filename in self.input:
            yield text_tokenize(open(filename).read())


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
Test the classifier
Can you write Python code for it?

from __future__ import division

#load test data
test_data= load_files('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/test/',shuffle=True, encoding='ISO-8859-2')
# variable to track the accuracy
Logisticount=0
SGDcount=0
print ' [Classification Started: ' + str(datetime.datetime.now()) + ']'
# Iterate over the test document file by file
for i in range(0,len(test_data.filenames)):
    docs_test = [test_data.data[i]]
    # Apply the count vectorizer we used to fit the training data on the test data
    doc_test_counts = count_vect.transform(docs_test)
    # apply the tfidf transformation
    doc_test_tfidf = tfidf_transformer.transform(doc_test_counts)
    predicted1 = clf1.predict(doc_test_tfidf)
    predicted2 = clf2.predict(doc_test_tfidf)
    # Predicted Target label
    predicted_label1=training_data.target_names[predicted1]
    predicted_label2 = training_data.target_names[predicted2]
    # True Target label of test document
    true_label=test_data.target_names[test_data.target[i]]
    # calculate the accuracy
    if predicted_label1==true_label:
        Logisticount+=1
    if predicted_label2==true_label:
        SGDcount+=1
print ' [Classification Ended: ' + str(datetime.datetime.now()) + ']'
print "ACCURACY of LogisticRegression : " + str(Logisticount/len(test_data.filenames))
print "ACCURACY of SGDClassifier : " + str(SGDcount/len(test_data.filenames))


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
Testing the Performance of the Classifier
We now test the performance of the classifier in terms of the accuracy. What we do is we begin to import the test documents using the load_files the same way we did for the training documents. We then apply the CountVectorizer and TFIDF transformation to the test documents. The predict function will output the predicted label of the test document.
Can you write Python code for it?

from __future__ import division
import numpy as np
test_data= load_files('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/test/',shuffle=True, encoding='ISO-8859-2')
count=0
print ' [Classification Started: ' + str(datetime.datetime.now()) + ']'
for i in range(0,len(test_data.filenames)):
    docs_test = [test_data.data[i]]
    # Apply the count vectorizer we used to fit the training data on the test data
    doc_test_counts = count_vect.transform(docs_test)
    # apply the tfidf transformation
    doc_test_tfidf = tfidf_transformer.transform(doc_test_counts)
    # predict
    predicted = clf.predict(doc_test_tfidf)
    # Predicted label based on the classifier prediction above
    predicted_label=training_data.target_names[predicted]
    # True label of test document
    true_label=test_data.target_names[test_data.target[i]]
    # calculate the accuracy
    if predicted_label==true_label:
        count+=1
print ' [Classification Ended: ' + str(datetime.datetime.now()) + ']'
print "ACCURACY : " + str(count/len(test_data.filenames))


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
To stream the input corpus we store the absolute path of the files as a list and pass that to the MyCorpus get_texts function. This function implements the streaming interface.
NOTE
I am going to run it only for the first 50 files in the test_data to show you the output. Iterating through the entire test set may take quite sometime.
Can you write Python code for it?


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
VOILA!
Now we have the input data in the required structure ready. Let us go ahead and port it with
sklearn.datasets.load_files.
Can you write Python code for it?


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
WOAH!
We see here that the accuracy of the classifier is now 82.67% for the top 5 predictions. However in real text classification system, you would need human intervention to choose one target label out of the top 5 predictions which may be cumbersome. Our idea here is to automate the decision making, hence printing the top 5 predictions may not be a good idea.

 What we will do now is apply the  Latent Semantic Indexing(LSI)  similarity measure between the test document and training document that belong to classes that were predicted in the Naive Bayes Model (which is the top 5 predictions that we made earlier) .
LSI is an indexing and retrieval method that uses a mathematical technique called singular value decomposition (SVD) to identify patterns in the relationships between the terms and concepts contained in an unstructured collection of text. LSI is based on the principle that words that are used in the same contexts tend to have similar meanings. A key feature of LSI is its ability to extract the conceptual content of a body of text by establishing associations between those terms that occur in similar contexts. It is called Latent Semantic Indexing because of its ability to correlate semantically related terms that are latent in a collection of text. The method, also called latent semantic analysis (LSA), uncovers the underlying latent semantic structure in the usage of words in a body of text and how it can be used to extract the meaning of the text in response to user queries, commonly referred to as concept searches. Queries, or concept searches, against a set of documents that have undergone LSI will return results that are conceptually similar in meaning to the search criteria even if the results don’t share a specific word or words with the search criteria. *Source

Fortunately Gensim comes to our rescue here. Gensim is a python package by Radim...
Can you write Python code for it?


I am working on the file "A Noob's guide to text classification.ipynb".
The first task is:
While doing the LSI similarity lookup, we will load only those categories from the training set that belong to the top 5 predictions. i.e for Test Document 0, since we know that the top 5 predictions are ['acq', 'earn', 'crude', 'trade', 'interest'], we'll load only training data that belong to these categories. The sklearn.datasets.load_files provides a categories parameter to do this. Categories takes in a list of classes and loads up training data only from those classes.
Can you write Python code for it?

cosine_data = load_files('/media/arvindramesh/CCF02769F02758CA/TextClassification-NewsGroup/training/',
                        categories=NBPredictions[0])
print "Loaded " + str(len(cosine_data.filenames)) + " Files belonging to " + str(len(cosine_data.target_names)) +" Classes "
print "Classes are " + str(list(NBPredictions[0]))


I am working on the file "A Walk Through PyStyl.ipynb".
The first task is:
As you can see, all personal pronouns have now been removed from our corpus segments. We are now ready to vectorize our corpus, which means that we will represent it as as a large two-dimensional matrix in which each row represents one of our textual segments, and each individual feature (e.g. a function word frequency) is represented in a column.
Can you write Python code for it?


I am working on the file "A Walk Through PyStyl.ipynb".
The first task is:
As you can see, we have now included the 30 most common words in our corpus model (mfi stands for 'most frequent items'). These features are returned by the vectorize() method. Many other options are available; to extract the 50 most common character trigrams, for instance, you could run:
Can you write Python code for it?


I am working on the file "A Walk Through PyStyl.ipynb".
The first task is:
By default, this function assumes that all texts under this directory have been encoded in UTF-8 and that they have a .txt extension. Additionally, the syntax of the filename should be <category>_<title>.txt, where category is a label indicates e.g. a text's authorship, genre or date of composition. In our case, this directory looked like:
Can you write Python code for it?


I am working on the file "A Walk Through PyStyl.ipynb".
The first task is:
In stylometry, it is common to manually remove certain words, such as personal pronouns, which are more strongly tied to narrative perspective than authorial writing style. To remove these from our English texts, we can do:
Can you write Python code for it?


I am working on the file "A Walk Through PyStyl.ipynb".
The first task is:
Now, the corpus is ready to be tokenized, which is helpful if we want to start counting words:
Can you write Python code for it?


I am working on the file "A Walk Through PyStyl.ipynb".
The first task is:
Our corpus currently holds these 7 texts in their raw form:
Can you write Python code for it?


I am working on the file "A Walk Through PyStyl.ipynb".
The first task is:
Specifying a language such as 'en' (English) is optional. Adding texts from a directory to the corpus is easy:
Can you write Python code for it?


I am working on the file "A tiny regex challenge solved without another regex.ipynb".
The first task is:
Introduction: the problem a friend of mine asked me
Can you write Python code for it?

import re

def bad_events(pattern: str, string: str) -> List[Interval]:
    # m.span(1) = (m.start(1), m.end(1))
    return [m.span(1) for m in re.finditer(f"(?=({pattern}))", string)]

pat = "aca"
strng = "acacavcacabacacbbacacazdbacaca"

# Do you know if there is a regex trick to obtain
# [(0, 5), (7, 10), (11, 14), (17, 22), (25, 30)]
# instead of
bad_events(pat, strng)
# [(0, 3), (2, 5), (7, 10), (11, 14), (17, 20), (19, 22), (25, 28), (27, 30)]
# ?


I am working on the file "A tiny regex challenge solved without another regex.ipynb".
The first task is:
Other examples
Can you write Python code for it?

test("acab", "acabacabacabacacavcacabacacbbacacazdbacacaacacavcacabacacbbacacazdbacacaacabacab")

test("merci", "mercimerciderienmercimerki")


I am working on the file "A tiny regex challenge solved without another regex.ipynb".
The first task is:
The solution I came up with
Let's write a simple function that will read this list of intervals, and compress the ones that are not disjoint.
For instance, when reading [(0, 3), (2, 5)], the second interval is not disjoint from the first one, so both can be compressed to (0, 5), which is disjoint from the next one (7, 10).

Let's test (in constant time wrt $n$ number of intervals) if two consecutive intervals are disjoint or not:
Can you write Python code for it?

def are_not_disjoint(interval1: Interval, interval2: Interval) -> bool:
    x1, y1 = interval1
    assert x1 <= y1, f"Error: interval = {intervals1} is not a valid interval."
    x2, y2 = interval2
    assert x2 <= y2, f"Error: interval = {intervals2} is not a valid interval."
    if x1 <= x2 <= y1 <= y2:   # interval1 finishes in interval2
        return True
    elif x2 <= x1 <= y2 <= y1: # interval2 finishes in interval1
        return True
    elif x1 <= x2 <= y2 <= y1: # interval2 is included in interval1
        return True
    elif x2 <= x1 <= y1 <= y2: # interval1 is included in interval2
        return True
    return False

assert are_not_disjoint((0, 3), (2, 5))  # True
assert not are_not_disjoint((0, 5), (7, 10))  # False


I am working on the file "A01_singular_value_decomposition.ipynb".
The first task is:
Check U, S and V^T Do Actually Reconstruct A
Can you write Python code for it?

# rebuild A2 from U.S.V
A2 = numpy.dot(U,numpy.dot(S,VT))
print("A2 = \n", A2)

# plot the reconstructed A2
p = plt.subplot(111)
p.axis('scaled'); p.axis([-2, 2, -2, 2]); p.axhline(y=0, color='lightgrey'); p.axvline(x=0, color='lightgrey')
p.set_yticklabels([]); p.set_xticklabels([])

p.set_title("A2")
p.plot(A2[0,],A2[1,],'ro')

plt.show()


I am working on the file "A01_singular_value_decomposition.ipynb".
The first task is:
Now Take the SVD
Can you write Python code for it?

# break it down into an SVD
U, s, VT = numpy.linalg.svd(A, full_matrices=False)
S = numpy.diag(s)

# what are U, S and V
print("U =\n", U, "\n")
print("S =\n", S, "\n")
print("V^T =\n", VT, "\n")

for px in [(131,U, "U"), (132,S, "S"), (133,VT, "VT")]:
    subplot = px[0]
    matrix = px[1]
    matrix_name = px[2]
    p = plt.subplot(subplot)

    p.axis('scaled'); p.axis([-2, 2, -2, 2]); p.axhline(y=0, color='lightgrey'); p.axvline(x=0, color='lightgrey')
    p.set_yticklabels([]); p.set_xticklabels([])

    p.set_title(matrix_name)
    p.plot(matrix[0,],matrix[1,],'ro')
    pass

plt.show()


I am working on the file "A01_singular_value_decomposition.ipynb".
The first task is:
Start With A Simple Matrix
Can you write Python code for it?

# create a really simple matrix
A = numpy.array([[-1,1], [1,1]])
# and show it
print("A = \n", A)

# plot the array
p = plt.subplot(111)
p.axis('scaled'); p.axis([-2, 2, -2, 2]); p.axhline(y=0, color='lightgrey'); p.axvline(x=0, color='lightgrey')
p.set_yticklabels([]); p.set_xticklabels([])

p.set_title("A")
p.plot(A[0,],A[1,],'ro')

plt.show()


I am working on the file "A02_svd_applied_to_word_document_matrix.ipynb".
The first task is:
A Simple Word-Document Matrix
The example word-document matrix is taken from http://makeyourowntextminingtoolkit.blogspot.co.uk/2016/11/so-many-dimensions-and-how-to-reduce.html
Can you write Python code for it?

# create a simple word-document matrix as a pandas dataframe, the content values have been normalised
words = ['wheel', ' seat', ' engine', ' slice', ' oven', ' boil']
print(words)
documents = ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6']
word_doc = pandas.DataFrame([[0.5,0.3333, 0.25, 0, 0, 0],
                      [0.25, 0.3333, 0, 0, 0, 0],
                      [0.25, 0.3333, 0.75, 0,0,0],
                      [0, 0, 0, 0.5, 0.5, 0.6],
                      [0, 0, 0, 0.3333, 0.1667, 0],
                      [0, 0, 0, 0.1667, 0.3333, 0.4]], index=words, columns=documents)
# and show it
word_doc


I am working on the file "A02_svd_applied_to_word_document_matrix.ipynb".
The first task is:
Now Take the SVD
Can you write Python code for it?

# break it down into an SVD
U, s, VT = numpy.linalg.svd(A, full_matrices=False)
S = numpy.diag(s)

# what are U, S and V
print("U =\n", numpy.round(U, decimals=2), "\n")
print("S =\n", numpy.round(S, decimals=2), "\n")
print("V^T =\n", numpy.round(VT, decimals=2), "\n")


I am working on the file "A02_svd_applied_to_word_document_matrix.ipynb".
The first task is:
The above shows that there are 2 clusters of documents. That matches our expectations as we constructed the example data set that way.
Topics from New View of Words
Can you write Python code for it?

# topics are a linear combination of original words
U_S_reduced = numpy.dot(U, S_reduced)
df = pandas.DataFrame(numpy.round(U_S_reduced, decimals=2), index=words)

# show colour coded so it is easier to see significant word contributions to a topic
df.style.background_gradient(cmap=plt.get_cmap('Blues'), low=0, high=2)


I am working on the file "A02_svd_applied_to_word_document_matrix.ipynb".
The first task is:
Word-Document Matrix is A
Can you write Python code for it?

# create a numpy array from the pandas dataframe
A = word_doc.values


I am working on the file "A02_svd_applied_to_word_document_matrix.ipynb".
The first task is:
Yes, that worked .. the reconstructed A2 is the same as the original A (within the bounds of small floating point accuracy)
Now Reduce Dimensions, Extract Topics
Here we use only the top 2 values of the S singular value matrix, pretty brutal reduction in dimensions!
Can you write Python code for it?

# S_reduced is the same as S but with only the top 2 elements kept
S_reduced = numpy.zeros_like(S)
# only keep top two eigenvalues
l = 2
S_reduced[:l, :l] = S[:l,:l]
# show S_reduced which has less info than original S
print("S_reduced =\n", numpy.round(S_reduced, decimals=2))


I am working on the file "A03_svd_applied_to_slightly_bigger_word_document_matrix.ipynb".
The first task is:
A Slightly Bigger Word-Document Matrix
The example word-document matrix is taken from http://makeyourowntextminingtoolkit.blogspot.co.uk/2016/11/so-many-dimensions-and-how-to-reduce.html but expanded to cover a 3rd topic related to a home or house
Can you write Python code for it?


I am working on the file "A03_svd_applied_to_slightly_bigger_word_document_matrix.ipynb".
The first task is:
New View Of Documents
Can you write Python code for it?

# what is the document matrix now?
S_reduced_VT = numpy.dot(S_reduced, VT)
print("S_reduced_VT = \n", numpy.round(S_reduced_VT, decimals=2))

# plot the array
p = plt.subplot(111)
p.axis('scaled'); p.axis([-2, 2, -2, 2]); p.axhline(y=0, color='lightgrey'); p.axvline(x=0, color='lightgrey')
p.set_yticklabels([]); p.set_xticklabels([])

p.set_title("S_reduced_VT")
p.plot(S_reduced_VT[0,],S_reduced_VT[1,],'ro')

plt.show()


I am working on the file "A03_svd_applied_to_slightly_bigger_word_document_matrix.ipynb".
The first task is:
Now Take the SVD
Can you write Python code for it?


I am working on the file "A03_svd_applied_to_slightly_bigger_word_document_matrix.ipynb".
The first task is:
The above shows that there are indeed 3 clusters of documents. That matches our expectations as we constructed the example data set that way.
Topics from New View of Words
Can you write Python code for it?


I am working on the file "A03_svd_applied_to_slightly_bigger_word_document_matrix.ipynb".
The first task is:
We can see above that the values in the diagonal S matrix are ordered by magnitide. There is a significant different between the biggest value 1.1, and the smallest 0.05. The halfway value of 0.28 is still much smaller than the largest.
Check U, S and V Do Actually Reconstruct A
Can you write Python code for it?

# rebuild A2 from U.S.V
A2 = numpy.dot(U,numpy.dot(S,VT))
print("A2 =\n", numpy.round(A2, decimals=2))


I am working on the file "A03_svd_applied_to_slightly_bigger_word_document_matrix.ipynb".
The first task is:
Yes, that worked .. the reconstructed A2 is the same as the original A (within the bounds of small floating point accuracy)
Now Reduce Dimensions, Extract Topics
Here we use only the top 3 values of the S singular value matrix, pretty brutal reduction in dimensions!
Why 3, and not 2?
We'll only plot 2 dimensions for the document cluster view, and later we'll use 3 dimensions for the topic word view
Can you write Python code for it?

# S_reduced is the same as S but with only the top 3 elements kept
S_reduced = numpy.zeros_like(S)
# only keep top two eigenvalues
l = 3
S_reduced[:l, :l] = S[:l,:l]
# show S_rediced which has less info than original S
print("S_reduced =\n", numpy.round(S_reduced, decimals=2))


I am working on the file "A1.Optimization/Optimization_professor.ipynb".
The first task is:
Part 1: The function and its derivatives.
Question 1.1: Implement the following three methods:

Method f: given $w$, it returns the value of function $f(w)$.
Method df: given $w$, it returns the derivative of $f$ at $w$
Medhod d2f: given $w$, it returns the second derivative of $f$ at $w$
Can you write Python code for it?

# Funcion f
# <SOL>
def f(w):
    y = - w * np.exp(-w)
    return y
# </SOL>

# First derivative
# <SOL>
def df(w):
    y = (w -1) * np.exp(-w)
    return y
# </SOL>

# Second derivative
# <SOL>
def d2f(w):
    y = (2 - w) * np.exp(-w)
    return y
# </SOL>


I am working on the file "A1.Optimization/Optimization_professor.ipynb".
The first task is:
Question 2.2: Apply the gradient descent to optimize the given function. To do so, start with an initial value $w=0$ and iterate $20$ times. Save two lists:

A list of succesive values of $w_n$
A list of succesive values of the function $f(w_n)$.
Can you write Python code for it?

# <SOL>
wn = []
fwn = []
niter = 20
rho = 0.2
w = 0
wn.append(w)
fwn.append(f(w))

for k in range(niter):
    w = gd(w,rho)
    wn.append(w)
    fwn.append(f(w))
# </SOL>


I am working on the file "A1.Optimization/Optimization_professor.ipynb".
The first task is:
Question 3: Apply the Newton's method to optimize the given function. To do so, start with an initial value $w=0$ and iterate $20$ times. Save two lists:

A list of succesive values of $w_n$
A list of succesive values of the function $f(w_n)$.
Can you write Python code for it?

# <SOL>
wn = []
fwn = []
niter = 20
rho = 0.5
w = 0
wn.append(w)
fwn.append(f(w))

for k in range(niter):
    w = newton(w,rho)
    wn.append(w)
    fwn.append(f(w))
# </SOL>


I am working on the file "A1.Optimization/Optimization_professor.ipynb".
The first task is:
You can check the effect of modifying the value of the learning rate.
Part 2: Newton's method.
Question 3.1: Implement a method newton that, given w and a learning rate parameter rho applies a single iteration of the Newton's method
Can you write Python code for it?

# <SOL>
def newton(w0, rho):
    y = w0 - rho * df(w) / d2f(w)
    return y
# </SOL>


I am working on the file "A1.ipynb".
The first task is:
Stage 1. Data Acquisition
Collect all months traffic data using two different Wikimedia REST API endpoints, Pagecounts and Pageviews, for both mobile and desktop (excluding spider bot).
Output is 5 JSON files:
1. pagecounts desktop
2. pagecounts mobile
3. pageview desktop
4. pageview mobile web
5. pageview mobile app
Can you write Python code for it?

pc_desk_params = {
    'apiname': 'pagecounts',
    'access': 'desktop-site',
    'agent': ''}
r = wikipedia_query(pc_desk_params)
store_to_json(pc_desk_params, r)
print('Number of items: {}'.format(len(r.json()['items'])))

pc_mob_params = {
    'apiname': 'pagecounts',
    'access': 'mobile-site',
    'agent': ''}
r = wikipedia_query(pc_mob_params)
store_to_json(pc_mob_params, r)
print('Number of items: {}'.format(len(r.json()['items'])))

pv_desk_params = {
    'apiname': 'pageviews',
    'access': 'desktop',
    'agent': 'user'}
r = wikipedia_query(pv_desk_params)
store_to_json(pv_desk_params, r)
print('Number of items: {}'.format(len(r.json()['items'])))

pv_mobapp_params = {
    'apiname': 'pageviews',
    'access': 'mobile-app',
    'agent': 'user'}
r = wikipedia_query(pv_mobapp_params)
store_to_json(pv_mobapp_params, r)
print('Number of items: {}'.format(len(r.json()['items'])))

pv_mobweb_params = {
    'apiname': 'pageviews',
    'access': 'mobile-web',
    'agent': 'user'}
r = wikipedia_query(pv_mobweb_params)
store_to_json(pv_mobweb_params, r)
print('Number of items: {}'.format(len(r.json()['items'])))

print('JSON files: P{}'.format(json_files))


I am working on the file "A1.ipynb".
The first task is:
The first step is to query each Wikipedia Rest API endpoint using $wikipedia_query$ function with different parameter combination. Each response then saved to a json file using $store_to_json$ function. Print statements are used to debug the number of items the response returns with.
Two JSON files from two extra queries (all views for each API) are also produced to help with data processing in the next stage.
Stage 2. Data Processing
Process these data files to prepare them for analysis by combining each JSON file into a single CSV file with these headers:
- year
- month
- pagecount_all_views
- pagecount_desktop_views
- pagecount_mobile_views
- pageview_all_views
- pageview_desktop_views
- pageview_mobile_views
Can you write Python code for it?

import csv
import pandas as pd

csv_file = 'en-wikipedia_traffic_200801-201709.csv'

headers = [
    'timestamp',
    'year',
    'month',
    'pagecounts_all_views',
    'pagecounts_desktop_views',
    'pagecounts_mobile_views',
    'pageviews_all_views',
    'pageviews_desktop_views',
    'pageviews_mobile_views']

def load_df(file):
    apiname = file.split('_')[0]
    accesstype = file.split('_')[1].split('-')[0]
    column = apiname + '_' + accesstype + '_views'
    with open(file, 'r') as f:
        views = json.load(f)
    data = pd.DataFrame.from_dict(views)
    if apiname == 'pageviews':
        data = data.drop(['access','agent','granularity','project'], axis=1)
        data = data.rename(columns = {'views': column})
    #                 if 'mobile' in view['access']:
    else:
        data = data.drop(['access-site', 'granularity','project'], axis=1)
        data = data.rename(columns = {'count': column})
    return data

df = pd.DataFrame()
for i in json_files:
    # Load json file to pandas dataframe
    data = load_df(i)
    if len(df) == 0:
        df = data.copy(True)
    else:
        df = df.merge(data, on='timestamp', how='outer')

# Create year and month out of timestamp attribute
df = df.assign(year=df.timestamp.str[0:4])
df = df.assign(month=df.timestamp.str[4:6])
df.timestamp = df.timestamp.str[:-2]

# Combining two pageviews_mobile_views columns, one from mobile-app, and the other from mobile-web
df = df.assign(pageviews_mobile_views= lambda x: x.pageviews_mobile_views_x + x.pageviews_mobile_views_y)
df = df.drop(['pageviews_mobile_views_x', 'pageviews_mobile_views_y'], axis=1)

# Sum mobile and desktop to get all views
df = df.assign(pageviews_all_views= lambda x: x.pageviews_mobile_views + x.pageviews_desktop_views)
df = df.assign(pagecounts_all_views= lambda x: x.pagecounts_mobile_views + x.pagecounts_desktop_views)
df = df.fillna(value=0)

df.to_csv(csv_file, columns=headers, index = False)


I am working on the file "A1.ipynb".
The first task is:
There are two basicways to combine number of views from pageviews and pagecounts API: using Pandas library or using Python built-in library. I found out it is very difficult to pivot from the JSON files to year and month using Python built-in basic library. On the other hand, pandas provide a nice way to mung the data. Each JSON file is loaded to a pandas dataframe, all of which is merged on timestamp to a single dataframe. Year and month are derived from the timestamp, and pageviews' mobile app and mobile web numbers are combined. Mobile and desktop views are summed to get pagecounts' and apgeviews' all views. Finally, I replaced all non-existant value with 0 before saving the dataframe to a CSV file.
Stage 3. Analysis
Visualize the dataset as a time series graph. This will include mobile, desktop, and combined (mobile+desktop) traffic.
Can you write Python code for it?

import matplotlib
import matplotlib.pyplot as plt
matplotlib.style.use('ggplot')

csv_file = 'en-wikipedia_traffic_200801-201709.csv'

# Read the CSV file to pandas dataframe in which the 'timestamp' column is the new index
df = pd.read_csv(csv_file, index_col=0, parse_dates=[0], infer_datetime_format=True)
# Drop year and month columns since it's not needed for plotting
df = df.drop(['year','month'], axis=1)

plt.figure()
df.plot()

plt.xlabel('datetime')
plt.ylabel('views (in 10 millions)')
plt.title('Wikipedia Traffic Data')
plt.legend()
plt.savefig('en-wikipedia_traffic_200801-201709.png')
plt.show()


I am working on the file "A1/player_notebook.ipynb".
The first task is:
The following are functions that might be useful to you in developing your agent:
game.get_legal_moves(): Returns a list of legal moves for the active player.

game.get_opponent_moves(): Returns a list of legal moves for the inactive player.

game.forecast_move(move): This returns a new board, whose state is the result of making the move specified on the current board.

game.get_state(): This returns a 2D array containing a copy of the explicit state of the board.

game.is_winner(player): Returns whether your player agent has won.

game.is_opponent_winner(player): Returns whether your player's opponent has won.

game.print_board(): Returns a string representation of the game board. This should be useful for debugging.
Can you write Python code for it?

class HumanPlayer():
    """Player that chooses a move according to
    user's input."""
    def move(self, game, legal_moves, time_left):
        print('\t'.join(['[%d] %s'%(i,str(move)) for i,move in enumerate(legal_moves)] ))

        valid_choice = False
        while not valid_choice:
            try:
                index = int(raw_input('Select move index:'))
                valid_choice = 0 <= index < len(legal_moves)

                if not valid_choice:
                    print('Illegal move! Try again.')

            except ValueError:
                print('Invalid index! Try again.')
        return legal_moves[index]


I am working on the file "A1/player_notebook.ipynb".
The first task is:
The following are some basic tests you can use to sanity-check your code. You will also be provided with a test server to which you will be able to submit your agents later this week. Good luck!
Can you write Python code for it?

"""Example test you can run
to make sure your AI does better
than random."""
from isolation import Board
if __name__ == "__main__":
    r = RandomPlayer()
    h = CustomPlayer()
    game = Board(h,r)
    game.play_isolation(500)

"""Example test you can run
to make sure your basic evaluation
function works."""
from isolation import Board

if __name__ == "__main__":
    sample_board = Board(RandomPlayer(),RandomPlayer())
    # setting up the board as though we've been playing
    sample_board.move_count = 3
    sample_board.__active_player__ = 0 # player 1 = 0, player 2 = 1
    # 1st board = 16 moves
    sample_board.__board_state__ = [
                [0,2,0,0,0],
                [0,0,0,0,0],
                [0,0,1,0,0],
                [0,0,0,0,0],
                [0,0,0,0,0]]
    sample_board.__last_player_move__ = [(2,2),(0,1)]

    # player 1 should have 16 moves available,
    # so board gets a score of 16
    h = OpenMoveEvalFn()
    print('This board has a score of %s.'%(h.score(sample_board)))


I am working on the file "A1/player_notebook.ipynb".
The first task is:
This is the first part of the assignment you are expected to implement. It is the evaluation function we've been using in class. The score of a specified game state is just the number of moves open to the active player.
Can you write Python code for it?

class OpenMoveEvalFn():

    def score(self, game, maximizing_player):
        if maximizing_player:
            eval_func = len(game.get_legal_moves())
        else:
            eval_func = len(game.get_opponent_moves())
        return eval_func


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Exercise 1: Bidirectional uniform-cost search
15 points
Implement bidirectional uniform-cost search. Remember that this requires starting your search at both the start and end states.
This function will return the goal, the set of explored nodes from the start node's search, and the set of explored nodes from the goal node's search.
Can you write Python code for it?

def bidirectional_ucs(graph, start, goal):
    """Run bidirectional uniform-cost search
    between start and goal, and return the path,
    the nodes explored from start-initial
    search, and the nodes explored from the
    goal-initial search."""
    # TODO: finish this function
    raise NotImplementedError
    #return path, (start_explored, goal_explored)

from search_test import bidirectional_tests

bidirectional_tests(bidirectional_ucs)


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Exercise 2: Bidirectional A* search
20 points
Implement bidirectional A* search. Remember that you need to calculate a heuristic for both the start-to-goal search and the goal-to-start search.
This function will return the final search path, the set of nodes explored during the start-to-goal search, and the set of nodes explored during the goal-to-start search.
Can you write Python code for it?

def bidirectional_a_star(graph, start, goal):
    """Run bidirectional A* search between
    start and goal, and return the path from
    start to goal, the nodes explored from
    start-initial search, and the nodes explored
    from the goal-initial search."""
    # TODO: finish this function
    raise NotImplementedError
    #return path, (start_explored, goal_explored)


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Exercise 3: Tridirectional search
20 points
Implement tridirectional search in the naive way: starting from each goal node, perform a uniform-cost search and keep expanding until two of the three searches meet.
This will return the path computed and the set of all nodes explored.
Can you write Python code for it?

def tridirectional_search(graph, goals):
    """Run tridirectional uniform-cost search
    between the goals, and return the path
    and the nodes explored."""
    # TODO: finish this function
    raise NotImplementedError
    #return path, nodes_explored


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Exercise 4: Tridirectional search
15 points
This is the heart of the assignment. Implement tridirectional search in such a way as to consistently improve on the performance of your previous implementation. This means consistently exploring fewer nodes during your search in order to reduce runtime.
The specifics are up to you, but we have a few suggestions:
- Tridirectional A*
- choosing landmarks and precomputing reach values
- ATL (A*, landmarks, and triangle-inequality)
- shortcuts (skipping nodes with low reach values)
This function will return the path computed and the set of all nodes explored.
Can you write Python code for it?

def tridirectional_search_advanced(graph, goals):
    """Run an improved tridirectional search between
    goals, and return the path and nodes explored."""
    # TODO: finish this function
    raise NotImplementedError
    #return path, nodes_explored


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Race!
Here's your chance to show us your best stuff. This part is mandatory if you want to compete in the race for extra credit. Implement custom_search() using whatever strategy you'd like. Remember that 'goals' will be a list of the three nodes between which you should route.
Can you write Python code for it?

def custom_search(graph, goals):
    """Run your best tridirectional search between
    goals, and return the path and nodes explored."""
    raise NotImplementedError
    # return path, nodes_explored


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Visualizing search results
When using a geographic network, you may want to visualize your searches. We can do this by converting the search results to a GeoJSON file which we then visualize on Gist by importing the file.
We provide a method for doing this in visualize_graph.py called plot_search(), which takes as parameters the graph, the name of the file to write, the nodes on the path, and the set of all nodes explored. This produces a GeoJSON file named as specified, which you can upload to Gist to visualize the search path and explored nodes.
Can you write Python code for it?

"""Example of how to visualize search results
with two sample nodes in Atlanta."""
from visualize_graph import plot_search
path, explored = bidirectional_ucs(atlanta, '69244359', '557989279')
plot_search(graph, 'atlanta_search.json', path, explored)
# then upload 'atlanta_search.json' to Gist


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Warmup 3: Uniform-cost search
10 points
Implement uniform-cost search using PriorityQueue() as your frontier. From now on, PriorityQueue() should be your default frontier.
uniform_cost_search() should return the same arguments as breadth-first search: the path to the goal node, the set of all nodes explored, and the total cost of the path.
Can you write Python code for it?

def uniform_cost_search(graph, start, goal):
    """Run uniform-cost search from start
    to goal and return the path, the nodes
    explored, and the total cost."""

    frontiers = PriorityQueue()
    path = []
    explored = []
    parent = {}
    flag = 0
    j=1
    cost = 0
    frontiers.append([0,start])
    while frontiers and flag==0:
        current = frontiers.pop()
        if current[1] == goal:
            flag = 1
            break
        explored.append(current[1])
        edges = graph.edges(current[1])
        for i in edges:
            if i[1]!=goal:
                if not(i[1] in explored) and not(i[1] in frontiers):
                    parent[i[1]] = current[1]
                    frontiers.append([j, i[1]])
                    j+=1
            else:
                parent[i[1]] = current[1]
                flag=1
                break
    if start!=goal:
        path.append(goal)
    else:
        return None, explored, 0
    while path[-1] != start:
        path.append(parent[path[-1]])
        cost += 1
    path.reverse()
    return path, explored, cost

from search_tests import ucs_tests
ucs_tests(uniform_cost_search)
pathes[('z', 'h')][0]


I am working on the file "A2/search_notebook.ipynb".
The first task is:
Warmups
We'll start by implementing some simpler search and optimization methods before the real exercises.
Warmup 1: Priority queue
5 points
In all searches that involve calculating path cost or heuristic (e.g. uniform-cost), we have to order our search frontier. It turns out the way that we do this can impact our overall search runtime.
To show this, you'll implement a priority queue and demonstrate its performance benefits. For large graphs, sorting all input to a priority queue is impractical. As such, the datastructure you implement should have an amortized O(1) insertion and removal time. It should do better than the queue you've been provided in InsertionSortQueue().
Hints:
1. The heapq module has been imported for you.
2. Each edge has an associated weight.
Can you write Python code for it?

import heapq

class PriorityQueue():
    """Implementation of a priority queue
    to store nodes during search."""

    # HINT look up the module heapq.

    def __init__(self):
        self.queue = []
        self.current = 0

    def next(self):
        if self.current >=len(self.queue):
            self.current
            raise StopIteration

        out = self.queue[self.current]
        self.current += 1

        return out

    def pop(self):
        return heapq.heappop(self.queue)

    def __iter__(self):
        return self

    def __str__(self):
        return 'PQ:[%s]'%(', '.join([str(i) for i in self.queue]))

    def append(self, node):
        heapq.heappush(self.queue, node)

    #def push(self,cost,node,path):
        # TODO: finish this

    def __contains__(self, key):
        self.current = 0
        return key in [n for v,n in self.queue]

    def __eq__(self, other):
        return self == other

    def clear(self):
        self.queue = []

    #def has_element(self, element):
    __next__ = next

from search_tests import priority_queue_tests

priority_queue_tests(PriorityQueue)


I am working on the file "A3.ipynb".
The first task is:
Again, none of these are beautiful, but for mean and standard deviation I think that magnetic_field_y and magnetic_field_z will be the most helpful.
That gives us a "who made the cut" feature list:
attitude_roll
attitude_pitch
attitude_yaw
rotation_rate_x
rotation_rate_y
gravity_z
user_acc_y
user_acc_z
magnetic_field_y
mangetic_Field_z
Still way too many! My next cut could be for which wave forms drift up or down a lot-- drifting would mess up mean and standard deviation. I'll go with attitude_roll, rotation_rate_x, and user_acc_z.
Can you write Python code for it?

walk_raw[['attitude_roll']].plot(xlim = (5000, 7000))
walk_raw[['rotation_rate_x']].plot(xlim = (5000, 7000))
walk_raw[['user_acc_z']].plot(xlim = (5000, 7000))


I am working on the file "A3.ipynb".
The first task is:
Hm. None of these are beautiful, but for mean and standard deviation I think that user_acc_y and user_acc_z will be the most helpful.
Can you write Python code for it?

walk_raw[['magnetic_field_x']].plot(xlim = (5000, 7000))
walk_raw[['magnetic_field_y']].plot(xlim = (5000, 7000))
walk_raw[['magnetic_field_z']].plot(xlim = (5000, 7000))


I am working on the file "A3.ipynb".
The first task is:
Next step: chunk up the data
Can you write Python code for it?

# http://stackoverflow.com/questions/17315737/split-a-large-pandas-dataframe
# input - df: a Dataframe, chunkSize: the chunk size
# output - a list of DataFrame
# purpose - splits the DataFrame into smaller of max size chunkSize (last may be smaller)
def splitDataFrameIntoSmaller(df, chunkSize = 1000):
    listOfDf = list()
    numberChunks = len(df) // chunkSize + 1
    for i in range(numberChunks):
        listOfDf.append(df[i*chunkSize:(i+1)*chunkSize])
    return listOfDf

# Set up the 10-second chunks
walk_chunked = splitDataFrameIntoSmaller(walk_raw)
for idx, df in enumerate(walk_chunked):
    walk_chunked[idx] = pd.DataFrame(df)

drive_chunked = splitDataFrameIntoSmaller(drive_raw)
for idx, df in enumerate(drive_chunked):
    drive_chunked[idx] = pd.DataFrame(df)

static_chunked = splitDataFrameIntoSmaller(static_raw)
for idx, df in enumerate(static_chunked):
    static_chunked[idx] = pd.DataFrame(df)

upstairs_chunked = splitDataFrameIntoSmaller(upstairs_raw)
for idx, df in enumerate(upstairs_chunked):
    upstairs_chunked[idx] = pd.DataFrame(df)

run_chunked = splitDataFrameIntoSmaller(run_raw)
for idx, df in enumerate(run_chunked):
    run_chunked[idx] = pd.DataFrame(df)


I am working on the file "A3.ipynb".
The first task is:
Now it's time to add those features!
Can you write Python code for it?

# This is where the feature data will go. The array for each activity will have length 30.
walk_featured = []
drive_featured = []
static_featured = []
upstairs_featured = []
run_featured = []

# Populate the features
for df in walk_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    walk_featured.append(features)

for df in drive_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    drive_featured.append(features)

for df in static_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    static_featured.append(features)

for df in upstairs_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    upstairs_featured.append(features)

for df in run_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    run_featured.append(features)

# Combine all of the feature sets into one big one. Along the way, generate my target array.
all_featured = walk_featured + drive_featured + static_featured + upstairs_featured + run_featured
target = [] + [0] * len(walk_featured)
target = target + [1] * len(drive_featured)
target = target + [2] * len(static_featured)
target = target + [3] * len(upstairs_featured)
target = target + [4] * len(run_featured)

# If I accidentally didn't add the right numbers to the target array, throw an error!
if target.count(0) != 30 or...


I am working on the file "A3.ipynb".
The first task is:
What should my features be?
I plotted 20 seconds of data for each type of data for walking:
Can you write Python code for it?

walk_raw[['attitude_roll']].plot(xlim = (5000, 7000))
walk_raw[['attitude_pitch']].plot(xlim = (5000, 7000))
walk_raw[['attitude_yaw']].plot(xlim = (5000, 7000))

walk_raw[['rotation_rate_x']].plot(xlim = (5000, 7000))
walk_raw[['rotation_rate_y']].plot(xlim = (5000, 7000))
walk_raw[['rotation_rate_z']].plot(xlim = (5000, 7000))


I am working on the file "A3.ipynb".
The first task is:
gravity_x and gravity_y are not periodic like the rest of our "pretty" data, so we'll cut those out as well.
Can you write Python code for it?


I am working on the file "A3.ipynb".
The first task is:
rotation_rate_z doesn't look too good, so we'll pull that one out.
Can you write Python code for it?

walk_raw[['gravity_x']].plot(xlim = (5000, 7000))
walk_raw[['gravity_y']].plot(xlim = (5000, 7000))
walk_raw[['gravity_z']].plot(xlim = (5000, 7000))


I am working on the file "A3/.ipynb_checkpoints/probability_notebook-checkpoint.ipynb".
The first task is:
1b: Polytrees
5 points
Is the network for the power plant system a polytree? Why or why not? Choose from the following answers.
Can you write Python code for it?

def is_polytree():
    """Multiple choice question about polytrees."""

    choice = 'c'
    answers = {
        'a' : 'Yes, because it can be decomposed into multiple sub-trees.',
        'b' : 'Yes, because its underlying undirected graph is a tree.',
        'c' : 'No, because its underlying undirected graph is not a tree.',
        'd' : 'No, because it cannot be decomposed into multiple sub-trees.'
    }
    return answers[choice]


I am working on the file "A3/.ipynb_checkpoints/probability_notebook-checkpoint.ipynb".
The first task is:
1c: Setting the probabilities
15 points
Assume that the following statements about the system are true:

The temperature gauge reads the correct temperature with 95% probability when it is not faulty and 20% probability when it is faulty. For simplicity, say that the gauge's "true" value corresponds with its "hot" reading and "false" with its "normal" reading, so the gauge would have a 95% chance of returning "true" when the temperature is hot and it is not faulty.
The alarm is faulty 15% of the time.
The temperature is hot (call this "true") 20% of the time.
When the temperature is hot, the gauge is faulty 80% of the time. Otherwise, the gauge is faulty 5% of the time.
The alarm responds correctly to the gauge 55% of the time when the alarm is faulty, and it responds correctly to the gauge 90% of the time when the alarm is not faulty. For instance, when it is faulty, the alarm sounds 55% of the time that the gauge is "hot" and remains silent 55% of the time that the gauge is "normal."

Knowing these facts, set the conditional probabilities for the necessary variables on the network you just built.
Using pbnt's Distribution class: if you wanted to set the distribution for P(A) to 70% true, 30% false, you would invoke the following commands.
A_distribution = DiscreteDistribution(A_node)
index = A_distribution.generate_index([],[])
A_distribution[index] = [0.3,0.7]
A_Node.set_dist(A_distribution)

If you wanted to set the distribution for P(A|G) to be
|$G$|$P(A=true| G)$|
|------|:-----:|
|T| 0.75|
|F| 0.85|
you would invoke:
from numpy import zeros, float32
dist = zeros([G_node.size(), A_node.size()], dtype=float32)
dist[0,:] = [0.15, 0.85]
dist[1,:] = [0.25, 0.75]
A_distribution = ConditionalDiscreteDistribution(nodes=[G_node,A_node], table=dist)
A_node.set_dist(A_distribution)

Modeling a three-variable relationship is a bit trickier. If you wanted to set the following distribution for $P(A|G,T)$ to be
|$G$|$T$|$P(A=true| G, T)$|
|--|--|:----:|
|T|T|0.15|
|T|F|0.6|
|F|T|0.2|
|F|F|0.1|
you would invoke:
from...
Can you write Python code for it?

from numpy import zeros, float32
import Distribution
from Distribution import DiscreteDistribution, ConditionalDiscreteDistribution
def set_probability(bayes_net):
    """Set probability distribution for each
    node in the power plant system."""

    A_node = bayes_net.get_node_by_name("alarm")
    F_A_node = bayes_net.get_node_by_name("faulty alarm")
    G_node = bayes_net.get_node_by_name("gauge")
    F_G_node = bayes_net.get_node_by_name("faulty gauge")
    T_node = bayes_net.get_node_by_name("temperature")
    nodes = [A_node, F_A_node, G_node, F_G_node, T_node]

    #for completely independent nodes
    T_dist = DiscreteDistribution(T_node)
    index = T_dist.generate_index([],[])
    T_dist[index] = [0.8,0.2]
    T_node.set_dist(T_dist)

    F_A_dist = DiscreteDistribution(F_A_node)
    index = F_A_dist.generate_index([],[])
    F_A_dist[index] = [0.85,0.15]
    F_A_node.set_dist(F_A_dist)

    #for single parent node
    dist = zeros([T_node.size(), F_G_node.size()], dtype=float32)
    dist[0,:] = [0.95,0.05]
    dist[1,:] = [0.2,0.8]
    F_G_dist = ConditionalDiscreteDistribution(nodes = [T_node, F_G_node], table=dist)
    F_G_node.set_dist(F_G_dist)

    #for double parent node
    dist = zeros([F_G_node.size(), T_node.size(), G_node.size()], dtype=float32)
    dist[0,0,:] = [0.95,0.05]
    dist[0,1,:] = [0.05,0.95]
    dist[1,0,:] = [0.2,0.8]
    dist[1,1,:] = [0.8,0.2]
    G_dist = ConditionalDiscreteDistribution(nodes=[F_G_node,T_node,G_node], table=dist)
    G_node.set_dist(G_dist)

    dist = zeros([F_A_node.size(),G_node.size(),A_node.size()], dtype=float32)
    dist[0,0,:] = [0.9,0.1]
    dist[0,1,:] = [0.1,0.9]
    dist[1,0,:] = [0.55,0.45]
    dist[1,1,:] = [0.45,0.55]
    A_dist = ConditionalDiscreteDistribution(nodes=[F_A_node,G_node,A_node], table=dist)
    A_node.set_dist(A_dist)

    return bayes_net

set_probability(power_plant)
from probability_tests import probability_setup_test
probability_setup_test(power_plant)


I am working on the file "A3/.ipynb_checkpoints/probability_notebook-checkpoint.ipynb".
The first task is:
1d: Probability calculations
10 points
To finish up, you're going to perform inference on the network to calculate the following probabilities:

the marginal probability that the alarm sounds
the marginal probability that the gauge shows "hot"
the probability that the temperature is actually hot, given that the alarm sounds and the alarm and gauge are both working

You'll fill out the "get_prob" functions to calculate the probabilities.
Here's an example of how to do inference for the marginal probability of the "faulty alarm" node being True (assuming "bayes_net" is your network):
F_A_node = bayes_net.get_node_by_name('faulty alarm')
engine = JunctionTreeEngine(bayes_net)
Q = engine.marginal(F_A_node)[0]
index = Q.generate_index([True],range(Q.nDims))
prob = Q[index]

To compute the conditional probability, set the evidence variables before computing the marginal as seen below (here we're computing $P(A = false | F_A = true, T = False)$):
engine.evidence[F_A_node] = True
engine.evidence[T_node] = False
Q = engine.marginal(A_node)[0]
index = Q.generate_index([False],range(Q.nDims))
prob = Q[index]

If you need to sanity-check to make sure you're doing inference correctly, you can run inference on one of the probabilities that we gave you in 1c. For instance, running inference on $P(T=true)$ should return 0.19999994 (i.e. almost 20%). You can also calculate the answers by hand to double-check.
Can you write Python code for it?

def get_alarm_prob(bayes_net, alarm_rings):
    """Calculate the marginal
    probability of the alarm
    ringing (T/F) in the
    power plant system."""
    A_node = bayes_net.get_node_by_name('alarm')
    engine = JunctionTreeEngine(bayes_net)
    Q = engine.marginal(A_node)[0]
    index = Q.generate_index([alarm_rings],range(Q.nDims))
    alarm_prob = Q[index]

    return alarm_prob

def get_gauge_prob(bayes_net, gauge_hot):
    """Calculate the marginal
    probability of the gauge
    showing hot (T/F) in the
    power plant system."""
    G_node = bayes_net.get_node_by_name('gauge')
    engine = JunctionTreeEngine(bayes_net)
    Q = engine.marginal(G_node)[0]
    index = Q.generate_index([gauge_hot],range(Q.nDims))
    gauge_prob = Q[index]

    return gauge_prob

from Inference import JunctionTreeEngine
def get_temperature_prob(bayes_net,temp_hot):
    """Calculate theprobability of the
    temperature being hot (T/F) in the
    power plant system, given that the
    alarm sounds and neither the gauge
    nor alarm is faulty."""
    T_node = bayes_net.get_node_by_name('temperature')
    A_node = bayes_net.get_node_by_name('alarm')
    F_A_node = bayes_net.get_node_by_name('faulty alarm')
    F_G_node = bayes_net.get_node_by_name('faulty gauge')
    engine = JunctionTreeEngine(bayes_net)
    engine.evidence[A_node] = True
    engine.evidence[F_A_node] = False
    engine.evidence[F_G_node] = False
    Q = engine.marginal(T_node)[0]
    index = Q.generate_index([temp_hot],range(Q.nDims))
    temp_prob = Q[index]

    return temp_prob

print get_alarm_prob(power_plant,True)
print get_gauge_prob(power_plant,True)
print get_temperature_prob(power_plant,True)


I am working on the file "A3/.ipynb_checkpoints/probability_notebook-checkpoint.ipynb".
The first task is:
2b: Metropolis-Hastings sampling
15 points
Now you will implement the Metropolis-Hastings algorithm, which is a method for estimating a probability distribution when it is prohibitively expensive (even for inference!) to completely compute the distribution. You'll do this in MH_sampling(), which takes a Bayesian network and initial state as a parameter and returns a sample state drawn from the network's distribution. The method should just perform a single iteration of the algorithm. If an initial value is not given, default to a state chosen uniformly at random from the possible states.
The general idea is to build an approximation of a latent probability distribution by repeatedly generating a "candidate" value for each random variable in the system, and then probabilistically accepting or rejecting the candidate value based on an underlying acceptance function. These slides provide a nice intro, and this cheat sheet provides an explanation of the details.
Hint 1: in both Metropolis-Hastings and Gibbs sampling, you'll need access to each node's probability distribution. You can access this distribution by calling
A_node.dist.table

which will return the same numpy array that you provided when constructing the probability distribution.
Hint 2: you'll also want to use the random package (e.g. random.randint()) for the probabilistic choices that sampling makes.
Hint 3: in order to count the sample states later on, you'll want to make sure the sample that you return is hashable. One way to do this is by returning the sample as a tuple.
Can you write Python code for it?

import random
def MH_sampling(bayes_net, initial_value):
    """Complete a single iteration of the
    Metropolis-Hastings algorithm given a
    Bayesian network and an initial state
    value. Returns the state sampled from
    the probability distribution."""

    var_id = random.randint(0,2)
    new_val = random.randint(0,2)
    sample = initial_value
    sample[var_id] = new_val
    for node in bayes_net.nodes:
        if node.id == 0:
            A = node
        if node.id == 1:
            B = node
        if node.id == 2:
            C = node
        if node.id == 3:
            AvB = node
        if node.id == 4:
            BvC = node
        if node.id == 5:
            CvA = node

    Adist = A.dist.table
    Bdist = B.dist.table
    Cdist = C.dist.table
    AvBdist = AvB.dist.table
    BvCdist = BvC.dist.table
    CvAdist = CvA.dist.table

    p_x = Adist[initial_value[0]]*Bdist[initial_value[1]]*Cdist[initial_value[2]]*AvBdist[initial_value[0],initial_value[1],initial_value[3]]*BvCdist[initial_value[1],initial_value[2],initial_value[4]]*CvAdist[initial_value[2],initial_value[0],initial_value[5]]
    p_x_dash = Adist[sample[0]]*Bdist[sample[1]]*Cdist[sample[2]]*AvBdist[sample[0],sample[1],sample[3]]*BvCdist[sample[1],sample[2],sample[4]]*CvAdist[sample[2],sample[0],sample[5]]

    if p_x!=0:
        A = p_x_dash/p_x
    else:
        A = 1

    if A>=1:
        initial_value[var_id] = new_val
        sample = initial_value
    else:
        weighted = [(1,int(A*100)), (0, 100-int(A*100))]
        population = [val for val, cnt in weighted for i in range(cnt)]
        acceptance = random.choice(population)
        if acceptance == 0:
            sample = initial_value
        else:
            sample = initial_value
            sample[var_id] = new_val

    return sample

# arbitrary initial state for the game system
initial_value = [0,0,0,0,2,1]
sample = MH_sampling(game_net, initial_value)
print sample


I am working on the file "A3/.ipynb_checkpoints/probability_notebook-checkpoint.ipynb".
The first task is:
2d: Comparing sampling methods
15 points
Suppose that you know the following outcome of two of the three games: A beats B and A draws with C. Start by calculating the posterior distribution for the outcome of the BvC match in calculate_posterior(), using the inference methods from 1d.
Estimate the likelihood of different outcomes for the third match by running Gibbs sampling until it converges to a stationary distribution. We'll say that the sampler has converged when, for 10 successive iterations, the difference in expected outcome for the third match differs from the previous estimated outcome by less than .1%.
Repeat this experiment for Metropolis-Hastings sampling.
Which algorithm converges more quickly? By approximately what factor? For instance, if Metropolis-Hastings takes twice as many iterations to converge as Gibbs sampling, you'd say that it converged faster by a factor of 2. Fill in sampling_question() to answer both parts.
Can you write Python code for it?

def calculate_posterior(games_net):
    """Calculate the posterior distribution
    of the BvC match given that A won against
    B and tied C. Return a list of probabilities
    corresponding to win, loss and tie likelihood."""
    posterior = [0,0,0]
    engine = JunctionTreeEngine(games_net)
    AvB = games_net.get_node_by_name('AvB')
    BvC = games_net.get_node_by_name('BvC')
    CvA = games_net.get_node_by_name('CvA')
    engine.evidence[AvB] = 0
    engine.evidence[CvA] = 2
    Q = engine.marginal(BvC)[0]
    index = Q.generate_index([0],range(Q.nDims))
    posterior[0] = Q[index]
    index = Q.generate_index([1],range(Q.nDims))
    posterior[1] = Q[index]
    index = Q.generate_index([2],range(Q.nDims))
    posterior[2] = Q[index]
    return posterior

iter_counts = [1e1,1e3,1e5,1e6]
def compare_sampling(bayes_net, posterior):
    """Compare Gibbs and Metropolis-Hastings
    sampling by calculating how long it takes
    for each method to converge to the
    provided posterior."""
    # TODO: finish this function
    return Gibbs_convergence, MH_convergence

def sampling_question():
    """Question about sampling performance."""
    # TODO: assign value to choice and factor
    choice = 1
    options = ['Gibbs','Metropolis-Hastings']
    factor = 0
    return options[choice], factor

# test your sampling methods here
posterior = calculate_posterior(game_net)
compare_sampling(game_net, posterior)


I am working on the file "A3/.ipynb_checkpoints/probability_notebook-checkpoint.ipynb".
The first task is:
2e: Theoretical follow-up
5 points
Suppose there are now $n$ teams in the competition, and all matches have been played except for the last match. Using inference by enumeration, how does the complexity of predicting the last match vary with $n$?
Fill in complexity_question() to answer, using big-O notation. For example, write 'O(n^2)' for second-degree polynomial runtime.
Can you write Python code for it?

def complexity_question():
    # TODO: write an expression for complexity
    complexity = 'O(2^n)'
    return complexity


I am working on the file "A3/.ipynb_checkpoints/probability_notebook-checkpoint.ipynb".
The first task is:
Part 2: Sampling
60 points total
For the main exercise, consider the following scenario.
There are three frisbee teams who play each other: the Airheads, the Buffoons, and the Clods (A, B and C for short). Each match is between two teams, and each team can either win, lose, or draw in a match. Each team has a fixed but unknown skill level, represented as an integer from 0 to 3. Each match's outcome is probabilistically proportional to the difference in skill level between the teams.
We want to predict the outcome of the matches, given prior knowledge of previous matches. Rather than using inference, we will do so by sampling the network using two Markov Chain Monte Carlo models: Metropolis-Hastings (2b) and Gibbs sampling (2c).
2a: Build the network
10 points
Build a Bayes Net to represent the three teams and their influences on the match outcomes. Assume the following variable conventions:
| variable name | description|
|---------|:------:|
|A| A's skill level|
|B | B's skill level|
|C | C's skill level|
|AvB | the outcome of A vs. B
 (0 = A wins, 1 = B wins, 2 = tie)|
|BvC | the outcome of B vs. C
 (0 = B wins, 1 = C wins, 2 = tie)|
|CvA | the outcome of C vs. A
 (0 = C wins, 1 = A wins, 2 = tie)|
Assume that each team has the following prior distribution of skill levels:
|skill level|P(skill level)|
|----|:----:|
|0|0.15|
|1|0.45|
|2|0.30|
|3|0.10|
In addition, assume that the differences in skill levels correspond to the following probabilities of winning:
| skill difference
 (T2 - T1) | T1 wins | T2 wins| Tie |
|------------|----------|---|:--------:|
|0|0.10|0.10|0.80|
|1|0.20|0.40|0.20|
|2|0.15|0.75|0.10|
|3|0.05|0.90|0.05|
Can you write Python code for it?

def get_game_network():
    """Create a Bayes Net representation
    of the game problem."""

    #create the network
    A = BayesNode(0,4,name='Ateam')
    B = BayesNode(1,4,name='Bteam')
    C = BayesNode(2,4,name='Cteam')
    AvB = BayesNode(3,3,name='AvB')
    BvC = BayesNode(4,3,name='BvC')
    CvA = BayesNode(5,3,name='CvA')

    A.add_child(AvB)
    A.add_child(CvA)
    B.add_child(AvB)
    B.add_child(BvC)
    C.add_child(BvC)
    C.add_child(CvA)
    AvB.add_parent(A)
    AvB.add_parent(B)
    BvC.add_parent(B)
    BvC.add_parent(C)
    CvA.add_parent(C)
    CvA.add_parent(A)

    nodes = [A,B,C,AvB,BvC,CvA]
    game_net = BayesNet(nodes)

    #setting priors for team skills
    skillDist = DiscreteDistribution(A)
    index = skillDist.generate_index([],[])
    skillDist[index] = [0.15,0.45,0.3,0.1]
    A.set_dist(skillDist)

    skillDist = DiscreteDistribution(B)
    index = skillDist.generate_index([],[])
    skillDist[index] = [0.15,0.45,0.3,0.1]
    B.set_dist(skillDist)

    skillDist = DiscreteDistribution(C)
    index = skillDist.generate_index([],[])
    skillDist[index] = [0.15,0.45,0.3,0.1]
    C.set_dist(skillDist)

    #setting probability priors for winning
    dist = zeros([A.size(),B.size(),AvB.size()], dtype=float32)
    dist[0,0,:] = [0.1,0.1,0.8]
    dist[1,1,:] = [0.1,0.1,0.8]
    dist[2,2,:] = [0.1,0.1,0.8]
    dist[3,3,:] = [0.1,0.1,0.8]
    dist[0,1,:] = [0.2,0.4,0.2]
    dist[1,2,:] = [0.2,0.4,0.2]
    dist[2,3,:] = [0.2,0.4,0.2]
    dist[0,2,:] = [0.15,0.75,0.1]
    dist[1,3,:] = [0.15,0.75,0.1]
    dist[0,3,:] = [0.05,0.9,0.05]
    dist[3,0,:] = [0.9,0.05,0.05]
    dist[1,0,:] = [0.4,0.2,0.2]
    dist[2,1,:] = [0.4,0.2,0.2]
    dist[3,2,:] = [0.4,0.2,0.2]
    dist[2,0,:] = [0.75,0.15,0.1]
    dist[3,1,:] = [0.75,0.15,0.1]
    AvB_dist = ConditionalDiscreteDistribution(nodes=[A,B,AvB], table=dist)
    AvB.set_dist(AvB_dist)

    dist = zeros([B.size(),C.size(),BvC.size()], dtype=float32)
    dist[0,0,:] = [0.1,0.1,0.8]
    dist[1,1,:] =...


I am working on the file "A3/probability_notebook.ipynb".
The first task is:
1c: Setting the probabilities
15 points
Assume that the following statements about the system are true:

The temperature gauge reads the correct temperature with 95% probability when it is not faulty and 20% probability when it is faulty. For simplicity, say that the gauge's "true" value corresponds with its "hot" reading and "false" with its "normal" reading, so the gauge would have a 95% chance of returning "true" when the temperature is hot and it is not faulty.
The alarm is faulty 15% of the time.
The temperature is hot (call this "true") 20% of the time.
When the temperature is hot, the gauge is faulty 80% of the time. Otherwise, the gauge is faulty 5% of the time.
The alarm responds correctly to the gauge 55% of the time when the alarm is faulty, and it responds correctly to the gauge 90% of the time when the alarm is not faulty. For instance, when it is faulty, the alarm sounds 55% of the time that the gauge is "hot" and remains silent 55% of the time that the gauge is "normal."

Knowing these facts, set the conditional probabilities for the necessary variables on the network you just built.
Using pbnt's Distribution class: if you wanted to set the distribution for P(A) to 70% true, 30% false, you would invoke the following commands.
A_distribution = DiscreteDistribution(A_node)
index = A_distribution.generate_index([],[])
A_distribution[index] = [0.3,0.7]
A_Node.set_dist(A_distribution)

If you wanted to set the distribution for P(A|G) to be
|$G$|$P(A=true| G)$|
|------|:-----:|
|T| 0.75|
|F| 0.85|
you would invoke:
from numpy import zeros, float32
dist = zeros([G_node.size(), A_node.size()], dtype=float32)
dist[0,:] = [0.15, 0.85]
dist[1,:] = [0.25, 0.75]
A_distribution = ConditionalDiscreteDistribution(nodes=[G_node,A_node], table=dist)
A_node.set_dist(A_distribution)

Modeling a three-variable relationship is a bit trickier. If you wanted to set the following distribution for $P(A|G,T)$ to be
|$G$|$T$|$P(A=true| G, T)$|
|--|--|:----:|
|T|T|0.15|
|T|F|0.6|
|F|T|0.2|
|F|F|0.1|
you would invoke:
from...
Can you write Python code for it?


I am working on the file "A3/probability_notebook.ipynb".
The first task is:
2b: Metropolis-Hastings sampling
15 points
Now you will implement the Metropolis-Hastings algorithm, which is a method for estimating a probability distribution when it is prohibitively expensive (even for inference!) to completely compute the distribution. You'll do this in MH_sampling(), which takes a Bayesian network and initial state as a parameter and returns a sample state drawn from the network's distribution. The method should just perform a single iteration of the algorithm. If an initial value is not given, default to a state chosen uniformly at random from the possible states.
The general idea is to build an approximation of a latent probability distribution by repeatedly generating a "candidate" value for each random variable in the system, and then probabilistically accepting or rejecting the candidate value based on an underlying acceptance function. These slides provide a nice intro, and this cheat sheet provides an explanation of the details.
Hint 1: in both Metropolis-Hastings and Gibbs sampling, you'll need access to each node's probability distribution. You can access this distribution by calling
A_node.dist.table

which will return the same numpy array that you provided when constructing the probability distribution.
Hint 2: you'll also want to use the random package (e.g. random.randint()) for the probabilistic choices that sampling makes.
Hint 3: in order to count the sample states later on, you'll want to make sure the sample that you return is hashable. One way to do this is by returning the sample as a tuple.
Can you write Python code for it?

import random
def MH_sampling(bayes_net, initial_value):
    """Complete a single iteration of the
    Metropolis-Hastings algorithm given a
    Bayesian network and an initial state
    value. Returns the state sampled from
    the probability distribution."""

    var_id = random.randint(0,2)
    new_val = random.randint(0,2)
    sample = [i for i in initial_value]
    sample[var_id] = new_val
    for node in bayes_net.nodes:
        if node.id == 0:
            A = node
        if node.id == 1:
            B = node
        if node.id == 2:
            C = node
        if node.id == 3:
            AvB = node
        if node.id == 4:
            BvC = node
        if node.id == 5:
            CvA = node

    Adist = A.dist.table
    Bdist = B.dist.table
    Cdist = C.dist.table
    AvBdist = AvB.dist.table
    BvCdist = BvC.dist.table
    CvAdist = CvA.dist.table

    p_x = Adist[initial_value[0]]*Bdist[initial_value[1]]*Cdist[initial_value[2]]*AvBdist[initial_value[0],initial_value[1],initial_value[3]]*BvCdist[initial_value[1],initial_value[2],initial_value[4]]*CvAdist[initial_value[2],initial_value[0],initial_value[5]]
    p_x_dash = Adist[sample[0]]*Bdist[sample[1]]*Cdist[sample[2]]*AvBdist[sample[0],sample[1],sample[3]]*BvCdist[sample[1],sample[2],sample[4]]*CvAdist[sample[2],sample[0],sample[5]]

    if p_x!=0:
        A = p_x_dash/p_x
    else:
        A = 1

    if A>=1:
        return sample
    else:
        weighted = [(1,int(A*100)), (0, 100-int(A*100))]
        population = [val for val, cnt in weighted for i in range(cnt)]
        acceptance = random.choice(population)
        if acceptance == 0:
            sample = initial_value
        else:
            sample = initial_value
            sample[var_id] = new_val

    return sample

# arbitrary initial state for the game system
initial_value = [0,0,0,0,2,1]
sample = MH_sampling(game_net, initial_value)
print sample


I am working on the file "A3/probability_notebook.ipynb".
The first task is:
2c: Gibbs sampling
15 points
Implement the Gibbs sampling algorithm, which is a special case of Metropolis-Hastings. You'll do this in Gibbs_sampling(), which takes a Bayesian network and initial state value as a parameter and returns a sample state drawn from the network's distribution. The method should just consist of a single iteration of the algorithm. If no initial value is provided, default to a uniform distribution over the possible states.
You may find this helpful in understanding the basics of Gibbs sampling over Bayesian networks. Make sure to identify what makes it different from Metropolis-Hastings.
Can you write Python code for it?

import random
def Gibbs_sampling(bayes_net, initial_value):
    """Complete a single iteration of the
    Gibbs sampling algorithm given a
    Bayesian network and an initial state
    value. Returns the state sampled from
    the probability distribution."""
    sample = initial_value
    if initial_value:
        #randomly select one variable and sample from it's posterior distribution for a new value
    else:
        #default
        for i in range(0,5):
            upper_bound = 3 if i<3 else 2
            sample[i] = random.randint(0,upper_bound)

    return sample


I am working on the file "A3/probability_notebook.ipynb".
The first task is:
2e: Theoretical follow-up
5 points
Suppose there are now $n$ teams in the competition, and all matches have been played except for the last match. Using inference by enumeration, how does the complexity of predicting the last match vary with $n$?
Fill in complexity_question() to answer, using big-O notation. For example, write 'O(n^2)' for second-degree polynomial runtime.
Can you write Python code for it?


I am working on the file "A3/probability_notebook.ipynb".
The first task is:
Part 1: Bayesian network tutorial
40 points total
To start, design a basic probabilistic model for the following system.
There's a nuclear power plant in which an alarm is supposed to ring when the core temperature, indicated by a gauge, exceeds a fixed threshold. For simplicity, we assume that the temperature is represented as either high or normal. Use the following Boolean variables in your implementation:

A = alarm sounds
FA = alarm is faulty
G = gauge reading (high = True, normal = False)
FG = gauge is faulty
T = actual temperature (high = True, normal = False)

In addition, assume that the gauge is more likely to fail when the temperature is high.
You will test your implementation at the end of the section.
1a: Casting the net
10 points
Design a Bayesian network for this system, using pbnt to represent the nodes and conditional probability arcs connecting nodes. Don't worry about the probabilities for now. Fill out the function below to create the net.
The following command will create a BayesNode with 2 values, an id of 0 and the name "alarm":
A_node = BayesNode(0,2,name='alarm')

You will use BayesNode.add_parent() and BayesNode.add_child() to connect nodes. For example, to connect the alarm and temperature nodes that you've already made (i.e. assuming that temperature affects the alarm probability):
A_node.add_parent(T_node)
T_node.add_child(A_node)

You can run probability_tests.network_setup_test() to make sure your network is set up correctly.
Can you write Python code for it?

from Node import BayesNode
from Graph import BayesNet
def make_power_plant_net():
    """Create a Bayes Net representation of
    the above power plant problem."""
    T_node = BayesNode(0,2,name='temperature')
    G_node = BayesNode(1,2,name='gauge')
    A_node = BayesNode(2,2,name='alarm')
    F_G_node = BayesNode(3,2,name='faulty gauge')
    F_A_node = BayesNode(4,2,name='faulty alarm')

    T_node.add_child(G_node)
    T_node.add_child(F_G_node)
    G_node.add_parent(T_node)
    F_G_node.add_parent(T_node)
    F_G_node.add_child(G_node)
    G_node.add_parent(F_G_node)
    G_node.add_child(A_node)
    A_node.add_parent(G_node)
    F_A_node.add_child(A_node)
    A_node.add_parent(F_A_node)

    nodes = [T_node, G_node, F_G_node, A_node, F_A_node]

    return BayesNet(nodes)

from probability_tests import network_setup_test
power_plant = make_power_plant_net()
network_setup_test(power_plant)


I am working on the file "A3/probability_notebook.ipynb".
The first task is:
Part 2: Sampling
60 points total
For the main exercise, consider the following scenario.
There are three frisbee teams who play each other: the Airheads, the Buffoons, and the Clods (A, B and C for short). Each match is between two teams, and each team can either win, lose, or draw in a match. Each team has a fixed but unknown skill level, represented as an integer from 0 to 3. Each match's outcome is probabilistically proportional to the difference in skill level between the teams.
We want to predict the outcome of the matches, given prior knowledge of previous matches. Rather than using inference, we will do so by sampling the network using two Markov Chain Monte Carlo models: Metropolis-Hastings (2b) and Gibbs sampling (2c).
2a: Build the network
10 points
Build a Bayes Net to represent the three teams and their influences on the match outcomes. Assume the following variable conventions:
| variable name | description|
|---------|:------:|
|A| A's skill level|
|B | B's skill level|
|C | C's skill level|
|AvB | the outcome of A vs. B
 (0 = A wins, 1 = B wins, 2 = tie)|
|BvC | the outcome of B vs. C
 (0 = B wins, 1 = C wins, 2 = tie)|
|CvA | the outcome of C vs. A
 (0 = C wins, 1 = A wins, 2 = tie)|
Assume that each team has the following prior distribution of skill levels:
|skill level|P(skill level)|
|----|:----:|
|0|0.15|
|1|0.45|
|2|0.30|
|3|0.10|
In addition, assume that the differences in skill levels correspond to the following probabilities of winning:
| skill difference
 (T2 - T1) | T1 wins | T2 wins| Tie |
|------------|----------|---|:--------:|
|0|0.10|0.10|0.80|
|1|0.20|0.40|0.20|
|2|0.15|0.75|0.10|
|3|0.05|0.90|0.05|
Can you write Python code for it?


I am working on the file "A6/hmm_notebook.ipynb".
The first task is:
Now, for the purposes of comparison, compute the most likely sequence of paths for the Swiss Horn examples using the Viterbi algorithm, then compute path by choosing the sequence of most likely states at each instant as given by the Forward-Backward algorithm. How do the total probabilities of the state sequences compare?
Can you write Python code for it?

def part_2_b():

    options = {'a': 'The total probabilities are the same.',
               'b': 'The Viterbi algorithm yields a more likely sequence',
               'c': 'The forward-backward algorithm yields a more likely sequence',
               'd': 'Insufficient data is provided',
               'e': 'No answer'}

    return options['b']


I am working on the file "A6/hmm_notebook.ipynb".
The first task is:
We can use the forward-backward algorithm to compute the posterior probabilities of the state variables given a sequence of observations. We can use these probabilities in turn to figure out the most likely state at any point in time.
The forward-backward algorithm is specified in figure 15.4 of Russell & Norvig, with the Forward operator given as equation 15.5, and the Backward operator given as 15.9.
Fill out forward_backward() to return the distribution of posterior probabilities of all states at each time step. We will provide tests for you - stay tuned!
Can you write Python code for it?

from functools import reduce
from operator import mul
def forward_backward(evidence_vector, prior, states, evidence_variables, transition_probs, emission_probs ):

    """
        This method takes as input the following:

        evidence_vector: A list of dictionaries mapping evidence variables to their values at each time step

        prior: A dictionary corresponding to the prior distribution over states

        states: A list of all possible system states

        evidence_variables: A list of all valid evidence variables

        transition_probs: A dictionary mapping states onto dictionaries mapping states onto probabilities

        emission_probs: A dictionary mapping states onto dictionaries mapping evidence variables onto
                    a list of probabilities for their possible values

        This method returns:
            A list of dictionaries giving the distribution of possible states at each time step

    """

    alpha = [{}]
    beta = [{}]

    #initialization
    smoothing_term = []
    for each_state in states:
        alpha[0][each_state] = prior[each_state]
        for evidence in evidence_variables:
            alpha[0][each_state] = alpha[0][each_state]*emission_probs[each_state][evidence][evidence_vector[0][evidence]]

    for each_state in states:
        beta[0][each_state] = 1

    #forward
    for t in range(1,len(evidence_vector)):
        alpha.append({})

        for curr_state in states:
            alpha[t][curr_state] = 0
            for prev_state in states:
                prob = alpha[t-1][prev_state]*transition_probs[prev_state][curr_state]
                for evidence in evidence_variables:
                    prob = prob*emission_probs[curr_state][evidence][evidence_vector[0][evidence]]

                alpha[t][curr_state] = alpha[t][curr_state] + prob

    #backward
    for rev_index in...