{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "To show off a recent command line tool for sketching, [dsrs](https://github.com/vlad17/datasketches-rs), let's plot the rolling 28-day average daily count of active reviewers on Amazon.\n", "\n", "The raw data here is `item,user,rating,timestamp` so this would map to a sophisticated `GROUP BY` with a `COUNT DISTINCT` over 28-day windows in SQL. But since the data's only available as CSV, how can we get to the same answer? If we're just interested in an approximate solution, can we do this without using a bunch of memory or custom (shuffle-inducing...) sliding window implementation?\n", "\n", "All timings below done on a 16-physical CPU machine (AWS r4.8xlarge)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# https://nijianmo.github.io/amazon/index.html\n", "# 6.7gb\n", "# May 1996 - Oct 2018, e.g.:\n", "# 0449819906,A3U4E9PIZ8OWH1,5.0,1383696000\n", "# timestamp is then unix time in seconds.\n", "prefix = 'http://deepyeti.ucsd.edu/jianmo/amazon/categoryFilesSmall/'\n", "review_data = {\n", " 'Amazon Fashion': 'AMAZON_FASHION.csv',\n", " 'All Beauty': 'All_Beauty.csv',\n", " 'Appliances': 'Appliances.csv',\n", " 'Arts, Crafts and Sewing': 'Arts_Crafts_and_Sewing.csv',\n", " 'Automotive': 'Automotive.csv',\n", " 'Books': 'Books.csv',\n", " 'CDs and Vinyl': 'CDs_and_Vinyl.csv',\n", " 'Cell Phones and Accessories': 'Cell_Phones_and_Accessories.csv',\n", " 'Clothing, Shoes and Jewelry': 'Clothing_Shoes_and_Jewelry.csv',\n", " 'Digital Music': 'Digital_Music.csv',\n", " 'Electronics': 'Electronics.csv',\n", " 'Gift Cards': 'Gift_Cards.csv',\n", " 'Grocery and Gourmet Food': 'Grocery_and_Gourmet_Food.csv',\n", " 'Home and Kitchen': 'Home_and_Kitchen.csv',\n", " 'Industrial and Scientific': 'Industrial_and_Scientific.csv',\n", " 'Kindle Store': 'Kindle_Store.csv',\n", " 'Luxury Beauty': 'Luxury_Beauty.csv',\n", " 'Magazine Subscriptions': 'Magazine_Subscriptions.csv',\n", " 'Movies and TV': 'Movies_and_TV.csv',\n", " 'Musical Instruments': 'Musical_Instruments.csv',\n", " 'Office Products': 'Office_Products.csv',\n", " 'Patio, Lawn and Garden': 'Patio_Lawn_and_Garden.csv',\n", " 'Pet Supplies': 'Pet_Supplies.csv',\n", " 'Prime Pantry': 'Prime_Pantry.csv',\n", " 'Software': 'Software.csv',\n", " 'Sports and Outdoors': 'Sports_and_Outdoors.csv',\n", " 'Tools and Home Improvement': 'Tools_and_Home_Improvement.csv',\n", " 'Toys and Games': 'Toys_and_Games.csv',\n", " 'Video Games': 'Video_Games.csv'\n", "}\n", "review_data = {k: prefix + v for k, v in review_data.items()}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Even with a 28d sliding window, if we're sliding by a day, it's still quite a few data points." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8188.0" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "(pd.Timestamp('Oct 2018') - pd.Timestamp('May 1996')) / pd.Timedelta('1d')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Store all urls in a variable" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from shlex import quote\n", "urls = ' '.join(list(map(quote, review_data.values())))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "%%bash -s {urls}\n", "\n", "echo 'will cite' | parallel --citation 1> /dev/null 2> /dev/null \n", "\n", "parallel curl -o \"/tmp/amazon{#}.csv\" -s {} ::: \"$@\"" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9.0G\ttotal\n", "230139802\n" ] } ], "source": [ "%%bash\n", "\n", "# Total data size\n", "du -hsc /tmp/amazon*.csv | tail -1\n", "\n", "# How many reviews?\n", "parallel --pipepart wc -l :::: /tmp/amazon*.csv \\\n", " | awk '{s+=$1}END{print s}'" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "43404924\n" ] } ], "source": [ "%%bash\n", "\n", "# How many users?\n", "parallel --pipepart 'cut -d, -f2 | dsrs --raw' :::: /tmp/amazon*.csv \\\n", " | dsrs --merge" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting /tmp/date-user-extract.awk\n" ] } ], "source": [ "%%writefile /tmp/date-user-extract.awk\n", "#!/usr/bin/awk\n", "\n", "BEGIN {\n", " FS = \",\" \n", "}\n", "\n", "1 {\n", " user = $2;\n", " epoch_sec = $4;\n", " # round down to nearest day\n", " rounded_epoch_sec = strftime(\"%Y %m %d 00 00 00\", epoch_sec);\n", " rounded_epoch_sec = mktime(rounded_epoch_sec)\n", " for (i = 0; i < 28; i += 1) {\n", " dt = strftime(\"%F\", rounded_epoch_sec);\n", " print dt \" \" user\n", " # a day can be more than this many seconds due to leaps but\n", " # since we only decrement 28 times the undershoot doesn't matter\n", " rounded_epoch_sec -= 86400\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2013-11-06 A3U4E9PIZ8OWH1\n", "2013-11-05 A3U4E9PIZ8OWH1\n", "2013-11-04 A3U4E9PIZ8OWH1\n" ] } ], "source": [ "%%bash\n", "\n", "# test date mapper\n", "echo 0449819906,A3U4E9PIZ8OWH1,5.0,1383696000 | awk -f /tmp/date-user-extract.awk | head -3" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "%%bash\n", " \n", "# How many 28d users?\n", "parallel --pipepart 'awk -f /tmp/date-user-extract.awk' :::: /tmp/amazon*.csv \\\n", " | dsrs --key >/tmp/ts" ] }, { "cell_type": "code", "execution_count": 10, "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", "
cnt
date
1996-04-231
1996-04-241
1996-04-251
1996-04-261
1996-04-271
\n", "
" ], "text/plain": [ " cnt\n", "date \n", "1996-04-23 1\n", "1996-04-24 1\n", "1996-04-25 1\n", "1996-04-26 1\n", "1996-04-27 1" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = pd.read_csv('/tmp/ts', delimiter=' ', names=[\"date\", \"cnt\"])\n", "t.set_index(\"date\", inplace=True, verify_integrity=True)\n", "t.sort_index(inplace=True)\n", "t.head()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "from matplotlib import pyplot as plt\n", "%matplotlib inline\n", "(t/28).plot(rot=45, legend=False)\n", "plt.xlabel(\"date\")\n", "plt.ylabel(\"users\")\n", "plt.title(\"28-day rolling average amazon reviewers\")\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "43249276\n", "How many users? awk time 190 sec\n", "\n", "43206238\n", "How many users? serial sketching time 11 sec\n", "\n", "43404924\n", "How many users? parallel sketching time 4 sec\n" ] } ], "source": [ "%%bash\n", "\n", "start=`date +%s`\n", "parallel --pipepart 'cut -d, -f2' :::: /tmp/amazon*.csv \\\n", " | awk '{a[$1]=1}END{print length(a)}'\n", "end=`date +%s`\n", "echo \"How many users? awk time\" $((end-start)) \"sec\"\n", "echo\n", "\n", "start=`date +%s`\n", "parallel --pipepart 'cut -d, -f2' :::: /tmp/amazon*.csv \\\n", " | dsrs\n", "end=`date +%s`\n", "echo \"How many users? serial sketching time\" $((end-start)) \"sec\"\n", "echo\n", "\n", "start=`date +%s`\n", "parallel --pipepart 'cut -d, -f2 | dsrs --raw' :::: /tmp/amazon*.csv \\\n", " | dsrs --merge\n", "end=`date +%s`\n", "echo \"How many users? parallel sketching time\" $((end-start)) \"sec\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I tried comparing the sketch-based rolling average computation to an `awk` one:\n", "\n", "```\n", "parallel --pipepart 'awk -f /tmp/date-user-extract.awk' :::: /tmp/amazon*.csv \\\n", " | awk '{a[$1][$2]=1}END{for(i in a)print i \" \" length(a[i])}' >/tmp/ts-awk\n", "```\n", "\n", "But this got OOM killed after 2700 seconds on a 240GB RAM machine. Perhaps the easiest non-sketch approach here would require ingesting the CSVs into postgres and just using a window function, but at this point we're well over a few-line solution." ] } ], "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.10" } }, "nbformat": 4, "nbformat_minor": 2 }