├── README.md ├── fraudml.JPG ├── LICENSE ├── Blockchain and XGBoosted K-means for Fraud Detection.ipynb └── Fraud Detection Using KMeans.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # Credit-card-fraud-detection-using-blockchain-and-ml 2 | -------------------------------------------------------------------------------- /fraudml.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrudhviGNV/Credit-card-fraud-detection-using-blockchain-and-ml/main/fraudml.JPG -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Blockchain and XGBoosted K-means for Fraud Detection.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "#
Blockchain and Machine Learning for Fraud Detection: Employing Artificial Intelligence in the Banking Sector" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "###
Blockchain Integrated with an XGBoosted K-means Model" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "#### Import Libraries & Packages" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "import pandas as pd\n", 31 | "import pandas.testing as tm\n", 32 | "import numpy as np\n", 33 | "from numpy import loadtxt\n", 34 | "from sklearn.cluster import KMeans\n", 35 | "from sklearn.preprocessing import LabelEncoder\n", 36 | "from sklearn.preprocessing import MinMaxScaler\n", 37 | "import xgboost\n", 38 | "from xgboost import XGBClassifier\n", 39 | "import hashlib\n", 40 | "import json\n", 41 | "from time import time\n", 42 | "from urllib.parse import urlparse\n", 43 | "from uuid import uuid4\n", 44 | "import requests\n", 45 | "from flask import Flask, jsonify, request" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "### Data Wrangling" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 7, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "#concatenate data into a single data frame\n", 62 | "\n", 63 | "account= pd.read_csv(\"account.csv\")\n", 64 | "order= pd.read_csv(\"order.csv\")\n", 65 | "transaction= pd.read_csv(\"transaction.csv\")\n", 66 | "\n", 67 | "X= pd.concat([account,order,transaction], axis=0)\n", 68 | "\n", 69 | "#dividing the data into train and test sets for the k-means model\n", 70 | "\n", 71 | "X_new= X.copy() #create a copy of your data \n", 72 | "\n", 73 | "x_train = X_new.sample(frac=0.40, random_state=0)\n", 74 | "x_test = X_new.drop(x_train.index)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "### Blockchain" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "#Create a class to store the block chain\n", 91 | "\n", 92 | "class Blockchain:\n", 93 | " def __init__(self):\n", 94 | " self.current_trans = []\n", 95 | " self.chain = []\n", 96 | " self.nodes = set()\n", 97 | "\n", 98 | " #Create the genesis block\n", 99 | " self.new_block(prev_hash='1', proof=100)\n", 100 | "\n", 101 | " def new_node(self, address):\n", 102 | " \"\"\"\n", 103 | " Add a new node. View the node here:'http://192.168.0.5:5000'\n", 104 | " \"\"\"\n", 105 | "\n", 106 | " parsed_url = urlparse(address)\n", 107 | " if parsed_url.netloc:\n", 108 | " self.nodes.add(parsed_url.netloc)\n", 109 | " elif parsed_url.path:\n", 110 | " self.nodes.add(parsed_url.path)\n", 111 | " else:\n", 112 | " raise ValueError('Invalid URL. Please try again.')\n", 113 | "\n", 114 | "\n", 115 | " def valid_chain(self, chain):\n", 116 | " \"\"\"\n", 117 | " Determine if blockchain is valid.\n", 118 | " \"\"\"\n", 119 | "\n", 120 | " prev_block = chain[0]\n", 121 | " current_index = 1\n", 122 | "\n", 123 | " while current_index < len(chain):\n", 124 | " block = chain[current_index]\n", 125 | " print(f'{prev_block}')\n", 126 | " print(f'{block}')\n", 127 | " print(\"\\n-----------\\n\")\n", 128 | " #Check that the hash of the block is correct\n", 129 | " prev_block_hash = self.hash(prev_block)\n", 130 | " if block['prev_hash'] != prev_block_hash:\n", 131 | " return False\n", 132 | "\n", 133 | " #Check that the Proof of Work is correct\n", 134 | " if not self.valid_proof(prev_block['proof'], block['proof'], prev_block_hash):\n", 135 | " return False\n", 136 | "\n", 137 | " prev_block = block\n", 138 | " current_index += 1\n", 139 | "\n", 140 | " return True\n", 141 | "\n", 142 | " def conflict_resolution(self):\n", 143 | " \"\"\"\n", 144 | " Resolves conflicts by replacing current chain with the longest one in the network.\n", 145 | " \"\"\"\n", 146 | "\n", 147 | " neighbours = self.nodes\n", 148 | " new_chain = None\n", 149 | "\n", 150 | " #Identifying long chains\n", 151 | " max_length = len(self.chain)\n", 152 | "\n", 153 | " #Grab and verify the chains from all the nodes in the network\n", 154 | " for node in neighbours:\n", 155 | " response = requests.get(f'http://{node}/chain')\n", 156 | "\n", 157 | " if response.status_code == 200:\n", 158 | " length = response.json()['length']\n", 159 | " chain = response.json()['chain']\n", 160 | "\n", 161 | " #Check if the length is longer and the chain is valid\n", 162 | " if length > max_length and self.valid_chain(chain):\n", 163 | " max_length = length\n", 164 | " new_chain = chain\n", 165 | "\n", 166 | " #Replace chain if a valid longer chain is discovered\n", 167 | " if new_chain:\n", 168 | " self.chain = new_chain\n", 169 | " return True\n", 170 | "\n", 171 | " return False\n", 172 | "\n", 173 | " def new_block(self, proof, prev_hash):\n", 174 | "\n", 175 | " block = {\n", 176 | " 'index': len(self.chain) + 1,\n", 177 | " 'timestamp': time(),\n", 178 | " 'transactions': self.current_trans,\n", 179 | " 'proof': proof,\n", 180 | " 'prev_hash': prev_hash or self.hash(self.chain[-1]),\n", 181 | " }\n", 182 | "\n", 183 | " #Reset the current list of transactions\n", 184 | " self.current_trans = []\n", 185 | "\n", 186 | " self.chain.append(block)\n", 187 | " return block\n", 188 | "\n", 189 | " def new_trans(self, sender, recipient, amount):\n", 190 | " \"\"\"\n", 191 | " Creates a new transaction to go into the next mined Block.\n", 192 | " \"\"\"\n", 193 | " self.current_trans.append({\n", 194 | " 'sender': sender,\n", 195 | " 'recipient': recipient,\n", 196 | " 'amount': amount,\n", 197 | " })\n", 198 | "\n", 199 | " return self.prev_block['index'] + 1\n", 200 | "\n", 201 | " @property\n", 202 | " def prev_block(self):\n", 203 | " return self.chain[-1]\n", 204 | "\n", 205 | " @staticmethod\n", 206 | " def hash(block):\n", 207 | " \"\"\"\n", 208 | " SHA-256 encryption\n", 209 | " \"\"\"\n", 210 | "\n", 211 | " #Ensure that dictionary is ordered, to avoid inconsistent hashes.\n", 212 | " block_str = json.dumps(block, sort_keys=True).encode()\n", 213 | " return hashlib.sha256(block_str).hexdigest()\n", 214 | "\n", 215 | " def proof_of_work(self, prev_block):\n", 216 | " \n", 217 | " #Proof of Work Algorithm:\n", 218 | " #- Find a number p' such that hash(pp') contains leading 4 zeroes\n", 219 | " #- Where p is the previous proof, and p' is the new proof\n", 220 | "\n", 221 | " prev_proof = prev_block['proof']\n", 222 | " prev_hash = self.hash(prev_block)\n", 223 | "\n", 224 | " proof = 0\n", 225 | " while self.valid_proof(prev_proof, proof, prev_hash) is False:\n", 226 | " proof += 1\n", 227 | "\n", 228 | " return proof\n", 229 | "\n", 230 | " @staticmethod\n", 231 | " def valid_proof(prev_proof, proof, prev_hash):\n", 232 | "\n", 233 | " #Validates Proof\n", 234 | "\n", 235 | " guess = f'{prev_proof}{proof}{prev_hash}'.encode()\n", 236 | " guess_hash = hashlib.sha256(guess).hexdigest()\n", 237 | " return guess_hash[:4] == \"0000\"" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "### Integration of XGBoosted KMeans with Blockchain" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "#Instantiate the Node\n", 254 | "app = Flask(__name__)\n", 255 | "\n", 256 | "#Generate a globally unique address for this node\n", 257 | "node_id = str(uuid4()).replace('-', '')\n", 258 | "\n", 259 | "#Instantiate the Blockchain\n", 260 | "blockchain = Blockchain()\n", 261 | "\n", 262 | "\n", 263 | "@app.route('/mine', methods=['GET'])\n", 264 | "def mine():\n", 265 | " #Run the proof of work algorithm to get the next proof...\n", 266 | " prev_block = blockchain.prev_block\n", 267 | " proof = blockchain.proof_of_work(prev_block)\n", 268 | "\n", 269 | " #Receive a reward for finding the proof.\n", 270 | " #The sender is \"0\" to signify a new transaction.\n", 271 | " blockchain.new_trans(\n", 272 | " sender=\"0\",\n", 273 | " recipient=node_id,\n", 274 | " amount=1,\n", 275 | " )\n", 276 | "\n", 277 | " #Forge the new Block by adding it to the chain\n", 278 | " prev_hash = blockchain.hash(prev_block)\n", 279 | " block = blockchain.new_block(proof, prev_hash)\n", 280 | "\n", 281 | " response = {\n", 282 | " 'message': \"New Block Forged\",\n", 283 | " 'index': block['index'],\n", 284 | " 'transactions': block['transactions'],\n", 285 | " 'proof': block['proof'],\n", 286 | " 'prev_hash': block['prev_hash'],\n", 287 | " }\n", 288 | " return jsonify(response), 200\n", 289 | "\n", 290 | "\n", 291 | "@app.route('/transactions/new', methods=['POST'])\n", 292 | "def new_trans():\n", 293 | " values = request.get_json()\n", 294 | "\n", 295 | " #Check that the required fields are in the POST'ed data\n", 296 | " required = ['sender', 'recipient', 'amount']\n", 297 | " if not all(k in values for k in required):\n", 298 | " return 'Missing values', 400\n", 299 | "\n", 300 | " #Create a new Transaction\n", 301 | " index = blockchain.new_trans(values['sender'], values['recipient'], values['amount'])\n", 302 | "\n", 303 | " response = {'message': f'Transaction will be added to Block {index}'}\n", 304 | " \n", 305 | " #Kmeans clustering is implemented on the newly formed chain\n", 306 | "\n", 307 | "\n", 308 | " #Building the k-means model\n", 309 | "\n", 310 | " kmeans = KMeans(n_clusters=2)\n", 311 | " kmeans.fit(x_train)\n", 312 | " KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,\n", 313 | " n_clusters=2, n_init=10, n_jobs=1, precompute_distances='auto',\n", 314 | " random_state=None, tol=0.0001, verbose=0)\n", 315 | " correct = 0\n", 316 | " for i in range(len(x_test)):\n", 317 | " predict_me = np.array(test_x[i].astype(float))\n", 318 | " predict_me = predict_me.reshape(-1, len(predict_me))\n", 319 | " prediction = kmeans.predict(predict_me)\n", 320 | " if prediction[0] == y[i]:\n", 321 | " correct += 1\n", 322 | "\n", 323 | " print(correct/len(x_test))\n", 324 | " return jsonify(response), 201\n", 325 | "\n", 326 | " #fit model no training data\n", 327 | " model = XGBClassifier()\n", 328 | "\n", 329 | "@app.route('/chain', methods=['GET'])\n", 330 | "def full_chain():\n", 331 | " response = {\n", 332 | " 'chain': blockchain.chain,\n", 333 | " 'length': len(blockchain.chain),\n", 334 | " }\n", 335 | " return jsonify(response), 200\n", 336 | " \n", 337 | "@app.route('/nodes/register', methods=['POST'])\n", 338 | "def new_nodes():\n", 339 | " values = request.get_json()\n", 340 | "\n", 341 | " nodes = values.get('nodes')\n", 342 | " if nodes is None:\n", 343 | " return \"Error: Please supply a valid list of nodes\", 400\n", 344 | "\n", 345 | " for node in nodes:\n", 346 | " blockchain.new_node(node)\n", 347 | "\n", 348 | " response = {\n", 349 | " 'message': 'New nodes have been added',\n", 350 | " 'total_nodes': list(blockchain.nodes),\n", 351 | " }\n", 352 | " return jsonify(response), 201\n", 353 | "\n", 354 | "\n", 355 | "@app.route('/nodes/resolve', methods=['GET'])\n", 356 | "def consensus():\n", 357 | " replaced = blockchain.conflict_resolution()\n", 358 | "\n", 359 | " if replaced:\n", 360 | " response = {\n", 361 | " 'message': 'Our chain was replaced',\n", 362 | " 'new_chain': blockchain.chain\n", 363 | " }\n", 364 | " else:\n", 365 | " response = {\n", 366 | " 'message': 'Our chain is authoritative',\n", 367 | " 'chain': blockchain.chain\n", 368 | " }\n", 369 | "\n", 370 | " return jsonify(response), 200\n", 371 | "\n", 372 | "\n", 373 | "if __name__ == '__main__':\n", 374 | " from argparse import ArgumentParser\n", 375 | "\n", 376 | " parser = ArgumentParser()\n", 377 | " parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on')\n", 378 | " args = parser.parse_args()\n", 379 | " port = args.port\n", 380 | "\n", 381 | " app.run(host='0.0.0.0', port=port)" 382 | ] 383 | } 384 | ], 385 | "metadata": { 386 | "kernelspec": { 387 | "display_name": "Python 3", 388 | "language": "python", 389 | "name": "python3" 390 | }, 391 | "language_info": { 392 | "codemirror_mode": { 393 | "name": "ipython", 394 | "version": 3 395 | }, 396 | "file_extension": ".py", 397 | "mimetype": "text/x-python", 398 | "name": "python", 399 | "nbconvert_exporter": "python", 400 | "pygments_lexer": "ipython3", 401 | "version": "3.7.4" 402 | }, 403 | "latex_envs": { 404 | "LaTeX_envs_menu_present": true, 405 | "autoclose": false, 406 | "autocomplete": true, 407 | "bibliofile": "biblio.bib", 408 | "cite_by": "apalike", 409 | "current_citInitial": 1, 410 | "eqLabelWithNumbers": true, 411 | "eqNumInitial": 1, 412 | "hotkeys": { 413 | "equation": "Ctrl-E", 414 | "itemize": "Ctrl-I" 415 | }, 416 | "labels_anchors": false, 417 | "latex_user_defs": false, 418 | "report_style_numbering": false, 419 | "user_envs_cfg": false 420 | } 421 | }, 422 | "nbformat": 4, 423 | "nbformat_minor": 2 424 | } 425 | -------------------------------------------------------------------------------- /Fraud Detection Using KMeans.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Blockchain and Machine Learning for Fraud Detection: Employing Artificial Intelligence in the Banking Sector" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "##
By Vinita Silaparasetty" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "#### Credits\n", 22 | "\n", 23 | "##### Source: http://lisp.vse.cz/pkdd99/berka.html\n", 24 | "\n", 25 | "##### Prepared by: Petr Berka and Marta Sochorova." 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Fraud Detection Using KMeans" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "#### Import Libraries" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 1, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "import numpy as np\n", 49 | "import scipy\n", 50 | "import pandas as pd\n", 51 | "import matplotlib\n", 52 | "%matplotlib inline\n", 53 | "import matplotlib.pyplot as plt\n", 54 | "import seaborn as sns\n", 55 | "import sklearn\n", 56 | "from sklearn.model_selection import train_test_split\n", 57 | "from sklearn.cluster import KMeans\n", 58 | "from sklearn.preprocessing import StandardScaler\n", 59 | "from sklearn.datasets import make_moons\n", 60 | "from sklearn.cluster import SpectralClustering" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "#### Import Data" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 2, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "order= pd.read_csv(\"order.csv\")\n", 77 | "account= pd.read_csv(\"account.csv\")\n", 78 | "transaction= pd.read_csv(\"transaction.csv\")" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "## Process Demo\n", 86 | "\n", 87 | "Demonstration of the working of KMeans clustering with visualization of the output." 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "### Order & Account Dataframes" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 8, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "#working with 'order' dataframe and 'account' dataframe\n", 104 | "x = (order['account_id'],order['account_to'],order['amount'])\n", 105 | "y = (account['account_id'],account['district_id'],account['frequency'])" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 9, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "# Splitting the dataset into the Training set and Test set\n", 115 | "X_train, X_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state = 0)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 10, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "# Feature Scaling\n", 125 | "from sklearn.preprocessing import StandardScaler\n", 126 | "sc_X = StandardScaler()\n", 127 | "X_train = sc_X.fit_transform(X_train)\n", 128 | "X_test = sc_X.transform(X_test)\n", 129 | "sc_y = StandardScaler()\n", 130 | "y_train = sc_y.fit_transform(y_train)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "#### Applying KMeans" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 11, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "kmeans = KMeans(n_clusters = 3, init = 'k-means++', random_state = 42)\n", 147 | "y_kmeans = kmeans.fit_predict(x)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "#### Visualize the Clusters" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 12, 160 | "metadata": {}, 161 | "outputs": [ 162 | { 163 | "data": { 164 | "image/png": "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\n", 165 | "text/plain": [ 166 | "
" 167 | ] 168 | }, 169 | "metadata": { 170 | "needs_background": "light" 171 | }, 172 | "output_type": "display_data" 173 | } 174 | ], 175 | "source": [ 176 | "x, y = make_moons(200, noise=.05, random_state=0)\n", 177 | "labels = KMeans(2, random_state=0).fit_predict(x)\n", 178 | "plt.scatter(x[:, 0], x[:, 1], c=labels,\n", 179 | " s=50, cmap='viridis');" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "###
-------------------- End of Demo --------------------" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "###### I skipped the visualizations for the rest of the analysis procedure as it is not needed for the main program." 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "### Order & Transaction Dataframes" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 13, 206 | "metadata": {}, 207 | "outputs": [], 208 | "source": [ 209 | "# working with 'order' dataframe and 'transaction' dataframe\n", 210 | "a = (order['account_id'],order['account_to'],order['amount'])\n", 211 | "b = (transaction['account_id'],transaction['balance'],transaction['amount'])" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 14, 217 | "metadata": {}, 218 | "outputs": [], 219 | "source": [ 220 | "# Splitting the dataset into the Training set and Test set\n", 221 | "X_train, X_test, y_train, y_test = train_test_split(a, b, test_size = 0.2, random_state = 0)" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 15, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "# Feature Scaling\n", 231 | "from sklearn.preprocessing import StandardScaler\n", 232 | "sc_X = StandardScaler()\n", 233 | "X_train = sc_X.fit_transform(X_train)\n", 234 | "X_test = sc_X.transform(X_test)\n", 235 | "sc_y = StandardScaler()\n", 236 | "y_train = sc_y.fit_transform(y_train)" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 16, 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [ 245 | "kmeans = KMeans(n_clusters = 3, init = 'k-means++', random_state = 42)\n", 246 | "y_kmeans = kmeans.fit_predict(a)" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "### Account & Transaction Dataframes" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 19, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "# working with 'account' dataframe and 'transaction' dataframe\n", 263 | "e = (account['account_id'],account['district_id'])\n", 264 | "f = (transaction['account_id'],transaction['amount'])" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 20, 270 | "metadata": {}, 271 | "outputs": [], 272 | "source": [ 273 | "# Splitting the dataset into the Training set and Test set\n", 274 | "X_train, X_test, y_train, y_test = train_test_split(e,f, test_size = 0.2, random_state = 0)" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": 21, 280 | "metadata": {}, 281 | "outputs": [], 282 | "source": [ 283 | "# Feature Scaling\n", 284 | "from sklearn.preprocessing import StandardScaler\n", 285 | "sc_X = StandardScaler()\n", 286 | "X_train = sc_X.fit_transform(X_train)\n", 287 | "X_test = sc_X.transform(X_test)\n", 288 | "sc_y = StandardScaler()\n", 289 | "y_train = sc_y.fit_transform(y_train)" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": 23, 295 | "metadata": {}, 296 | "outputs": [], 297 | "source": [ 298 | "kmeans = KMeans(n_clusters = 2, init = 'k-means++', random_state = 42)\n", 299 | "y_kmeans = kmeans.fit_predict(e)" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "##### Note: \n", 307 | "The dataset consisists of several csv files, but I have used only three of the files they serve the pupose of my research. the files I used are:\n", 308 | "\n", 309 | "1) order.csv\n", 310 | "\n", 311 | "2) account.csv\n", 312 | "\n", 313 | "3) transaction.csv\n", 314 | " " 315 | ] 316 | } 317 | ], 318 | "metadata": { 319 | "kernelspec": { 320 | "display_name": "Python 3", 321 | "language": "python", 322 | "name": "python3" 323 | }, 324 | "language_info": { 325 | "codemirror_mode": { 326 | "name": "ipython", 327 | "version": 3 328 | }, 329 | "file_extension": ".py", 330 | "mimetype": "text/x-python", 331 | "name": "python", 332 | "nbconvert_exporter": "python", 333 | "pygments_lexer": "ipython3", 334 | "version": "3.7.3" 335 | } 336 | }, 337 | "nbformat": 4, 338 | "nbformat_minor": 2 339 | } 340 | --------------------------------------------------------------------------------