{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Generating synthetic payments data\n", "\n", "In this notebook, we'll build up a very simple simulator to generate payments data corresponding to legitimate and fraudulent transactions. (There are many ways you could improve this generator and we'll call some of them out.) We'll start by building up some functionality to run simulations in general.\n", "\n", "## An (extremely) basic discrete-event simulation framework\n", "\n", "The next function is all you need to run simple discrete-event simulations. Here's how to use it:\n", "\n", "- you'll define several streams of events, each of which is modeled by a Python generator,\n", "- each event stream generator will `yield` a tuple consisting of *an offset* (the amount of time that has passed since the last event of that type) and *a result* (an arbitrary Python value associated with the event),\n", "- the generator produced by the `simulate` function will yield the next event from all event streams indefinitely." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import heapq\n", "\n", "def simulate(event_generators, initial_time=0):\n", " pq = []\n", " for event in event_generators:\n", " offset, result = next(event)\n", " heapq.heappush(pq, (offset + initial_time, result, event))\n", " \n", " while True:\n", " timestamp, result, event = heapq.heappop(pq)\n", " offset, next_result = next(event)\n", " heapq.heappush(pq, (timestamp + offset, next_result, event))\n", " yield (timestamp, result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It may be easier to see how this works with an example. In the next three cells, we \n", "\n", "1. define a generator for event streams, which samples interarrival times from a Poisson distribution and returns a predefined string as the result at each event,\n", "2. set up a simulation with four streams, each of which has a different distribution of interarrival times and value, and\n", "3. take the first twenty events from the simulation" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from scipy import stats\n", "\n", "def bedrockstream(mu, name):\n", " while True:\n", " offset, = stats.poisson.rvs(mu, size=1)\n", " yield (offset, name)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "sim = simulate([bedrockstream(10, \"fred\"), \n", " bedrockstream(12, \"betty\"), \n", " bedrockstream(20, \"wilma\"), \n", " bedrockstream(35, \"barney\")])" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(9, 'fred')\n", "(12, 'betty')\n", "(13, 'fred')\n", "(22, 'betty')\n", "(22, 'wilma')\n", "(26, 'barney')\n", "(30, 'betty')\n", "(32, 'fred')\n", "(40, 'betty')\n", "(40, 'wilma')\n", "(41, 'fred')\n", "(48, 'fred')\n", "(53, 'betty')\n", "(60, 'fred')\n", "(63, 'betty')\n", "(65, 'wilma')\n", "(69, 'barney')\n", "(73, 'betty')\n", "(76, 'fred')\n", "(87, 'betty')\n" ] } ], "source": [ "for i in range(20):\n", " print(next(sim))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Modeling transactions\n", "\n", "The first problem we have to do is to decide what data we'll generate for each transaction. Some interesting possibilities include:\n", "\n", "- user ID\n", "- merchant ID\n", "- merchant type\n", "- transaction amount (assuming a single currency)\n", "- card entry mode (e.g., contactless, chip and pin, swipe, card manually keyed, or online transaction)\n", "- foreign transaction (whether or not the user's home country matches the country in which the transaction is taking place)\n", "\n", "We'll also generate a label for each transaction (`legitimate` or `fraud`). We'll start with a very basic user event stream generator: all of the transactions we generate will be legitimate, and we won't do anything particularly interesting with most of the fields." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "MERCHANT_COUNT = 20000\n", "\n", "# a small percentage of merchants account for most transactions\n", "COMMON_MERCHANT_COUNT = MERCHANT_COUNT // 21\n", "\n", "common_merchants = np.random.choice(MERCHANT_COUNT, \n", " size=COMMON_MERCHANT_COUNT, \n", " replace=True)\n", "\n", "def basic_user_stream(user_id, mu):\n", " favorite_merchants = np.random.choice(common_merchants,\n", " size=len(common_merchants) // 5)\n", " while True:\n", " amount = 100.00\n", " entry = \"chip_and_pin\"\n", " foreign = False\n", " \n", " merchant_id, = np.random.choice(favorite_merchants, size=1)\n", " offset, = stats.poisson.rvs(mu, size=1)\n", " result = {\n", " \"user_id\": user_id,\n", " \"amount\": amount,\n", " \"merchant_id\": merchant_id,\n", " \"entry\": entry,\n", " \"foreign\": foreign\n", " }\n", " yield (offset, (\"legitimate\", *result.values()))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "sim = simulate([basic_user_stream(1, 700), basic_user_stream(2, 105), basic_user_stream(3, 40)])" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(43, ('legitimate', 3, 100.0, 12937, 'chip_and_pin', False))\n", "(85, ('legitimate', 3, 100.0, 12531, 'chip_and_pin', False))\n", "(104, ('legitimate', 2, 100.0, 9627, 'chip_and_pin', False))\n", "(133, ('legitimate', 3, 100.0, 11515, 'chip_and_pin', False))\n", "(176, ('legitimate', 3, 100.0, 3011, 'chip_and_pin', False))\n", "(218, ('legitimate', 2, 100.0, 19708, 'chip_and_pin', False))\n", "(221, ('legitimate', 3, 100.0, 17109, 'chip_and_pin', False))\n", "(259, ('legitimate', 3, 100.0, 8559, 'chip_and_pin', False))\n", "(310, ('legitimate', 3, 100.0, 842, 'chip_and_pin', False))\n", "(323, ('legitimate', 2, 100.0, 18709, 'chip_and_pin', False))\n", "(347, ('legitimate', 3, 100.0, 2361, 'chip_and_pin', False))\n", "(396, ('legitimate', 3, 100.0, 2589, 'chip_and_pin', False))\n", "(431, ('legitimate', 2, 100.0, 19497, 'chip_and_pin', False))\n", "(435, ('legitimate', 3, 100.0, 11085, 'chip_and_pin', False))\n", "(460, ('legitimate', 3, 100.0, 14329, 'chip_and_pin', False))\n", "(498, ('legitimate', 3, 100.0, 16310, 'chip_and_pin', False))\n", "(533, ('legitimate', 2, 100.0, 18873, 'chip_and_pin', False))\n", "(537, ('legitimate', 3, 100.0, 7115, 'chip_and_pin', False))\n", "(579, ('legitimate', 3, 100.0, 16801, 'chip_and_pin', False))\n", "(620, ('legitimate', 3, 100.0, 5686, 'chip_and_pin', False))\n" ] } ], "source": [ "for i in range(20):\n", " print(next(sim))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise: some quick improvements\n", "\n", "1. Users don't always just buy things from a few favorite merchants. Change `basic_user_stream` so that they occasionally buy from any merchant.\n", "2. Most people buy many inexpensive things and relatively few expensive things. Use this insight to generate (more) realistic transaction amounts.\n", "3. Some small percentage of online sales will be foreign transactions. When a user is traveling abroad, nearly all of his or her transactions will be foreign transactions. Add some state to `basic_user_stream` to model occasional international travel." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building a better transaction stream\n", "\n", "We'll start by building a generator to build a mixture model we can use to make several kinds of transactions: small, medium, and large." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def transaction_amounts(means, percentages, distribution=None):\n", " size = 256\n", " \n", " if distribution is None:\n", " distribution = lambda m, sz: stats.gamma.rvs(a=1.1, scale=min(m, 750), loc=m, size=sz)\n", " \n", " while True:\n", " streams = [distribution(m * 100, size) for m in means]\n", " stream = np.floor(np.choose(np.random.choice(len(means), p=percentages, size=size), streams)) / 100\n", " \n", " yield from stream" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1., 2., 3.])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([1.1,2.1,3.5])\n", "np.floor(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's plot a histogram of some simulated transaction amounts sampled from three distributions with means corresponding to three kinds of purchases: a latte, take-out for lunch, and a pair of Levi's." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "var spec = {\"config\": {\"view\": {\"width\": 400, \"height\": 300}, \"mark\": {\"tooltip\": null}}, \"data\": {\"url\": \"altair-data-47347fa42bcb1dbb2e27c10723e8f3a7.json\", \"format\": {\"type\": \"json\"}}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"bin\": {\"maxbins\": 100}, \"field\": \"amounts\"}, \"y\": {\"type\": \"quantitative\", \"aggregate\": \"count\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v3.4.0.json\"};\n", "var opt = {};\n", "var type = \"vega-lite\";\n", "var id = \"b26bae69-2d22-4cf3-95e3-923754f7dc1e\";\n", "\n", "var output_area = this;\n", "\n", "require([\"nbextensions/jupyter-vega/index\"], function(vega) {\n", " var target = document.createElement(\"div\");\n", " target.id = id;\n", " target.className = \"vega-embed\";\n", "\n", " var style = document.createElement(\"style\");\n", " style.textContent = [\n", " \".vega-embed .error p {\",\n", " \" color: firebrick;\",\n", " \" font-size: 14px;\",\n", " \"}\",\n", " ].join(\"\\\\n\");\n", "\n", " // element is a jQuery wrapped DOM element inside the output area\n", " // see http://ipython.readthedocs.io/en/stable/api/generated/\\\n", " // IPython.display.html#IPython.display.Javascript.__init__\n", " element[0].appendChild(target);\n", " element[0].appendChild(style);\n", "\n", " vega.render(\"#\" + id, spec, type, opt, output_area);\n", "}, function (err) {\n", " if (err.requireType !== \"scripterror\") {\n", " throw(err);\n", " }\n", "});\n" ], "text/plain": [ "<vega.vegalite.VegaLite at 0x1a22144940>" ] }, "metadata": { "jupyter-vega": "#b26bae69-2d22-4cf3-95e3-923754f7dc1e" }, "output_type": "display_data" }, { "data": { "text/plain": [] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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" }, "metadata": { "jupyter-vega": "#b26bae69-2d22-4cf3-95e3-923754f7dc1e" }, "output_type": "display_data" } ], "source": [ "import pandas as pd\n", "\n", "import altair as alt\n", "alt.renderers.enable('notebook')\n", "alt.data_transformers.enable('json')\n", "\n", "amt = transaction_amounts([5, 15, 50], [0.5, 0.35, 0.15])\n", "amounts = [next(amt) for i in range(80000)]\n", "\n", "source = pd.DataFrame({\"amounts\": amounts})\n", "\n", "alt.Chart(source).mark_bar().encode(\n", " alt.X(\"amounts\", bin=alt.Bin(maxbins=100)),\n", " y='count()'\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also plot a broader distribution of transactions:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "var spec = {\"config\": {\"view\": {\"width\": 400, \"height\": 300}, \"mark\": {\"tooltip\": null}}, \"data\": {\"url\": \"altair-data-4ebf890e7ffb72c9c50f1b8de668d36a.json\", \"format\": {\"type\": \"json\"}}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"bin\": {\"maxbins\": 100}, \"field\": \"amounts\"}, \"y\": {\"type\": \"quantitative\", \"aggregate\": \"count\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v3.4.0.json\"};\n", "var opt = {};\n", "var type = \"vega-lite\";\n", "var id = \"539bf5d1-909a-4cf6-9154-c8da3b78dfb5\";\n", "\n", "var output_area = this;\n", "\n", "require([\"nbextensions/jupyter-vega/index\"], function(vega) {\n", " var target = document.createElement(\"div\");\n", " target.id = id;\n", " target.className = \"vega-embed\";\n", "\n", " var style = document.createElement(\"style\");\n", " style.textContent = [\n", " \".vega-embed .error p {\",\n", " \" color: firebrick;\",\n", " \" font-size: 14px;\",\n", " \"}\",\n", " ].join(\"\\\\n\");\n", "\n", " // element is a jQuery wrapped DOM element inside the output area\n", " // see http://ipython.readthedocs.io/en/stable/api/generated/\\\n", " // IPython.display.html#IPython.display.Javascript.__init__\n", " element[0].appendChild(target);\n", " element[0].appendChild(style);\n", "\n", " vega.render(\"#\" + id, spec, type, opt, output_area);\n", "}, function (err) {\n", " if (err.requireType !== \"scripterror\") {\n", " throw(err);\n", " }\n", "});\n" ], "text/plain": [ "<vega.vegalite.VegaLite at 0x1a225162b0>" ] }, "metadata": { "jupyter-vega": "#539bf5d1-909a-4cf6-9154-c8da3b78dfb5" }, "output_type": "display_data" }, { "data": { "text/plain": [] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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" }, "metadata": { "jupyter-vega": "#539bf5d1-909a-4cf6-9154-c8da3b78dfb5" }, "output_type": "display_data" } ], "source": [ "amt = transaction_amounts([5, 10, 15, 20, 50, 100], \n", " [0.35, 0.25, 0.15, 0.1, 0.1, 0.05])\n", "amounts = [next(amt) for i in range(40000)]\n", "\n", "source = pd.DataFrame({\"amounts\": amounts})\n", "\n", "alt.Chart(source).mark_bar().encode(\n", " alt.X(\"amounts\", bin=alt.Bin(maxbins=100)),\n", " y='count()',\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next up, we'll make a generator to create the entry types:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def legitimate_entry_types():\n", " size = 256\n", " \n", " entry_types = [\"contactless\", \"chip_and_pin\", \"swipe\", \"manual\", \"online\"]\n", " entry_probs = [0.25,0.2,0.15,0.05,0.35]\n", "\n", " while True:\n", " stream = [entry_types[i] for i in np.random.choice(len(entry_types), p=entry_probs, size=size)]\n", " yield from stream" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "...and one for selecting merchants (primarily a user's favorite merchants):" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def merchant_stream(common_merchants, all_merchants, fav_percentage=0.2, probs=[0.6,0.37,0.03]):\n", " favorite_merchants = np.random.choice(common_merchants,\n", " size=int(len(common_merchants) * fav_percentage))\n", " merchants = [favorite_merchants, common_merchants, all_merchants]\n", " while True:\n", " pool = merchants[np.random.choice(len(merchants), p=probs)]\n", " yield int(np.random.choice(pool))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can combine all of these to generate a stream of legitimate activity for a single user:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [], "source": [ "def legitimate_user_stream(user_id, transactions_per_day=12, amount_means=[20,100,500], amount_probs=[0.9,0.075,0.025]):\n", " amounts = transaction_amounts(amount_means, amount_probs)\n", " entry_types = legitimate_entry_types()\n", " merchants = merchant_stream(common_merchants, np.arange(MERCHANT_COUNT))\n", " \n", " SECONDS_PER_DAY = 86400\n", " loc = SECONDS_PER_DAY // transactions_per_day\n", " p = 1 / (loc / 10)\n", "\n", " while True:\n", " amount = next(amounts)\n", " entry = next(entry_types)\n", " foreign = entry == \"online\" and np.random.choice([True, False], p=[0.4, 0.6])\n", " \n", " merchant_id = next(merchants)\n", " offset, = stats.geom.rvs(p=p, loc=loc, size=1)\n", " result = {\n", " \"user_id\": user_id,\n", " \"amount\": amount,\n", " \"merchant_id\": merchant_id,\n", " \"entry\": entry,\n", " \"foreign\": foreign\n", " }\n", " yield (offset, (\"legitimate\", *result.values()))" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(7408, ('legitimate', 1, 26.34, 13610, 'contactless', False))\n", "(7417, ('legitimate', 2, 27.82, 1445, 'chip_and_pin', False))\n", "(7895, ('legitimate', 3, 20.94, 1456, 'chip_and_pin', False))\n", "(14735, ('legitimate', 1, 23.31, 514, 'contactless', False))\n", "(15296, ('legitimate', 3, 103.68, 2930, 'contactless', False))\n", "(15397, ('legitimate', 2, 24.56, 14863, 'online', True))\n", "(22482, ('legitimate', 1, 24.43, 14887, 'online', False))\n", "(23033, ('legitimate', 2, 20.01, 10007, 'contactless', False))\n", "(23812, ('legitimate', 3, 106.4, 3699, 'contactless', False))\n", "(30717, ('legitimate', 1, 37.06, 11025, 'online', False))\n", "(32080, ('legitimate', 2, 23.45, 16013, 'manual', False))\n", "(33635, ('legitimate', 3, 20.17, 16846, 'swipe', False))\n", "(38367, ('legitimate', 1, 23.7, 5183, 'chip_and_pin', False))\n", "(39723, ('legitimate', 2, 22.04, 16275, 'online', False))\n", "(41180, ('legitimate', 3, 23.9, 5793, 'online', True))\n", "(46503, ('legitimate', 1, 25.06, 10416, 'online', False))\n", "(47127, ('legitimate', 2, 24.0, 6489, 'swipe', False))\n", "(50501, ('legitimate', 3, 40.52, 15992, 'online', True))\n", "(54701, ('legitimate', 2, 21.15, 246, 'contactless', False))\n", "(56336, ('legitimate', 1, 29.68, 6719, 'contactless', False))\n", "(58156, ('legitimate', 3, 29.24, 5845, 'chip_and_pin', False))\n", "(62027, ('legitimate', 2, 67.74, 9116, 'online', False))\n", "(63942, ('legitimate', 1, 55.43, 7243, 'online', True))\n", "(66610, ('legitimate', 3, 26.01, 3793, 'online', False))\n", "(69469, ('legitimate', 2, 20.74, 15659, 'chip_and_pin', False))\n", "(72172, ('legitimate', 1, 26.66, 8324, 'contactless', False))\n", "(74762, ('legitimate', 3, 53.7, 8038, 'online', False))\n", "(77407, ('legitimate', 2, 33.29, 10216, 'online', False))\n", "(79896, ('legitimate', 1, 24.05, 6882, 'chip_and_pin', False))\n", "(85155, ('legitimate', 3, 21.09, 1165, 'contactless', False))\n", "(85846, ('legitimate', 2, 38.8, 18234, 'online', False))\n", "(87770, ('legitimate', 1, 23.84, 15444, 'manual', False))\n", "(92374, ('legitimate', 3, 33.8, 6678, 'contactless', False))\n", "(94397, ('legitimate', 2, 22.36, 11625, 'contactless', False))\n", "(95623, ('legitimate', 1, 31.35, 1931, 'chip_and_pin', False))\n", "(99798, ('legitimate', 3, 20.59, 17237, 'online', False))\n", "(102388, ('legitimate', 2, 23.29, 1454, 'online', True))\n", "(103733, ('legitimate', 1, 21.25, 6196, 'contactless', False))\n", "(109406, ('legitimate', 3, 46.19, 7625, 'chip_and_pin', False))\n", "(111226, ('legitimate', 2, 33.2, 3635, 'online', True))\n", "(112057, ('legitimate', 1, 26.88, 1814, 'online', False))\n", "(118591, ('legitimate', 2, 26.45, 5441, 'chip_and_pin', False))\n", "(119379, ('legitimate', 3, 20.39, 12698, 'online', False))\n", "(119828, ('legitimate', 1, 24.02, 9389, 'online', True))\n", "(125842, ('legitimate', 2, 504.72, 5406, 'manual', False))\n", "(126603, ('legitimate', 3, 25.77, 18420, 'online', False))\n", "(127339, ('legitimate', 1, 62.57, 13248, 'chip_and_pin', False))\n", "(134049, ('legitimate', 3, 126.85, 18892, 'chip_and_pin', False))\n", "(134202, ('legitimate', 2, 38.84, 11808, 'swipe', False))\n", "(134937, ('legitimate', 1, 28.23, 4106, 'contactless', False))\n", "(142161, ('legitimate', 3, 23.37, 3161, 'contactless', False))\n", "(142264, ('legitimate', 2, 23.88, 13441, 'chip_and_pin', False))\n", "(142312, ('legitimate', 1, 22.66, 5307, 'contactless', False))\n", "(149536, ('legitimate', 3, 27.97, 7900, 'contactless', False))\n", "(151041, ('legitimate', 2, 21.13, 7186, 'online', False))\n", "(151394, ('legitimate', 1, 22.09, 11444, 'chip_and_pin', False))\n", "(157247, ('legitimate', 3, 35.36, 3533, 'online', False))\n", "(158603, ('legitimate', 2, 20.87, 5612, 'online', False))\n", "(160414, ('legitimate', 1, 20.96, 424, 'contactless', False))\n", "(165536, ('legitimate', 3, 508.73, 16609, 'swipe', False))\n", "(165983, ('legitimate', 2, 20.16, 5057, 'contactless', False))\n", "(169245, ('legitimate', 1, 23.78, 5742, 'swipe', False))\n", "(173460, ('legitimate', 2, 28.95, 16882, 'chip_and_pin', False))\n", "(173586, ('legitimate', 3, 26.02, 19339, 'online', True))\n", "(176649, ('legitimate', 1, 30.67, 2212, 'swipe', False))\n", "(180847, ('legitimate', 2, 20.72, 246, 'swipe', False))\n", "(181084, ('legitimate', 3, 20.86, 3069, 'contactless', False))\n", "(184682, ('legitimate', 1, 505.5, 2496, 'contactless', False))\n", "(188350, ('legitimate', 3, 103.24, 19011, 'manual', False))\n", "(189235, ('legitimate', 2, 45.69, 10007, 'contactless', False))\n", "(192379, ('legitimate', 1, 118.27, 252, 'chip_and_pin', False))\n", "(197203, ('legitimate', 2, 29.57, 18014, 'chip_and_pin', False))\n", "(197558, ('legitimate', 3, 39.76, 3487, 'swipe', False))\n", "(200216, ('legitimate', 1, 28.31, 11096, 'online', False))\n", "(204529, ('legitimate', 2, 100.96, 6489, 'contactless', False))\n", "(205066, ('legitimate', 3, 27.36, 17660, 'contactless', False))\n", "(207775, ('legitimate', 1, 24.28, 13593, 'contactless', False))\n", "(212956, ('legitimate', 3, 25.29, 1346, 'swipe', False))\n", "(213482, ('legitimate', 2, 512.4, 19042, 'contactless', False))\n", "(215476, ('legitimate', 1, 30.84, 2496, 'chip_and_pin', False))\n", "(220385, ('legitimate', 3, 21.0, 8362, 'online', False))\n", "(221307, ('legitimate', 2, 21.04, 16058, 'chip_and_pin', False))\n", "(222724, ('legitimate', 1, 30.93, 15809, 'swipe', False))\n", "(227959, ('legitimate', 3, 30.53, 3988, 'contactless', False))\n", "(229982, ('legitimate', 1, 28.6, 668, 'online', True))\n", "(229993, ('legitimate', 2, 31.82, 14860, 'contactless', False))\n", "(235512, ('legitimate', 3, 21.28, 15992, 'online', True))\n", "(237810, ('legitimate', 2, 29.77, 1230, 'online', False))\n", "(238666, ('legitimate', 1, 504.4, 3692, 'online', True))\n", "(243136, ('legitimate', 3, 20.24, 17933, 'swipe', False))\n", "(245076, ('legitimate', 2, 21.5, 17403, 'online', False))\n", "(245949, ('legitimate', 1, 26.43, 4230, 'chip_and_pin', False))\n", "(252278, ('legitimate', 3, 25.47, 16385, 'contactless', False))\n", "(252788, ('legitimate', 2, 52.09, 10364, 'manual', False))\n", "(253948, ('legitimate', 1, 28.78, 5559, 'online', False))\n", "(259832, ('legitimate', 3, 26.82, 3609, 'online', False))\n", "(261967, ('legitimate', 2, 32.88, 4520, 'contactless', False))\n", "(262236, ('legitimate', 1, 32.44, 12607, 'chip_and_pin', False))\n", "(268436, ('legitimate', 3, 29.85, 4470, 'chip_and_pin', False))\n", "(269421, ('legitimate', 2, 106.69, 13441, 'contactless', False))\n", "(269873, ('legitimate', 1, 41.98, 15691, 'contactless', False))\n", "(275774, ('legitimate', 3, 24.62, 6038, 'contactless', False))\n", "(277318, ('legitimate', 1, 35.63, 19450, 'contactless', False))\n", "(277332, ('legitimate', 2, 32.76, 18482, 'contactless', False))\n", "(283273, ('legitimate', 3, 25.61, 14506, 'swipe', False))\n", "(284747, ('legitimate', 2, 24.7, 19941, 'contactless', False))\n", "(284982, ('legitimate', 1, 25.8, 1687, 'chip_and_pin', False))\n", "(292247, ('legitimate', 2, 32.02, 12572, 'chip_and_pin', False))\n", "(292247, ('legitimate', 3, 35.65, 6103, 'online', False))\n", "(293384, ('legitimate', 1, 21.23, 6951, 'contactless', False))\n", "(299467, ('legitimate', 3, 21.07, 12314, 'swipe', False))\n", "(301881, ('legitimate', 1, 47.21, 12819, 'chip_and_pin', False))\n", "(303674, ('legitimate', 2, 119.57, 16187, 'contactless', False))\n", "(307710, ('legitimate', 3, 41.32, 2372, 'chip_and_pin', False))\n", "(309106, ('legitimate', 1, 50.33, 3049, 'online', False))\n", "(310999, ('legitimate', 2, 32.22, 7243, 'chip_and_pin', False))\n", "(315847, ('legitimate', 3, 103.78, 5981, 'manual', False))\n", "(316497, ('legitimate', 1, 24.68, 12179, 'contactless', False))\n", "(318545, ('legitimate', 2, 511.94, 16924, 'chip_and_pin', False))\n", "(323253, ('legitimate', 3, 21.5, 6678, 'contactless', False))\n", "(323919, ('legitimate', 1, 27.27, 2528, 'swipe', False))\n", "(326344, ('legitimate', 2, 27.39, 16801, 'chip_and_pin', False))\n", "(331786, ('legitimate', 1, 32.29, 2281, 'online', True))\n", "(332684, ('legitimate', 3, 20.9, 10314, 'manual', False))\n", "(334392, ('legitimate', 2, 31.83, 9484, 'contactless', False))\n", "(339065, ('legitimate', 1, 106.69, 6837, 'online', False))\n", "(339936, ('legitimate', 3, 26.63, 16160, 'chip_and_pin', False))\n", "(341934, ('legitimate', 2, 33.69, 5310, 'chip_and_pin', False))\n", "(349037, ('legitimate', 1, 511.87, 3692, 'online', True))\n", "(349293, ('legitimate', 2, 25.25, 4267, 'chip_and_pin', False))\n", "(350517, ('legitimate', 3, 31.55, 19941, 'swipe', False))\n", "(357456, ('legitimate', 2, 65.74, 8800, 'swipe', False))\n", "(358139, ('legitimate', 3, 29.95, 10574, 'online', False))\n", "(358779, ('legitimate', 1, 29.55, 4891, 'chip_and_pin', False))\n", "(365006, ('legitimate', 2, 32.75, 4450, 'chip_and_pin', False))\n", "(365994, ('legitimate', 1, 24.27, 10308, 'online', True))\n", "(366124, ('legitimate', 3, 22.98, 10314, 'online', True))\n", "(372957, ('legitimate', 2, 27.0, 7432, 'swipe', False))\n", "(374466, ('legitimate', 1, 23.7, 18341, 'contactless', False))\n", "(374528, ('legitimate', 3, 119.87, 2584, 'manual', False))\n", "(380483, ('legitimate', 2, 503.49, 4106, 'online', False))\n", "(381714, ('legitimate', 1, 30.13, 5816, 'online', True))\n", "(382130, ('legitimate', 3, 26.39, 16047, 'online', True))\n", "(388980, ('legitimate', 1, 23.19, 6837, 'online', False))\n", "(389251, ('legitimate', 2, 20.77, 17311, 'chip_and_pin', False))\n", "(389639, ('legitimate', 3, 33.21, 17958, 'contactless', False))\n", "(396658, ('legitimate', 2, 27.58, 3049, 'chip_and_pin', False))\n", "(397381, ('legitimate', 3, 37.64, 5727, 'online', False))\n", "(397460, ('legitimate', 1, 21.6, 16876, 'chip_and_pin', False))\n", "(404077, ('legitimate', 2, 25.54, 3629, 'manual', False))\n", "(405068, ('legitimate', 1, 26.02, 11128, 'manual', False))\n", "(406224, ('legitimate', 3, 39.71, 2483, 'contactless', False))\n", "(412038, ('legitimate', 2, 33.57, 16461, 'chip_and_pin', False))\n", "(412614, ('legitimate', 1, 23.49, 1661, 'chip_and_pin', False))\n", "(414364, ('legitimate', 3, 26.73, 12191, 'swipe', False))\n", "(419392, ('legitimate', 2, 22.42, 5365, 'contactless', False))\n", "(420493, ('legitimate', 1, 108.5, 1814, 'contactless', False))\n", "(422400, ('legitimate', 3, 20.81, 12050, 'chip_and_pin', False))\n", "(427471, ('legitimate', 2, 27.65, 7645, 'online', False))\n", "(427824, ('legitimate', 1, 23.95, 572, 'chip_and_pin', False))\n", "(429892, ('legitimate', 3, 25.62, 9158, 'chip_and_pin', False))\n", "(435210, ('legitimate', 1, 24.28, 7578, 'manual', False))\n", "(435570, ('legitimate', 2, 24.04, 3629, 'swipe', False))\n", "(438726, ('legitimate', 3, 25.96, 18059, 'swipe', False))\n", "(443073, ('legitimate', 2, 20.15, 10725, 'online', False))\n", "(443128, ('legitimate', 1, 36.46, 8127, 'chip_and_pin', False))\n", "(445967, ('legitimate', 3, 26.79, 2372, 'online', False))\n", "(450456, ('legitimate', 2, 101.42, 15816, 'manual', False))\n", "(450638, ('legitimate', 1, 503.23, 5413, 'online', False))\n", "(453343, ('legitimate', 3, 25.16, 6041, 'online', False))\n", "(457708, ('legitimate', 2, 36.5, 7186, 'contactless', False))\n", "(458310, ('legitimate', 1, 33.48, 5612, 'online', False))\n", "(463065, ('legitimate', 3, 32.81, 12032, 'online', False))\n", "(465102, ('legitimate', 2, 21.96, 12725, 'chip_and_pin', False))\n", "(465878, ('legitimate', 1, 20.72, 19871, 'online', True))\n", "(470780, ('legitimate', 3, 105.6, 16625, 'contactless', False))\n", "(472344, ('legitimate', 2, 25.47, 4528, 'chip_and_pin', False))\n", "(473167, ('legitimate', 1, 101.71, 9143, 'contactless', False))\n", "(478163, ('legitimate', 3, 22.72, 3699, 'online', False))\n", "(480534, ('legitimate', 2, 40.94, 3049, 'manual', False))\n", "(480652, ('legitimate', 1, 22.85, 9919, 'chip_and_pin', False))\n", "(485478, ('legitimate', 3, 100.75, 15320, 'contactless', False))\n", "(488027, ('legitimate', 2, 24.07, 13593, 'online', False))\n", "(488537, ('legitimate', 1, 32.04, 13248, 'online', True))\n", "(493245, ('legitimate', 3, 66.43, 18993, 'contactless', False))\n", "(495539, ('legitimate', 2, 32.47, 6961, 'contactless', False))\n", "(495922, ('legitimate', 1, 500.17, 477, 'chip_and_pin', False))\n", "(501083, ('legitimate', 3, 36.05, 12179, 'online', False))\n", "(502958, ('legitimate', 2, 22.06, 12725, 'contactless', False))\n", "(503456, ('legitimate', 1, 23.54, 9356, 'online', False))\n", "(510238, ('legitimate', 2, 21.05, 11004, 'swipe', False))\n", "(511615, ('legitimate', 1, 21.76, 9898, 'manual', False))\n", "(512643, ('legitimate', 3, 27.02, 517, 'online', True))\n", "(517608, ('legitimate', 2, 28.52, 7133, 'online', True))\n", "(520154, ('legitimate', 3, 23.75, 9567, 'online', True))\n", "(520813, ('legitimate', 1, 21.69, 18244, 'online', False))\n", "(525036, ('legitimate', 2, 112.76, 7052, 'online', False))\n", "(527715, ('legitimate', 3, 26.02, 15397, 'swipe', False))\n", "(528490, ('legitimate', 1, 24.67, 4087, 'chip_and_pin', False))\n", "(532894, ('legitimate', 2, 39.96, 3629, 'chip_and_pin', False))\n" ] } ], "source": [ "sim = simulate([legitimate_user_stream(1), \n", " legitimate_user_stream(2), \n", " legitimate_user_stream(3)])\n", "\n", "for i in range(200):\n", " print(next(sim))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulating fraud\n", "\n", "We'll start with some basic assumptions: \n", "\n", "1. fraudulent transactions are equally likely to happen at any arbitrary merchant,\n", "2. fraudulent transactions are typically for small dollar amounts,\n", "3. fraudulent transactions are rare overall, but when they occur, several will occur close together,\n", "4. fraudulent transactions are far more likely to be certain entry types (manual or online) or foreign transactions, and\n", "5. fraudulent transactions occur without regard for the user's typical schedule.\n", "\n", "These will guide the design of a fraudulent transaction generator.\n", "\n", "WIP" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def fraud_entry_types():\n", " size = 256\n", " \n", " entry_types = [\"contactless\", \"chip_and_pin\", \"swipe\", \"manual\", \"online\"]\n", " entry_probs = [0.05,0.05,0.05,0.35,0.5]\n", "\n", " while True:\n", " stream = [entry_types[i] for i in np.random.choice(len(entry_types), p=entry_probs, size=size)]\n", " yield from stream\n", "\n", "def fraudulent_user_stream(user_id, transactions_per_day=12, amount_means=[5,10,20], amount_probs=[0.2, 0.2, 0.6]):\n", " amounts = transaction_amounts(amount_means, amount_probs)\n", " entry_types = fraud_entry_types()\n", " \n", " SECONDS_PER_DAY = 86400\n", " loc = SECONDS_PER_DAY // transactions_per_day * 10\n", " p = 1 / (loc / 10)\n", "\n", " while True:\n", " fraud_delay, = np.floor(stats.gamma.rvs(a=6.4, loc=SECONDS_PER_DAY * 90, scale=SECONDS_PER_DAY, size=1))\n", " fraud_delay = int(fraud_delay)\n", " \n", " amount = next(amounts)\n", " entry = next(entry_types)\n", " foreign = np.random.choice([True, False], p=[0.4, 0.6])\n", " \n", " merchant_id = np.random.choice(MERCHANT_COUNT)\n", " offset, = stats.geom.rvs(p=p, loc=loc, size=1)\n", " result = {\n", " \"user_id\": user_id,\n", " \"amount\": amount,\n", " \"merchant_id\": merchant_id,\n", " \"entry\": entry,\n", " \"foreign\": foreign\n", " }\n", " yield (offset, (\"legitimate\", *result.values()))" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "var spec = {\"config\": {\"view\": {\"width\": 400, \"height\": 300}, \"mark\": {\"tooltip\": null}}, \"data\": {\"url\": \"altair-data-2c9575e01c9b7e4d9e0aa3f58e38ef6a.json\", \"format\": {\"type\": \"json\"}}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"quantitative\", \"bin\": {\"maxbins\": 100}, \"field\": \"amounts\"}, \"y\": {\"type\": \"quantitative\", \"aggregate\": \"count\"}}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v3.4.0.json\"};\n", "var opt = {};\n", "var type = \"vega-lite\";\n", "var id = \"f82732d0-479c-4e0c-a372-e380559c68ec\";\n", "\n", "var output_area = this;\n", "\n", "require([\"nbextensions/jupyter-vega/index\"], function(vega) {\n", " var target = document.createElement(\"div\");\n", " target.id = id;\n", " target.className = \"vega-embed\";\n", "\n", " var style = document.createElement(\"style\");\n", " style.textContent = [\n", " \".vega-embed .error p {\",\n", " \" color: firebrick;\",\n", " \" font-size: 14px;\",\n", " \"}\",\n", " ].join(\"\\\\n\");\n", "\n", " // element is a jQuery wrapped DOM element inside the output area\n", " // see http://ipython.readthedocs.io/en/stable/api/generated/\\\n", " // IPython.display.html#IPython.display.Javascript.__init__\n", " element[0].appendChild(target);\n", " element[0].appendChild(style);\n", "\n", " vega.render(\"#\" + id, spec, type, opt, output_area);\n", "}, function (err) {\n", " if (err.requireType !== \"scripterror\") {\n", " throw(err);\n", " }\n", "});\n" ], "text/plain": [ "<vega.vegalite.VegaLite at 0x1a225165c0>" ] }, "metadata": { "jupyter-vega": "#f82732d0-479c-4e0c-a372-e380559c68ec" }, "output_type": "display_data" }, { "data": { "text/plain": [] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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" }, "metadata": { "jupyter-vega": "#f82732d0-479c-4e0c-a372-e380559c68ec" }, "output_type": "display_data" } ], "source": [ "source = pd.DataFrame({\"amounts\": stats.gamma.rvs(a=6.4, loc=86400 * 90, scale=80000, size=10000)})\n", "\n", "alt.Chart(source).mark_bar().encode(\n", " alt.X(\"amounts\", bin=alt.Bin(maxbins=100)),\n", " y='count()'\n", ")" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.2" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum([0.05,0.05,0.25,0.5,0.35])" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.choice([True, False], p=[0.3, 0.7])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.7.3" } }, "nbformat": 4, "nbformat_minor": 2 }