{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Basic features and readability scores\n",
"> Learn to compute basic features such as number of words, number of characters, average word length and number of special characters (such as Twitter hashtags and mentions). You will also learn to compute readability scores and determine the amount of education required to comprehend a piece of text. This is the Summary of lecture \"Feature Engineering for NLP in Python\", via datacamp.\n",
"\n",
"- toc: true \n",
"- badges: true\n",
"- comments: true\n",
"- author: Chanseok Kang\n",
"- categories: [Python, Datacamp, Natural_Language_Processing]\n",
"- image: images/mention_count.png"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.rcParams['figure.figsize'] = (8, 8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introduction to NLP feature engineering"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### One-hot encoding\n",
"We encountered a dataframe `df1` which contained categorical features and therefore, was unsuitable for applying ML algorithms to.\n",
"\n",
"In this exercise, your task is to convert `df1` into a format that is suitable for machine learning."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"df1 = pd.read_csv('./dataset/FE_df1.csv')"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Index(['feature 1', 'feature 2', 'feature 3', 'feature 4', 'feature 5',\n",
" 'label'],\n",
" dtype='object')\n",
"Index(['feature 1', 'feature 2', 'feature 3', 'feature 4', 'label',\n",
" 'feature 5_female', 'feature 5_male'],\n",
" dtype='object')\n",
" feature 1 feature 2 feature 3 feature 4 label feature 5_female \\\n",
"0 29.0000 0 0 211.3375 1 1 \n",
"1 0.9167 1 2 151.5500 1 0 \n",
"2 2.0000 1 2 151.5500 0 1 \n",
"3 30.0000 1 2 151.5500 0 0 \n",
"4 25.0000 1 2 151.5500 0 1 \n",
"\n",
" feature 5_male \n",
"0 0 \n",
"1 1 \n",
"2 0 \n",
"3 1 \n",
"4 0 \n"
]
}
],
"source": [
"# Print the features of df1\n",
"print(df1.columns)\n",
"\n",
"# Perform one-hot encoding\n",
"df1 = pd.get_dummies(df1, columns=['feature 5'])\n",
"\n",
"# Print the new features of df1\n",
"print(df1.columns)\n",
"\n",
"# Print first five rows of df1\n",
"print(df1.head())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic feature extraction"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Character count of Russian tweets\n",
"In this exercise, you have been given a dataframe `tweets` which contains some tweets associated with Russia's Internet Research Agency and compiled by FiveThirtyEight.\n",
"\n",
"Your task is to create a new feature `'char_count'` in tweets which computes the number of characters for each tweet. Also, compute the average length of each tweet. The tweets are available in the `content` feature of `tweets`.\n",
"\n",
"*Be aware that this is real data from Twitter and as such there is always a risk that it may contain profanity or other offensive content (in this exercise, and any following exercises that also use real Twitter data).*"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Unnamed: 0
\n",
"
content
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
127447
\n",
"
LIVE STREAM VIDEO=> Donald Trump Rallies in Co...
\n",
"
\n",
"
\n",
"
1
\n",
"
123642
\n",
"
Muslim Attacks NYPD Cops with Meat Cleaver. Me...
\n",
"
\n",
"
\n",
"
2
\n",
"
226970
\n",
"
.@vfpatlas well that's a swella word there (di...
\n",
"
\n",
"
\n",
"
3
\n",
"
138339
\n",
"
RT wehking_pamela: Bobby_Axelrod2k MMFlint don...
\n",
"
\n",
"
\n",
"
4
\n",
"
161610
\n",
"
Жители обстреливаемых районов Донецка проводят...
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Unnamed: 0 content\n",
"0 127447 LIVE STREAM VIDEO=> Donald Trump Rallies in Co...\n",
"1 123642 Muslim Attacks NYPD Cops with Meat Cleaver. Me...\n",
"2 226970 .@vfpatlas well that's a swella word there (di...\n",
"3 138339 RT wehking_pamela: Bobby_Axelrod2k MMFlint don...\n",
"4 161610 Жители обстреливаемых районов Донецка проводят..."
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tweets = pd.read_csv('./dataset/russian_tweets.csv')\n",
"tweets.head()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"103.462\n"
]
}
],
"source": [
"# Create a feature char_count\n",
"tweets['char_count'] = tweets['content'].apply(len)\n",
"\n",
"# Print the average character count\n",
"print(tweets['char_count'].mean())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice that the average character count of these tweets is approximately 104, which is much higher than the overall average tweet length of around 40 characters. Depending on what you're working on, this may be something worth investigating into. For your information, there is research that indicates that fake news articles tend to have longer titles! Therefore, even extremely basic features such as character counts can prove to be very useful in certain applications."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Word count of TED talks\n",
"`ted` is a dataframe that contains the transcripts of 500 TED talks. Your job is to compute a new feature `word_count` which contains the approximate number of words for each talk. Consequently, you also need to compute the average word count of the talks. The transcripts are available as the `transcript` feature in `ted`.\n",
"\n",
"In order to complete this task, you will need to define a function `count_words` that takes in a string as an argument and returns the number of words in the string. You will then need to apply this function to the `transcript` feature of `ted` to create the new feature `word_count` and compute its mean."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
transcript
\n",
"
url
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
We're going to talk — my — a new lecture, just...
\n",
"
https://www.ted.com/talks/al_seckel_says_our_b...
\n",
"
\n",
"
\n",
"
1
\n",
"
This is a representation of your brain, and yo...
\n",
"
https://www.ted.com/talks/aaron_o_connell_maki...
\n",
"
\n",
"
\n",
"
2
\n",
"
It's a great honor today to share with you The...
\n",
"
https://www.ted.com/talks/carter_emmart_demos_...
\n",
"
\n",
"
\n",
"
3
\n",
"
My passions are music, technology and making t...
\n",
"
https://www.ted.com/talks/jared_ficklin_new_wa...
\n",
"
\n",
"
\n",
"
4
\n",
"
It used to be that if you wanted to get a comp...
\n",
"
https://www.ted.com/talks/jeremy_howard_the_wo...
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" transcript \\\n",
"0 We're going to talk — my — a new lecture, just... \n",
"1 This is a representation of your brain, and yo... \n",
"2 It's a great honor today to share with you The... \n",
"3 My passions are music, technology and making t... \n",
"4 It used to be that if you wanted to get a comp... \n",
"\n",
" url \n",
"0 https://www.ted.com/talks/al_seckel_says_our_b... \n",
"1 https://www.ted.com/talks/aaron_o_connell_maki... \n",
"2 https://www.ted.com/talks/carter_emmart_demos_... \n",
"3 https://www.ted.com/talks/jared_ficklin_new_wa... \n",
"4 https://www.ted.com/talks/jeremy_howard_the_wo... "
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ted = pd.read_csv('./dataset/ted.csv')\n",
"ted.head()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1987.1\n"
]
}
],
"source": [
"# Function that returns number of words in a string\n",
"def count_words(string):\n",
" # Split the string into words\n",
" words = string.split()\n",
" \n",
" # Return the number of words\n",
" return len(words)\n",
"\n",
"# Create a new feature word_count\n",
"ted['word_count'] = ted['transcript'].apply(count_words)\n",
"\n",
"# Print the average word count of the talks\n",
"print(ted['word_count'].mean())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You now know how to compute the number of words in a given piece of text. Also, notice that the average length of a talk is close to 2000 words. You can use the `word_count` feature to compute its correlation with other variables such as number of views, number of comments, etc. and derive extremely interesting insights about TED."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Hashtags and mentions in Russian tweets\n",
"Let's revisit the tweets dataframe containing the Russian tweets. In this exercise, you will compute the number of hashtags and mentions in each tweet by defining two functions `count_hashtags()` and `count_mentions()` respectively and applying them to the content feature of tweets."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAeYAAAHiCAYAAAA9Am/ZAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAZ9klEQVR4nO3df7DldX3f8ddb1x8IURR0w6/Jmkr8Sa26GtSpXcUmKio0I6kJUTRY2sYYjKRKnLYm6Y8hnahRY9LZigkZib/QEYwm1VFvOzaRRtQRETOgQVlZQeRHXIxR4qd/nO/W63p37xHu3fP23sdjhtlzvt/v+X4/34/Xfe73e86erTFGAIAe7rLoAQAA3yXMANCIMANAI8IMAI0IMwA0IswA0IgwwwFU1aiqBy16HJ1V1VJVvWh6fHpVfWAN931FVe2YHv9GVb1lDff9yqp601rtD9aKMPNDr6quqaqn7rPsBVX10XU+7rof42Crqj+qqv98R18/xrhwjPFTa3WcMcbDxxhLd3Q8y463o6p27bPv/zrGeNGd3TesNWEG2qmqLYseAyyKMLMpVNW5VfX5qvp6VX22qv7FsnUPqqr/VVW3VtWNVfX2fV7+1Kq6qqpurqo31sxDk/z3JI+vqj1Vdcu0r5Or6pNV9bdVdW1V/cY+43h+VX2xqr5WVf9hpav9ZdseUlWvnra/tao+WlWHTOuePd3mvWW6lfzQZa/7ntvvy69O9145VtU5VXVDVe2uqhdO685KcnqSl0/n9N79jOufV9XnpjH9XpJatu7/30WY5um103FurapPV9Uj9necaS5eUVWfTnJbVW1ZYX7uWVVvn/53/ERVPXK1866qQ5P8WZKjp+Ptqaqj9701vsqcXlNVvzadw63TGO650vzAnSXMbBafT/JPk9wnyW8meUtVHTWt+09JPpDkvkmOTfKGfV77zCSPTfLIJD+b5KfHGFcm+TdJ/nKMcdgY4/Bp29uSPD/J4UlOTvJvq+rUJKmqhyX5/cyidNQ0lmMOMObfSfKYJE9Icr8kL0/ynar6iSRvTfLSJPdP8v4k762qu885Fz+67NhnJnljVd13jLEzyYVJ/tt0Ts/a94VVdWSSdyX590mOzGxen7if4/xUkicl+YnM5uNfJvnaKsf5uczm7fAxxu0r7POUJO+c5uNPkrynqu52oJMdY9yW5OlJrpuOd9gY47p9zmueOf3ZJE9L8sAk/zjJCw50XLijhJmN4j3Tlc4t09Xr7y9fOcZ45xjjujHGd8YYb09yVZLHTau/neTHkhw9xvjmGGPf943PG2PcMsb4UpKPJPkn+xvEGGNpjHH5dJxPZ/ab/T+bVj8nyXvHGB8dY3wryX9MsuKX1VfVXZL8YpKzxxhfHmP8wxjjL8YYf59Z4N43xvjgGOPbmQX8kMwCPo9vJ/mtMca3xxjvT7InyYPnfO0zknx2jHHRdOzfTfKVAxznR5I8JEmNMa4cY+xeZf+vH2NcO8b4u/2sv2zZsV+T5J5JTpxz7Acyz5y+fvoZuinJe3OAnwO4M4SZjeLUMcbhe/9L8kvLV063kD+1LNyPyOyKL5ldiVaS/zvdyvzFffa9PDzfSHLY/gZRVT9ZVR+pqq9W1a2ZXVXvPc7RSa7du+0Y4xtJvrafXR2ZWXQ+v8K6o5N8cdl+vjPt90BX38t9bZ+r0QOe0wrHXn4OY/nz5cYYH07ye0nemOT6qtpZVfdeZf8r7mul9dN575rGdGfNM6dz/xzAnSHMbHhV9WNJ/keSX05yxBTuz2R6b3SM8ZUxxr8aYxyd5F8n+f2a769IrXS1+ydJLkly3BjjPpm9D733Pdjdmd0q3zuuQ5IcsZ9935jkm0n+0QrrrsvsCn/vfirJcUm+PC36RpJ7Ldv+R1c7kWVW++fmdk/H2vfYK+9sjNePMR6T5OGZ3dL+d6scZ7XjLz/2XTKbz723pQ903qvtd7U5hYNGmNkMDs3sN+avJsn0YadH7F1ZVadV1d5g3jxt+w9z7Pf6JMfu8z7kjyS5aYzxzap6XJKfX7buoiTPqqonTK/5zSz74NRy0xXbm5O8Zvqg0l2r6vFVdY8k70hyclWdNL2/ek6Sv0/yF9PLP5Xk56fXPC3fvZU+j+uT/PgB1r8vycOr6mdq9snpX8l+wl9Vj53uINwts/fev5nvzutqx9mfxyw79kszO++PTesOdN7XJzmiqu6zn/2uNqdw0AgzG94Y47NJXp3kLzP7DfqEJP9n2SaPTXJpVe3J7Gr37DHG38yx6w8nuSLJV6rqxmnZLyX5rar6embvIb9j2TiuSPKSJG/L7Mrz60luyCwAK/m1JJcn+askNyX57SR3GWP8dZJfyOxDajcmeVaSZ03vWyfJ2dOyWzL7oNl75jiXvc5P8rDplv/3vW6McWOS05Kcl9lt+OPzvXO53L0zu1Nxc2a3ib+W2Xu3qx7nAC7O7P3gm5M8L8nPTO8JJwc47zHG5zJ7v/8L0zG/5/b3HHMKB03N3iICDraqOiyziBw/5x8EgE3AFTMcRFX1rKq61/R3a38nsyviaxY7KqATYYaD65TMPmh0XWa3gZ873LYClnErGwAaccUMAI0IMwA00uJfcDnyyCPHtm3b1mx/t912Ww499NA1299GZZ7mY57mY57mY55Wtxnm6LLLLrtxjHH/lda1CPO2bdvy8Y9/fM32t7S0lB07dqzZ/jYq8zQf8zQf8zQf87S6zTBHVfXF/a1zKxsAGhFmAGhEmAGgEWEGgEaEGQAaEWYAaESYAaARYQaARoQZABoRZgBoRJgBoBFhBoBGhBkAGhFmAGhEmAGgEWEGgEaEGQAaEWYAaESYAaCRLYsewHq4/Mu35gXnvm/Rwziga847edFDAKAhV8wA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0MhcYa6qX62qK6rqM1X11qq6Z1U9sKouraqrqurtVXX3adt7TM+vntZvW88TAICNZNUwV9UxSX4lyfYxxiOS3DXJc5P8dpLXjjGOT3JzkjOnl5yZ5OYxxoOSvHbaDgCYw7y3srckOaSqtiS5V5LdSZ6S5KJp/QVJTp0enzI9z7T+pKqqtRkuAGxsNcZYfaOqs5P8lyR/l+QDSc5O8rHpqjhVdVySPxtjPKKqPpPkaWOMXdO6zyf5yTHGjfvs86wkZyXJ1q1bH/O2t71tzU7qhptuzfV/t2a7WxcnHHOfRQ8he/bsyWGHHbboYbRnnuZjnuZjnla3GeboyU9+8mVjjO0rrduy2our6r6ZXQU/MMktSd6Z5OkrbLq38CtdHX9f/ccYO5PsTJLt27ePHTt2rDaUub3hwovz6stXPbWFuub0HYseQpaWlrKW875Rmaf5mKf5mKfVbfY5mudW9lOT/M0Y46tjjG8neXeSJyQ5fLq1nSTHJrluerwryXFJMq2/T5Kb1nTUALBBzRPmLyU5saruNb1XfFKSzyb5SJLnTNuckeTi6fEl0/NM6z885rlfDgCsHuYxxqWZfYjrE0kun16zM8krkrysqq5OckSS86eXnJ/kiGn5y5Kcuw7jBoANaa43YscYr0ryqn0WfyHJ41bY9ptJTrvzQwOAzcc3fwFAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0MleYq+rwqrqoqj5XVVdW1eOr6n5V9cGqumr69b7TtlVVr6+qq6vq01X16PU9BQDYOOa9Yn5dkj8fYzwkySOTXJnk3CQfGmMcn+RD0/MkeXqS46f/zkryB2s6YgDYwFYNc1XdO8mTkpyfJGOMb40xbklySpILps0uSHLq9PiUJH88Zj6W5PCqOmrNRw4AG9A8V8w/nuSrSf6wqj5ZVW+qqkOTbB1j7E6S6dcHTNsfk+TaZa/fNS0DAFaxZc5tHp3kJWOMS6vqdfnubeuV1ArLxvdtVHVWZre6s3Xr1iwtLc0xlPlsPSQ554Tb12x/62Etz/eO2rNnT4txdGee5mOe5mOeVrfZ52ieMO9KsmuMcen0/KLMwnx9VR01xtg93aq+Ydn2xy17/bFJrtt3p2OMnUl2Jsn27dvHjh077tgZrOANF16cV18+z6ktzjWn71j0ELK0tJS1nPeNyjzNxzzNxzytbrPP0aq3sscYX0lybVU9eFp0UpLPJrkkyRnTsjOSXDw9viTJ86dPZ5+Y5Na9t7wBgAOb97LyJUkurKq7J/lCkhdmFvV3VNWZSb6U5LRp2/cneUaSq5N8Y9oWAJjDXGEeY3wqyfYVVp20wrYjyYvv5LgAYFPyzV8A0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjcwd5qq6a1V9sqr+dHr+wKq6tKquqqq3V9Xdp+X3mJ5fPa3ftj5DB4CN5we5Yj47yZXLnv92kteOMY5PcnOSM6flZya5eYzxoCSvnbYDAOYwV5ir6tgkJyd50/S8kjwlyUXTJhckOXV6fMr0PNP6k6btAYBVzHvF/LtJXp7kO9PzI5LcMsa4fXq+K8kx0+NjklybJNP6W6ftAYBVbFltg6p6ZpIbxhiXVdWOvYtX2HTMsW75fs9KclaSbN26NUtLS/OMdy5bD0nOOeH21TdcoLU83ztqz549LcbRnXmaj3maj3la3Wafo1XDnOSJSZ5dVc9Ics8k987sCvrwqtoyXRUfm+S6aftdSY5LsquqtiS5T5Kb9t3pGGNnkp1Jsn379rFjx447eSrf9YYLL86rL5/n1BbnmtN3LHoIWVpaylrO+0ZlnuZjnuZjnla32edo1VvZY4xfH2McO8bYluS5ST48xjg9yUeSPGfa7IwkF0+PL5meZ1r/4THG910xAwDf7878PeZXJHlZVV2d2XvI50/Lz09yxLT8ZUnOvXNDBIDN4we63zvGWEqyND3+QpLHrbDNN5OctgZjA4BNxzd/AUAjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADSyapir6riq+khVXVlVV1TV2dPy+1XVB6vqqunX+07Lq6peX1VXV9Wnq+rR630SALBRzHPFfHuSc8YYD01yYpIXV9XDkpyb5ENjjOOTfGh6niRPT3L89N9ZSf5gzUcNABvUqmEeY+weY3xievz1JFcmOSbJKUkumDa7IMmp0+NTkvzxmPlYksOr6qg1HzkAbEA/0HvMVbUtyaOSXJpk6xhjdzKLd5IHTJsdk+TaZS/bNS0DAFaxZd4Nq+qwJO9K8tIxxt9W1X43XWHZWGF/Z2V2qztbt27N0tLSvENZ1dZDknNOuH3N9rce1vJ876g9e/a0GEd35mk+5mk+5ml1m32O5gpzVd0tsyhfOMZ497T4+qo6aoyxe7pVfcO0fFeS45a9/Ngk1+27zzHGziQ7k2T79u1jx44dd+wMVvCGCy/Oqy+f+88cC3HN6TsWPYQsLS1lLed9ozJP8zFP8zFPq9vsczTPp7IryflJrhxjvGbZqkuSnDE9PiPJxcuWP3/6dPaJSW7de8sbADiweS4rn5jkeUkur6pPTctemeS8JO+oqjOTfCnJadO69yd5RpKrk3wjyQvXdMQAsIGtGuYxxkez8vvGSXLSCtuPJC++k+MCgE3JN38BQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADQizADQiDADQCPCDACNCDMANCLMANCIMANAI1sWPYDNatu571v0EHLOCbfnBQcYxzXnnXwQRwNA4ooZAFoRZgBoRJgBoBFhBoBGhBkAGhFmAGhEmAGgEWEGgEaEGQAaEWYAaESYAaARYQaARoQZABoRZgBoRJgBoBFhBoBGhBkAGhFmAGhEmAGgEWEGgEaEGQAaEWYAaESYAaCRLYseAH1tO/d9ix7CAV1z3smLHgLAmnPFDACNCDMANCLMANCIMANAI8IMAI0IMwA0IswA0Ii/xwzryN8FB35QwswPrYMVvXNOuD0vaB5YYONYl1vZVfW0qvrrqrq6qs5dj2MAwEa05mGuqrsmeWOSpyd5WJKfq6qHrfVxAGAjWo9b2Y9LcvUY4wtJUlVvS3JKks+uw7GAO2Et3w5Yr1v+3gffHJb/LHZ8++hg/hyuR5iPSXLtsue7kvzkOhwH2AR8gI7NpsYYa7vDqtOS/PQY40XT8+cledwY4yX7bHdWkrOmpw9O8tdrOIwjk9y4hvvbqMzTfMzTfMzTfMzT6jbDHP3YGOP+K61YjyvmXUmOW/b82CTX7bvRGGNnkp3rcPxU1cfHGNvXY98biXmaj3maj3maj3la3Wafo/X4VPZfJTm+qh5YVXdP8twkl6zDcQBgw1nzK+Yxxu1V9ctJ/meSuyZ58xjjirU+DgBsROvyBSNjjPcnef967HtO63KLfAMyT/MxT/MxT/MxT6vb1HO05h/+AgDuOP+IBQA0suHC7OtAV1dVx1XVR6rqyqq6oqrOXvSYuqqqu1bVJ6vqTxc9lq6q6vCquqiqPjf9TD1+0WPqqKp+dfr/22eq6q1Vdc9Fj6mDqnpzVd1QVZ9Ztux+VfXBqrpq+vW+ixzjwbahwuzrQOd2e5JzxhgPTXJikhebp/06O8mVix5Ec69L8udjjIckeWTM1/epqmOS/EqS7WOMR2T2wdjnLnZUbfxRkqfts+zcJB8aYxyf5EPT801jQ4U5y74OdIzxrSR7vw6UZcYYu8cYn5gefz2z30iPWeyo+qmqY5OcnORNix5LV1V17yRPSnJ+kowxvjXGuGWxo2prS5JDqmpLkntlhe932IzGGP87yU37LD4lyQXT4wuSnHpQB7VgGy3MK30dqOAcQFVtS/KoJJcudiQt/W6Slyf5zqIH0tiPJ/lqkj+cbvm/qaoOXfSguhljfDnJ7yT5UpLdSW4dY3xgsaNqbesYY3cyu5BI8oAFj+eg2mhhrhWW+dj5flTVYUneleSlY4y/XfR4OqmqZya5YYxx2aLH0tyWJI9O8gdjjEcluS2b7LbjPKb3SE9J8sAkRyc5tKp+YbGjoquNFua5vg6UpKrullmULxxjvHvR42noiUmeXVXXZPaWyFOq6i2LHVJLu5LsGmPsveNyUWah5ns9NcnfjDG+Osb4dpJ3J3nCgsfU2fVVdVSSTL/esODxHFQbLcy+DnQOVVWZvSd45RjjNYseT0djjF8fYxw7xtiW2c/Rh8cYrnD2Mcb4SpJrq+rB06KT4p94XcmXkpxYVfea/v93UnxI7kAuSXLG9PiMJBcvcCwH3bp889ei+DrQuT0xyfOSXF5Vn5qWvXL6xjb4Qb0kyYXTH4a/kOSFCx5PO2OMS6vqoiSfyOxvRXwym/zbrfaqqrcm2ZHkyKraleRVSc5L8o6qOjOzP9SctrgRHny++QsAGtlot7IB4IeaMANAI8IMAI0IMwA0IswA0IgwA0AjwgwAjQgzADTy/wAXMWRKd7vLzAAAAABJRU5ErkJggg==\n",
"text/plain": [
"