From 531b3eb395619e6f131876c849ce831848bd928f Mon Sep 17 00:00:00 2001 From: nruest Date: Mon, 4 Mar 2019 23:26:51 -0500 Subject: [PATCH 1/4] Fix case, and actually resolve #16. --- auk-notebook-example.ipynb | 4 ++-- auk-notebook.ipynb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/auk-notebook-example.ipynb b/auk-notebook-example.ipynb index 0772790..7be45eb 100644 --- a/auk-notebook-example.ipynb +++ b/auk-notebook-example.ipynb @@ -467,9 +467,9 @@ "for i in year_results[:5]:\n", " print(international(i)[:MAX_CHARACTERS]) # first 50 characters in output\n", "\n", - "## Removing the # on the following line will write the results to a file entitled `filtered.txt`\n", + "## Removing the # on the following line will write the results to OUTPUT_FILENAME (set in User Configuration).\n", "\n", - "#write_output('filtered.txt', year_results)" + "#write_output(OUTPUT_FILENAME, year_results)" ] }, { diff --git a/auk-notebook.ipynb b/auk-notebook.ipynb index 5be5c4f..a770295 100644 --- a/auk-notebook.ipynb +++ b/auk-notebook.ipynb @@ -440,9 +440,9 @@ "for i in year_results[:5]:\n", " print(international(i)[:MAX_CHARACTERS]) # first 50 characters in output\n", "\n", - "## Removing the # on the following line will write the results to a file entitled `filtered.txt`\n", + "## Removing the # on the following line will write the results to OUTPUT_FILENAME (set in User Configuration).\n", "\n", - "#write_output('filtered.txt', year_results)" + "#write_output(OUTPUT_FILENAME, year_results)" ] }, { @@ -770,7 +770,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.0" + "version": "3.7.1" } }, "nbformat": 4, From 504716e767c3ada45a377ee15b354b4d3469b563 Mon Sep 17 00:00:00 2001 From: nruest Date: Tue, 5 Mar 2019 00:07:42 -0500 Subject: [PATCH 2/4] copyediting --- auk-notebook-example.ipynb | 162 +++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/auk-notebook-example.ipynb b/auk-notebook-example.ipynb index 7be45eb..6bc1f13 100644 --- a/auk-notebook-example.ipynb +++ b/auk-notebook-example.ipynb @@ -40,8 +40,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Required imports from sys\n", - "\n", + "# Required packages.\n", "from collections import Counter\n", "import logging\n", "import matplotlib.pyplot as plt\n", @@ -57,6 +56,7 @@ "from nltk.sentiment.vader import SentimentIntensityAnalyzer\n", "from nltk.corpus import stopwords\n", "\n", + "# Setup Archives Unleashed Cloud data.\n", "coll_id = \"4656\"\n", "auk_fp = \"./data/\"\n", "auk_full_text = auk_fp + coll_id + \"-fulltext.txt\"\n", @@ -81,45 +81,46 @@ "metadata": {}, "outputs": [], "source": [ - "# maximum number of words to show in output.\n", + "# Maximum number of words to show in output.\n", "# Jupyter will create an output error if the number is too high.\n", "TOP_COUNT = 30 \n", "\n", - "# Domain suffixes to check non-U.S. domains.\n", - "# so that (e.g.) www.google.co.uk will become \"google\"\n", + "# Domain suffixes to check non-U.S. domains so that (e.g.) www.google.co.uk will become \"google\".\n", "STOP_DOMAINS = [\"co\", \"org\", \"net\", \"edu\"] # domain suffixes to remove\n", "\n", - "# minimum number of characters for a word to be included in a corpus\n", + "# Minimum number of characters for a word to be included in a corpus.\n", "MINIMUM_WORD_LENGTH = 3 # eliminates \"it\", \"I\", \"be\" etc.\n", "\n", - "# list of substrings to filter a text line, if desired\n", + "# List of substrings to filter a text line, if desired.\n", "LINE_FILTER = ['404 Not Found']\n", "\n", - "# The number of the last line of text to ingest\n", + "# How many lines of text to use.\n", "RESULTS_LIMIT = 2500\n", "\n", - "# If you want to start ingesting at a different line, you can increase this.\n", + "# If you want to start at a different line, you can increase this.\n", "# If RESULTS_START is great than RESULTS_LIMIT you will get no results.\n", "RESULTS_START = 0\n", "\n", - "# If you have a large file but want to sample the file more broadly, you\n", - "# can increase this value skip to every Nth line.\n", + "# If you have a large file but want to sample the file more broadly.\n", + "# You can increase this value skip to every Nth line.\n", "RESULTS_STEP = 5\n", "\n", - "# change if you want a different filename.\n", + "# Change if you want a different filename.\n", "OUTPUT_FILENAME = \"./filtered_text.txt\" # filename if you want to output to another file.\n", "\n", - "# characters to show per text file in output. Larger numbers will results in more\n", - "# text showing in output\n", + "# Characters to show per text file in output.\n", + "# Larger numbers will results in more text showing in output.\n", "MAX_CHARACTERS = 75\n", "\n", - "# The years to include in the analysis. If empty, you will get all available years.\n", + "# The years to include in the analysis.\n", + "# If empty, you will get all available years.\n", "FILTERED_YEARS = [] # e.g. ['2015', '2016', '2019']\n", "\n", - "# The domains to include in the analysis. If empty, you will get all available domains.\n", + "# The domains to include in the analysis.\n", + "# If empty, you will get all available domains.\n", "FILTERED_DOMAINS = [] # e.g [\"google\", \"apple\", \"facebook\"]\n", "\n", - "# List of words not to include in a corpus for text analysis\n", + "# List of words not to include in a corpus for text analysis.\n", "STOP_WORDS = set(stopwords.words('english'))" ] }, @@ -141,11 +142,10 @@ "def clean_domain(s):\n", " \"\"\"Extracts the name from the domain (e.g. 'www.google.com' becomes 'google').\n", " \n", - " :param: s: the domain name to clean\n", - " :return: the relevant name\n", - " -------\n", - " \n", + " :param: s: the domain name to clean.\n", + " :return: the relevant name.\n", " \"\"\"\n", + " \n", " ret = \"\"\n", " dom = s.split(\".\")\n", " if len(dom) <3: # x.com is always x\n", @@ -157,12 +157,13 @@ " return ret\n", "\n", "def get_domains(split_method=\"full\"):\n", - " \"\"\"Extracts the domains from a file by method..\n", + " \"\"\"Extracts the domains from a file by method.\n", " \n", " :param split_method: Either \"full\" \"name\" or \"sub\". \"name\" provides just the domain name, \n", " \"sub\" produces the name with subdomains. \"full\" provides the entire name. \n", - " :return: a list of tuples containing (urlname, count)\n", + " :return: a list of tuples containing (urlname, count).\n", " \"\"\"\n", + " \n", " ret = []\n", " with open(auk_domains) as fin:\n", " for line in fin:\n", @@ -189,12 +190,13 @@ " return ret\n", "\n", "def get_text(by=\"all\", minline=MINIMUM_WORD_LENGTH):\n", - " \"\"\"Get the text from the files (by domain or year if desired)\n", + " \"\"\"Get the text from the files (by domain or year if desired).\n", " \n", - " :param by: \"all\", \"domain\" or \"year\" the output to return\n", - " :param minline: the minimum size of a line to be included in the output\n", + " :param by: \"all\", \"domain\" or \"year\" the output to return.\n", + " :param minline: the minimum size of a line to be included in the output.\n", " :return: [(year/domain, text)] or [text] depending on by\n", " \"\"\"\n", + " \n", " text = []\n", " form = range(RESULTS_START, RESULTS_LIMIT, RESULTS_STEP)\n", " with open(auk_full_text) as fin:\n", @@ -219,20 +221,25 @@ " :param minlen: the minimum word size to be included in the list of words.\n", " :return: a list of words included in the text file.\n", " \"\"\"\n", + " \n", " return [x.lower() for x in word_tokenize(' '.join(get_text())) if len(x) > minlen]\n", "\n", "def get_tokens_domains(minlen=MINIMUM_WORD_LENGTH):\n", - " \"\"\"Get tokens by domain\n", + " \"\"\"Get tokens by domain.\n", " \n", " :param minlen: the minimum word size to be included in the list of words.\n", - " :return: a list of tuples with (domain, Counter)\"\"\"\n", + " :return: a list of tuples with (domain, Counter).\n", + " \"\"\"\n", + " \n", " return [(x[0], Counter([y for y in word_tokenize(x[1]) if len(y) > minlen])) for x in get_text(\"domain\")]\n", "\n", "def get_tokens_years(minlen=MINIMUM_WORD_LENGTH):\n", " \"\"\"Get tokens by year.\n", " \n", " :para minlen: the minimum word size to be included in the list of words.\n", - " :return: a list of tuples with (year, Counter)\"\"\"\n", + " :return: a list of tuples with (year, Counter).\n", + " \"\"\"\n", + " \n", " return [(x[0], Counter([y for y in word_tokenize(x[1]) if len(y) > minlen])) for x in get_text(\"year\")]\n", " \n", "\n", @@ -251,15 +258,10 @@ "def get_top_tokens_by(fun, total=TOP_COUNT, minlen=MINIMUM_WORD_LENGTH):\n", " \"\"\" Get the top tokens by a function.\n", " \n", - " Parameters\n", - " ----------\n", - " fun: A function that returns a list of (key, Counter([tokenized_list]))\n", - " total: The number of top tokens to return for each key.\n", - " minlen: The minimum word length.\n", - " \n", - " Returns\n", - " -------\n", - " ret: list of minlen tokens by fun.\n", + " :para fun: A function that returns a list of (key, Counter([tokenized_list])).\n", + " :para total: The number of top tokens to return for each key.\n", + " :para minlen: The minimum word length.\n", + " :return: list of minlen tokens by fun.\n", " \"\"\"\n", " \n", " sep = dict()\n", @@ -274,7 +276,7 @@ "def international(text):\n", " \"\"\"Applies UTF-16 if possible.\n", " \n", - " :param text: The text to decode (assumes Utf-8)\n", + " :param text: The text to decode (assumes UTF-8).\n", " :return: UTF-32 or UTF-16 decoded string or else original string.\n", " \"\"\"\n", " unicode = text.encode(\"utf-8\")\n", @@ -295,13 +297,14 @@ " \n", " return ret\n", "\n", - "# writes results to stdout\n", "def write_output (stdout, results):\n", " \"\"\" Writes results to file.\n", " \n", " :param stdout: Filepath for file\n", " :param results: A list of results.\n", - " :return: Nothing\"\"\"\n", + " :return: Nothing\n", + " \"\"\"\n", + " \n", " try:\n", " with open(filename, \"w\") as output:\n", " for value in results:\n", @@ -310,9 +313,12 @@ " print(\"Error writing the file.\")\n", " \n", "def sentiment_scores(by=\"domain\"):\n", - " \"\"\" Calculates sentiment scores for a body of text\n", - " :param by: either \"year\" or \"domain\"\n", - " :return: a list of tuples with (year/domain, (\"neg\", score), (\"neu\", score) etc.)\"\"\"\n", + " \"\"\" Calculates sentiment scores for a body of text.\n", + " \n", + " :param by: either \"year\" or \"domain\".\n", + " :return: a list of tuples with (year/domain, (\"neg\", score), (\"neu\", score) etc.).\n", + " \"\"\"\n", + " \n", " sep = dict()\n", " corpus = get_text(by)\n", " sep = {k[0]: [] for k in corpus}\n", @@ -358,18 +364,17 @@ ], "source": [ "EXCLUDE = ['google', 'facebook', 'youtube', 'apple']\n", - "plt.rcParams['figure.figsize'] = [10, 4] # set the figure size for the graph\n", + "plt.rcParams['figure.figsize'] = [10, 4] # Set the figure size for the graph.\n", "\n", - "# Get a list of the top words in the collection\n", - "# (regardless of year).\n", + "# Get a list of the top words in the collection (regardless of year).\n", "\n", - "domains = get_domains('name').most_common(30) # Can choose 'sub' for subdomains\n", + "domains = get_domains('name').most_common(30) # Can choose 'sub' for subdomains.\n", "\n", "vals = [x[1] for x in domains if x[0] not in EXCLUDE]\n", "labs = [x[0] for x in domains if x[0] not in EXCLUDE]\n", "\n", - "ind = np.arange(len(vals)) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(len(vals)) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, vals, width)\n", "\n", @@ -421,9 +426,9 @@ } ], "source": [ - "method = \"year\" # choose \"year\", \"domain\" or \"all\"\n", + "method = \"year\" # Choose \"year\", \"domain\" or \"all\".\n", "\n", - "# Get the set of available years in the collection \n", + "# Get the set of available years in the collection.\n", "year_range = set([x[0] for x in get_text(method)])\n", "print(year_range)" ] @@ -460,12 +465,12 @@ } ], "source": [ - "year_filter = FILTERED_YEARS if FILTERED_YEARS else year_range # add or remove years for filter\n", + "year_filter = FILTERED_YEARS if FILTERED_YEARS else year_range # Add or remove years for filter.\n", "year_results = [t[1] for t in get_text(\"year\") if t[0] in list(year_filter)]\n", " \n", "# Some of the text may be in an international font.\n", "for i in year_results[:5]:\n", - " print(international(i)[:MAX_CHARACTERS]) # first 50 characters in output\n", + " print(international(i)[:MAX_CHARACTERS]) # First 50 characters in output.\n", "\n", "## Removing the # on the following line will write the results to OUTPUT_FILENAME (set in User Configuration).\n", "\n", @@ -493,7 +498,7 @@ } ], "source": [ - "# Get the set of available domains in the collection \n", + "# Get the set of available domains in the collection.\n", "domain_set = set([x[0] for x in get_text(\"domain\")])\n", "print(domain_set)" ] @@ -545,7 +550,7 @@ } ], "source": [ - "# extract only the given domain to a file and see how many results there are\n", + "# Extract only the given domain to a file and see how many results there are.\n", "\n", "domain_set = FILTERED_DOMAINS if FILTERED_DOMAINS else domain_set\n", "domain_results = [t[1] for t in get_text(\"domain\") if t[0] in domain_set]\n", @@ -582,15 +587,14 @@ } ], "source": [ - "# Get a list of the top words in the collection\n", - "# (regardless of year).\n", + "# Get a list of the top words in the collection (regardless of year).\n", "tokens = get_top_tokens()[:20]\n", "\n", "vals = [x[1] for x in tokens if x[0] not in STOP_WORDS]\n", "labs = [x[0] for x in tokens if x[0] not in STOP_WORDS]\n", "\n", - "ind = np.arange(len(vals)) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(len(vals)) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, vals, width)\n", "\n", @@ -666,10 +670,10 @@ } ], "source": [ - "# Create a dispersion plot, showing where the list of words appear\n", - "# in the text.\n", + "# Create a dispersion plot, showing where the list of words appear in the text.\n", + "\n", "text = get_text_tokens(1) # Need to have one to include words with fewer than 3 letters.\n", - "dp(text, [\"he\", \"she\"]) # uses the nltk dispersion plot library (dp)." + "dp(text, [\"he\", \"she\"]) # Uses the nltk dispersion plot library (dp)." ] }, { @@ -700,8 +704,8 @@ "neu = [x[3][1] for x in sent]\n", "labs = [x[0] for x in sent]\n", "\n", - "ind = np.arange(N) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(N) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, neg, width)\n", "p2 = plt.bar(ind, neu, width,\n", @@ -744,8 +748,8 @@ "neu = [x[3][1] for x in sent]\n", "labs = sorted([x[0] for x in sent])\n", "\n", - "ind = np.arange(N) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(N) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, neg, width)\n", "p2 = plt.bar(ind, neu, width,\n", @@ -790,14 +794,12 @@ } ], "source": [ - "import networkx as nx\n", - "\n", - "plt.rcParams['figure.figsize'] = [10, 4] # set the figure size for the graph\n", + "plt.rcParams['figure.figsize'] = [10, 4] # Set the figure size for the graph.\n", "\n", "NETWORK_EXCLUDE = [\"google.com\"]\n", - "graph = nx.read_gexf(auk_gephi) #import the graph\n", + "graph = nx.read_gexf(auk_gephi) # Import the graph.\n", "\n", - "# Degree distribution for the graph\n", + "# Degree distribution for the graph.\n", "\n", "g_nodes = zip([x[1] for x in graph.nodes('label')], [x[1] for x in graph.nodes('Degree')])\n", "\n", @@ -841,17 +843,17 @@ "rgbs = zip([x[1]/255 for x in graph.nodes('r')], [x[1]/255 for x in graph.nodes('g')], [x[1]/255 for x in graph.nodes('b')])\n", "colormap = [np.array(x) for x in rgbs]\n", "\n", - "# Labels\n", + "# Labels.\n", "mapping = {x[0]: x[1] for x in graph.nodes('label')}\n", "\n", - "# Use Archive Unleashed Clouds Positions (saves on load time)\n", + "# Use Archive Unleashed Clouds Positions (saves on load time).\n", "zippos = zip(graph.nodes, [x[1] for x in graph.nodes('x')], [x[1] for x in graph.nodes('y')])\n", "positions = {x[0]: np.array([x[1],x[2]]) for x in zippos}\n", "\n", - "# Node sizes based on degree\n", + "# Node sizes based on degree.\n", "size = np.array([x[1] * 100 for x in graph.nodes('size')])\n", "\n", - "# Draw the graph\n", + "# Draw the graph.\n", "nx.draw(graph, pos=positions, show_labels=True, labels=mapping, font_size=10, node_size=size, node_color=colormap)\n", "plt.show()\n" ] @@ -873,7 +875,7 @@ } ], "source": [ - "# Ego network for a particular node\n", + "# Ego network for a particular node.\n", "\n", "largest_node = sorted(graph.nodes('Degree'), key=lambda s: s[1], reverse=True)[0][0] # [1][0] is second largest, etc\n", "neigh = graph.subgraph(graph.neighbors(largest_node))\n", @@ -881,14 +883,14 @@ "rgbs = zip([x[1]/255 for x in neigh.nodes('r')], [x[1]/255 for x in neigh.nodes('g')], [x[1]/255 for x in neigh.nodes('b')])\n", "colormap = [np.array(x) for x in rgbs]\n", "\n", - "# Labels\n", + "# Labels.\n", "mapping = {x[0]: x[1] for x in neigh.nodes('label')}\n", "\n", - "# Use Archive Unleashed Clouds Positions (saves on load time)\n", + "# Use Archive Unleashed Clouds Positions (saves on load time).\n", "zippos = zip(neigh.nodes, [x[1] for x in neigh.nodes('x')], [x[1] for x in neigh.nodes('y')])\n", "positions = {x[0]: np.array([x[1],x[2]]) for x in zippos}\n", "\n", - "# Node sizes based on degree\n", + "# Node sizes based on degree.\n", "size = np.array([x[1] * 100 for x in neigh.nodes('size')])\n", "\n", "nx.draw(neigh, pos=positions, show_labels=True, labels=mapping, font_size=10, node_size=size, node_color=colormap)\n", From d843db562ad901d972ceade3d6fe10740b60f94c Mon Sep 17 00:00:00 2001 From: nruest Date: Tue, 5 Mar 2019 13:15:39 -0500 Subject: [PATCH 3/4] review. --- auk-notebook-example.ipynb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/auk-notebook-example.ipynb b/auk-notebook-example.ipynb index 6bc1f13..28de297 100644 --- a/auk-notebook-example.ipynb +++ b/auk-notebook-example.ipynb @@ -109,7 +109,7 @@ "OUTPUT_FILENAME = \"./filtered_text.txt\" # filename if you want to output to another file.\n", "\n", "# Characters to show per text file in output.\n", - "# Larger numbers will results in more text showing in output.\n", + "# Larger numbers will result in more text showing in output.\n", "MAX_CHARACTERS = 75\n", "\n", "# The years to include in the analysis.\n", @@ -194,7 +194,7 @@ " \n", " :param by: \"all\", \"domain\" or \"year\" the output to return.\n", " :param minline: the minimum size of a line to be included in the output.\n", - " :return: [(year/domain, text)] or [text] depending on by\n", + " :return: [({year or domain}, textString)] if by is 'domain' or 'year', otherwise [textString].\n", " \"\"\"\n", " \n", " text = []\n", @@ -300,9 +300,9 @@ "def write_output (stdout, results):\n", " \"\"\" Writes results to file.\n", " \n", - " :param stdout: Filepath for file\n", + " :param stdout: Filepath for file.\n", " :param results: A list of results.\n", - " :return: Nothing\n", + " :return: None.\n", " \"\"\"\n", " \n", " try:\n", @@ -846,7 +846,7 @@ "# Labels.\n", "mapping = {x[0]: x[1] for x in graph.nodes('label')}\n", "\n", - "# Use Archive Unleashed Clouds Positions (saves on load time).\n", + "# Use Archive Unleashed Clouds positions (saves on load time).\n", "zippos = zip(graph.nodes, [x[1] for x in graph.nodes('x')], [x[1] for x in graph.nodes('y')])\n", "positions = {x[0]: np.array([x[1],x[2]]) for x in zippos}\n", "\n", @@ -886,7 +886,7 @@ "# Labels.\n", "mapping = {x[0]: x[1] for x in neigh.nodes('label')}\n", "\n", - "# Use Archive Unleashed Clouds Positions (saves on load time).\n", + "# Use Archive Unleashed Clouds positions (saves on load time).\n", "zippos = zip(neigh.nodes, [x[1] for x in neigh.nodes('x')], [x[1] for x in neigh.nodes('y')])\n", "positions = {x[0]: np.array([x[1],x[2]]) for x in zippos}\n", "\n", From f65c6c43882b71209ccf3e6e125b88cb0c117caa Mon Sep 17 00:00:00 2001 From: nruest Date: Tue, 5 Mar 2019 14:09:37 -0500 Subject: [PATCH 4/4] second notebook --- auk-notebook-example.ipynb | 6 +- auk-notebook.ipynb | 208 +++++++++++++++++-------------------- 2 files changed, 98 insertions(+), 116 deletions(-) diff --git a/auk-notebook-example.ipynb b/auk-notebook-example.ipynb index 28de297..eb518d5 100644 --- a/auk-notebook-example.ipynb +++ b/auk-notebook-example.ipynb @@ -717,7 +717,7 @@ "plt.xticks(ind, labs, rotation='vertical')\n", "plt.legend((p1[0], p2[0], p3[0]), ('Negative', 'Neutral', 'Positive'))\n", "\n", - "plt.show()\n" + "plt.show()" ] }, { @@ -817,7 +817,7 @@ "plt.title('Top domains by Degree.')\n", "plt.xticks(ind, labs, rotation='vertical')\n", "\n", - "plt.show()\n" + "plt.show()" ] }, { @@ -855,7 +855,7 @@ "\n", "# Draw the graph.\n", "nx.draw(graph, pos=positions, show_labels=True, labels=mapping, font_size=10, node_size=size, node_color=colormap)\n", - "plt.show()\n" + "plt.show()" ] }, { diff --git a/auk-notebook.ipynb b/auk-notebook.ipynb index a770295..b00bfb5 100644 --- a/auk-notebook.ipynb +++ b/auk-notebook.ipynb @@ -36,12 +36,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# Required imports from sys\n", - "\n", + "# Required packages.\n", "from collections import Counter\n", "import logging\n", "import matplotlib.pyplot as plt\n", @@ -59,8 +58,11 @@ "\n", "# Add the collection id of your Archive-It collection:\n", "coll_id = \"\"\n", + "\n", "# Change the path to your derivatives files if they are not in the data directory.\n", "auk_fp = \"./data/\"\n", + "\n", + "# Setup Archives Unleashed Cloud data.\n", "auk_full_text = auk_fp + coll_id + \"-fulltext.txt\"\n", "auk_gephi = auk_fp + coll_id + \"-gephi.gexf\"\n", "auk_graphml = auk_fp + coll_id + \"-gephi.graphml\"\n", @@ -83,45 +85,46 @@ "metadata": {}, "outputs": [], "source": [ - "# maximum number of words to show in output.\n", + "# Maximum number of words to show in output.\n", "# Jupyter will create an output error if the number is too high.\n", "TOP_COUNT = 30 \n", "\n", - "# Domain suffixes to check non-U.S. domains.\n", - "# so that (e.g.) www.google.co.uk will become \"google\"\n", + "# Domain suffixes to check non-U.S. domains so that (e.g.) www.google.co.uk will become \"google\".\n", "STOP_DOMAINS = [\"co\", \"org\", \"net\", \"edu\"] # domain suffixes to remove\n", "\n", - "# minimum number of characters for a word to be included in a corpus\n", + "# Minimum number of characters for a word to be included in a corpus.\n", "MINIMUM_WORD_LENGTH = 3 # eliminates \"it\", \"I\", \"be\" etc.\n", "\n", - "# list of substrings to filter a text line, if desired\n", + "# List of substrings to filter a text line, if desired.\n", "LINE_FILTER = ['404 Not Found']\n", "\n", - "# The number of the last line of text to ingest\n", + "# How many lines of text to use.\n", "RESULTS_LIMIT = 2500\n", "\n", - "# If you want to start ingesting at a different line, you can increase this.\n", + "# If you want to start at a different line, you can increase this.\n", "# If RESULTS_START is great than RESULTS_LIMIT you will get no results.\n", "RESULTS_START = 0\n", "\n", - "# If you have a large file but want to sample the file more broadly, you\n", - "# can increase this value skip to every Nth line.\n", + "# If you have a large file but want to sample the file more broadly.\n", + "# You can increase this value skip to every Nth line.\n", "RESULTS_STEP = 5\n", "\n", - "# change if you want a different filename.\n", + "# Change if you want a different filename.\n", "OUTPUT_FILENAME = \"./filtered_text.txt\" # filename if you want to output to another file.\n", "\n", - "# characters to show per text file in output. Larger numbers will results in more\n", - "# text showing in output\n", + "# Characters to show per text file in output.\n", + "# Larger numbers will result in more text showing in output.\n", "MAX_CHARACTERS = 75\n", "\n", - "# The years to include in the analysis. If empty, you will get all available years.\n", + "# The years to include in the analysis.\n", + "# If empty, you will get all available years.\n", "FILTERED_YEARS = [] # e.g. ['2015', '2016', '2019']\n", "\n", - "# The domains to include in the analysis. If empty, you will get all available domains.\n", + "# The domains to include in the analysis.\n", + "# If empty, you will get all available domains.\n", "FILTERED_DOMAINS = [] # e.g [\"google\", \"apple\", \"facebook\"]\n", "\n", - "# List of words not to include in a corpus for text analysis\n", + "# List of words not to include in a corpus for text analysis.\n", "STOP_WORDS = set(stopwords.words('english'))" ] }, @@ -143,11 +146,10 @@ "def clean_domain(s):\n", " \"\"\"Extracts the name from the domain (e.g. 'www.google.com' becomes 'google').\n", " \n", - " :param: s: the domain name to clean\n", - " :return: the relevant name\n", - " -------\n", - " \n", + " :param: s: the domain name to clean.\n", + " :return: the relevant name.\n", " \"\"\"\n", + " \n", " ret = \"\"\n", " dom = s.split(\".\")\n", " if len(dom) <3: # x.com is always x\n", @@ -159,12 +161,13 @@ " return ret\n", "\n", "def get_domains(split_method=\"full\"):\n", - " \"\"\"Extracts the domains from a file by method..\n", + " \"\"\"Extracts the domains from a file by method.\n", " \n", " :param split_method: Either \"full\" \"name\" or \"sub\". \"name\" provides just the domain name, \n", " \"sub\" produces the name with subdomains. \"full\" provides the entire name. \n", - " :return: a list of tuples containing (urlname, count)\n", + " :return: a list of tuples containing (urlname, count).\n", " \"\"\"\n", + " \n", " ret = []\n", " with open(auk_domains) as fin:\n", " for line in fin:\n", @@ -191,12 +194,13 @@ " return ret\n", "\n", "def get_text(by=\"all\", minline=MINIMUM_WORD_LENGTH):\n", - " \"\"\"Get the text from the files (by domain or year if desired)\n", + " \"\"\"Get the text from the files (by domain or year if desired).\n", " \n", - " :param by: \"all\", \"domain\" or \"year\" the output to return\n", - " :param minline: the minimum size of a line to be included in the output\n", - " :return: [(year/domain, text)] or [text] depending on by\n", + " :param by: \"all\", \"domain\" or \"year\" the output to return.\n", + " :param minline: the minimum size of a line to be included in the output.\n", + " :return: [({year or domain}, textString)] if by is 'domain' or 'year', otherwise [textString].\n", " \"\"\"\n", + " \n", " text = []\n", " form = range(RESULTS_START, RESULTS_LIMIT, RESULTS_STEP)\n", " with open(auk_full_text) as fin:\n", @@ -221,20 +225,25 @@ " :param minlen: the minimum word size to be included in the list of words.\n", " :return: a list of words included in the text file.\n", " \"\"\"\n", + " \n", " return [x.lower() for x in word_tokenize(' '.join(get_text())) if len(x) > minlen]\n", "\n", "def get_tokens_domains(minlen=MINIMUM_WORD_LENGTH):\n", - " \"\"\"Get tokens by domain\n", + " \"\"\"Get tokens by domain.\n", " \n", " :param minlen: the minimum word size to be included in the list of words.\n", - " :return: a list of tuples with (domain, Counter)\"\"\"\n", + " :return: a list of tuples with (domain, Counter).\n", + " \"\"\"\n", + " \n", " return [(x[0], Counter([y for y in word_tokenize(x[1]) if len(y) > minlen])) for x in get_text(\"domain\")]\n", "\n", "def get_tokens_years(minlen=MINIMUM_WORD_LENGTH):\n", " \"\"\"Get tokens by year.\n", " \n", " :para minlen: the minimum word size to be included in the list of words.\n", - " :return: a list of tuples with (year, Counter)\"\"\"\n", + " :return: a list of tuples with (year, Counter).\n", + " \"\"\"\n", + " \n", " return [(x[0], Counter([y for y in word_tokenize(x[1]) if len(y) > minlen])) for x in get_text(\"year\")]\n", " \n", "\n", @@ -253,15 +262,10 @@ "def get_top_tokens_by(fun, total=TOP_COUNT, minlen=MINIMUM_WORD_LENGTH):\n", " \"\"\" Get the top tokens by a function.\n", " \n", - " Parameters\n", - " ----------\n", - " fun: A function that returns a list of (key, Counter([tokenized_list]))\n", - " total: The number of top tokens to return for each key.\n", - " minlen: The minimum word length.\n", - " \n", - " Returns\n", - " -------\n", - " ret: list of minlen tokens by fun.\n", + " :para fun: A function that returns a list of (key, Counter([tokenized_list])).\n", + " :para total: The number of top tokens to return for each key.\n", + " :para minlen: The minimum word length.\n", + " :return: list of minlen tokens by fun.\n", " \"\"\"\n", " \n", " sep = dict()\n", @@ -276,7 +280,7 @@ "def international(text):\n", " \"\"\"Applies UTF-16 if possible.\n", " \n", - " :param text: The text to decode (assumes Utf-8)\n", + " :param text: The text to decode (assumes UTF-8).\n", " :return: UTF-32 or UTF-16 decoded string or else original string.\n", " \"\"\"\n", " unicode = text.encode(\"utf-8\")\n", @@ -297,13 +301,14 @@ " \n", " return ret\n", "\n", - "# writes results to stdout\n", "def write_output (stdout, results):\n", " \"\"\" Writes results to file.\n", " \n", - " :param stdout: Filepath for file\n", + " :param stdout: Filepath for file.\n", " :param results: A list of results.\n", - " :return: Nothing\"\"\"\n", + " :return: None.\n", + " \"\"\"\n", + " \n", " try:\n", " with open(filename, \"w\") as output:\n", " for value in results:\n", @@ -312,9 +317,12 @@ " print(\"Error writing the file.\")\n", " \n", "def sentiment_scores(by=\"domain\"):\n", - " \"\"\" Calculates sentiment scores for a body of text\n", - " :param by: either \"year\" or \"domain\"\n", - " :return: a list of tuples with (year/domain, (\"neg\", score), (\"neu\", score) etc.)\"\"\"\n", + " \"\"\" Calculates sentiment scores for a body of text.\n", + " \n", + " :param by: either \"year\" or \"domain\".\n", + " :return: a list of tuples with (year/domain, (\"neg\", score), (\"neu\", score) etc.).\n", + " \"\"\"\n", + " \n", " sep = dict()\n", " corpus = get_text(by)\n", " sep = {k[0]: [] for k in corpus}\n", @@ -328,7 +336,7 @@ " scores.update(sid.polarity_scores(c))\n", " result += [(a, (\"neg\", scores['neg']/len(b)), (\"pos\", scores['neg']/len(b)), (\"neu\", scores['neu']/len(b)), (\"compound\", scores['compound']/len(b)))]\n", " \n", - " return(result)" + " return(result) " ] }, { @@ -342,34 +350,22 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnoAAAFqCAYAAACNlPcKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzs3XeYq1W5/vHvTRPpoIhIVzkqggiCYBcrNkBEFJF2OKJHVDy2H3hEFOziUbGgHEEBlSKCIkUEpFmQXqQdtogCgiC9CQL374+1ws6ePTM7efNmZsi+P9eVayZvkidrMilPVnmWbBMRERERo2eB6W5ARERERAxHEr2IiIiIEZVELyIiImJEJdGLiIiIGFFJ9CIiIiJGVBK9iIiIiBGVRC8iRoKkL0j63jTc7w8kfazlmO+RdEqbMSNi/pRELyJaI+mertMjku7vOr/tdLdvGGzvaPtL092OmUTS2ZLeOd3tiAhYaLobEBGjw/YSnd8lXQv8h+30TEVETJP06EXElJH0eEnfknSjpOslfVnSwvWyTSXNkvRpSbdJ+rOkt04S6+mSfivpbkknAsuOufwtki6XdIekUySt2XXZTZI+JOmy2tu4v6QVJZ0s6S5Jv5S0VL3uQpJ+KunvNdZpkp7RFetwSZ8Y8zd8XNItkm7o7smUtLmkK2ubr5P0gUkergUkfbe253JJL60xtpP02zF/68clHTHB4/RESYfUv/n27utJ2lXSnyTdKuloSSvU48+U9NCYOI/20tWh5VMl7Vcfkz9JelW97CvAhsD36mP7lUn+xogYsiR6ETGVPg08B1gHeB7wcqB7ftvqwCLAk4F3AQdLWmNsEEkCfgKcCTwB2BfYruvydYAfAO8FngScAfxcUvcoxpuBlwFrAW8Hfg58CFgBWAL4z67r/hx4Wm3XlcDBk/yNqwECngK8D/iOpE5P50HA9raXBJ4LnDVJnJcCF9e/7wvAz2ryeTSwzpjHZTvgkAniHFHb88z6t30LQNLrgT3r47AS8A/g0EnaM177zqvt+ybwPQDbHwbOpfTmLlHPR8Q0SaIXEVNpW2Av2/+w/XfgM3QlaMBDwKdtP1iHfE8BthonzprAs7queyrwy67L3w4cY/t02w8CnwOWBzbous7Xajv+CvwO+K3tS23fT0ns1gOw/ZDtQ2zfY/uflGT1+ZIWneBvvA/4vO1/2T4GMPD0etnDwLMlLWn7VtsXTvJYXWf72zXOIcD1wGtt3wv8FOj0rm1ASbZOGhugJoMvAd5r+476WJ1ZL94WOMD2JfXv+hjwKklPnqRN3a6qj8vDlMR3NUnL9HjbiJgiSfQiYkrUXrgnA3/pOvwXSm9Sxy016ei+/CnjhHvKBNftvvzR8zUZuWHMff296/f7xzm/RG33QpK+IukaSXdRevRESa7Gc4vtR7rO39eJBWwOvAX4q6RfS9pwghhQErtu3Y/FwZREDUrCd5jth5jbKsDNtu8e57Kxj9EdwF3M+RhN5qau3++rP5cY74oRMX2S6EXElLBtSnKwWtfhVSkJWMcTx/SUrQr8bZxwN05w3Y6/dd+PpAUpCUz3ffVqJ+DVwCbA0pQhUCjJXl9s/972GylDqL8CfjzJ1Vcec777sTgDWFTSxsA2TDzkeh3wpK6h425jH6NlgKUoj9G9wIKSHtd1/V57+qD0YkbEDJBELyKm0mHAXpKeIOlJwH8DP+y6fGFgT0mLSHoFJcH66Thx/g+4quu6mwCbdl1+BPBmSS+tiz12B26lzCnr15LAP+vtF6cMN/dN0uKS3l7n2f0LuBt4ZJKbrFIXPSxUF0GsQkkOO0nzocABwK22x/27bP+ZMo/xm5KWro/VS+vFhwHvkrR2TZi/APza9k2UJPAWYFtJC0p6L7339EHpHX1qH9ePiCFJohcRU+mTwOXAZcBFwG+B7hp011Lm6d1EWbiwk+1rxgapic7WlF622yjzy37YdfklwM7AdykJyyuBzScY3pyXA2uMm4BLgd80iNHx75Th0juB7etpImdS5gneRkmIt7R9Z9flh1AWtcxrAcU2lAT6asrf8J8Ato8DPg8cS0nsnkydL1mHuv8D2IuySGMV4Pwe/0aArwLb11W+XwKoK3Pf0keMiGiByvtlRMT0krQp8E3bT5/nlQNJS1J6zp5ZF5RERMwlPXoREY9N7wdOT5IXEZPJzhgREY8xkm6irHTdbLrbEhEzW4ZuIyIiIkZUhm4jIiIiRlSGbqsnPvGJXn311ae7GRERERHzdP755//D9vLzul4SvWr11VfnvPOalNiKiIiImFqS/jLva2XoNiIiImJkJdGLiIiIGFFJ9CIiIiJGVBK9iIiIiBGVRC8iIiJiRCXRi4iIiBhRSfQiIiIiRlQSvYiIiIgRlUQvIiIiYkRlZ4wptPrux0942bVfeMMUtiQiIiLmB+nRi4iIiBhRSfQiIiIiRlQSvYiIiIgRlUQvIiIiYkQl0YuIiIgYUUn0IiIiIkZUEr2IiIiIEZVELyIiImJEJdGLiIiIGFFJ9CIiIiJGVBK9iIiIiBGVRC8iIiJiRCXRi4iIiBhRSfQiIiIiRlQSvYiIiIgRlUQvIiIiYkQl0YuIiIgYUUn0IiIiIkZUEr2IiIiIETW0RE/SQZJulvTHrmPLSTpZ0tX157L1uCTtJ2mWpEskrd91mx3q9a+WtEPX8edJurTeZj9Jmuw+IiIiIuY3w+zR+wGw6ZhjuwOn2l4TOLWeB3gdsGY97QLsDyVpA/YCNgKeD+zVlbjtD7yr63abzuM+IiIiIuYrQ0v0bJ8J3Dbm8ObAwfX3g4Etuo4f4uJsYBlJKwKvBU62fZvt24GTgU3rZUvZPtu2gUPGxBrvPiIiIiLmK1M9R28F2zfW328CVqi/rwRc13W96+uxyY5fP87xye5jLpJ2kXSepPNuueWWBn9ORERExMw1bYsxak+cp/M+bB9gewPbGyy//PLDbEpERETElJvqRO/vddiV+vPmevwGYJWu661cj012fOVxjk92HxERERHzlalO9I4FOitndwB+3nV8+7r6dmPgzjr8ehLwGknL1kUYrwFOqpfdJWnjutp2+zGxxruPiIiIiPnKQsMKLOkw4OXAEyVdT1k9+wXgSEk7A38Btq5XPwF4PTALuA/YCcD2bZL2Ac6t19vbdmeBx3spK3sfD5xYT0xyHxERERHzlaElera3meCiV45zXQO7ThDnIOCgcY6fB6w9zvFbx7uPiIiIiPlNdsaIiIiIGFFJ9CIiIiJGVBK9iIiIiBGVRC8iIiJiRCXRi4iIiBhRSfQiIiIiRlQSvYiIiIgRlUQvIiIiYkQl0YuIiIgYUUn0IiIiIkZUEr2IiIiIEZVELyIiImJEzTPRk7S4pAXq7/8maTNJCw+/aRERERExiF569M4EFpW0EvArYDvgB8NsVEREREQMrpdET7bvA7YEvm37rcCzh9usiIiIiBhUT4mepBcA2wLH12MLDq9JEREREdGGXhK9DwJ7AMfYvkzSU4HThtusiIiIiBjUQvO6gu0zgDMkLVbPXwN8YNgNi4iIiIjB9LLq9gWSLgeurOfXlfTtobcsIiIiIgbSy9Dt14DXArcC2L4YeOkwGxURERERg+upYLLt68YcengIbYmIiIiIFs1zjh5wnaQXAq6FkncDrhhusyIiIiJiUL306L0H2BVYCbgBeG49HxEREREzWC+rbv9BqaEXEREREY8hvay6PVjSMl3nl5V00HCbFRERERGD6mXo9jm27+icsX07sN7wmhQRERERbegl0VtA0rKdM5KWo7dFHBERERExjXpJ2L4C/F7STwABWwGfHWqrIiIiImJg8+zRs30I8Bbg78BNwJa2Dx3kTiX9l6TLJP1R0mGSFpW0hqQ/SJol6QhJi9TrPq6en1UvX70rzh71+FWSXtt1fNN6bJak3Qdpa0RERMRjVU8Fkynbnx0NHAvcI2nVpncoaSXKXrkb2F4bWBB4O/BF4Ku2nw7cDuxcb7IzcHs9/tV6PSStVW/3bGBT4NuSFpS0IPAt4HXAWsA29boRERER85VeVt2+n9KbdzJwHHB8/TmIhYDHS1oIWAy4EXgFcFS9/GBgi/r75vU89fJXSlI9frjtB2z/GZgFPL+eZtm+xvaDwOH1uhERERHzlV7m6O0GPMP2rW3coe0bJO0L/BW4H/gVcD5wh+2H6tWupxRopv68rt72IUl3Ak+ox8/uCt19m+vGHN9ovLZI2gXYBWDVVRt3UkZERETMSL0M3V4H3NnWHdYVvJsDawBPARanDL1OOdsH2N7A9gbLL7/8dDQhIiIiYmh66dG7Bjhd0vHAA52Dtv+n4X2+Cviz7VsAJB0NvAhYRtJCtVdvZcp2a9SfqwDX16HepYFbu453dN9mouMRERER841eevT+SpmftwiwZNepqb8CG0tarM61eyVwOXAapXQLwA7Az+vvx9bz1Mt/bdv1+Nvrqtw1gDWBc4BzgTXrKt5FKAs2jh2gvRERERGPSb3sdftpAEmL2b5v0Du0/QdJRwEXAA8BFwIHUBZ5HC7pM/XYgfUmBwKHSpoF3EZJ3LB9maQjKUniQ8Cuth+ubX0fcBJlRe9Bti8btN0RERERjzXzTPQkvYCSbC0BrCppXeDdtt/b9E5t7wXsNebwNZQVs2Ov+0/grRPE+SzjFG+2fQJwQtP2RURERIyCXoZuvwa8ljIvDtsXAy8dZqMiIiIiYnA9FUy2fd2YQw8PoS0RERER0aJeVt1eJ+mFgCUtTKmrd8VwmxURERERg+qlR+89wK6UYsQ3AM+t5yMiIiJiBpu0R6/uG7ud7W2nqD0RERER0ZJJe/RquZJ3TFFbIiIiIqJFvczR+42kbwJHAPd2Dtq+YGitioiIiIiB9ZLoPbf+3LvrmIFXtN+ciIiIiGjLvOboLQDsb/vIKWpPRERERLRkXnP0HgE+NkVtiYiIiIgW9VJe5RRJH5G0iqTlOqehtywiIiIiBtLLHL231Z/dtfMMPLX95kREREREW+aZ6NleYyoaEhERERHtmmeiJ2n78Y7bPqT95kREREREW3oZut2w6/dFgVcCFwBJ9CIiIiJmsF6Gbt/ffV7SMsDhQ2tRRERERLSil1W3Y90LZN5eRERExAzXyxy9X1BW2UJJDNcCUkA5IiIiYobrZY7evl2/PwT8xfb1Q2pPRERERLSkl0Tvr8CNtv8JIOnxkla3fe1QWxYRERERA+lljt5PgEe6zj9cj0VERETEDNZLoreQ7Qc7Z+rviwyvSRERERHRhl4SvVskbdY5I2lz4B/Da1JEREREtKGXOXrvAX4k6Zv1/PXAuLtlRERERMTM0UvB5D8BG0taop6/Z+itioiIiIiBzXPoVtLnJC1j+x7b90haVtJnpqJxEREREdFcL3P0Xmf7js4Z27cDrx9ekyIiIiKiDb0kegtKelznjKTHA4+b5PoRERERMQP0kuj9CDhV0s6SdgZOBg4e5E4lLSPpKElXSrpC0gskLSfpZElX15/L1utK0n6SZkm6RNL6XXF2qNe/WtIOXcefJ+nSepv9JGmQ9kZEREQ8Fs0z0bP9ReAzwLPqaR/bXxrwfr8O/NL2M4F1gSuA3YFTba8JnFrPA7wOWLOedgH2B5C0HLAXsBHwfGCvTnJYr/OurtttOmB7IyIiIh5zeunRA7gQOAM4vf7emKSlgZcCB0IpwFznAG7O7J7Cg4Et6u+bA4e4OBtYRtKKwGuBk23fVucNngxsWi9byvbZtg0c0hUrIiIiYr7Ry6rbrYFzgK2ArYE/SNpqgPtcA7gF+L6kCyV9T9LiwAq2b6zXuQlYof6+EnBd1+2vr8cmO379OMfH+9t2kXSepPNuueWWAf6kiIiIiJmnlx69/wY2tL2D7e0pw6R7DnCfCwHrA/vbXg+4l9nDtADUnjgPcB89sX2A7Q1sb7D88ssP++4iIiIiplQvid4Ctm/uOn9rj7ebyPXA9bb/UM8fRUn8/l6HXak/O/d5A7BK1+1XrscmO77yOMcjIiIi5iu9JGy/lHSSpB0l7QgcD5zQ9A5t3wRcJ+kZ9dArgcuBY4HOytkdgJ/X348Ftq+rbzcG7qxDvCcBr6kFnJcFXgOcVC+7S9LGdbXt9l2xIiIiIuYbvWyB9lFJWwIvrocOsH3MgPf7fsr+uYsA1wA7UZLOI2sJl79Q5gNCSSpfD8wC7qvXxfZtkvYBzq3X29v2bfX39wI/AB4PnFhPEREREfOVeSZ6ALaPBo5u605tXwRsMM5FrxznugZ2nSDOQcBB4xw/D1h7wGZGREREPKYNMtcuIiIiImawJHoRERERI2rCRE/SqfXnF6euORERERHRlsnm6K0o6YXAZpIOB+bYL9b2BUNtWUREREQMZLJE75OUwsgrA/8z5jIDrxhWoyIiIiJicBMmeraPAo6StKftfaawTRERERHRgl7q6O0jaTPgpfXQ6baPG26zIiIiImJQ81x1K+nzwG6U3SsuB3aT9LlhNywiIiIiBtNLweQ3AM+1/QiApIOBC4GPD7NhERERETGYXuvoLdP1+9LDaEhEREREtKuXHr3PAxdKOo1SYuWlwO5DbVVEREREDKyXxRiHSTod2LAe+n+2bxpqqyIiIiJiYL306GH7RuDYIbclIiIiIlqUvW4jIiIiRlQSvYiIiIgRNWmiJ2lBSVdOVWMiIiIioj2TJnq2HwaukrTqFLUnIiIiIlrSy2KMZYHLJJ0D3Ns5aHuzobUqIiIiIgbWS6K359BbERERERGt66WO3hmSVgPWtH2KpMWABYfftIiIiIgYxDxX3Up6F3AU8N16aCXgZ8NsVEREREQMrpfyKrsCLwLuArB9NfCkYTYqIiIiIgbXS6L3gO0HO2ckLQR4eE2KiIiIiDb0kuidIenjwOMlvRr4CfCL4TYrIiIiIgbVS6K3O3ALcCnwbuAE4BPDbFREREREDK6XVbePSDoY+ANlyPYq2xm6jYiIiJjh5pnoSXoD8B3gT4CANSS92/aJw25cRERERDTXy9DtV4BNbL/c9suATYCvDnrHdR/dCyUdV8+vIekPkmZJOkLSIvX44+r5WfXy1bti7FGPXyXptV3HN63HZknafdC2RkRERDwW9ZLo3W17Vtf5a4C7W7jv3YArus5/Efiq7acDtwM71+M7A7fX41+t10PSWsDbgWcDmwLfrsnjgsC3gNcBawHb1OtGREREzFcmTPQkbSlpS+A8SSdI2lHSDpQVt+cOcqeSVgbeAHyvnhfwCkphZoCDgS3q75vX89TLX1mvvzlwuO0HbP8ZmAU8v55m2b6mloU5vF43IiIiYr4y2Ry9N3X9/nfgZfX3W4DHD3i/XwM+BixZzz8BuMP2Q/X89ZQdOKg/rwOw/ZCkO+v1VwLO7orZfZvrxhzfaLxGSNoF2AVg1VVXHeDPiYiIiJh5Jkz0bO80jDuU9EbgZtvnS3r5MO6jV7YPAA4A2GCDDbKSOCIiIkZKL6tu1wDeD6zefX3bmzW8zxcBm0l6PbAosBTwdWAZSQvVXr2VgRvq9W8AVgGur7tyLA3c2nW8o/s2Ex2PiIiImG/0shjjZ8C1wDcoK3A7p0Zs72F7ZdurUxZT/Nr2tsBpwFb1ajsAP6+/H1vPUy//da3jdyzw9roqdw1gTeAcyvzBNesq3kXqfRzbtL0RERERj1Xz7NED/ml7v6G3BP4fcLikzwAXAgfW4wcCh0qaBdxGSdywfZmkI4HLgYeAXW0/DCDpfcBJwILAQbYvm4L2R0RERMwovSR6X5e0F/Ar4IHOQdsXDHrntk8HTq+/X0NZMTv2Ov8E3jrB7T8LfHac4ydQtmqLiIiImG/1kuitA2xHKX/ySD3mej4iIiIiZqheEr23Ak+tNekiIiIi4jGil8UYfwSWGXZDIiIiIqJdvfToLQNcKelc5pyj17S8SkRERERMgV4Svb2G3oqIiIiIaN08Ez3bZ0xFQyIiIiKiXb3sjHE3ZZUtwCLAwsC9tpcaZsMiIiIiYjC99Ogt2fldkoDNgY2H2aiIiIiIGFwvq24f5eJnwGuH1J6IiIiIaEkvQ7dbdp1dANgA+OfQWhQRERERrehl1e2bun5/CLiWMnwbERERETNYL3P0dpqKhkREREREuyZM9CR9cpLb2fY+Q2hPRERERLRksh69e8c5tjiwM/AEIIleRERExAw2YaJn+yud3yUtCewG7AQcDnxlottFRERExMww6Rw9ScsBHwK2BQ4G1rd9+1Q0LCIiIiIGM9kcvS8DWwIHAOvYvmfKWhURERERA5usYPKHgacAnwD+Jumuerpb0l1T07yIiIiIaGqyOXp97ZoRERERETNLkrmIiIiIEZVELyIiImJEJdGLiIiIGFFJ9CIiIiJGVBK9iIiIiBGVRC8iIiJiRCXRi4iIiBhRSfQiIiIiRlQSvYiIiIgRNeWJnqRVJJ0m6XJJl0narR5fTtLJkq6uP5etxyVpP0mzJF0iaf2uWDvU618taYeu48+TdGm9zX6SNNV/Z0RERMR0m44evYeAD9teC9gY2FXSWsDuwKm21wROrecBXgesWU+7APtDSQyBvYCNgOcDe3WSw3qdd3XdbtMp+LsiIiIiZpQpT/Rs32j7gvr73cAVwErA5sDB9WoHA1vU3zcHDnFxNrCMpBWB1wIn277N9u3AycCm9bKlbJ9t28AhXbEiIiIi5hvTOkdP0urAesAfgBVs31gvuglYof6+EnBd182ur8cmO379OMfHu/9dJJ0n6bxbbrlloL8lIiIiYqaZtkRP0hLAT4EP2r6r+7LaE+dht8H2AbY3sL3B8ssvP+y7i4iIiJhS05LoSVqYkuT9yPbR9fDf67Ar9efN9fgNwCpdN1+5Hpvs+MrjHI+IiIiYr0zHqlsBBwJX2P6frouOBTorZ3cAft51fPu6+nZj4M46xHsS8BpJy9ZFGK8BTqqX3SVp43pf23fFioiIiJhvLDQN9/kiYDvgUkkX1WMfB74AHClpZ+AvwNb1shOA1wOzgPuAnQBs3yZpH+Dcer29bd9Wf38v8APg8cCJ9RQRERExX5nyRM/2b4CJ6tq9cpzrG9h1glgHAQeNc/w8YO0BmhkRERHxmJedMSIiIiJGVBK9iIiIiBGVRC8iIiJiRCXRi4iIiBhRSfQiIiIiRlQSvYiIiIgRlUQvIiIiYkQl0YuIiIgYUUn0IiIiIkZUEr2IiIiIEZVELyIiImJEJdGLiIiIGFFJ9CIiIiJGVBK9iIiIiBGVRC8iIiJiRCXRi4iIiBhRSfQiIiIiRlQSvYiIiIgRlUQvIiIiYkQl0YuIiIgYUUn0IiIiIkZUEr2IiIiIEZVELyIiImJEJdGLiIiIGFFJ9CIiIiJGVBK9iIiIiBGVRC8iIiJiRCXRi4iIiBhRC013A4ZF0qbA14EFge/Z/sI0N6lVq+9+/ISXXfuFN0xhSyIiImKmGskePUkLAt8CXgesBWwjaa3pbVVERETE1BrVHr3nA7NsXwMg6XBgc+DyaW3VDDZRD2GT3sE2exvTcxkREdGcbE93G1onaStgU9v/Uc9vB2xk+31jrrcLsEs9+wzgqiltKDwR+EdiTVu8+SFW2/Hmh1htx5sfYrUdb36I1Xa8+SFW2/FmaqxerWZ7+XldaVR79Hpi+wDggOm6f0nn2d4gsaYn3vwQq+1480OstuPND7Hajjc/xGo73vwQq+14MzVW20Zyjh5wA7BK1/mV67GIiIiI+caoJnrnAmtKWkPSIsDbgWOnuU0RERERU2okh25tPyTpfcBJlPIqB9m+bJqbNZ42h43nh1htx5sfYrUdb36I1Xa8+SFW2/Hmh1htx5sfYrUdb6bGatVILsaIiIiIiNEduo2IiIiY7yXRi4iIiBhRSfQiIiIiRlQSvZjRJG0nackxx97YII4krTLva/YVs5W2zWSSFpS073S3Y9gkfWic086Snjtg3AUkLdVWOyOGoT5Pt24x3jptxYrBZTHGFJL0JeAzwP3AL4HnAP9l+4cN4wnYFniq7b0lrQo82fY5DWI9Dbje9gOSXl7bdojtOxrEeul4x22f2SDWHcC1wDa2r6jHLrC9foNYl9pu7Q2o5ba1+fg/AfgU8CLAwG+AvW3f2m+sGu9s2xs3ue0E8dYFXlLPnmX74gYxFgROsb1JS236MbAB8It66I3AJcDqwE9sf6nPWO8BHqaUeloK+LrtLw/YxicBi3bO2/5rH7d9p+0fSvrQeJfb/p8G7VkB+BzwFNuvq/uJv8D2gQ1ibTnZ5baPnso4Y2K29r4taWPgMtt31/NLAc+y/Yc+41xKeW3PdRFg289p0LY2iwefBTwO+AHwI9t3DhDr34D9gRVsry3pOcBmtj/TMN4LKa/rR6uO2D6kYay3Ar+0fbekTwDrA5+xfUGTeMOSHr2p9Rrbd1E+RK4Fng58dIB43wZeAGxTz98NfKthrJ8CD0t6OmWZ+CrAjxvG+mjXaU/Kh+enGsb6M/DvwFH1RQXlzayJCyRt2PC242mzbW0+/ocDNwNvAbYCbgGOaBgL4EJJx9YezC07pyaBJO0G/Ah4Uj39UNL7+41j+2HgEUlLN2nHOFYG1rf9YdsfBp5X2/dSYMc+Y61VX+dbACcCawDbNW2YpM0kXU15vp1Bee84sc8wi9efS05wauIHlBJWT6nn/w/4YMNYOwMHUr64bgt8j/LaehPl/bJXb5rk1LS3vc337f2Be7rO31OP9euNTPw3vqlh206R9BFJq0harnNqEsj2Syj/x1WA8yX9WNKrG7brf4E9gH/V2JdQauP2TdKhwL7Ai4EN62mQ5HbPmuS9GHgV5Tnc5P85VCNZR28G6zzeb6D0EtxZOuUa28j2+pIuBLB9ey0Q3cQjtf7gm4Fv2P5GJ26/bM/xRlOHTL/WsF22fYGklwGHSdqIUhuxiY2AbSX9BbiXAb79DqFtrT3+wIq29+k6/xlJb2sYC0ov0q3AK7qOGei7d4Tygb6R7XsBJH0R+D3wjQax7gEulXQy5f9ZGmZ/oEGsJwEPdJ3/F6UH4X5JD0xwm4ksLGlhSqL3Tdv/kjTI0Mk+wMaUHsz1JG0CvLOfALa/W39+eoB2jPVE20dK2qPGfkjSww1jLUxJkG8EkLQi8APbO/UTpN/r96jN923x/YkgAAAgAElEQVS5axjN9iOS+v4ctv2Xpg2YROc9YtfuuwKe2iSY7atrL9d5wH7AenUU6uN99qwuZvucMY/5Q03aREnq1ur+Hwyo83x/A3CA7eMlNeppHKYkelPrOElXUoYA/lPS8sA/B4j3rzqEZYAa75EBYm0D7MDsb4QLD9C2btcDz2p42xsBbP9D0muBLwJrN4z12oa3m0ibbWvz8f+VpLcDR9bzW1F6Xhpp+cNTzH5zpP7e9FPzaJolm+P5EfAHST+v598E/FjS4sDlfcb6LqXn52LgTEmrAXcN0LZ/2b61zqNawPZpkhp9cZK0KCXZfjZzDgP/e4Nw99ZpAp33n42BpkN0q3SSvOrvwKoNY1Hb8wbm/jv3bhCqzfftayR9gNm9Pu8Fruk3iKTf2H6xpLuZcwi38+W173mhttfo9zaTtO85wE6UBOhk4E31S/FTKF/s+nnd/qNObek8z7aivvc28EfgyQPcfqwbJH0XeDXwRUmPYwaOlGaO3hSrXeF32n64fogsafumhrG2pXwLWx84mPKB/gnbP2kQay3KvKLf2z5M0hrA1ra/2CDWN5j95rMA8FzgWtt99ULUWE+z/ad+bzcmxqTDD7Zvaxj3JcDv6jBi59jzbJ/fIFabj//dwGKUpF+U/0Gnx6vvD4E258jUOWI7AMfUQ1tQem6aJi6PB1a1fVWT24+JtQFlXiPAb22fN2jMrtgL2W7UCyHpFMrj9HngiZRh+Q1tv7BBrJ8AVwLvAPamDK9dYXu3BrGeR+mpWZvyAbo8sFUdWus31jeBNYHD6qG3AbNs9z2sX+N9h/Ia2IQyDLwVcI7tnRvG637fXgxYqsn7dp1nuR+ld9zAqcAHbd/cpF1tqn/Xhyivp10krQk8w/ZxDWKdQXncj7J9/5jLtrN9aB+xnkqZzvJC4HbKFIZ32r62QbtOo3wenUNXD77tzfqNVeMtBmwKXFp7MFcE1rH9qybxhiWJ3hSS9BvKHJuzKB8kd7cQ85nAKykf6Kd2FgU0iPNKStJy/zyvPO9YO3SdfYiS5P22YawzKPOnzqU8bmfavrTPGH+mvKmO13Nk242GJiTdV9v11s4btZovxmjz8f8hcCZloUOj58OYeGdQ5iR91/Z69dgfbTfqvZS0PmWODLWNjYaoJb2JMt9mEdtrqKyQ3bvpm3Zb6jzE71PmzH4PWA/Yvembf/1C+E/K83dbYGnKBPe+F9dIurAO/15i+zl1iPksN1xsU4cdn1HbdpXtfzWJU2O9mTInEsrr/JjJrj+PWJ2/r/NzCeDEOnes31gzfsK9Blio0xXjCOB8YPv6hW4xyntSzyvPJZ1q+5WSvmT7Y/22YR6xFwcWGORzs06zmYvtM/qMs5TtuybqRGjaeTAsGbqdWttRVhu+Bfhynftzlu3/GiDm1ZRhoYUAJK3a5EUObA/sL+k2akIF/Mb27f0Gsn1wg/ufKNbLVOYdbgi8HDhe0hK2e54k3OaQxBhXAV8GzpC0s+3f0XwYsrXHnzIh+CXAfnXI4wLK8+zrDdvW5hwZKD0td9v+vqTlJa1h+88N4nwKeD5wOoDti+q3/+n277a/Xofzl6W87g8FGiV6XfMZl2L2quCmOonYHZLWBm6izE/sm6RLKAt/jhi01726gPK8OEXSYpKWHOBDvfOF6b46XHgrsGLDWHva/olmT7j/MqWHe6N+A7U8dI6kzYCvUBbE3AysBlxR4/frabbfVqeQYPs+qe/JiCuqrGp9k6TDGPN+2CQ5lrQM5f1xdWChTpPcYC6u7TNUVot3FuWd07A39ceUhS/nM3cnQuN5jcOSRG8K2f6zpH8CD9bTJjSfu4bKasW9KPNZOnOdTFn+32/bdqgxn0IZ5vgW5c2j7+dIVw/a2Pvo+8lf31xfUk/LAMdREqFGVFaLvri27yzbP2sai9IbeJykq4AjJB3E+CUPegnU2uNf53CdSXkz24QyJLw20DTRa22OjKS9KBOin0Hp9VoY+CGzh0z78S/PPTG+6RzVNnUa9HrgUNuXNfjAnB1MejfwaUqvXmc4vumHyQGSlgU+ARwLLAF8smHT3kQZYj1S0iOUld1HNuxNehewC7Ac8DRgJeA7lNGKJo6rCcKXKQmkKb2rTbQ54f5QytD5a+kaOm8YC1pYqNPlwToVovM6fxpzLlDqxScplRZWBsaW7DFzLujq1QnA2cClDPj6VqkV+GXKl0MB35D0UdtH9RPH9hvrz2F1IrTLdk5TdAL+BPwB2I3S/b/AgPFmAU9oqW3vpEwi/x3lA+BjlJpYTWI9oeu0EqXkwt4NYz1UH7MtKEN0g/yN36b0quxUT78EvjVAvAu7fl+CsvjhoRnw+J9KeWP8KrAl8KQBH7enAqcA9wE3UOryrd4w1kWUN9jux+6ShrEOpMw1u4Qyv+sbwHcG+VvbOFES2F9RetsXo5QvOX+AeFdTVri20bbHjXNsuRbirgkcAjw8wPNikTHPi0vb+puBpQe4/XH1tXkN5cvm44CLG8a6sP68pP5cGDh7gLadV39e3Pk8GaBtr6ZMLbqFsjjpWuDlDWPt2cb/rsa6oMVYF3e/H1LmlTZ6vLpibElJar8CbNFWW9s8pUdvau1H6U3ahjJv5wxJZ7r5sMd1NF/lNtbXKInod4DT3GCia4fnnjv0NUnn06zn4ImU3p6XAh+oPQe/t71ng1ivoBQn7XxjPRi4rEEcAFznq9Xf7wG2Vila3URrjz8l8XkepRfvTsow3e/dcP6f7WuAV7UxRwZ40LZVy43UmE29H/hvSq/Djykri/eZ9BZTY2fKhO9rXIa/nkD5YtHUnyhJdhuOlrSF61w6SU8Gjqc8X/qmsqL4bfX0MOULShMP2H6w0/FZ5/41nkCuceo8SrqTkjz2O1S3NWXC/b6276gT7pvW0Wtt6LwrzhKUqR4/knQzXaWG+mH7ZEkXUHoIBexm+x/9xJD0TNtXUqbYzDVX2c3mNR5ae3yPY84FFE3mwS0w5v9/KwOskpX0bUpdxc4iovdIerXtXSe52ZRLojeFXOZIfb2+MHeizDFamea1164BTpd0PHO+APqucm/7iZKeTUmoPquy4uoq230Xeh3zAl+AMlTX6LlW31ivoRTeXJmy8qpp2ZFZlJINnRpUq9RjjdSh2vH0Pd+mzcffdc6nyvZsO1J6mJ5M6YnomSbYRaFrjkzfzzPKMN93gWXqm/e/UwqiNvEG2/9NSfY6bXsr0Peq85YZWIsyh2dvSrHiRSe9xeT2AH4n6Q/M+TpvUi/wZ5T/wVaU5/+xwEeaNKq2Z2HK4/3W+oWgqTMkfRx4vEph3fcy2HzEnSnF5E+r519OmU+1hqS93ceqz5qs30z5kn41ZZTh6obtanPoHGBzynzE/2L2Qp0mJWQ6FqWsbF0IWEsS7m9How9RhuC/Ms5lTYduH6QMt/43s5P/plMXfinpJOZc3X1CgzgdrXYeDEsSvSkk6SuUN4slKEN0n2SA+WbAX+tpkXoapG1LUZKg1SiTXpem+XyI7hf5Q5QhgEb7KNYk70rKcOH+wE62H+wzxi8obwxLAldIOqee34iyzL6p47t+XxR4M/C3JoHafPwlvY8yp/F5lMf+IJo9zzo7JjyDMt/v2Hr+TTR83GzvWz/I76pxP2n75CaxKAnQ2KRuvGNT7duU/90rKB+6d1N2Pmm6K8t3gV/Twhwl2/9bFzf9jPI8e7fLIqImtncLZW2q3SnJ2aXAuykfvk3n1EH5bHuW7b8Dne3aDqG85s+kzJXrSZvzSm13/qYzGXDCvkoN1eNctgF8hFJia5B4X6QkPpcx+3lmSlt74lKWZQFKma9GlRbG8WHg6f32Lo7H9kclvYXZ/7sDPMDqblruPBiWlFeZQvVb9FmdN5+ZpK6g+009nWn7+mluEgAqBWIHnYA77pL6Dve5tH6S+1mAslK2SX2z1h5/SR+hJHbnu2HttjHxzqT0nnX251wSON72uHsaD5uk11EWOmzNnFu7LUWpev/86WhXh2qJHdVSJvXYxbbXbRjv0TgDtKm7d1aUVYyXAJ1ddZr0zrZZlLjtmoiX216r67woe8yu1e/jKekiylSbC7r+n5e42X6yhwLvc937tQ59H2S70aITSacCW3qAvWS7Yl0FPMd2vwswxos18HO2K9avKHPf2pq+0BqV0lMbUr74mlIF4DzqlCpPc6mnjvToTa2jgXeolJPYp87nerLtvnpHJH3N9ge7eqrm0OTJ1XnTkrTYoC8ozVlH7H8pC0+a1hF7uqSBivWOTeRq79kwnvtr0nC+TZuPv+19B7n9OFagDJ90PFiP9UxzV/Cfg/sr4vw3ypvpZpThuI67KUNY063NHWsATpS0C2Uos+kcpbH72R49wfGeaYKixA1jbUYZnluEMrw6aE3E0yUdx+ze3a3qscWBO/qM1ea80t9QdmD5EGWh2kcpPVZNtbkN4DWU3sqBEz3g1NpzdnRnWHMA9wIXqRQ7bjR1QUPYSaQaZNh9yqRHbwrVhOUR4BW2n1XnavzKdl9DOqq7L0zUU9Wkh0rSCyirGJewvaqkdSnDOu9tEOti2+uq1BF7D2U+yqFuVki4tWK99cNyb8aUqXDzgsmdN41OuYubgD1s/7RBrNYe/7ZJ+m9K71n3bhZH2P58g1j7UEqzHMrsAsAr2u77DVPSx2x/acyx3dy8XmAr1OKONTXeeDUGGz9va8w2vtC1WZT4fMpQ9+ldr/NLba/TsG2irIbsFOb+LfDTJklH7SFfk7Iq9fOUeaU/tt1kf+ZOyajTgH8A67nhzkg11g7jHXcftUw1eyejlYB1Kav2B5oLWt8bF6cs0LmfARKqNv7G+V169KbWRp0hHQDbt9f5Mn3x7C22VgV+5q5VkJLe2LBtX6PUdjq23sfFkpoOzXXXETvEg9URa7NY70eBtduY6wFgu3FvyDjafPxbZfuzkk6kzPuDMk+y0W4WlN7Y7iHM/SVdTLNvxm8HvjTm2I40rxfYCts/qolLZ8eaLTzADiVudw/SR79QAIN+oejs99opSnwbzYsSj1cTsXEvRO2BO4+ybdkpKrs8LEHp9e03VmvzSiVtR6kztz2l3ukJknayfXGTeLYPbmHIu7PV3/nMnof76F00bFdr741tJnSSDvWYBW7jHesj3saUsk7PovRGLwjcO0AP4VAk0ZtabQ/pfAP4sKRtuj5I9qYsQ++b7evGvNE+PNF15+H8Oq9iDWCPOqer6d/Z5obWbZapANotwNzi498Kzbm9z7X19OhlfQ4ddtxbe7wOpzxm29BnOQiVyv3voAzxdX8wLUlJNqZNfX1fZvuZlEVEbcRsbQ9S2v1C8QvNXZS46QrqyyS9A1iw/n0foCxYa0QtFWCu/89TXBY8NF001O0twItdSnwcJukYSq9vz9uMjWnfo9sA0nDIu5NIjdcbXqfhNFKH4zvPrdP7fb5KOtL21pIuZe6E0w3nvM6xY4hKGZ9GpYWqb1K+cP6EsmBne+DfBog3FEn0ptZ+lOGvJ0n6LHVIZ4B4f6asVDtK0qfq0FDTnrPrVLauscr+l7vRvGL72Dpiy9G8jtiulA2tnynpBuqG1g1jtVmmou0aSm0+/m0Zu71P5812kJ0Z3kHpcft6jfHbeqwfv6Mk+09kzhXed1MWGEwbl03vr1LzrQjH833K/6KzyOcGygfLdH+hu5JSIPmnktaiDFU3/aLTXRPxMAavibgrZWL8HwBcNpzve/5s/X8+ImnpNhY82N5izPlzJA2yeOhTtLcN4A7M3Ru+4zjH5knSFyiLFH5UD+0m6UW29+gjTCfJvII56xaKuXvy59WePYBO+Z67uuI8SPl8acz2LEkL2n4Y+H4dsevn7xy6JHpTqO0hnRLSF9S5eodJ2ojmNfneQ3lBr0T5IPkV5c2yiRcAF9m+V9I7KR8AjYbT3G6x3tbKVFRt1lBq8/FvRZtDhl0xr6XU/hokxl8o5Qxe0EabhmBZSg/VOcw5Qb7pwoI29iDtaPMLRfcesK+g9Cw12gO2zhecoybigNoswNzaggdJ/0Z5jOZYXAY03VJt4G0Ah9RD/nrgua4VE+p7Y18JkO3OyM3T62u+u83P7KcxdT7x5yV9vs9kc17uq9OvLpL0JcoX0MYFmIclid7Uu5oy12MhgAG/+d8IYPsfKgsfvkjZDaFvdd7atg3bMdb+wLp1/s+HKSvyDgEmLXMyHkmPowx3rM6cG1o3KeGwsO1xiwA31FoNpZYf/1aoVrnXOBXuob8q96oLJ7omfo+NNRNW0LWlya4tk2ljD9KONr9QdO8B+78eYA/YmgB9hPo67xy33aTALrRbgPloZq9SHtT/UheXAdi+RNKPaZ7otTHkPawe8mWYnSgu3e+NJf0n5f/2VJXyUx1LUkYC+mZ7D5VFkGsyZ0mgfopCd9uOkti9j7LifxXK59WMklW3U0jS+4G9gL9T3iQ7H0x912NqW50v+C7mfqPte5cHza4j9kngBtsHdo41iPVLSk2i8+kaYrI9XuX1ecX6HGWe2SBlKroLMC/NnDWUNgLOsf3yBm1r7fFvi6QD6pyw08a52P18CEt6k+1faD5ZQaeytdjzKc+Lcz3YyspXU6Z4rEVJzF4E7Gj79Baa2phK+ZIbKKtR16esrjynydwplQU532Hu1/n5E95o8ngLUKaQvIbyPnsS8D03/MCrvTbPpPw/r3KfRdu74pxre0PNWWPxIttN5+gtRukFfU09dBLwGdv/nPhWw1d7Cb9AWV0syly9PWwf3keMpSm945+nFNTuuLvh/GAk/QelF3tlyv7KG1O21Gz6haLV+o/DkkRvCkmaRVl5O3Yv2Kbxlgf+H+UDoPvbSd9PWkm/oxbZZc432ialQs4AfkmZl/dS4GbKxtF9l0pQw1IqE8RqpUyFhlCAuc3HfyaTtH4/PYHziLUPZRP239tutL/nMNQPk09SpgmI0pO9t+2JtszrJeYTmL0H6dluuHJc0vcZv0e1yRe6xSh7wF5a58CtCKzjBvUyJZ1ve5BJ8UMj6fWUHrg/UR7/NSgrlU9sEOtESu/PT+qX4a2AnW2/rmHb2nw9tbqCtD4fOqXDzhnky05bVBZ2bEh5DT23DgF/zvZceyP3GO/RxTC219Dg9R+HIoneFKo9I692C7sV1Hi/ouwM8BHKkMwOwC22/1+DWI2/VY4T68mUOR/n2j5LpTD0y20f0iDWAcA3bF/aRtvaVHtof2j79hZitfb4t03S9uMdb/j/PI2y7+5RlFp8fxygXTtRSr68gDLMdBZlV5GfN43ZBpUdBl7Y+UJXk7Tf2X7GADFXomyP193b2/dwk0oR245Ht+1rMt+sTZI+RflCeAyD9baPt0LzUU1GTyRdCbzR9qx6/mmUnWH6midWb/tUyuT/F1L2lP0zsO3YOWh9xGvz9XQe46wgbTKnTdKpHrPbx3jHplpXj+pFlE6XByRdZvvZ87zx+PFarf84LEn0ppCkAyl1mI5nzjezptsPnW/7eerajqfzRG4Q6zOUD6NBNnhunaTLKStb/0x5zBoPd7eZsNR4n6G8MV5A2U/2pAGGhmbk4w90Cqp2LEpZTHSB7a0axnsypQDz2yjblh3hPnY6mSTeR4Bl3W59wybt+R3li82D9fwilA+CvrfGq7cfdw/SNnoNNMC2fW1qsbd9tckub5JQjX1PVZkofE7D99nHUaotrE4p/XJXaVazbeNqzFZeT5LOs73BmM+TfreLW5SyW8ppwMuZvWJ/KeCXTZLjNqmUs9kJ+CAlQbudMnf79Q3jnW174zFD8Y22xxumJHpTSGVz7LnY/nTDeJ0n2UmU0i1/A46y/bQGsTqVzB8A/kWDie0awiT5id64G75ht5qw1JiizI/ZifIt+EjgQNt/6jPOwI//VFGpnXa47U0HjLMO8DHgbbb7Lhwu6XuUaQt/p/Tm/Yby/2ylx7wpSYcA6wA/p7wONqdMar8E+v9ipxb3IB0n9jMovVNPbzv2dJL0xbEjG+Md6zHW/pTe1CMp/8+3An8FTgGw3fNCDZU5x3dQvhwONOd4nNiDvp7OBF5FWTx3E2WBxo7uY86lSt29DwJPoczf7JRiuhs4wPa3+m3XsNQpOEtTEtCmcy4PpOwksjtlEcYHKInje1praAuS6E0hSeu0OQSpsgvGWZSVPt+gfGv6tO2x1c0f01TKN6xp+/t1XuIStsfrAeg3blsJy7qURG9TyjfZjYGTbX9s0DbORCplOf7YZChS0rMoPQ9bUbaAOoKyNdXNDWIdQ/lAuZwyV+9Ml3I802qiL3Qd/X6xq/O63mr7noEaxqNfKFrZtq9NQ+htn2vxV9OeljqvcSLuZ35jm3OOa7zO6+ktwK0M9npajfKlaRHKCtKlgW93hqz7jPVJ4Gu275K0J2Wxzj5tzSdsqs5DvMy1TJfKvufPsv2HhvG6F8N0Fv3s42leDDNWEr0pJOks4HHAD4AfuYUCnIPSBKUzOmbAC3MvSk/ZM2z/m8pWSz+x/aIWYi9MedE3qmRev71uT0lYvkfZju5fdTjs6l56Vmf64w+g2auMoUzQfhZwpO3dJ77VhLF+T9kV4ye2/9ZS+55F2e3hv4AFba/cRtzppiHsQTpTtdXbrq6SHJTFEx1LAr+13XexdY2zC4ykNZp82Wx7znF9PR1BeT0O/HpSSytINXv/4xdTCl/vS9k6ru8ai21SKWa8fmeKTX2vPm/sl4JRkzp6U8j2S1TqRe1E2SbsHOAHbrBKDUDSfuMcvpPyxO11QnpnyGBRSkJ1MeWbyXMoeyBOd1HaNwPrUYY6sP03lS3V+qY5i4EuQBn2O3KAti0HbDl2GNn2I+p9z+HJhmxMmUcy3fbt+v0h4C+2r28SyPYLOh8mgzaqPsYvoazsXoayyvWsQeMO0J7uhHguDebUTbYHaV9m+hcK2+/vPt/pbW8Q6sfAibRYkoOy1dvrbN9V2/YsyoKFJj1zLwZ2rHMSB5pzTLlha+/PamE7tS6t1VhsmbrnUdf36r7zoCG81ocqid4Us/1/kj5BeRPfD1ivzvP6eD9zPapFKbWdflLPv4WyaGFdSZvY/mAP7dkEQNLRlG86l9bza1O215luD9q2pM43sMUHiPVkZm+l8xBlns37+g2i2XvAfn3MeaCsFHSPO550Hv+ZzPYZmrMuXF/zD7u1/GGyJWWo5Oud3oy6cGG6dBLiLSnPtR/W89tQhsT64tl7kC4O/NNliyVU9l99XJ/hur9QzDV/lpnxhaLbvUDfO7PUUZI7gW3qlIqX1IvOovkuD5+jJHtvoCymO4Tmxc0blVEZSxPvAztI4vgp5t5OrenuODdI+i6lxuIXVRahzIQdI66R9AFKUX8ovb9NpnvsO++rzBwZup1CKtvd7ET5lnMyZdL+BXU48ve2J10xNk68s4EXdX0ALER5Q3sxpbbVWn3EmmuJ+XjHppqkj1CqmL+a8i3934Ef2/7GpDccP1Yr83bqt/HufV+72X2uFOyKuzZz10RsNEepTWqxLpxaLEfQ5jysNqmuXpzXsT7inQ28qjNHT9ISwK/cYKVs7U19L+U9wpT3i/2ne05Rm9MDarwPALswe0eLN1MWA/T9vlHjbUFZ6LAk8Bbb/9ckTlskrWj7RrW7WK21FaRqscZim1T2O96P8h5kynSIDzaZ0/hYkh69qfUN4EBK7939nYN1OPITDeItCyxB+QYLZdXmci4bcfe7Qu8SlVWMnV6IbZnmDeIBbO+rsjPAXZRv05+0fXI/MdTyVjoewh6wdS7iyymJ3gmUb/6/ofQeTLePAut5TF04SkmZfo23N2df3zbb/n8OweKSntpZGFJ7RQbpiV60eyGG7XvqB2kTB1NeS51pH++gPMe2HqB9bWhtekD1H5Q6affCoz29v6e8B/dEc2/XtzSlN/t9kqZ1jqTrPrBNErpJtLGdWqd999G1bVxt740T32Jq1ITu7W3FG6dHFer0KcoOJa1sjjCoJHpTyPaEOyrYPrRByC9RNlM+HR7dZuZzdajnlD5j7QT8J2V7GIAzmd29PW0kfYhSF6qv5G6MVuftqMU9YLtsRZlwf6HtnSStwOyke7rdSimP0HF3PdZEGx8mw5iH1ab/Ak6XdA3ldbkapXepqXvVtQOCpA0o2401sfaYnv7TVGpVTqs6PWAFZu+kcPWAIUVX+RJmbznZj/PGnG+0HdswaO4SVo9eRPOyTO+nrCB9ADiMsrvRTJhXNzC1uNf2GCdSnls/ruffTqkjeBNl0eWbGsZtVYZup8AEWT8MOBG3xl6RMq8Cyk4UraxknClqT9fWlPk1R1BWa/Y936nlNrW2B2xXzHNsP78ObW5CSaau8DQWGK1JNsBzGacunO0dG8R8TJQjaKqu4tuYkhR0/ndXeoAaeDWxO4JSJxNgRUqttL4TD0k/BL5p++x6fiNgV9vjljeZKpK2Br5MmR8myty6j9o+qmG8D1F2CjqmHtqCsvDtaw1ijTtHsvZajQxJT3Of9T8fKzSkvbYnmD7S2et9xuyQkURvCkw0j6JjkO53SctS5rB1z+tqsjXSiyiTccdus9Rovlnb6vzGTr2o622/apqb1CpJ3wY+TvlG+GHgHuAi2ztNY5tarQc3v1Cfuwn0EO+tlIR4VcpCj42APfvpOe76srkwZQrEX+v51SiJaM/zeYdB0sWU7SFvrueXB05xH8V6x4m5PmUuIsBZti9sGKe1OZIzmcoe5SsD5zJ7O8EZt/XkTFKft++yfU49vyHwPdvrtv0+MIgkeo9hdZL8bpQX50WUnoTfN+xRupIy5HQ+c1ZsnxFzDFRWfb6VkggtOd0T7juGsYBC0urAUranfY5kW/QYK0cwCEn7UuaDHe0W3mDVQk2yYX7ZbMPY3o/aM3px0x4RldJTh9tuNMdsTKy59qEe79goUNmub0PKfOF3U4rTLzfpjR4DhvX+UxO7gyhz5UWZ/7ozpYj7G2wPUr6rNZmjNwU0hK3Bqt0oL7t4Ib8AABfLSURBVMqzbW8i6ZmUUgBN3Gn7xIa3HRpJ76UM3S5PKSPzLtvTPqcI2l1AIenNwK9t32n7WknLSNrC9s/abHMTKrUfP0LZn7O7t7efLxSPqXIEA3o38CHgYUn3M/jrfOCaZNOdyPXgRJWtHA+r599GeU01dT7wCZUt3o6hJH1j59z1auwcyefRfI7kjFW/SLyknpYBjmMa61K2rNXSRx22zwXWkbR0Pd+9CcKMSPIgPXqPaaqbbUu6iLLC7AE1LIki6QuUsgZHM2f1/eneGePzlMUYF01nO8ZTh8M6CyjW7SygsP3qBrHG6zWYEV3/dXjiO8zd2ztjJqePMknHUfYNfTVlK6n7gXMGGdacaVTKoVxHV90728dMcpNe4y5Hme7xdsqOD2s2iLEhpXjz3yhJ+5NpOEdyJpP0EOU1/nngBDfc/3UmU/ulj5YG9qIshISyFePengG7XnVLj940UKnl0z3U99eGoa5XqSD/M+BkSbcDTb+5d4aBup/w01ZIVdJSLpXov1zPz1WUeDraNcb9LpXVH1LZM/Fmyr7DTYxXTHSmvD4fst3KCuzHSjmCQUnajNlv/qfbPm6AcFtTapLta/uOugDro/O4zWPNkygrsC+gDIWd1FLcp1MWxawG9FTEfCzb59bRks7ezlfZ/ldL7ZtJngi8iPK8/YCkRyhTgfac3ma1qu3SRwcBf2R2eaLtgO9Teg5njPToTaH65v8VykbsN1PffJr0wI0T+2WUOk+/HIVvYpKOs/1GzS5O3F0awTNhkUibCygkHQTcAXyrHtqVUhNxx3Za25ykT1Ger8cwZ29vk9I0X2LicgQvtj0jyhEMovaObwj8qB7ahrIt4R7T16qZT5Ioq7F3onzhPJJSVL7vlaD1efZmSt27I4BjbN8xQNtmZDHztqls7/YySs/qC4G/epKyYI81kjYFDqDshtEpffRu242+WDxW5m8m0ZtCdQjsFZTVZOtJ2gR4p+2dB4i5LKUXqXvuVKPhVpUtfp7NnG9mezdt2/xk0AUUtYTDnvD/27v3YLvK8o7j318iJSg3lYtQ5BYcaLiECQSlMh2EsSADMgKBQrTKpQgqQh0RHXUQqDpFwaLU4g2EDHhBQLAoghghAeQS7qBoR9RpFSlIuKcG+fWP5905J8dzQs7aa++19j7PZ+ZMstbhvHmY7Oz9rvd93uehc5r4OmKF69laAuxCmWyPVWmyPSjlCLqhKOK8s+0Xy/V0Ynu/FQeI2kzRtuxIYgVzIXHA7DrbH5rkOO8GLrP9WA0xjZuLa/uQbsduE0Xdx58TecY3EukBA79oMJaiHVtdpY9uIcoALS7XbyRW3pvuEb+StmwNTRXLbT8uaZqkabYXSpp0XacOSWcA7yKeTl4styttt0o6j1hZeRPwVaKA721VY6uLpOtt7/1S9/oc04QN4kcnbU9GmdB9WNI6cTnSCaFprrcTyHRJu40pRzC9fO+FGv+cpq3PSG/V9ZoMZBBIOhH4R+Ax4v3nZNvLy+nbXxLtx1ZnnO1s/5woEbK5pM1Hf7/iQ3Cbi5nXaZvOw8mQ24WRg2WzFV1Oqq7OHg9cWHL1RPybH7dWX5NyotdfSxU1mG4ELpb0KNG8u6pDgZk1PXX9bSnhcK/t0ySdRVT9boSkGcTEc4OyatnZul0X+Oum4io6DeJnEFtM9xDx7UTkmk36aU7SjsRp3VeV68eAd9q+v46AuyFpDeINbUXOGfClinlKxwDnl38HK8oRlBXNT9cQbht8CrhTK3esqdSzdQp5FXDQ2NPBJQd2/0mM8wGiC8lZjFPhgGo5x3Xm4rbZV7Vya0IAbB/VQCw9IWkBMJMoR9Y5WGYqtposhwRnl9cFJa+8dXKi118HAsuIenXziSf9brZG7ydWDupoyNwpF/CcpE2JFleb1DBuVe8GTiLyGZcwMtF7Cji3qaAAbL8JQNLlwByXoqIlj+cTFYf9EvAB2wvLWHsSuSRtKMr6H0Sh3S+W63eUe8dMdqBBKUfQpf2JJO0ngF8Dp9h+pNGIWs72hMW5ba/2IQrbnVZz+xH9kPcgPsgXUb2l4x3l0NtXiPeiZ4g6icPm6lG/n0HkOA5VpyXiwXyWa8pZG3vqVlF0unWnbjNHb4ApWiNdSUz4RifJT7r4o6SPEw2/9yYOBJio8N3oiStJJ9he7Ubk/TReKZsuytvcM7Zcxnj3mlBnbINSjqAbJfe2U49sJnAX0WXgnEYDm0IkfZt4KOwciDkCWM/2oRP/1GqNuyVDVsx8ImXbfLGHqAOIpEuB99v+fU3jXUZ8/nZaqL0DmG07T91ONZq4ATUArlhIVdIDxErQfYzk6GH7hirjjRp3TWBGWz5823riTdI3iK33Tr7OfKKS/OEVxrqCKC2xoNx6O7CL7bfVEWs3JN0JzOucfpS0NfCdsYcqVnOsgXhj7FY5gDGXyHk9jtj+a6xv8VQj6UGPaes23r2XGGOVr++qh94GhaLY9NW2t2k6lroo+pPvTOSfd7U4UsYbiFO3uXXbB7bXgRWHJ35PfJiLmBh0sz36nO3PdxObpAk/YEuS6uXdjN+tiU68UTGnomZHErlrJ5brG6m+PXQUcBpRsBpiq6ktuTEnAwvLqbxOSYKqPXhn2j541PVpioLfQ0PS9URtrluIv8e5Lj1cU9/cKekNtn8KIOn1RP7sZJw16vd15fu11jgLEo8ApzQUTq98oubxnpe0x5hTt63rmpIren1U9/acpLOJp5KrqNjNQtIFq/i2m07EVY3dJ+pWDhAss/3ncj0dWNP2c81GVr+yyju6YGylkgSDUo6gG5I+R5zs+z/gJuIB4BbbrfsAGFaSfka8XjvF6DcHHiJOdnsypW4krcU4+X62l9UadOo5SScQnx9P1DTebGLRoXOy/gniEF2rtvZzRa+/npU0n2inY6KQajenbjvtsd4w6t6knjRdobhvn7X5xNv1RN27TimUtYBrmcQBCvWo2XYdJO1l+8fjrPpu08Vq70CUI+iG7X8GKKVy3kVUyn8NsGaDYU01+9Y41oVEvl9n9+QI4sO9q3y/ttGoftvlen1gT7eg33aNNgZuL+ko5wM/7PJgxt7E62Ptcv0MMLeUT2vNTkWu6PVRSeQ9h2gzA7EFeZLtXzcU0gqljtUFwNPE6bI5wIdtX9twXLV1n6hbHfkZio4mE+o237Ibkk6zfeoEq75drfa2vRxBNyS9jziIsQtx6nYR0bv1x03GlaqpI99vEEzwftaKftt1kmrtwHJJGeMq4sF1f+Beok7fpbbPrCnsruSKXh+VCd2BdY1XtjE/BWxq+y2SZgG72/5aheGOsn2OpH2AVxNJ8guIFarG2H5P+e15kq6hXSfentWoAsnlFPSktuc6E7myDfy8V+6m0OgK0KiSF6fbXqk7hqJH5KQNSjmCLs0AzgaW2B6mItBTVR35foOgzf22a2Pbkh4hchBfAF4JfEfSpDuwAJsRJbaegRU55VcT729LgFZM9Mb7i009ImkzSVdIerR8XSZpsy6G/DrR/HvTcv0LovZcpfDKr/sBF9l+YNS9vpM0Z+wXUVT1ZS91Gq6PTgIulbRI0iJiS/59Fce6nigQ3bEW8KMu46vLZePc+07Fsc4nVo0PLV9PESvJQ8P2Z23fmpO8wSbpPkU7u12AmyX9WtEO8BZiFWfY3CHpbEkzy9fZxGRlaEg6UVJnAnYTsKPt44m/44NX+cPj24hR+fHAcmDjko9bubVa3YZutt5yFxDN3OeV67eXe1UPFmxg+9uSPgJg+wVJf36pH5rAEknXAlsBHyn5RU22wzlrFd9ry4m3+4DzgH2ICctVwAMVx5rhUW3PbD8j6eWr+oFek7Qd0ft4vTF5eusyqtTNJA39qds0NCbTkWMYnED02/4W8R57HfDeRiOqX10dWDouBm6VdGW5PgC4pOzQPNhdqPXJiV5/bWh79OrF1yVVXYGD2Dp8NSWZX9IbgKpbYEcT9YV+Zfu5Mm5jeXAu3Sda7iJigvfJcn0Esd09b8KfmFjX28A9sC3xYbc+8QbW8TTwTxXHHIhyBCmNnQwMO5d+203H0WMXEVu2ne5DOxE7WEs9iQ4sHbbPkPQDRvLuj7Pd2dafX0O8tcjDGH1U6mtdAHyj3DocONL23hXHm0N0s9iBKEK7IXDIZHLYVJqAT7Qd2nRR0LKq9QFgc9vHSnodsK3t/2wyLqg3SVvSXGLrt9NyaBPgMNuNb51I2t12LS2fBqUcQUpTjaTriMLoS8v1K4Fv2t6n2cjqU3YPdiUOS3yf6Cy1ve39moyr13JFr7+OIiZmnyNW4W6mi1Uz23eWU5vbEvl0D3nyjeZHNwH/iz+C5rdILyDyRDolS/4HuBRofKJHvUnadW4D1+0uSe8ltnFHdyepcup2IMoRpDQFbdCZ5AHYfkLSRk0G1AMvlhSntwFfsP0FSXc1HVSv5USvj8pWQN110XYjnk5eBswp9c1Wu2tEWSWbBnzM9k01x1aHmbYPk3Q4QNlWbuyQCKwo4mxgDSJJ+7flegvg5xWHrXMbuG4LiP+vfYDTiS2JSW9zFLuycjmC+UQ5guMktaYcQUpT0IuSNrf9W1hRDmzYtvyWl8+SdzKSjrJGg/H0RU70+qiUpDiBkYkZ0FWfvQVE0/S7gc4hDDPJ9mAlEfVcRgowt8mfFJXpO3mIM2n+NFMvkrR3GLPlu1BSW5J5t7E9T9KBti8staMWVRxrIMoRpDQFfRRYXEoeiagFeWyzIdXuSKL39CdtP1w+kxe8xM8MvJzo9dd3ga8B36OeE627ArO6rOzdcb2kg4HLaxqva2Xl7jzgGuC1ki4mkl7f1WRcPUrSbnOtrk46wFJJOxDJzFW3dCYsRyCp6Ql8SlOW7WvKIbBjgbuIz6uhOihl+0FJpxAt8Sj1Qf+12ah6Lyd6/bXM9udf+j9bbfcTrZV+X8NY7yby9f4s6XlK427b69YwdiWlsOXJwJ5EmzcBJ9p+rKmY6tajbeC6fbkkZn+M2HJdmyjDUMVAlCNIaaqRdAxwIrHqfjfxnnsLzedp10bSAcBngb8CtpK0M1GwvbFWk/2Qp277SNIRwOuIbhMrVi+qnmyVtJAoiXLbmPGG5kUr6ULgXNu3Nx1LL0jaYlXfb0OJB0lrEsVEt2Qkn8W2T6843q6MlCO4aVQ5gpRSQ8pD51zgp7Z3LnU0P2V7bK/rgVWKJe8F/KTT2k3S/bZ3aDay3soVvf7akWgtthcjW7fdnGz9RA0xASu2SecDW5XaQK8FNrF9W11/RkWvB+ZL+g3wLCMrjTs1G1Y92jCRWw1XEvUZl1BDfmSZ2OXkLqV2WWZ7mSQkrVnKbm3bdFA1W277yTHn+ZpsDNAXOdHrr3nA1rb/VMdgtm9Q9LudW27dZvvRisN9kXjB7wWcQZS9+PdRYzdlaGo4DbDNbO/bdBAppZ76b0nrE7l510l6AhiEB9HJeKDsrE0vNVnfT5Q5G2q5ddtHkr4LHNvFZGzseIcCnwF+wsgpqZNtT7oPqaQ7bc+RdNeoJe17bM+uI9Y0uCR9mag5dV/TsaSUeq/UZ10PuKauhYk2KAX4Pwr8PfGZ+UPgDNvLGg2sx3Ki10eSfkK0XLmdGnLqJN0DvLkzcZS0IfCjKpMzSbcSRYlvLxO+DYFrO5O+NHWVMi/bAA8Tr9uh2j5PKaVhllu3/XVqzeNNG7M6+DgwreJYnweuADaS9EngEOKUZUpvaTqAlFKqStL3WEXx52E6wDieXNEbYJI+Q6wQdnrnHgbca/uUiuNtR7SoEnB9lSbPKaWUUpuUregJ2b6hX7E0ISd6fSBpse09JD3Nyk8VXdeqK0WOO6UqFtm+ouI4ZwA3AjfbfrZqPCmllFJqj5zoJQAkHUkc5tgdeJpocXWj7StX+YMppZTSABhVoH60J4lyT/9i+/H+R9V7OdFrgKSNgBmd604T6QrjHES0b9mIWB2sY4XwNcChwAeBV9pep+pYKaWUUltIOpPoC39JufUPwMuJto572D6gqdh6KSd6fSTprcBZwKbAo0Sbq5/Z3r7ieP8FHFBHLp2krwKzgD8Qq3mLgTttv9Dt2CmllFLTOmXExrsn6T7bOzYVWy9VPaGZqjmD6B/4C9tbEQcfftrFeH+o8cDEq4HpwFLgj8BjOclLKaU0RKZL2q1zIWku8bkHMLSfd1lepb+W235c0jRJ02wvlPRvkx2kbNkC3CHpW0Ql89F1+S6f7Ji231bG/huiG8VCSdNtbzbZsVJKKaUWOgY4X9LaRKrTU8DRkl4BfLrRyHooJ3r9tbS8wG4ELpb0KNG/dbJG5xE8R1T57jAw6YmepP2Jwxh/B6wP/JjYwk0ppZQGnu3bgR0lrVeunxz17W83E1XvZY5eH5WnhmXEk8R8osXMxW046SPpXGJit8j275qOJ6WUUqpTmeCdSixoANwAnD5mwjd0cqI3wCRtDZxD5P0ZuAU4yfbDFcfbGJhbLm+rqydvSiml1DRJlwH3AxeWW+8AZts+aOKfGnx5GKOPJB0k6ZeSnpT0lKSnJT3VxZCXEMvNmxAneS8FvlkxtnnAbcA8orzKrZIO6SK2lFJKqU1m2j7V9q/K12nA1k0H1Wu5otdHdZZDKePdO7axvKR7bM+uMNY9wJs7q3iSNgR+VGWslFJKqW0k3QKcbHtxuX4j8FnbuzcbWW/lYYz+qrMcCsAPJH2YWMUz0ev2+5JeBWD7j5MYa9qYrdrHyRXflFJKw+N44MKSqyeilNg7mw2p93JFr48knQO8hhrKoZTxVpWLZ9urvSQt6TPATsA3yq3DgHttn1IltpRSSqmNJK0LYLub1KmBkRO9PpJ0wTi3bfuovgczDkkHA28sl4tsX9FkPCmllFJd8tRtGkiSdiBal43unXtRcxGllFJK7TNVT93mRK+PJM0Ajga2Z+WJWaUVPUmnAnsSE73vA28BFtte7dOykp4m8vv+4lsRmtetEltKKaXUJpLutr3zS90bNpls318LiBy9fYgl482Ap7sY7xCiX+4jto8EZhNFmFeb7XVsrzvO1zo5yUsppTREnpe0R+einLp9vsF4+iJP3fbXNrbnSTrQ9oWSLqG7NmPP235R0gslufRR4LX1hJpSSikNleOAizot0IAnmAKnbnOi11/Ly69LS27dI8BGXYx3h6T1ga8AS4BniO4YKaWUUlrZ3kR+3trl+hlgrqRptu9uLqzeyhy9PpJ0DHAZsCPwdeLF9nHbX6ph7C2BdW3f2+1YKaWU0rApu2i7AlcReej7A/cCWwKX2j6zueh6Jyd6fSRpTeBg4kW1Rrlt26dXHG/OOLefBH5j+4VKQaaUUkpDSNKNwH62nynXawNXA/sCS2zPajK+Xsmt2/66kpiILWFUweQufBGYQzyRCNgBeABYT9Lxtq+t4c9IKaWUhsFGrPzZuxzY2Pbzkur4TG6lnOj112a2961xvN8BR9t+AEDSLOB04EPA5UBO9FJKKaVwMXCrpCvL9QHAJZJeATzYXFi9lVu3fSTpy8AXbN9X03j3295hvHtToTZQSimlNBmSdmWkA9RNtu9oMp5+yIleH0l6ENgGeJhYPu4UJd6p4njfIpoyf7PcOgzYgKj2vdj23K6DTimllNLAyoleH0naYrz7tn9Tcby1gPcAnQKQNxF5e8uAl3cSTlNKKaU0NeVEb8CVyd7mth9qOpaUUkoptUu2QBtgkt4K3A1cU653lnRVs1GllFJKqS1yojfYTgV2A5YClMreWzUaUUoppZRaIyd6g2257SfH3Mu9+JRSSikBWUdv0D0g6QhguqTXAe8Hbm44ppRSSim1RK7oDbYTgO2JUi2XEF03Tmw0opRSSim1Rk70Btus8vUyYAZwIHB7oxGllFJKqTWyvMoAk/QQ8EHgfuDFzv2qdflSSimlNFwyR2+w/a/t7zUdREoppZTaKVf0BpikvYHDgeuJPD0AbF/eWFAppZRSao1c0RtsRwLbAWswsnVrICd6KaWUUsoVvUEm6SHb2zYdR0oppZTaKU/dDrabJc1qOoiUUkoptVOu6A0wST8DZgIPEzl6Amx7p0YDSymllFIr5ERvgEnaYrz7WV4lpZRSSpATvZRSSimloZU5eimllFJKQyoneimllFJKQyoneimllFJKQyoneimllFJKQ+r/AWBYHU4n360YAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "EXCLUDE = ['google', 'facebook', 'youtube', 'apple']\n", - "plt.rcParams['figure.figsize'] = [10, 4] # set the figure size for the graph\n", + "plt.rcParams['figure.figsize'] = [10, 4] # Set the figure size for the graph.\n", "\n", - "# Get a list of the top words in the collection\n", - "# (regardless of year).\n", + "# Get a list of the top words in the collection (regardless of year).\n", "\n", - "domains = get_domains('name').most_common(30) # Can choose 'sub' for subdomains\n", + "domains = get_domains('name').most_common(30) # Can choose 'sub' for subdomains.\n", "\n", "vals = [x[1] for x in domains if x[0] not in EXCLUDE]\n", "labs = [x[0] for x in domains if x[0] not in EXCLUDE]\n", "\n", - "ind = np.arange(len(vals)) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(len(vals)) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, vals, width)\n", "\n", @@ -413,9 +409,9 @@ "metadata": {}, "outputs": [], "source": [ - "method = \"year\" # choose \"year\", \"domain\" or \"all\"\n", + "method = \"year\" # Choose \"year\", \"domain\" or \"all\".\n", "\n", - "# Get the set of available years in the collection \n", + "# Get the set of available years in the collection.\n", "year_range = set([x[0] for x in get_text(method)])\n", "print(year_range)" ] @@ -433,12 +429,12 @@ "metadata": {}, "outputs": [], "source": [ - "year_filter = FILTERED_YEARS if FILTERED_YEARS else year_range # add or remove years for filter\n", + "year_filter = FILTERED_YEARS if FILTERED_YEARS else year_range # Add or remove years for filter.\n", "year_results = [t[1] for t in get_text(\"year\") if t[0] in list(year_filter)]\n", " \n", "# Some of the text may be in an international font.\n", "for i in year_results[:5]:\n", - " print(international(i)[:MAX_CHARACTERS]) # first 50 characters in output\n", + " print(international(i)[:MAX_CHARACTERS]) # First 50 characters in output.\n", "\n", "## Removing the # on the following line will write the results to OUTPUT_FILENAME (set in User Configuration).\n", "\n", @@ -458,7 +454,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Get the set of available domains in the collection \n", + "# Get the set of available domains in the collection.\n", "domain_set = set([x[0] for x in get_text(\"domain\")])\n", "print(domain_set)" ] @@ -469,7 +465,7 @@ "metadata": {}, "outputs": [], "source": [ - "# extract only the given domain to a file and see how many results there are\n", + "# Extract only the given domain to a file and see how many results there are.\n", "\n", "domain_set = FILTERED_DOMAINS if FILTERED_DOMAINS else domain_set\n", "domain_results = [t[1] for t in get_text(\"domain\") if t[0] in domain_set]\n", @@ -489,30 +485,18 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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HHjNfMa7tXaBUn/eVstvBtbsOm+NWjZWeURERMSk00tX6c5dh4tQJgr8Yz7jTQf2AA6uP3/RVf5+SUdRJifca3uWpFOAz0lauZ73auAT8xk7IiIiYqj1Mqv0dV33H6Us47HTRE+S9FPK5IInS7qJMjv0YOAYSe8Crgd2qaefTFkKZCZlOZA9AWzfJekzwAX1vAM7ExUiIiIiJptexrjtOT8Xtr3bGA+9cpRzDew9xnUOBw6fnzpERERELEzGTNwk/c84z7Ptz7RQn4iIiIgYw3gtbg+OUrYs8C7gSUASt4iIiIg+GjNxs/2Vzn1JywP7UMaeHQV8ZaznRUREREQ7xh3jVrec+i9gd8pOB5tmu6mIiIiIwRhvjNuXgJ0pi9o+z/YDfatVRERERMxlvAV4Pww8DfgU8DdJ99Xb/ZLu60/1IiIiIqJjvDFu87SrQkRERES0K8lZRERExJBI4hYRERExJJK4RURERAyJJG4RERERQyKJW0RERMSQSOIWERERMSSSuEVEREQMiSRuEREREUMiiVtERETEkEjiFhERETEkkrhFREREDIkkbhERERFDIolbRERExJBI4hYRERExJJK4RURERAyJgSRukq6TdLmkSyRdWMtWkXSapL/UnyvXckn6hqSZki6TtOkg6hwRERExaINscdvK9sa2p9bjfYEzbK8PnFGPAbYD1q+3vYBD+17TiIiIiAXAgtRVuhMwrd6fBry+q/wIF38AVpK02iAqGBERETFIg0rcDJwqaYakvWrZqrZn1fu3AKvW+6sDN3Y996ZaNgdJe0m6UNKFt99+e1v1joiIiBiYxQYUd0vbN0t6CnCapKu7H7RtSZ6XC9o+DDgMYOrUqfP03IiIiIhhMJAWN9s315+3AScALwJu7XSB1p+31dNvBtbsevoatSwiIiJiUul74iZpWUnLd+4Drwb+BEwH9qin7QH8ot6fDry9zi7dHLi3q0s1IiIiYtIYRFfpqsAJkjrxf2L715IuAI6R9C7gemCXev7JwGuBmcBDwJ79r3JERETE4PU9cbN9DfD8UcrvBF45SrmBvftQtYiIiIgF2oK0HEhEREREjCOJW0RERMSQSOIWERERMSSSuEVEREQMiSRuEREREUMiiVtERETEkEjiFhERETEkkrhFREREDIkkbhERERFDIolbRERExJBI4hYRERExJJK4RURE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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "# Get a list of the top words in the collection\n", - "# (regardless of year).\n", + "# Get a list of the top words in the collection (regardless of year).\n", "tokens = get_top_tokens()[:20]\n", "\n", "vals = [x[1] for x in tokens if x[0] not in STOP_WORDS]\n", "labs = [x[0] for x in tokens if x[0] not in STOP_WORDS]\n", "\n", - "ind = np.arange(len(vals)) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(len(vals)) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, vals, width)\n", "\n", @@ -576,10 +560,10 @@ "metadata": {}, "outputs": [], "source": [ - "# Create a dispersion plot, showing where the list of words appear\n", - "# in the text.\n", + "# Create a dispersion plot, showing where the list of words appear in the text.\n", + "\n", "text = get_text_tokens(1) # Need to have one to include words with fewer than 3 letters.\n", - "dp(text, [\"he\", \"she\"]) # uses the nltk dispersion plot library (dp)." + "dp(text, [\"he\", \"she\"]) # Uses the nltk dispersion plot library (dp)." ] }, { @@ -597,8 +581,8 @@ "neu = [x[3][1] for x in sent]\n", "labs = [x[0] for x in sent]\n", "\n", - "ind = np.arange(N) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(N) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, neg, width)\n", "p2 = plt.bar(ind, neu, width,\n", @@ -610,7 +594,7 @@ "plt.xticks(ind, labs, rotation='vertical')\n", "plt.legend((p1[0], p2[0], p3[0]), ('Negative', 'Neutral', 'Positive'))\n", "\n", - "plt.show()\n" + "plt.show()" ] }, { @@ -628,8 +612,8 @@ "neu = [x[3][1] for x in sent]\n", "labs = sorted([x[0] for x in sent])\n", "\n", - "ind = np.arange(N) # the x locations for the groups\n", - "width = 0.35 # the width of the bars: can also be len(x) sequence\n", + "ind = np.arange(N) # The x locations for the groups.\n", + "width = 0.35 # The width of the bars: can also be len(x) sequence.\n", "\n", "p1 = plt.bar(ind, neg, width)\n", "p2 = plt.bar(ind, neu, width,\n", @@ -661,14 +645,12 @@ "metadata": {}, "outputs": [], "source": [ - "import networkx as nx\n", - "\n", - "plt.rcParams['figure.figsize'] = [10, 4] # set the figure size for the graph\n", + "plt.rcParams['figure.figsize'] = [10, 4] # Set the figure size for the graph.\n", "\n", "NETWORK_EXCLUDE = [\"google.com\"]\n", - "graph = nx.read_gexf(auk_gephi) #import the graph\n", + "graph = nx.read_gexf(auk_gephi) # Import the graph.\n", "\n", - "# Degree distribution for the graph\n", + "# Degree distribution for the graph.\n", "\n", "g_nodes = zip([x[1] for x in graph.nodes('label')], [x[1] for x in graph.nodes('Degree')])\n", "\n", @@ -686,7 +668,7 @@ "plt.title('Top domains by Degree.')\n", "plt.xticks(ind, labs, rotation='vertical')\n", "\n", - "plt.show()\n" + "plt.show()" ] }, { @@ -701,19 +683,19 @@ "rgbs = zip([x[1]/255 for x in graph.nodes('r')], [x[1]/255 for x in graph.nodes('g')], [x[1]/255 for x in graph.nodes('b')])\n", "colormap = [np.array(x) for x in rgbs]\n", "\n", - "# Labels\n", + "# Labels.\n", "mapping = {x[0]: x[1] for x in graph.nodes('label')}\n", "\n", - "# Use Archive Unleashed Clouds Positions (saves on load time)\n", + "# Use Archive Unleashed Clouds positions (saves on load time).\n", "zippos = zip(graph.nodes, [x[1] for x in graph.nodes('x')], [x[1] for x in graph.nodes('y')])\n", "positions = {x[0]: np.array([x[1],x[2]]) for x in zippos}\n", "\n", - "# Node sizes based on degree\n", + "# Node sizes based on degree.\n", "size = np.array([x[1] * 100 for x in graph.nodes('size')])\n", "\n", - "# Draw the graph\n", + "# Draw the graph.\n", "nx.draw(graph, pos=positions, show_labels=True, labels=mapping, font_size=10, node_size=size, node_color=colormap)\n", - "plt.show()\n" + "plt.show()" ] }, { @@ -722,7 +704,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Ego network for a particular node\n", + "# Ego network for a particular node.\n", "\n", "largest_node = sorted(graph.nodes('Degree'), key=lambda s: s[1], reverse=True)[0][0] # [1][0] is second largest, etc\n", "neigh = graph.subgraph(graph.neighbors(largest_node))\n", @@ -730,14 +712,14 @@ "rgbs = zip([x[1]/255 for x in neigh.nodes('r')], [x[1]/255 for x in neigh.nodes('g')], [x[1]/255 for x in neigh.nodes('b')])\n", "colormap = [np.array(x) for x in rgbs]\n", "\n", - "# Labels\n", + "# Labels.\n", "mapping = {x[0]: x[1] for x in neigh.nodes('label')}\n", "\n", - "# Use Archive Unleashed Clouds Positions (saves on load time)\n", + "# Use Archive Unleashed Clouds positions (saves on load time).\n", "zippos = zip(neigh.nodes, [x[1] for x in neigh.nodes('x')], [x[1] for x in neigh.nodes('y')])\n", "positions = {x[0]: np.array([x[1],x[2]]) for x in zippos}\n", "\n", - "# Node sizes based on degree\n", + "# Node sizes based on degree.\n", "size = np.array([x[1] * 100 for x in neigh.nodes('size')])\n", "\n", "nx.draw(neigh, pos=positions, show_labels=True, labels=mapping, font_size=10, node_size=size, node_color=colormap)\n",