2016-05-26 12:33:27 +00:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![](images/EscUpmPolit_p.gif \"UPM\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Course Notes for Learning Intelligent Systems"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
2019-03-06 16:46:12 +00:00
"Department of Telematic Engineering Systems, Universidad Politécnica de Madrid, © Carlos A. Iglesias"
2016-05-26 12:33:27 +00:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Vector Representation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Table of Contents\n",
"* [Objectives](#Objectives)\n",
"* [Tools](#Tools)\n",
"* [Vector representation: Count vector](#Vector-representation:-Count-vector)\n",
"* [Binary vectors](#Binary-vectors)\n",
"* [Bigram vectors](#Bigram-vectors)\n",
"* [Tf-idf vector representation](#Tf-idf-vector-representation)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Objectives"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this notebook we are going to transform text into feature vectors, using several representations as presented in class.\n",
"\n",
"We are going to use the examples from the slides."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2016-05-26 12:33:27 +00:00
"outputs": [],
"source": [
"doc1 = 'Summer is coming but Summer is short'\n",
"doc2 = 'I like the Summer and I like the Winter'\n",
"doc3 = 'I like sandwiches and I like the Winter'\n",
"documents = [doc1, doc2, doc3]"
]
},
{
"cell_type": "markdown",
2019-02-28 14:30:33 +00:00
"metadata": {},
2016-05-26 12:33:27 +00:00
"source": [
"# Tools"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The different tools we have presented so far (NLTK, Scikit-Learn, TextBlob and CLiPS) provide overlapping functionalities for obtaining vector representations and apply machine learning algorithms.\n",
"\n",
"We are going to focus on the use of scikit-learn so that we can also use easily Pandas as we saw in the previous topic.\n",
"\n",
"Scikit-learn provides specific facililities for processing texts, as described in the [manual](http://scikit-learn.org/stable/modules/feature_extraction.html)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Vector representation: Count vector"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scikit-learn provides two classes for binary vectors: [CountVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html#sklearn.feature_extraction.text.CountVectorizer) and [HashingVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.HashingVectorizer.html). The latter is more efficient but does not allow to understand which features are more important, so we use the first class. Nevertheless, they are compatible, so, they can be interchanged for production environments.\n",
"\n",
"The first step for vectorizing with scikit-learn is creating a CountVectorizer object and then we should call 'fit_transform' to fit the vocabulary."
]
},
{
"cell_type": "code",
2023-04-23 14:41:53 +00:00
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<style>#sk-container-id-1 {color: black;background-color: white;}#sk-container-id-1 pre{padding: 0;}#sk-container-id-1 div.sk-toggleable {background-color: white;}#sk-container-id-1 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-1 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-1 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-1 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-1 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-1 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-1 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-1 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-1 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-1 div.sk-item {position: relative;z-index: 1;}#sk-container-id-1 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-1 div.sk-item::before, #sk-container-id-1 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-1 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-1 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-1 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-1 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-1 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-1 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-1 div.sk-label-container {text-align: center;}#sk-container-id-1 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-1 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-1\" class=\"sk-top-container\
],
"text/plain": [
"CountVectorizer(max_features=5000)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
2016-05-26 12:33:27 +00:00
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"\n",
"vectorizer = CountVectorizer(analyzer = \"word\", max_features = 5000) \n",
"vectorizer"
]
},
{
"cell_type": "markdown",
2019-02-28 14:30:33 +00:00
"metadata": {},
2016-05-26 12:33:27 +00:00
"source": [
"As we can see, [CountVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html#sklearn.feature_extraction.text.CountVectorizer) comes with many options. We can define many configuration options, such as the maximum or minimum frequency of a term (*min_fd*, *max_df*), maximum number of features (*max_features*), if we analyze words or characters (*analyzer*), or if the output is binary or not (*binary*). *CountVectorizer* also allows us to include if we want to preprocess the input (*preprocessor*) before tokenizing it (*tokenizer*) and exclude stop words (*stop_words*).\n",
"\n",
"We can use NLTK preprocessing and tokenizer functions to tune *CountVectorizer* using these parameters.\n",
"\n",
"We are going to see how the vectors look like."
]
},
{
"cell_type": "code",
2023-04-23 14:41:53 +00:00
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<3x10 sparse matrix of type '<class 'numpy.int64'>'\n",
"\twith 15 stored elements in Compressed Sparse Row format>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
2016-05-26 12:33:27 +00:00
"source": [
"vectors = vectorizer.fit_transform(documents)\n",
"vectors"
]
},
{
"cell_type": "markdown",
2019-02-28 14:30:33 +00:00
"metadata": {},
2016-05-26 12:33:27 +00:00
"source": [
"We see the vectors are stored as a sparse matrix of 3x6 dimensions.\n",
"We can print the matrix as well as the feature names."
]
},
{
"cell_type": "code",
2023-04-23 14:41:53 +00:00
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[0 1 1 2 0 0 1 2 0 0]\n",
" [1 0 0 0 2 0 0 1 2 1]\n",
" [1 0 0 0 2 1 0 0 1 1]]\n",
"['and' 'but' 'coming' 'is' 'like' 'sandwiches' 'short' 'summer' 'the'\n",
" 'winter']\n"
]
}
],
2016-05-26 12:33:27 +00:00
"source": [
"print(vectors.toarray())\n",
2023-04-23 14:41:53 +00:00
"print(vectorizer.get_feature_names_out())"
2016-05-26 12:33:27 +00:00
]
},
{
"cell_type": "markdown",
2019-02-28 14:30:33 +00:00
"metadata": {},
2016-05-26 12:33:27 +00:00
"source": [
"As you can see, the pronoun 'I' has been removed because of the default token_pattern. \n",
"We can change this as follows."
]
},
{
"cell_type": "code",
2023-04-23 14:41:53 +00:00
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array(['and', 'but', 'coming', 'i', 'is', 'like', 'sandwiches', 'short',\n",
" 'summer', 'the', 'winter'], dtype=object)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
2016-05-26 12:33:27 +00:00
"source": [
"vectorizer = CountVectorizer(analyzer=\"word\", stop_words=None, token_pattern='(?u)\\\\b\\\\w+\\\\b') \n",
"vectors = vectorizer.fit_transform(documents)\n",
2023-04-23 14:41:53 +00:00
"vectorizer.get_feature_names_out()"
2016-05-26 12:33:27 +00:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now filter the stop words (it will remove 'and', 'but', 'I', 'is' and 'the')."
]
},
{
"cell_type": "code",
2023-04-23 14:41:53 +00:00
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/cif/anaconda3/lib/python3.10/site-packages/sklearn/utils/deprecation.py:87: FutureWarning: Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n",
" warnings.warn(msg, category=FutureWarning)\n"
]
},
{
"data": {
"text/plain": [
"['coming', 'like', 'sandwiches', 'short', 'summer', 'winter']"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
2016-05-26 12:33:27 +00:00
"source": [
"vectorizer = CountVectorizer(analyzer=\"word\", stop_words='english', token_pattern='(?u)\\\\b\\\\w+\\\\b') \n",
"vectors = vectorizer.fit_transform(documents)\n",
2023-04-23 14:41:53 +00:00
"vectorizer.get_feature_names_out()"
2016-05-26 12:33:27 +00:00
]
},
{
"cell_type": "code",
2023-04-23 14:41:53 +00:00
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"frozenset({'or', 'be', 'least', 'ours', 'very', 'noone', 'more', 'can', 'front', 'last', 'co', 'where', 'beyond', 'you', 'was', 'to', 'nine', 'here', 'describe', 'than', 'rather', 'therefore', 'except', 'at', 'again', 'ourselves', 'most', 'anyway', 'thick', 'whither', 'thereupon', 'someone', 'hereupon', 'besides', 'among', 'hasnt', 'across', 'namely', 'because', 'is', 'out', 'same', 'yourself', 'somehow', 'sincere', 'con', 'hereby', 'towards', 'interest', 'much', 'up', 'why', 'myself', 'all', 'nobody', 'though', 'every', 'show', 'not', 'there', 'whether', 'still', 'name', 'when', 'the', 'each', 'six', 'nor', 'and', 'under', 'thereby', 'less', 'either', 'thence', 'into', 'seemed', 'something', 'four', 'sometimes', 'himself', 'those', 'nowhere', 'almost', 'are', 'empty', 'must', 'while', 'afterwards', 'perhaps', 'from', 'detail', 'through', 'any', 'have', 'may', 'he', 'anywhere', 'alone', 'without', 'beforehand', 'had', 'too', 'yourselves', 'our', 'see', 'how', 'please', 'what', 'am', 'do', 'it', 'serious', 'yet', 'down', 'top', 'amount', 'then', 'both', 'fire', 'been', 'wherein', 'done', 'etc', 'whose', 'whereafter', 'who', 'ltd', 'meanwhile', 'further', 'few', 'first', 'behind', 'made', 'yours', 'until', 'toward', 'amoungst', 'anyhow', 'we', 'with', 'give', 'go', 'no', 'back', 'else', 'becomes', 'your', 'fill', 'together', 'another', 'throughout', 'onto', 'de', 'me', 'ten', 'system', 'became', 'per', 'therein', 'everyone', 'often', 'ie', 'put', 'hers', 'herself', 'nevertheless', 'itself', 'eg', 'herein', 'his', 'this', 'cry', 'due', 'bill', 'one', 'on', 'being', 'themselves', 'of', 'some', 'their', 'neither', 'elsewhere', 'since', 'whole', 'eight', 'i', 'a', 'whoever', 'own', 'call', 'them', 'mostly', 'she', 'my', 'cannot', 'us', 'never', 'as', 'thin', 'upon', 'cant', 'un', 'before', 'her', 'otherwise', 'full', 'these', 'next', 'they', 'side', 'somewhere', 'fifty', 'hence', 'so', 'along', 'already', 'three', 'latter', 'anything', 'whom', 'could', 'indeed', 'nothing', 'whereby', 'which', 'sometime', 'become', 'ever', 'amongst', 'by', 'in', 'five', 'after', 'mine', 'fifteen', 'wherever', 'found', 'thereafter', 'third', 'keep', 'anyone', 'will', 'bottom', 'off', 'seem', 'none', 'an', 'whatever', 'over', 'during', 'also', 'latterly', 'via', 'take', 'former', 'above', 'now', 'becoming', 'hereafter', 'such', 'two', 'only', 'about', 'sixty', 're', 'everything', 'others', 'hundred', 'twelve', 'thus', 'even', 'well', 'always', 'once', 'beside', 'get', 'mill', 'seems', 'if', 'whereupon', 'find', 'forty', 'inc', 'whenever', 'around', 'other', 'should', 'many', 'enough', 'however', 'move', 'against', 'several', 'everywhere', 'has', 'whereas', 'that', 'whence', 'eleven', 'its', 'within', 'twenty', 'part', 'although', 'thru', 'couldnt', 'moreover', 'him', 'formerly', 'might', 'seeming', 'but', 'below', 'would', 'between', 'were', 'for'})\n"
]
}
],
2016-05-26 12:33:27 +00:00
"source": [
"#stop words in scikit-learn for English\n",
"print(vectorizer.get_stop_words())"
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"# Vectors\n",
"f_array = vectors.toarray()\n",
"f_array"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can compute now the **distance** between vectors."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"from scipy.spatial.distance import cosine\n",
"d12 = cosine(f_array[0], f_array[1])\n",
"d13 = cosine(f_array[0], f_array[2])\n",
"d23 = cosine(f_array[1], f_array[2])\n",
"print(d12, d13, d23)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Binary vectors"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also get **binary vectors** as follows."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"vectorizer = CountVectorizer(analyzer=\"word\", stop_words='english', binary=True) \n",
"vectors = vectorizer.fit_transform(documents)\n",
"vectorizer.get_feature_names()"
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"vectors.toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Bigram vectors"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It is also easy to get bigram vectors."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"vectorizer = CountVectorizer(analyzer=\"word\", stop_words='english', ngram_range=[2,2]) \n",
"vectors = vectorizer.fit_transform(documents)\n",
"vectorizer.get_feature_names()"
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"vectors.toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tf-idf vector representation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we can also get a tf-idf vector representation using the class [TfidfVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html#sklearn.feature_extraction.text.TfidfVectorizer) instead of CountVectorizer."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"\n",
"vectorizer = TfidfVectorizer(analyzer=\"word\", stop_words='english')\n",
"vectors = vectorizer.fit_transform(documents)\n",
"vectorizer.get_feature_names()"
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"vectors.toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now compute the similarity of a query and a set of documents as follows."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"train = [doc1, doc2, doc3]\n",
"vectorizer = TfidfVectorizer(analyzer=\"word\", stop_words='english')\n",
"\n",
"# We learn the vocabulary (fit) and tranform the docs into vectors\n",
"vectors = vectorizer.fit_transform(train)\n",
"vectorizer.get_feature_names()"
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"vectors.toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scikit-learn provides a method to calculate the cosine similarity between one vector and a set of vectors. Based on this, we can rank the similarity. In this case, the ranking for the query is [d1, d2, d3]."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"from sklearn.metrics.pairwise import cosine_similarity\n",
"\n",
"query = ['winter short']\n",
"\n",
"# We transform the query into a vector of the learnt vocabulary\n",
"vector_query = vectorizer.transform(query)\n",
"\n",
"# Here we calculate the distance of the query to the docs\n",
"cosine_similarity(vector_query, vectors)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The same result can be obtained with pairwise metrics (kernels in ML terminology) if we use the linear kernel."
]
},
{
"cell_type": "code",
2017-04-20 10:56:39 +00:00
"execution_count": null,
2019-02-28 14:30:33 +00:00
"metadata": {},
2017-04-20 10:56:39 +00:00
"outputs": [],
2016-05-26 12:33:27 +00:00
"source": [
"from sklearn.metrics.pairwise import linear_kernel\n",
"cosine_similarity = linear_kernel(vector_query, vectors).flatten()\n",
"cosine_similarity"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## References\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* [Scikit-learn](http://scikit-learn.org/stable/datasets/twenty_newsgroups.html#converting-text-to-vectors) Scikit-learn Convert Text to Vectors"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Licence"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The notebook is freely licensed under under the [Creative Commons Attribution Share-Alike license](https://creativecommons.org/licenses/by/2.0/). \n",
"\n",
2019-03-06 16:46:12 +00:00
"© Carlos A. Iglesias, Universidad Politécnica de Madrid."
2016-05-26 12:33:27 +00:00
]
}
],
"metadata": {
"kernelspec": {
2023-04-23 14:41:53 +00:00
"display_name": "Python 3 (ipykernel)",
2016-05-26 12:33:27 +00:00
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
2023-04-23 14:41:53 +00:00
"version": "3.10.10"
2019-03-06 16:46:12 +00:00
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
2016-05-26 12:33:27 +00:00
}
},
"nbformat": 4,
2019-03-06 16:46:12 +00:00
"nbformat_minor": 1
2016-05-26 12:33:27 +00:00
}