{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Scalable Web Server Log Analytics with Apache Spark\n", "\n", "Apache Spark is an excellent and ideal framework for wrangling, analyzing and modeling on structured and unstructured data - at scale! In this tutorial, we will be focusing on one of the most popular case studies in the industry - log analytics.\n", "\n", "Typically, server logs are a very common data source in enterprises and often contain a gold mine of actionable insights and information. Log data comes from many sources in an enterprise, such as the web, client and compute servers, applications, user-generated content, flat files. They can be used for monitoring servers, improving business and customer intelligence, building recommendation systems, fraud detection, and much more.\n", "\n", "Spark allows you to dump and store your logs in files on disk cheaply, while still providing rich APIs to perform data analysis at scale. This hands-on will show you how to use Apache Spark on real-world production logs from NASA and learn data wrangling and basic yet powerful techniques in exploratory data analysis." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Part 1 - Setting up Dependencies" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "

SparkSession - hive

\n", " \n", "
\n", "

SparkContext

\n", "\n", "

Spark UI

\n", "\n", "
\n", "
Version
\n", "
v2.4.0
\n", "
Master
\n", "
local[*]
\n", "
AppName
\n", "
PySparkShell
\n", "
\n", "
\n", " \n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spark" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sqlContext" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from pyspark.context import SparkContext\n", "from pyspark.sql.context import SQLContext\n", "from pyspark.sql.session import SparkSession\n", " \n", "sc = SparkContext()\n", "sqlContext = SQLContext(sc)\n", "spark = SparkSession(sc)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "import re\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic Regular Expressions" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<_sre.SRE_Match object; span=(0, 25), match=\"I'm searching for a spark\"> 0 25\n", "<_sre.SRE_Match object; span=(25, 36), match=' in PySpark'> 25 36\n" ] } ], "source": [ "m = re.finditer(r'.*?(spark).*?', \"I'm searching for a spark in PySpark\", re.I)\n", "for match in m:\n", " print(match, match.start(), match.end())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case study, we will analyze log datasets from NASA Kennedy Space Center web server in Florida. The full data set is freely available for download [__here__](http://ita.ee.lbl.gov/html/contrib/NASA-HTTP.html).\n", "\n", "These two datasets contain two months' worth of all HTTP requests to the NASA Kennedy Space Center WWW server in Florida. You can head over to the [__website__](http://ita.ee.lbl.gov/html/contrib/NASA-HTTP.html) and download the following files as needed.\n", "\n", "- Jul 01 to Jul 31, ASCII format, 20.7 MB gzip compressed, 205.2 MB uncompressed: [ftp://ita.ee.lbl.gov/traces/NASA_access_log_Jul95.gz](ftp://ita.ee.lbl.gov/traces/NASA_access_log_Jul95.gz)\n", "- Aug 04 to Aug 31, ASCII format, 21.8 MB gzip compressed, 167.8 MB uncompressed: [ftp://ita.ee.lbl.gov/traces/NASA_access_log_Aug95.gz](ftp://ita.ee.lbl.gov/traces/NASA_access_log_Aug95.gz)\n", "\n", "Make sure both the files are in the same directory as this notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Part 2 - Loading and Viewing the NASA Log Dataset\n", "\n", "Given that our data is stored in the following mentioned path, let's load it into a DataFrame. We'll do this in steps. First, we'll use `sqlContext.read.text()` or `spark.read.text()` to read the text file. This will produce a DataFrame with a single string column called `value`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['NASA_access_log_Jul95.gz', 'NASA_access_log_Aug95.gz']" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import glob\n", "\n", "raw_data_files = glob.glob('*.gz')\n", "raw_data_files" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Taking a look at the metadata of our dataframe" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "root\n", " |-- value: string (nullable = true)\n", "\n" ] } ], "source": [ "base_df = spark.read.text(raw_data_files)\n", "base_df.printSchema()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pyspark.sql.dataframe.DataFrame" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(base_df)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also convert a dataframe to an RDD if needed" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pyspark.rdd.RDD" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "base_df_rdd = base_df.rdd\n", "type(base_df_rdd)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Viewing sample data in our dataframe\n", "Looks like it needs to be wrangled and parsed!" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+-----------------------------------------------------------------------------------------------------------------------+\n", "|value |\n", "+-----------------------------------------------------------------------------------------------------------------------+\n", "|199.72.81.55 - - [01/Jul/1995:00:00:01 -0400] \"GET /history/apollo/ HTTP/1.0\" 200 6245 |\n", "|unicomp6.unicomp.net - - [01/Jul/1995:00:00:06 -0400] \"GET /shuttle/countdown/ HTTP/1.0\" 200 3985 |\n", "|199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] \"GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0\" 200 4085 |\n", "|burger.letters.com - - [01/Jul/1995:00:00:11 -0400] \"GET /shuttle/countdown/liftoff.html HTTP/1.0\" 304 0 |\n", "|199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] \"GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0\" 200 4179|\n", "|burger.letters.com - - [01/Jul/1995:00:00:12 -0400] \"GET /images/NASA-logosmall.gif HTTP/1.0\" 304 0 |\n", "|burger.letters.com - - [01/Jul/1995:00:00:12 -0400] \"GET /shuttle/countdown/video/livevideo.gif HTTP/1.0\" 200 0 |\n", "|205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] \"GET /shuttle/countdown/countdown.html HTTP/1.0\" 200 3985 |\n", "|d104.aa.net - - [01/Jul/1995:00:00:13 -0400] \"GET /shuttle/countdown/ HTTP/1.0\" 200 3985 |\n", "|129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] \"GET / HTTP/1.0\" 200 7074 |\n", "+-----------------------------------------------------------------------------------------------------------------------+\n", "only showing top 10 rows\n", "\n" ] } ], "source": [ "base_df.show(10, truncate=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Getting data from an RDD is slightly different. You can see how the data representation is different in the following RDD" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Row(value='199.72.81.55 - - [01/Jul/1995:00:00:01 -0400] \"GET /history/apollo/ HTTP/1.0\" 200 6245'),\n", " Row(value='unicomp6.unicomp.net - - [01/Jul/1995:00:00:06 -0400] \"GET /shuttle/countdown/ HTTP/1.0\" 200 3985'),\n", " Row(value='199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] \"GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0\" 200 4085'),\n", " Row(value='burger.letters.com - - [01/Jul/1995:00:00:11 -0400] \"GET /shuttle/countdown/liftoff.html HTTP/1.0\" 304 0'),\n", " Row(value='199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] \"GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0\" 200 4179'),\n", " Row(value='burger.letters.com - - [01/Jul/1995:00:00:12 -0400] \"GET /images/NASA-logosmall.gif HTTP/1.0\" 304 0'),\n", " Row(value='burger.letters.com - - [01/Jul/1995:00:00:12 -0400] \"GET /shuttle/countdown/video/livevideo.gif HTTP/1.0\" 200 0'),\n", " Row(value='205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] \"GET /shuttle/countdown/countdown.html HTTP/1.0\" 200 3985'),\n", " Row(value='d104.aa.net - - [01/Jul/1995:00:00:13 -0400] \"GET /shuttle/countdown/ HTTP/1.0\" 200 3985'),\n", " Row(value='129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] \"GET / HTTP/1.0\" 200 7074')]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "base_df_rdd.take(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Part 3 - Data Wrangling\n", "\n", "In this section, we will try and clean and parse our log dataset to really extract structured attributes with meaningful information from each log message.\n", "\n", "### Data understanding\n", "If you're familiar with web server logs, you'll recognize that the above displayed data is in [Common Log Format](https://www.w3.org/Daemon/User/Config/Logging.html#common-logfile-format). \n", "\n", "The fields are:\n", "__`remotehost rfc931 authuser [date] \"request\" status bytes`__\n", "\n", "\n", "| field | meaning |\n", "| ------------- | ---------------------------------------------------------------------- |\n", "| _remotehost_ | Remote hostname (or IP number if DNS hostname is not available or if [DNSLookup](https://www.w3.org/Daemon/User/Config/General.html#DNSLookup) is off). |\n", "| _rfc931_ | The remote logname of the user if at all it is present. |\n", "| _authuser_ | The username of the remote user after authentication by the HTTP server. |\n", "| _[date]_ | Date and time of the request. |\n", "| _\"request\"_ | The request, exactly as it came from the browser or client. |\n", "| _status_ | The [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) the server sent back to the client. |\n", "| _bytes_ | The number of bytes (`Content-Length`) transferred to the client. |\n", "\n", "We will need to use some specific techniques to parse, match and extract these attributes from the log data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data Parsing and Extraction with Regular Expressions\n", "\n", "Next, we have to parse it into individual columns. We'll use the special built-in [regexp\\_extract()](http://spark.apache.org/docs/latest/api/python/pyspark.sql.html#pyspark.sql.functions.regexp_extract)\n", "function to do the parsing. This function matches a column against a regular expression with one or more [capture groups](http://regexone.com/lesson/capturing_groups) and allows you to extract one of the matched groups. We'll use one regular expression for each field we wish to extract.\n", "\n", "You must have heard or used a fair bit of regular expressions by now. If you find regular expressions confusing (and they certainly _can_ be), and you want to learn more about them, we recommend checking out the\n", "[RegexOne web site](http://regexone.com/). You might also find [_Regular Expressions Cookbook_](http://shop.oreilly.com/product/0636920023630.do), by Goyvaerts and Levithan, to be useful as a reference." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Let's take a look at our dataset dimensions" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3461613, 1)\n" ] } ], "source": [ "print((base_df.count(), len(base_df.columns)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's extract and take a look at some sample log messages" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['199.72.81.55 - - [01/Jul/1995:00:00:01 -0400] \"GET /history/apollo/ HTTP/1.0\" 200 6245',\n", " 'unicomp6.unicomp.net - - [01/Jul/1995:00:00:06 -0400] \"GET /shuttle/countdown/ HTTP/1.0\" 200 3985',\n", " '199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] \"GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0\" 200 4085',\n", " 'burger.letters.com - - [01/Jul/1995:00:00:11 -0400] \"GET /shuttle/countdown/liftoff.html HTTP/1.0\" 304 0',\n", " '199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] \"GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0\" 200 4179',\n", " 'burger.letters.com - - [01/Jul/1995:00:00:12 -0400] \"GET /images/NASA-logosmall.gif HTTP/1.0\" 304 0',\n", " 'burger.letters.com - - [01/Jul/1995:00:00:12 -0400] \"GET /shuttle/countdown/video/livevideo.gif HTTP/1.0\" 200 0',\n", " '205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] \"GET /shuttle/countdown/countdown.html HTTP/1.0\" 200 3985',\n", " 'd104.aa.net - - [01/Jul/1995:00:00:13 -0400] \"GET /shuttle/countdown/ HTTP/1.0\" 200 3985',\n", " '129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] \"GET / HTTP/1.0\" 200 7074',\n", " 'unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] \"GET /shuttle/countdown/count.gif HTTP/1.0\" 200 40310',\n", " 'unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] \"GET /images/NASA-logosmall.gif HTTP/1.0\" 200 786',\n", " 'unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] \"GET /images/KSC-logosmall.gif HTTP/1.0\" 200 1204',\n", " 'd104.aa.net - - [01/Jul/1995:00:00:15 -0400] \"GET /shuttle/countdown/count.gif HTTP/1.0\" 200 40310',\n", " 'd104.aa.net - - [01/Jul/1995:00:00:15 -0400] \"GET /images/NASA-logosmall.gif HTTP/1.0\" 200 786']" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sample_logs = [item['value'] for item in base_df.take(15)]\n", "sample_logs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extracting host names\n", "\n", "Let's try and write some regular expressions to extract the host name from the logs" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['199.72.81.55',\n", " 'unicomp6.unicomp.net',\n", " '199.120.110.21',\n", " 'burger.letters.com',\n", " '199.120.110.21',\n", " 'burger.letters.com',\n", " 'burger.letters.com',\n", " '205.212.115.106',\n", " 'd104.aa.net',\n", " '129.94.144.152',\n", " 'unicomp6.unicomp.net',\n", " 'unicomp6.unicomp.net',\n", " 'unicomp6.unicomp.net',\n", " 'd104.aa.net',\n", " 'd104.aa.net']" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "host_pattern = r'(^\\S+\\.[\\S+\\.]+\\S+)\\s'\n", "hosts = [re.search(host_pattern, item).group(1)\n", " if re.search(host_pattern, item)\n", " else 'no match'\n", " for item in sample_logs]\n", "hosts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extracting timestamps \n", "\n", "Let's now try and use regular expressions to extract the timestamp fields from the logs" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['01/Jul/1995:00:00:01 -0400',\n", " '01/Jul/1995:00:00:06 -0400',\n", " '01/Jul/1995:00:00:09 -0400',\n", " '01/Jul/1995:00:00:11 -0400',\n", " '01/Jul/1995:00:00:11 -0400',\n", " '01/Jul/1995:00:00:12 -0400',\n", " '01/Jul/1995:00:00:12 -0400',\n", " '01/Jul/1995:00:00:12 -0400',\n", " '01/Jul/1995:00:00:13 -0400',\n", " '01/Jul/1995:00:00:13 -0400',\n", " '01/Jul/1995:00:00:14 -0400',\n", " '01/Jul/1995:00:00:14 -0400',\n", " '01/Jul/1995:00:00:14 -0400',\n", " '01/Jul/1995:00:00:15 -0400',\n", " '01/Jul/1995:00:00:15 -0400']" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ts_pattern = r'\\[(\\d{2}/\\w{3}/\\d{4}:\\d{2}:\\d{2}:\\d{2} -\\d{4})]'\n", "timestamps = [re.search(ts_pattern, item).group(1) for item in sample_logs]\n", "timestamps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extracting HTTP Request Method, URIs and Protocol \n", "\n", "Let's now try and use regular expressions to extract the HTTP request methods, URIs and Protocol patterns fields from the logs" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('GET', '/history/apollo/', 'HTTP/1.0'),\n", " ('GET', '/shuttle/countdown/', 'HTTP/1.0'),\n", " ('GET', '/shuttle/missions/sts-73/mission-sts-73.html', 'HTTP/1.0'),\n", " ('GET', '/shuttle/countdown/liftoff.html', 'HTTP/1.0'),\n", " ('GET', '/shuttle/missions/sts-73/sts-73-patch-small.gif', 'HTTP/1.0'),\n", " ('GET', '/images/NASA-logosmall.gif', 'HTTP/1.0'),\n", " ('GET', '/shuttle/countdown/video/livevideo.gif', 'HTTP/1.0'),\n", " ('GET', '/shuttle/countdown/countdown.html', 'HTTP/1.0'),\n", " ('GET', '/shuttle/countdown/', 'HTTP/1.0'),\n", " ('GET', '/', 'HTTP/1.0'),\n", " ('GET', '/shuttle/countdown/count.gif', 'HTTP/1.0'),\n", " ('GET', '/images/NASA-logosmall.gif', 'HTTP/1.0'),\n", " ('GET', '/images/KSC-logosmall.gif', 'HTTP/1.0'),\n", " ('GET', '/shuttle/countdown/count.gif', 'HTTP/1.0'),\n", " ('GET', '/images/NASA-logosmall.gif', 'HTTP/1.0')]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "method_uri_protocol_pattern = r'\\\"(\\S+)\\s(\\S+)\\s*(\\S*)\\\"'\n", "method_uri_protocol = [re.search(method_uri_protocol_pattern, item).groups()\n", " if re.search(method_uri_protocol_pattern, item)\n", " else 'no match'\n", " for item in sample_logs]\n", "method_uri_protocol" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extracting HTTP Status Codes\n", "\n", "Let's now try and use regular expressions to extract the HTTP status codes from the logs" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['200', '200', '200', '304', '200', '304', '200', '200', '200', '200', '200', '200', '200', '200', '200']\n" ] } ], "source": [ "status_pattern = r'\\s(\\d{3})\\s'\n", "status = [re.search(status_pattern, item).group(1) for item in sample_logs]\n", "print(status)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extracting HTTP Response Content Size\n", "\n", "Let's now try and use regular expressions to extract the HTTP response content size from the logs" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['6245', '3985', '4085', '0', '4179', '0', '0', '3985', '3985', '7074', '40310', '786', '1204', '40310', '786']\n" ] } ], "source": [ "content_size_pattern = r'\\s(\\d+)$'\n", "content_size = [re.search(content_size_pattern, item).group(1) for item in sample_logs]\n", "print(content_size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Putting it all together \n", "\n", "Let's now try and leverage all the regular expression patterns we previously built and use the `regexp_extract(...)` method to build our dataframe with all the log attributes neatly extracted in their own separate columns." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------------------+--------------------+------+--------------------+--------+------+------------+\n", "| host| timestamp|method| endpoint|protocol|status|content_size|\n", "+--------------------+--------------------+------+--------------------+--------+------+------------+\n", "| 199.72.81.55|01/Jul/1995:00:00...| GET| /history/apollo/|HTTP/1.0| 200| 6245|\n", "|unicomp6.unicomp.net|01/Jul/1995:00:00...| GET| /shuttle/countdown/|HTTP/1.0| 200| 3985|\n", "| 199.120.110.21|01/Jul/1995:00:00...| GET|/shuttle/missions...|HTTP/1.0| 200| 4085|\n", "| burger.letters.com|01/Jul/1995:00:00...| GET|/shuttle/countdow...|HTTP/1.0| 304| 0|\n", "| 199.120.110.21|01/Jul/1995:00:00...| GET|/shuttle/missions...|HTTP/1.0| 200| 4179|\n", "| burger.letters.com|01/Jul/1995:00:00...| GET|/images/NASA-logo...|HTTP/1.0| 304| 0|\n", "| burger.letters.com|01/Jul/1995:00:00...| GET|/shuttle/countdow...|HTTP/1.0| 200| 0|\n", "| 205.212.115.106|01/Jul/1995:00:00...| GET|/shuttle/countdow...|HTTP/1.0| 200| 3985|\n", "| d104.aa.net|01/Jul/1995:00:00...| GET| /shuttle/countdown/|HTTP/1.0| 200| 3985|\n", "| 129.94.144.152|01/Jul/1995:00:00...| GET| /|HTTP/1.0| 200| 7074|\n", "+--------------------+--------------------+------+--------------------+--------+------+------------+\n", "only showing top 10 rows\n", "\n", "(3461613, 7)\n" ] } ], "source": [ "from pyspark.sql.functions import regexp_extract\n", "\n", "logs_df = base_df.select(regexp_extract('value', host_pattern, 1).alias('host'),\n", " regexp_extract('value', ts_pattern, 1).alias('timestamp'),\n", " regexp_extract('value', method_uri_protocol_pattern, 1).alias('method'),\n", " regexp_extract('value', method_uri_protocol_pattern, 2).alias('endpoint'),\n", " regexp_extract('value', method_uri_protocol_pattern, 3).alias('protocol'),\n", " regexp_extract('value', status_pattern, 1).cast('integer').alias('status'),\n", " regexp_extract('value', content_size_pattern, 1).cast('integer').alias('content_size'))\n", "logs_df.show(10, truncate=True)\n", "print((logs_df.count(), len(logs_df.columns)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Finding Missing Values\n", "\n", "Missing and null values are the bane of data analysis and machine learning. Let's see how well our data parsing and extraction logic worked. First, let's verify that there are no null rows in the original dataframe." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(base_df\n", " .filter(base_df['value']\n", " .isNull())\n", " .count())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If our data parsing and extraction worked properly, we should not have any rows with potential null values. Let's try and put that to test!" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "33905" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bad_rows_df = logs_df.filter(logs_df['host'].isNull()| \n", " logs_df['timestamp'].isNull() | \n", " logs_df['method'].isNull() |\n", " logs_df['endpoint'].isNull() |\n", " logs_df['status'].isNull() |\n", " logs_df['content_size'].isNull()|\n", " logs_df['protocol'].isNull())\n", "bad_rows_df.count()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ouch! Looks like we have over 33K missing values in our data! Can we handle this?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do remember, this is not a regular pandas dataframe which you can directly query and get which columns have null. Our so-called _big dataset_ is residing on disk which can potentially be present in multiple nodes in a spark cluster. So how do we find out which columns have potential nulls? \n", "\n", "### Finding Null Counts\n", "\n", "We can typically use the following technique to find out which columns have null values. \n", "\n", "(__Note:__ This approach is adapted from an [excellent answer](http://stackoverflow.com/a/33901312) on StackOverflow.)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['host',\n", " 'timestamp',\n", " 'method',\n", " 'endpoint',\n", " 'protocol',\n", " 'status',\n", " 'content_size']" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logs_df.columns" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+----+---------+------+--------+--------+------+------------+\n", "|host|timestamp|method|endpoint|protocol|status|content_size|\n", "+----+---------+------+--------+--------+------+------------+\n", "| 0| 0| 0| 0| 0| 1| 33905|\n", "+----+---------+------+--------+--------+------+------------+\n", "\n" ] } ], "source": [ "from pyspark.sql.functions import col\n", "from pyspark.sql.functions import sum as spark_sum\n", "\n", "def count_null(col_name):\n", " return spark_sum(col(col_name).isNull().cast('integer')).alias(col_name)\n", "\n", "# Build up a list of column expressions, one per column.\n", "exprs = [count_null(col_name) for col_name in logs_df.columns]\n", "\n", "# Run the aggregation. The *exprs converts the list of expressions into\n", "# variable function arguments.\n", "logs_df.agg(*exprs).show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, looks like we have one missing value in the `status` column and everything else is in the `content_size` column. \n", "Let's see if we can figure out what's wrong!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Handling nulls in HTTP status\n", "\n", "Our original parsing regular expression for the `status` column was:\n", "\n", "__```\n", "regexp_extract('value', r'\\s(\\d{3})\\s', 1).cast('integer').alias('status')\n", "```__ \n", "\n", "Could it be that there are more digits making our regular expression wrong? or is the data point itself bad? Let's try and find out!\n", "\n", "**Note**: In the expression below, `~` means \"not\"." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "null_status_df = base_df.filter(~base_df['value'].rlike(r'\\s(\\d{3})\\s'))\n", "null_status_df.count()" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------+\n", "|value |\n", "+--------+\n", "|alyssa.p|\n", "+--------+\n", "\n" ] } ], "source": [ "null_status_df.show(truncate=False)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+----+---------+------+--------+--------+------+------------+\n", "|host|timestamp|method|endpoint|protocol|status|content_size|\n", "+----+---------+------+--------+--------+------+------------+\n", "| | | | | |null |null |\n", "+----+---------+------+--------+--------+------+------------+\n", "\n" ] } ], "source": [ "bad_status_df = null_status_df.select(regexp_extract('value', host_pattern, 1).alias('host'),\n", " regexp_extract('value', ts_pattern, 1).alias('timestamp'),\n", " regexp_extract('value', method_uri_protocol_pattern, 1).alias('method'),\n", " regexp_extract('value', method_uri_protocol_pattern, 2).alias('endpoint'),\n", " regexp_extract('value', method_uri_protocol_pattern, 3).alias('protocol'),\n", " regexp_extract('value', status_pattern, 1).cast('integer').alias('status'),\n", " regexp_extract('value', content_size_pattern, 1).cast('integer').alias('content_size'))\n", "bad_status_df.show(truncate=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks like the record itself is an incomplete record with no useful information, the best option would be to drop this record as follows!" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3461613" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logs_df.count()" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3461612" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logs_df = logs_df[logs_df['status'].isNotNull()] \n", "logs_df.count()" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+----+---------+------+--------+--------+------+------------+\n", "|host|timestamp|method|endpoint|protocol|status|content_size|\n", "+----+---------+------+--------+--------+------+------------+\n", "| 0| 0| 0| 0| 0| 0| 33904|\n", "+----+---------+------+--------+--------+------+------------+\n", "\n" ] } ], "source": [ "exprs = [count_null(col_name) for col_name in logs_df.columns]\n", "logs_df.agg(*exprs).show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Handling nulls in HTTP content size\n", "\n", "Based on our previous regular expression, our original parsing regular expression for the `content_size` column was:\n", "\n", "__```\n", "regexp_extract('value', r'\\s(\\d+)$', 1).cast('integer').alias('content_size')\n", "```__ \n", "\n", "Could there be missing data in our original dataset itself? Let's try and find out!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Find out the records in our base data frame with potential missing content sizes" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "33905" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "null_content_size_df = base_df.filter(~base_df['value'].rlike(r'\\s\\d+$'))\n", "null_content_size_df.count()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Display the top ten records of your data frame having missing content sizes" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Row(value='dd15-062.compuserve.com - - [01/Jul/1995:00:01:12 -0400] \"GET /news/sci.space.shuttle/archive/sci-space-shuttle-22-apr-1995-40.txt HTTP/1.0\" 404 -'),\n", " Row(value='dynip42.efn.org - - [01/Jul/1995:00:02:14 -0400] \"GET /software HTTP/1.0\" 302 -'),\n", " Row(value='ix-or10-06.ix.netcom.com - - [01/Jul/1995:00:02:40 -0400] \"GET /software/winvn HTTP/1.0\" 302 -'),\n", " Row(value='ix-or10-06.ix.netcom.com - - [01/Jul/1995:00:03:24 -0400] \"GET /software HTTP/1.0\" 302 -'),\n", " Row(value='link097.txdirect.net - - [01/Jul/1995:00:05:06 -0400] \"GET /shuttle HTTP/1.0\" 302 -'),\n", " Row(value='ix-war-mi1-20.ix.netcom.com - - [01/Jul/1995:00:05:13 -0400] \"GET /shuttle/missions/sts-78/news HTTP/1.0\" 302 -'),\n", " Row(value='ix-war-mi1-20.ix.netcom.com - - [01/Jul/1995:00:05:58 -0400] \"GET /shuttle/missions/sts-72/news HTTP/1.0\" 302 -'),\n", " Row(value='netport-27.iu.net - - [01/Jul/1995:00:10:19 -0400] \"GET /pub/winvn/readme.txt HTTP/1.0\" 404 -'),\n", " Row(value='netport-27.iu.net - - [01/Jul/1995:00:10:28 -0400] \"GET /pub/winvn/readme.txt HTTP/1.0\" 404 -'),\n", " Row(value='dynip38.efn.org - - [01/Jul/1995:00:10:50 -0400] \"GET /software HTTP/1.0\" 302 -')]" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "null_content_size_df.take(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is quite evident that the bad raw data records correspond to error responses, where no content was sent back and the server emitted a \"`-`\" for the `content_size` field. \n", "\n", "Since we don't want to discard those rows from our analysis, let's impute or fill them to 0." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Fix the rows with null content\\_size\n", "\n", "The easiest solution is to replace the null values in `logs_df` with 0 like we discussed earlier. The Spark DataFrame API provides a set of functions and fields specifically designed for working with null values, among them:\n", "\n", "* [fillna()](http://spark.apache.org/docs/latest/api/python/pyspark.sql.html#pyspark.sql.DataFrame.fillna), which fills null values with specified non-null values.\n", "* [na](http://spark.apache.org/docs/latest/api/python/pyspark.sql.html#pyspark.sql.DataFrame.na), which returns a [DataFrameNaFunctions](http://spark.apache.org/docs/latest/api/python/pyspark.sql.html#pyspark.sql.DataFrameNaFunctions) object with many functions for operating on null columns.\n", "\n", "There are several ways to invoke this function. The easiest is just to replace _all_ null columns with known values. But, for safety, it's better to pass a Python dictionary containing (column\\_name, value) mappings. That's what we'll do. A sample example from the documentation is depicted below\n", "\n", "```\n", ">>> df4.na.fill({'age': 50, 'name': 'unknown'}).show()\n", "+---+------+-------+\n", "|age|height| name|\n", "+---+------+-------+\n", "| 10| 80| Alice|\n", "| 5| null| Bob|\n", "| 50| null| Tom|\n", "| 50| null|unknown|\n", "+---+------+-------+\n", "```\n", "\n", "Now we use this function and fill all the missing values in the `content_size` field with 0!" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": true }, "outputs": [], "source": [ "logs_df = logs_df.na.fill({'content_size': 0})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now assuming everything we have done so far worked, we should have no missing values \\ nulls in our dataset. Let's verify this!" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+----+---------+------+--------+--------+------+------------+\n", "|host|timestamp|method|endpoint|protocol|status|content_size|\n", "+----+---------+------+--------+--------+------+------------+\n", "| 0| 0| 0| 0| 0| 0| 0|\n", "+----+---------+------+--------+--------+------+------------+\n", "\n" ] } ], "source": [ "exprs = [count_null(col_name) for col_name in logs_df.columns]\n", "logs_df.agg(*exprs).show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Look at that, no missing values! " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Handling Temporal Fields (Timestamp)\n", "\n", "Now that we have a clean, parsed DataFrame, we have to parse the timestamp field into an actual timestamp. The Common Log Format time is somewhat non-standard. A User-Defined Function (UDF) is the most straightforward way to parse it." ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from pyspark.sql.functions import udf\n", "\n", "month_map = {\n", " 'Jan': 1, 'Feb': 2, 'Mar':3, 'Apr':4, 'May':5, 'Jun':6, 'Jul':7,\n", " 'Aug':8, 'Sep': 9, 'Oct':10, 'Nov': 11, 'Dec': 12\n", "}\n", "\n", "def parse_clf_time(text):\n", " \"\"\" Convert Common Log time format into a Python datetime object\n", " Args:\n", " text (str): date and time in Apache time format [dd/mmm/yyyy:hh:mm:ss (+/-)zzzz]\n", " Returns:\n", " a string suitable for passing to CAST('timestamp')\n", " \"\"\"\n", " # NOTE: We're ignoring the time zones here, might need to be handled depending on the problem you are solving\n", " return \"{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}\".format(\n", " int(text[7:11]),\n", " month_map[text[3:6]],\n", " int(text[0:2]),\n", " int(text[12:14]),\n", " int(text[15:17]),\n", " int(text[18:20])\n", " )" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['01/Jul/1995:00:00:01 -0400',\n", " '01/Jul/1995:00:00:06 -0400',\n", " '01/Jul/1995:00:00:09 -0400',\n", " '01/Jul/1995:00:00:11 -0400',\n", " '01/Jul/1995:00:00:11 -0400']" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sample_ts = [item['timestamp'] for item in logs_df.select('timestamp').take(5)]\n", "sample_ts" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1995-07-01 00:00:01',\n", " '1995-07-01 00:00:06',\n", " '1995-07-01 00:00:09',\n", " '1995-07-01 00:00:11',\n", " '1995-07-01 00:00:11']" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[parse_clf_time(item) for item in sample_ts]" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------------------+------+--------------------+--------+------+------------+-------------------+\n", "| host|method| endpoint|protocol|status|content_size| time|\n", "+--------------------+------+--------------------+--------+------+------------+-------------------+\n", "| 199.72.81.55| GET| /history/apollo/|HTTP/1.0| 200| 6245|1995-07-01 00:00:01|\n", "|unicomp6.unicomp.net| GET| /shuttle/countdown/|HTTP/1.0| 200| 3985|1995-07-01 00:00:06|\n", "| 199.120.110.21| GET|/shuttle/missions...|HTTP/1.0| 200| 4085|1995-07-01 00:00:09|\n", "| burger.letters.com| GET|/shuttle/countdow...|HTTP/1.0| 304| 0|1995-07-01 00:00:11|\n", "| 199.120.110.21| GET|/shuttle/missions...|HTTP/1.0| 200| 4179|1995-07-01 00:00:11|\n", "| burger.letters.com| GET|/images/NASA-logo...|HTTP/1.0| 304| 0|1995-07-01 00:00:12|\n", "| burger.letters.com| GET|/shuttle/countdow...|HTTP/1.0| 200| 0|1995-07-01 00:00:12|\n", "| 205.212.115.106| GET|/shuttle/countdow...|HTTP/1.0| 200| 3985|1995-07-01 00:00:12|\n", "| d104.aa.net| GET| /shuttle/countdown/|HTTP/1.0| 200| 3985|1995-07-01 00:00:13|\n", "| 129.94.144.152| GET| /|HTTP/1.0| 200| 7074|1995-07-01 00:00:13|\n", "+--------------------+------+--------------------+--------+------+------------+-------------------+\n", "only showing top 10 rows\n", "\n" ] } ], "source": [ "udf_parse_time = udf(parse_clf_time)\n", "\n", "logs_df = logs_df.select('*', udf_parse_time(logs_df['timestamp']).cast('timestamp').alias('time')).drop('timestamp')\n", "logs_df.show(10, truncate=True)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "root\n", " |-- host: string (nullable = true)\n", " |-- method: string (nullable = true)\n", " |-- endpoint: string (nullable = true)\n", " |-- protocol: string (nullable = true)\n", " |-- status: integer (nullable = true)\n", " |-- content_size: integer (nullable = false)\n", " |-- time: timestamp (nullable = true)\n", "\n" ] } ], "source": [ "logs_df.printSchema()" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
hostmethodendpointprotocolstatuscontent_sizetime
0199.72.81.55GET/history/apollo/HTTP/1.020062451995-07-01 00:00:01
1unicomp6.unicomp.netGET/shuttle/countdown/HTTP/1.020039851995-07-01 00:00:06
2199.120.110.21GET/shuttle/missions/sts-73/mission-sts-73.htmlHTTP/1.020040851995-07-01 00:00:09
3burger.letters.comGET/shuttle/countdown/liftoff.htmlHTTP/1.030401995-07-01 00:00:11
4199.120.110.21GET/shuttle/missions/sts-73/sts-73-patch-small.gifHTTP/1.020041791995-07-01 00:00:11
\n", "
" ], "text/plain": [ " host method \\\n", "0 199.72.81.55 GET \n", "1 unicomp6.unicomp.net GET \n", "2 199.120.110.21 GET \n", "3 burger.letters.com GET \n", "4 199.120.110.21 GET \n", "\n", " endpoint protocol status \\\n", "0 /history/apollo/ HTTP/1.0 200 \n", "1 /shuttle/countdown/ HTTP/1.0 200 \n", "2 /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0 200 \n", "3 /shuttle/countdown/liftoff.html HTTP/1.0 304 \n", "4 /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0 200 \n", "\n", " content_size time \n", "0 6245 1995-07-01 00:00:01 \n", "1 3985 1995-07-01 00:00:06 \n", "2 4085 1995-07-01 00:00:09 \n", "3 0 1995-07-01 00:00:11 \n", "4 4179 1995-07-01 00:00:11 " ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logs_df.limit(5).toPandas()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now cache `logs_df` since we will be using it extensively for our data analysis section in the next part!" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "DataFrame[host: string, method: string, endpoint: string, protocol: string, status: int, content_size: int, time: timestamp]" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logs_df.cache()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Part 4 - Data Analysis on our Web Logs\n", "\n", "Now that we have a DataFrame containing the parsed log file as a data frame, we can perform some interesting exploratory data analysis (EDA)\n", "\n", "## Content Size Statistics\n", "\n", "Let's compute some statistics about the sizes of content being returned by the web server. In particular, we'd like to know what are the average, minimum, and maximum content sizes.\n", "\n", "We can compute the statistics by calling `.describe()` on the `content_size` column of `logs_df`. The `.describe()` function returns the count, mean, stddev, min, and max of a given column." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
summarycontent_size
0count3461612
1mean18928.844398216785
2stddev73031.47260949228
3min0
4max6823936
\n", "
" ], "text/plain": [ " summary content_size\n", "0 count 3461612\n", "1 mean 18928.844398216785\n", "2 stddev 73031.47260949228\n", "3 min 0\n", "4 max 6823936" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "content_size_summary_df = logs_df.describe(['content_size'])\n", "content_size_summary_df.toPandas()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, we can use SQL to directly calculate these statistics. You can explore many useful functions within the `pyspark.sql.functions` module in the [documentation](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html#module-pyspark.sql.functions).\n", "\n", "After we apply the `.agg()` function, we call `toPandas()` to extract and convert the result into a `pandas` dataframe which has better formatting on Jupyter notebooks" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
min_content_sizemax_content_sizemean_content_sizestd_content_sizecount_content_size
00682393618928.84439873031.4726093461612
\n", "
" ], "text/plain": [ " min_content_size max_content_size mean_content_size std_content_size \\\n", "0 0 6823936 18928.844398 73031.472609 \n", "\n", " count_content_size \n", "0 3461612 " ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from pyspark.sql import functions as F\n", "\n", "(logs_df.agg(F.min(logs_df['content_size']).alias('min_content_size'),\n", " F.max(logs_df['content_size']).alias('max_content_size'),\n", " F.mean(logs_df['content_size']).alias('mean_content_size'),\n", " F.stddev(logs_df['content_size']).alias('std_content_size'),\n", " F.count(logs_df['content_size']).alias('count_content_size'))\n", " .toPandas())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## HTTP Status Code Analysis\n", "\n", "Next, let's look at the status code values that appear in the log. We want to know which status code values appear in the data and how many times. \n", "\n", "We again start with `logs_df`, then group by the `status` column, apply the `.count()` aggregation function, and sort by the `status` column." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": true }, "outputs": [], "source": [ "status_freq_df = (logs_df\n", " .groupBy('status')\n", " .count()\n", " .sort('status')\n", " .cache())" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total distinct HTTP Status Codes: 8\n" ] } ], "source": [ "print('Total distinct HTTP Status Codes:', status_freq_df.count())" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
statuscount
02003100524
2304266773
130273070
540420899
4403225
650065
750141
340015
\n", "
" ], "text/plain": [ " status count\n", "0 200 3100524\n", "2 304 266773\n", "1 302 73070\n", "5 404 20899\n", "4 403 225\n", "6 500 65\n", "7 501 41\n", "3 400 15" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "status_freq_pd_df = (status_freq_df\n", " .toPandas()\n", " .sort_values(by=['count'],\n", " ascending=False))\n", "status_freq_pd_df" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already up-to-date: seaborn in /usr/local/anaconda/lib/python3.6/site-packages (0.9.0)\n", "Requirement not upgraded as not directly required: matplotlib>=1.4.3 in /usr/local/anaconda/lib/python3.6/site-packages (from seaborn) (2.2.2)\n", "Requirement not upgraded as not directly required: numpy>=1.9.3 in /usr/local/anaconda/lib/python3.6/site-packages (from seaborn) (1.13.3)\n", "Requirement not upgraded as not directly required: scipy>=0.14.0 in /usr/local/anaconda/lib/python3.6/site-packages (from seaborn) (1.1.0)\n", "Requirement not upgraded as not directly required: pandas>=0.15.2 in /usr/local/anaconda/lib/python3.6/site-packages (from seaborn) (0.20.3)\n", "Requirement not upgraded as not directly required: cycler>=0.10 in /usr/local/anaconda/lib/python3.6/site-packages (from matplotlib>=1.4.3->seaborn) (0.10.0)\n", "Requirement not upgraded as not directly required: pytz in /usr/local/anaconda/lib/python3.6/site-packages (from matplotlib>=1.4.3->seaborn) (2018.4)\n", "Requirement not upgraded as not directly required: six>=1.10 in /usr/local/anaconda/lib/python3.6/site-packages (from matplotlib>=1.4.3->seaborn) (1.11.0)\n", "Requirement not upgraded as not directly required: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/anaconda/lib/python3.6/site-packages (from matplotlib>=1.4.3->seaborn) (2.2.0)\n", "Requirement not upgraded as not directly required: kiwisolver>=1.0.1 in /usr/local/anaconda/lib/python3.6/site-packages (from matplotlib>=1.4.3->seaborn) (1.0.1)\n", "Requirement not upgraded as not directly required: python-dateutil>=2.1 in /usr/local/anaconda/lib/python3.6/site-packages (from matplotlib>=1.4.3->seaborn) (2.7.3)\n", "Requirement not upgraded as not directly required: setuptools in /usr/local/anaconda/lib/python3.6/site-packages (from kiwisolver>=1.0.1->matplotlib>=1.4.3->seaborn) (39.2.0)\n", "\u001b[31mpyspark 2.4.0 requires py4j==0.10.7, which is not installed.\u001b[0m\n", "\u001b[33mYou are using pip version 10.0.1, however version 19.0.3 is available.\n", "You should consider upgrading via the 'pip install --upgrade pip' command.\u001b[0m\n" ] } ], "source": [ "!pip install -U seaborn" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import numpy as np\n", "%matplotlib inline\n", "\n", "sns.catplot(x='status', y='count', data=status_freq_pd_df, \n", " kind='bar', order=status_freq_pd_df['status'])" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+------+-------+------------------+\n", "|status| count| log(count)|\n", "+------+-------+------------------+\n", "| 200|3100524|14.947081687429097|\n", "| 302| 73070|11.199173164785263|\n", "| 304| 266773|12.494153388502301|\n", "| 400| 15| 2.70805020110221|\n", "| 403| 225| 5.41610040220442|\n", "| 404| 20899| 9.947456589918252|\n", "| 500| 65| 4.174387269895637|\n", "| 501| 41| 3.713572066704308|\n", "+------+-------+------------------+\n", "\n" ] } ], "source": [ "log_freq_df = status_freq_df.withColumn('log(count)', F.log(status_freq_df['count']))\n", "log_freq_df.show()" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "log_freq_pd_df = (log_freq_df\n", " .toPandas()\n", " .sort_values(by=['log(count)'],\n", " ascending=False))\n", "sns.catplot(x='status', y='log(count)', data=log_freq_pd_df, \n", " kind='bar', order=status_freq_pd_df['status'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Analyzing Frequent Hosts\n", "\n", "Let's look at hosts that have accessed the server frequently. We will try to get the count of total accesses by each `host` and then sort by the counts and display only the top ten most frequent hosts." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------------------+-----+\n", "|host |count|\n", "+--------------------+-----+\n", "|piweba3y.prodigy.com|21988|\n", "|piweba4y.prodigy.com|16437|\n", "|piweba1y.prodigy.com|12825|\n", "|edams.ksc.nasa.gov |11964|\n", "|163.206.89.4 |9697 |\n", "|news.ti.com |8161 |\n", "|www-d1.proxy.aol.com|8047 |\n", "|alyssa.prodigy.com |8037 |\n", "| |7660 |\n", "|siltb10.orl.mmc.com |7573 |\n", "+--------------------+-----+\n", "\n" ] } ], "source": [ "host_sum_df =(logs_df\n", " .groupBy('host')\n", " .count()\n", " .sort('count', ascending=False).limit(10))\n", "\n", "host_sum_df.show(truncate=False)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "''" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "host_sum_pd_df = host_sum_df.toPandas()\n", "host_sum_pd_df.iloc[8]['host']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks like we have some empty strings as one of the top host names! This teaches us a valuable lesson to not just check for nulls but also potentially empty strings when data wrangling." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Display the Top 20 Frequent EndPoints\n", "\n", "Now, let's visualize the number of hits to endpoints (URIs) in the log. To perform this task, we start with our `logs_df` and group by the `endpoint` column, aggregate by count, and sort in descending order like the previous question." ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "collapsed": true }, "outputs": [], "source": [ "paths_df = (logs_df\n", " .groupBy('endpoint')\n", " .count()\n", " .sort('count', ascending=False).limit(20))" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
endpointcount
0/images/NASA-logosmall.gif208714
1/images/KSC-logosmall.gif164970
2/images/MOSAIC-logosmall.gif127908
3/images/USA-logosmall.gif127074
4/images/WORLD-logosmall.gif125925
5/images/ksclogo-medium.gif121572
6/ksc.html83909
7/images/launch-logo.gif76006
8/history/apollo/images/apollo-logo1.gif68896
9/shuttle/countdown/64736
10/63171
11/images/ksclogosmall.gif61393
12/shuttle/missions/missions.html47315
13/images/launchmedium.gif40687
14/htbin/cdt_main.pl39871
15/shuttle/missions/sts-69/mission-sts-69.html31574
16/shuttle/countdown/liftoff.html29865
17/icons/menu.xbm29190
18/shuttle/missions/sts-69/sts-69-patch-small.gif29118
19/icons/blank.xbm28852
\n", "
" ], "text/plain": [ " endpoint count\n", "0 /images/NASA-logosmall.gif 208714\n", "1 /images/KSC-logosmall.gif 164970\n", "2 /images/MOSAIC-logosmall.gif 127908\n", "3 /images/USA-logosmall.gif 127074\n", "4 /images/WORLD-logosmall.gif 125925\n", "5 /images/ksclogo-medium.gif 121572\n", "6 /ksc.html 83909\n", "7 /images/launch-logo.gif 76006\n", "8 /history/apollo/images/apollo-logo1.gif 68896\n", "9 /shuttle/countdown/ 64736\n", "10 / 63171\n", "11 /images/ksclogosmall.gif 61393\n", "12 /shuttle/missions/missions.html 47315\n", "13 /images/launchmedium.gif 40687\n", "14 /htbin/cdt_main.pl 39871\n", "15 /shuttle/missions/sts-69/mission-sts-69.html 31574\n", "16 /shuttle/countdown/liftoff.html 29865\n", "17 /icons/menu.xbm 29190\n", "18 /shuttle/missions/sts-69/sts-69-patch-small.gif 29118\n", "19 /icons/blank.xbm 28852" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "paths_pd_df = paths_df.toPandas()\n", "paths_pd_df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Top Ten Error Endpoints\n", "\n", "What are the top ten endpoints requested which did not have return code 200 (HTTP Status OK)? \n", "\n", "We create a sorted list containing the endpoints and the number of times that they were accessed with a non-200 return code and show the top ten." ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "collapsed": true }, "outputs": [], "source": [ "not200_df = (logs_df\n", " .filter(logs_df['status'] != 200))\n", "\n", "error_endpoints_freq_df = (not200_df\n", " .groupBy('endpoint')\n", " .count()\n", " .sort('count', ascending=False)\n", " .limit(10)\n", " )" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+---------------------------------------+-----+\n", "|endpoint |count|\n", "+---------------------------------------+-----+\n", "|/images/NASA-logosmall.gif |40082|\n", "|/images/KSC-logosmall.gif |23763|\n", "|/images/MOSAIC-logosmall.gif |15245|\n", "|/images/USA-logosmall.gif |15142|\n", "|/images/WORLD-logosmall.gif |14773|\n", "|/images/ksclogo-medium.gif |13559|\n", "|/images/launch-logo.gif |8806 |\n", "|/history/apollo/images/apollo-logo1.gif|7489 |\n", "|/ |6296 |\n", "|/images/ksclogosmall.gif |5669 |\n", "+---------------------------------------+-----+\n", "\n" ] } ], "source": [ "error_endpoints_freq_df.show(truncate=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Total number of Unique Hosts\n", "\n", "What were the total number of unique hosts who visited the NASA website in these two months? We can find this out with a few transformations." ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "137933" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unique_host_count = (logs_df\n", " .select('host')\n", " .distinct()\n", " .count())\n", "unique_host_count" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Number of Unique Daily Hosts\n", "\n", "For an advanced example, let's look at a way to determine the number of unique hosts in the entire log on a day-by-day basis. This computation will give us counts of the number of unique daily hosts. \n", "\n", "We'd like a DataFrame sorted by increasing day of the month which includes the day of the month and the associated number of unique hosts for that day. \n", "\n", "Think about the steps that you need to perform to count the number of different hosts that make requests *each* day.\n", "*Since the log only covers a single month, you can ignore the month.* You may want to use the [`dayofmonth` function](https://spark.apache.org/docs/latest/api/python/pyspark.sql.html#pyspark.sql.functions.dayofmonth) in the `pyspark.sql.functions` module (which we have already imported as __`F`__.\n", "\n", "\n", "**`host_day_df`**\n", "\n", "A DataFrame with two columns\n", "\n", "| column | explanation |\n", "| ------ | -------------------- |\n", "| `host` | the host name |\n", "| `day` | the day of the month |\n", "\n", "There will be one row in this DataFrame for each row in `logs_df`. Essentially, we are just transforming each row of `logs_df`. For example, for this row in `logs_df`:\n", "\n", "```\n", "unicomp6.unicomp.net - - [01/Aug/1995:00:35:41 -0400] \"GET /shuttle/missions/sts-73/news HTTP/1.0\" 302 -\n", "```\n", "\n", "your `host_day_df` should have:\n", "\n", "```\n", "unicomp6.unicomp.net 1\n", "```" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+--------------------+---+\n", "|host |day|\n", "+--------------------+---+\n", "|199.72.81.55 |1 |\n", "|unicomp6.unicomp.net|1 |\n", "|199.120.110.21 |1 |\n", "|burger.letters.com |1 |\n", "|199.120.110.21 |1 |\n", "+--------------------+---+\n", "only showing top 5 rows\n", "\n" ] } ], "source": [ "host_day_df = logs_df.select(logs_df.host, \n", " F.dayofmonth('time').alias('day'))\n", "host_day_df.show(5, truncate=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**`host_day_distinct_df`**\n", "\n", "This DataFrame has the same columns as `host_day_df`, but with duplicate (`day`, `host`) rows removed." ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+-----------------------+---+\n", "|host |day|\n", "+-----------------------+---+\n", "|129.94.144.152 |1 |\n", "|slip1.yab.com |1 |\n", "|205.184.190.47 |1 |\n", "|204.120.34.71 |1 |\n", "|ppp3_130.bekkoame.or.jp|1 |\n", "+-----------------------+---+\n", "only showing top 5 rows\n", "\n" ] } ], "source": [ "host_day_distinct_df = (host_day_df\n", " .dropDuplicates())\n", "host_day_distinct_df.show(5, truncate=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**`daily_unique_hosts_df`**\n", "\n", "A DataFrame with two columns:\n", "\n", "| column | explanation |\n", "| ------- | -------------------------------------------------- |\n", "| `day` | the day of the month |\n", "| `count` | the number of unique requesting hosts for that day |" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
daycount
017609
124858
2310238
349411
459640
.........
26276846
27286090
28294825
29305265
30315913
\n", "

31 rows × 2 columns

\n", "
" ], "text/plain": [ " day count\n", "0 1 7609\n", "1 2 4858\n", "2 3 10238\n", "3 4 9411\n", "4 5 9640\n", ".. ... ...\n", "26 27 6846\n", "27 28 6090\n", "28 29 4825\n", "29 30 5265\n", "30 31 5913\n", "\n", "[31 rows x 2 columns]" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def_mr = pd.get_option('max_rows')\n", "pd.set_option('max_rows', 10)\n", "\n", "daily_hosts_df = (host_day_distinct_df\n", " .groupBy('day')\n", " .count()\n", " .sort(\"day\"))\n", "\n", "daily_hosts_df = daily_hosts_df.toPandas()\n", "daily_hosts_df" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "c = sns.catplot(x='day', y='count', \n", " data=daily_hosts_df, \n", " kind='point', height=5, \n", " aspect=1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Average Number of Daily Requests per Host\n", "\n", "In the previous example, we looked at a way to determine the number of unique hosts in the entire log on a day-by-day basis. Let's now try and find the average number of requests being made per Host to the NASA website per day based on our logs. \n", "\n", "We'd like a DataFrame sorted by increasing day of the month which includes the day of the month and the associated number of average requests made for that day per Host. " ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
daytotal_reqstotal_hostsavg_reqs
0198710760912.972795
1260265485812.405311
231309721023812.792733
34130009941113.814579
45126468964013.119087
...............
262794503684613.804119
272882617609013.566010
282967988482514.090777
293080641526515.316429
303190125591315.241840
\n", "

31 rows × 4 columns

\n", "
" ], "text/plain": [ " day total_reqs total_hosts avg_reqs\n", "0 1 98710 7609 12.972795\n", "1 2 60265 4858 12.405311\n", "2 3 130972 10238 12.792733\n", "3 4 130009 9411 13.814579\n", "4 5 126468 9640 13.119087\n", ".. ... ... ... ...\n", "26 27 94503 6846 13.804119\n", "27 28 82617 6090 13.566010\n", "28 29 67988 4825 14.090777\n", "29 30 80641 5265 15.316429\n", "30 31 90125 5913 15.241840\n", "\n", "[31 rows x 4 columns]" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daily_hosts_df = (host_day_distinct_df\n", " .groupBy('day')\n", " .count()\n", " .select(col(\"day\"), \n", " col(\"count\").alias(\"total_hosts\")))\n", "\n", "total_daily_reqests_df = (logs_df\n", " .select(F.dayofmonth(\"time\")\n", " .alias(\"day\"))\n", " .groupBy(\"day\")\n", " .count()\n", " .select(col(\"day\"), \n", " col(\"count\").alias(\"total_reqs\")))\n", "\n", "avg_daily_reqests_per_host_df = total_daily_reqests_df.join(daily_hosts_df, 'day')\n", "avg_daily_reqests_per_host_df = (avg_daily_reqests_per_host_df\n", " .withColumn('avg_reqs', col('total_reqs') / col('total_hosts'))\n", " .sort(\"day\"))\n", "avg_daily_reqests_per_host_df = avg_daily_reqests_per_host_df.toPandas()\n", "avg_daily_reqests_per_host_df" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "image/png": "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\nAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "c = sns.catplot(x='day', y='avg_reqs', \n", " data=avg_daily_reqests_per_host_df, \n", " kind='point', height=5, aspect=1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Counting 404 Response Codes\n", "\n", "Create a DataFrame containing only log records with a 404 status code (Not Found). \n", "\n", "We make sure to `cache()` the `not_found_df` dataframe as we will use it in the rest of the examples here.\n", "\n", "__How many 404 records are in the log?__" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total 404 responses: 20899\n" ] } ], "source": [ "not_found_df = logs_df.filter(logs_df[\"status\"] == 404).cache()\n", "print(('Total 404 responses: {}').format(not_found_df.count()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Listing the Top Twenty 404 Response Code Endpoints\n", "\n", "Using the DataFrame containing only log records with a 404 response code that we cached earlier, we will now print out a list of the top twenty endpoints that generate the most 404 errors.\n", "\n", "*Remember, top endpoints should be in sorted order*" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+-----------------------------------------------------------------+-----+\n", "|endpoint |count|\n", "+-----------------------------------------------------------------+-----+\n", "|/pub/winvn/readme.txt |2004 |\n", "|/pub/winvn/release.txt |1732 |\n", "|/shuttle/missions/STS-69/mission-STS-69.html |683 |\n", "|/shuttle/missions/sts-68/ksc-upclose.gif |428 |\n", "|/history/apollo/a-001/a-001-patch-small.gif |384 |\n", "|/history/apollo/sa-1/sa-1-patch-small.gif |383 |\n", "|/://spacelink.msfc.nasa.gov |381 |\n", "|/images/crawlerway-logo.gif |374 |\n", "|/elv/DELTA/uncons.htm |372 |\n", "|/history/apollo/pad-abort-test-1/pad-abort-test-1-patch-small.gif|359 |\n", "|/images/nasa-logo.gif |319 |\n", "|/shuttle/resources/orbiters/atlantis.gif |314 |\n", "|/history/apollo/apollo-13.html |304 |\n", "|/shuttle/resources/orbiters/discovery.gif |263 |\n", "|/shuttle/missions/sts-71/images/KSC-95EC-0916.txt |190 |\n", "|/shuttle/resources/orbiters/challenger.gif |170 |\n", "|/shuttle/missions/technology/sts-newsref/stsref-toc.html |158 |\n", "|/history/apollo/images/little-joe.jpg |150 |\n", "|/images/lf-logo.gif |143 |\n", "|/history/apollo/publications/sp-350/sp-350.txt~ |140 |\n", "+-----------------------------------------------------------------+-----+\n", "\n" ] } ], "source": [ "endpoints_404_count_df = (not_found_df\n", " .groupBy(\"endpoint\")\n", " .count()\n", " .sort(\"count\", ascending=False)\n", " .limit(20))\n", "\n", "endpoints_404_count_df.show(truncate=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Listing the Top Twenty 404 Response Code Hosts\n", "\n", "Using the DataFrame containing only log records with a 404 response code that we cached earlier, we will now print out a list of the top twenty hosts that generate the most 404 errors.\n", "\n", "*Remember, top hosts should be in sorted order*" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+---------------------------+-----+\n", "|host |count|\n", "+---------------------------+-----+\n", "|hoohoo.ncsa.uiuc.edu |251 |\n", "|piweba3y.prodigy.com |157 |\n", "|jbiagioni.npt.nuwc.navy.mil|132 |\n", "|piweba1y.prodigy.com |114 |\n", "| |112 |\n", "|www-d4.proxy.aol.com |91 |\n", "|piweba4y.prodigy.com |86 |\n", "|scooter.pa-x.dec.com |69 |\n", "|www-d1.proxy.aol.com |64 |\n", "|phaelon.ksc.nasa.gov |64 |\n", "|dialip-217.den.mmc.com |62 |\n", "|www-b4.proxy.aol.com |62 |\n", "|www-b3.proxy.aol.com |61 |\n", "|www-a2.proxy.aol.com |60 |\n", "|www-d2.proxy.aol.com |59 |\n", "|piweba2y.prodigy.com |59 |\n", "|alyssa.prodigy.com |56 |\n", "|monarch.eng.buffalo.edu |56 |\n", "|www-b2.proxy.aol.com |53 |\n", "|www-c4.proxy.aol.com |53 |\n", "+---------------------------+-----+\n", "\n" ] } ], "source": [ "hosts_404_count_df = (not_found_df\n", " .groupBy(\"host\")\n", " .count()\n", " .sort(\"count\", ascending=False)\n", " .limit(20))\n", "\n", "hosts_404_count_df.show(truncate=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualizing 404 Errors per Day\n", "\n", "Let's explore our 404 records temporally (by time) now. Similar to the example showing the number of unique daily hosts, we will break down the 404 requests by day and get the daily counts sorted by day in `errors_by_date_sorted_df`." ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
daycount
01559
12291
23778
34705
45733
.........
2627706
2728504
2829420
2930571
3031526
\n", "

31 rows × 2 columns

\n", "
" ], "text/plain": [ " day count\n", "0 1 559\n", "1 2 291\n", "2 3 778\n", "3 4 705\n", "4 5 733\n", ".. ... ...\n", "26 27 706\n", "27 28 504\n", "28 29 420\n", "29 30 571\n", "30 31 526\n", "\n", "[31 rows x 2 columns]" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "errors_by_date_sorted_df = (not_found_df\n", " .groupBy(F.dayofmonth('time').alias('day'))\n", " .count()\n", " .sort(\"day\"))\n", "\n", "errors_by_date_sorted_pd_df = errors_by_date_sorted_df.toPandas()\n", "errors_by_date_sorted_pd_df" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "c = sns.catplot(x='day', y='count', \n", " data=errors_by_date_sorted_pd_df, \n", " kind='point', height=5, aspect=1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Top Three Days for 404 Errors\n", "\n", "What are the top three days of the month having the most 404 errors, we can leverage our previously created __`errors_by_date_sorted_df`__ for this. " ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+---+-----+\n", "|day|count|\n", "+---+-----+\n", "| 7| 1107|\n", "| 6| 1013|\n", "| 25| 876|\n", "+---+-----+\n", "only showing top 3 rows\n", "\n" ] } ], "source": [ "(errors_by_date_sorted_df\n", " .sort(\"count\", ascending=False)\n", " .show(3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualizing Hourly 404 Errors\n", "\n", "Using the DataFrame `not_found_df` we cached earlier, we will now group and sort by hour of the day in increasing order, to create a DataFrame containing the total number of 404 responses for HTTP requests for each hour of the day (midnight starts at 0)" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "collapsed": true }, "outputs": [], "source": [ "hourly_avg_errors_sorted_df = (not_found_df\n", " .groupBy(F.hour('time')\n", " .alias('hour'))\n", " .count()\n", " .sort('hour'))\n", "hourly_avg_errors_sorted_pd_df = hourly_avg_errors_sorted_df.toPandas()" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhQAAAFgCAYAAADjIeCvAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAHNVJREFUeJzt3XuwZWV55/HvT1pUTJRbg9jdDIwh\nJsQyih0kMTGO7UCDxkaDt/LSIlYnDka8TAzGqZDEskqjxmiSISECgiEKQdHWEC7B20xVQC4B5JbQ\nKkr3AN0KYkYqGvSZP/bb40l7zund+9377HPo76dq11nr3Ws9/TRnr82v37X22qkqJEmSejxs2g1I\nkqSlz0AhSZK6GSgkSVI3A4UkSepmoJAkSd0MFJIkqZuBQpIkdTNQSJKkbgYKSZLUbdm0G5iEtWvX\n1iWXXDLtNiRJeijIMBs9JGcovvnNb067BUmSdisPyUAhSZIWloFCkiR1M1BIkqRuBgpJktTNQCFJ\nkrpNLFAkOSvJ1iQ3zfLcW5JUkv3bepJ8MMmmJDcmOWLGtuuT3N4e6yfVryRJGt0kZyg+DKzdcTDJ\nKuBo4Bszho8FDmuPDcDpbdt9gdOApwNHAqcl2WeCPUuSpBFMLFBU1ReBe2d56v3AW4GaMbYOOLcG\nrgT2TnIQcAxweVXdW1X3AZczS0iRJEnTtaDXUCRZB2ypqht2eGoFcOeM9c1tbK5xSZK0iCzYrbeT\n7AX8LoPTHZOov4HB6RIOPvjgSfwRkiRpDgs5Q/EE4FDghiR3ACuB65I8DtgCrJqx7co2Ntf4j6mq\nM6pqdVWtXr58+QTalyRJc1mwQFFVX66qA6rqkKo6hMHpiyOq6m5gI/Cq9mmPo4D7q+ou4FLg6CT7\ntIsxj25jkiRpEZnYKY8kHwWeBeyfZDNwWlWdOcfmFwPHAZuAB4ATAarq3iTvAK5u2/1hVc12oack\nLXonfPy6rv0v/PUjdr6RNCUTCxRV9bKdPH/IjOUCTp5ju7OAs8banCRJGivvlClJkroZKCRJUjcD\nhSRJ6magkCRJ3QwUkiSpm4FCkiR1M1BIkqRuBgpJktTNQCFJkroZKCRJUjcDhSRJ6magkCRJ3QwU\nkiSpm4FCkiR1m9jXl0vSsJ534Xkj7/uZE14+xk4kjcoZCkmS1M1AIUmSuhkoJElSNwOFJEnqZqCQ\nJEndDBSSJKmbHxuVpHkcf+EVI+/7yRPWjLETaXFzhkKSJHUzUEiSpG4GCkmS1M1AIUmSuhkoJElS\nNwOFJEnqZqCQJEndDBSSJKmbgUKSJHUzUEiSpG4GCkmS1G1igSLJWUm2Jrlpxth7ktyW5MYkFyXZ\ne8Zzb0uyKck/JzlmxvjaNrYpyamT6leSJI1ukjMUHwbW7jB2OfCkqnoy8C/A2wCSHA68FPi5ts//\nTLJHkj2APweOBQ4HXta2lSRJi8jEAkVVfRG4d4exy6rqwbZ6JbCyLa8DPlZV36uqrwGbgCPbY1NV\nfbWqvg98rG0rSZIWkWl+fflrgPPb8goGAWO7zW0M4M4dxp8+W7EkG4ANAAcffPBYG5Wkh7oPfWJr\n1/6vfeEBY+pES9VULspM8nbgQeC8cdWsqjOqanVVrV6+fPm4ykqSpCEs+AxFklcDzwPWVFW14S3A\nqhmbrWxjzDMuSZIWiQWdoUiyFngr8PyqemDGUxuBlyZ5RJJDgcOALwFXA4clOTTJngwu3Ny4kD1L\nkqSdm9gMRZKPAs8C9k+yGTiNwac6HgFcngTgyqr6zaq6OckFwC0MToWcXFU/aHVeD1wK7AGcVVU3\nT6pnSVpK3nDRnTvfaA4ffMGqnW8k7YKJBYqqetksw2fOs/07gXfOMn4xcPEYW5MkSWPmnTIlSVI3\nA4UkSepmoJAkSd2meWMrSUvUcz9+Rtf+f/frG8bUiaTFwhkKSZLUzRkKSQ8pv3bhJ7v2//QJx4+p\nE2n34gyFJEnqZqCQJEndDBSSJKmbgUKSJHUzUEiSpG4GCkmS1M1AIUmSuhkoJElSNwOFJEnqZqCQ\nJEndDBSSJKmbgUKSJHXzy8Gk3cRzL3rPyPv+3Qt+e4ydSHoocoZCkiR1M1BIkqRuBgpJktTNQCFJ\nkroZKCRJUjcDhSRJ6magkCRJ3QwUkiSpm4FCkiR1M1BIkqRuBgpJktTNQCFJkroZKCRJUjcDhSRJ\n6jaxQJHkrCRbk9w0Y2zfJJcnub393KeNJ8kHk2xKcmOSI2bss75tf3uS9ZPqV5IkjW6SMxQfBtbu\nMHYqcEVVHQZc0dYBjgUOa48NwOkwCCDAacDTgSOB07aHEEmStHhMLFBU1ReBe3cYXgec05bPAY6f\nMX5uDVwJ7J3kIOAY4PKqureq7gMu58dDiiRJmrKFvobiwKq6qy3fDRzYllcAd87YbnMbm2v8xyTZ\nkOSaJNds27ZtvF1LkqR5Te2izKoqoMZY74yqWl1Vq5cvXz6uspIkaQgLHSjuaacyaD+3tvEtwKoZ\n261sY3ONS5KkRWShA8VGYPsnNdYDn5ox/qr2aY+jgPvbqZFLgaOT7NMuxjy6jUmSpEVk2aQKJ/ko\n8Cxg/ySbGXxa413ABUlOAr4OvLhtfjFwHLAJeAA4EaCq7k3yDuDqtt0fVtWOF3pKkqQpm1igqKqX\nzfHUmlm2LeDkOeqcBZw1xtYkSdKYeadMSZLUzUAhSZK6GSgkSVI3A4UkSepmoJAkSd0MFJIkqZuB\nQpIkdZvYfSgkSdJ0bf2zvx953wNef+wube8MhSRJ6uYMhSRp7P7+/G927X/sS/YfUydaKM5QSJKk\nbgYKSZLUzVMekiQtEvd84B+79j/wlF8cUye7zhkKSZLUzUAhSZK6GSgkSVI3A4UkSepmoJAkSd0M\nFJIkqZuBQpIkdfM+FJIkjeju993Wtf/j3vIzY+pk+pyhkCRJ3ZyhmJJvfPCErv0PfsOFY+pEkqR+\nzlBIkqRuBgpJktTNUx6SpEXt6rO3du3/CyceMKZONB9nKCRJUjcDhSRJ6uYpD2mROu6Tb+na/+Lj\n3zemTiRp55yhkCRJ3QwUkiSpm4FCkiR1M1BIkqRuU7koM8mbgNcCBXwZOBE4CPgYsB9wLfDKqvp+\nkkcA5wJPA74FvKSq7phG35Kkpe+OP7m7a/9D3vi4MXXy0LLgMxRJVgBvAFZX1ZOAPYCXAu8G3l9V\nPwXcB5zUdjkJuK+Nv79tJ0mSFpFpnfJYBjwqyTJgL+Au4NnA9m+8Ogc4vi2va+u059ckyQL2KkmS\ndmLBA0VVbQHeC3yDQZC4n8Epjm9X1YNts83Aira8Ariz7ftg236/Hesm2ZDkmiTXbNu2bbJ/CUmS\n9B9M45THPgxmHQ4FHg88GljbW7eqzqiq1VW1evny5b3lJEnSLpjGKY/nAF+rqm1V9e/AJ4BnAHu3\nUyAAK4EtbXkLsAqgPf9YBhdnSpKkRWIageIbwFFJ9mrXQqwBbgE+B5zQtlkPfKotb2zrtOc/W1W1\ngP1KkqSdmMY1FFcxuLjyOgYfGX0YcAbwO8Cbk2xicI3EmW2XM4H92vibgVMXumdJkjS/oe5DkeSK\nqlqzs7FhVdVpwGk7DH8VOHKWbf8NeNEof44kSVoY8waKJI9k8LHO/dvFlNs/rvkYfvQpjEVr2+l/\n3bX/8te9YkydSJL00LazGYrfAN7I4NMY1/KjQPEd4M8m2JckSVpC5g0UVfUB4ANJfquq/nSBepIk\nSUvMUNdQVNWfJvkl4JCZ+1TVuRPqS5IkLSHDXpT5EeAJwPXAD9pwMfjSLkmStJsb9ttGVwOHe/8H\nSZI0m2HvQ3ET4Pe1SpKkWQ07Q7E/cEuSLwHf2z5YVc+fSFeSJGlJGTZQ/P4km5AkSUvbsJ/y+MKk\nG5EkSUvXsJ/y+FcGn+oA2BN4OPDdqnrMpBqTJElLx7AzFD+5fbl9Q+g64KhJNSVJkpaWXf620Rr4\nJHDMBPqRJElL0LCnPF44Y/VhDO5L8W8T6UiSJC05w37K49dmLD8I3MHgtIekGU68aO3I+579gkvG\n2IkkLaxhr6E4cdKNSJKkpWuoayiSrExyUZKt7fHxJCsn3ZwkSVoahr0o82xgI/D49vh0G5MkSRo6\nUCyvqrOr6sH2+DCwfIJ9SZKkJWTYQPGtJK9Iskd7vAL41iQbkyRJS8ewgeI1wIuBu4G7gBOAV0+o\nJ0mStMQM+7HRPwTWV9V9AEn2Bd7LIGhIkqTd3LAzFE/eHiYAqupe4KmTaUmSJC01wwaKhyXZZ/tK\nm6EYdnZDkiQ9xA0bCt4H/GOSv23rLwLeOZmWJEnSUjPsnTLPTXIN8Ow29MKqumVybUmSpKVk6NMW\nLUDs1iHintPf07X/ga/77TF1IknS4rLLX18uSZK0IwOFJEnqZqCQJEndDBSSJKmbgUKSJHUzUEiS\npG5TCRRJ9k5yYZLbktya5BeT7Jvk8iS3t5/7tG2T5INJNiW5MckR0+hZkiTNbVozFB8ALqmqnwF+\nHrgVOBW4oqoOA65o6wDHAoe1xwbg9IVvV5IkzWfBA0WSxwLPBM4EqKrvV9W3gXXAOW2zc4Dj2/I6\n4NwauBLYO8lBC9y2JEmaxzS+4OtQYBtwdpKfB64FTgEOrKq72jZ3Awe25RXAnTP239zG7poxRpIN\nDGYwOPjggyfW/GJ19V/+Wtf+v/Abnx5TJ5Kk3dE0TnksA44ATq+qpwLf5UenNwCoqgJqV4pW1RlV\ntbqqVi9fvnxszUqSpJ2bRqDYDGyuqqva+oUMAsY9209ltJ9b2/NbgFUz9l/ZxiRJ0iKx4IGiqu4G\n7kzyxDa0hsGXjm0E1rex9cCn2vJG4FXt0x5HAffPODUiSZIWgWlcQwHwW8B5SfYEvgqcyCDcXJDk\nJODrwIvbthcDxwGbgAfatpIkaRGZSqCoquuB1bM8tWaWbQs4eeJNSZKkkXmnTEmS1M1AIUmSuhko\nJElSNwOFJEnqZqCQJEndDBSSJKmbgUKSJHUzUEiSpG4GCkmS1M1AIUmSuhkoJElSNwOFJEnqZqCQ\nJEndDBSSJKmbgUKSJHUzUEiSpG4GCkmS1M1AIUmSuhkoJElSNwOFJEnqZqCQJEndDBSSJKmbgUKS\nJHUzUEiSpG7Lpt2ANE3vPP+Yrv3f/pJLx9SJJC1tzlBIkqRuBgpJktTNQCFJkroZKCRJUjcDhSRJ\n6magkCRJ3QwUkiSpm4FCkiR1m1qgSLJHkn9K8pm2fmiSq5JsSnJ+kj3b+CPa+qb2/CHT6lmSJM1u\nmjMUpwC3zlh/N/D+qvop4D7gpDZ+EnBfG39/206SJC0iUwkUSVYCzwU+1NYDPBu4sG1yDnB8W17X\n1mnPr2nbS5KkRWJaMxR/ArwV+GFb3w/4dlU92NY3Ayva8grgToD2/P1t+/8gyYYk1yS5Ztu2bZPs\nXZIk7WDBA0WS5wFbq+racdatqjOqanVVrV6+fPk4S0uSpJ2YxreNPgN4fpLjgEcCjwE+AOydZFmb\nhVgJbGnbbwFWAZuTLAMeC3xr4duWJElzWfAZiqp6W1WtrKpDgJcCn62qlwOfA05om60HPtWWN7Z1\n2vOfrapawJYlSdJOLKb7UPwO8OYkmxhcI3FmGz8T2K+Nvxk4dUr9SZKkOUzjlMf/V1WfBz7flr8K\nHDnLNv8GvGhBG5MkSbtkMc1QSJKkJcpAIUmSuhkoJElSNwOFJEnqNtWLMrV4XXrmcSPve8xJF4+x\nE0nSUuAMhSRJ6magkCRJ3QwUkiSpm4FCkiR1M1BIkqRuBgpJktTNQCFJkroZKCRJUjcDhSRJ6uad\nMjVx55+9tmv/l5x4yZg6kSRNijMUkiSpm4FCkiR1M1BIkqRuBgpJktTNQCFJkroZKCRJUjc/Nqol\n5y8/cszI+/7GKy8dYyeSpO2coZAkSd0MFJIkqZuBQpIkdTNQSJKkbgYKSZLUzUAhSZK6GSgkSVI3\nA4UkSepmoJAkSd0MFJIkqZuBQpIkdVvwQJFkVZLPJbklyc1JTmnj+ya5PMnt7ec+bTxJPphkU5Ib\nkxyx0D1LkqT5TWOG4kHgLVV1OHAUcHKSw4FTgSuq6jDgirYOcCxwWHtsAE5f+JYlSdJ8FjxQVNVd\nVXVdW/5X4FZgBbAOOKdtdg5wfFteB5xbA1cCeyc5aIHbliRJ85jqNRRJDgGeClwFHFhVd7Wn7gYO\nbMsrgDtn7La5je1Ya0OSa5Jcs23bton1LEmSftzUAkWSnwA+Dryxqr4z87mqKqB2pV5VnVFVq6tq\n9fLly8fYqSRJ2pmpBIokD2cQJs6rqk+04Xu2n8poP7e28S3Aqhm7r2xjkiRpkZjGpzwCnAncWlV/\nPOOpjcD6trwe+NSM8Ve1T3scBdw/49SIJElaBJZN4c98BvBK4MtJrm9jvwu8C7ggyUnA14EXt+cu\nBo4DNgEPACcubLuSJGlnFjxQVNX/BjLH02tm2b6AkyfalCRJ6uKdMiVJUjcDhSRJ6magkCRJ3QwU\nkiSpm4FCkiR1M1BIkqRuBgpJktTNQCFJkroZKCRJUjcDhSRJ6magkCRJ3QwUkiSpm4FCkiR1M1BI\nkqRuBgpJktTNQCFJkroZKCRJUjcDhSRJ6magkCRJ3QwUkiSpm4FCkiR1M1BIkqRuBgpJktTNQCFJ\nkroZKCRJUjcDhSRJ6magkCRJ3QwUkiSpm4FCkiR1M1BIkqRuBgpJktTNQCFJkroZKCRJUrclEyiS\nrE3yz0k2JTl12v1IkqQfWRKBIskewJ8DxwKHAy9Lcvh0u5IkSdstiUABHAlsqqqvVtX3gY8B66bc\nkyRJalJV0+5hp5KcAKytqte29VcCT6+q18/YZgOwoa0+EfjnIUrvD3xzjK2Os569Tb/WuOvZ2+Ko\nZ2/TrzXuevY22VrfrKq1O9toWX8/i0NVnQGcsSv7JLmmqlaPq4dx1rO36dcadz17Wxz17G36tcZd\nz96mXwuWzimPLcCqGesr25gkSVoElkqguBo4LMmhSfYEXgpsnHJPkiSpWRKnPKrqwSSvBy4F9gDO\nqqqbx1B6l06RLHA9e5t+rXHXs7fFUc/epl9r3PXsbfq1lsZFmZIkaXFbKqc8JEnSImagkCRJ3Xbb\nQDHOW3knOSvJ1iQ3jaGvVUk+l+SWJDcnOaWj1iOTfCnJDa3WH/T21+rukeSfknyms84dSb6c5Pok\n14yhr72TXJjktiS3JvnFjlpPbH1tf3wnyRs76r2p/Q5uSvLRJI/sqHVKq3PzKD3N9npNsm+Sy5Pc\n3n7u01nvRa2/HyYZ+mNpc9R6T/ud3pjkoiR7d9Z7R6t1fZLLkjx+1FoznntLkkqyf2dvv59ky4zX\n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\nzH8zEfoC54QAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "c = sns.catplot(x='hour', y='count', \n", " data=hourly_avg_errors_sorted_pd_df, \n", " kind='bar', height=5, aspect=1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reset the max rows displayed in pandas" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "collapsed": true }, "outputs": [], "source": [ "pd.set_option('max_rows', def_mr)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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", "version": "3.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }