{"cells":[{"metadata":{"_uuid":"48f08b7514f325b1d8dd73bea52c15ae2d2433c1"},"cell_type":"markdown","source":"# Objective of the notebook\n\nThe main objective is to show that there is little information in the merchant_id column by itself (in historical_transactions.csv)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b7a1d9a25db379dbcc4df96c18cac1d499947382"},"cell_type":"code","source":"hist_df = pd.read_csv(\"../input/historical_transactions.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"308b143b4cd816232fcb95a4ec8a6855a639857e"},"cell_type":"code","source":"hist_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"246ce4cdba4706b5a6f0c2e7161ba2627344a214"},"cell_type":"markdown","source":"Lets index card_ids and merchant_ids so that we can use matrix methods"},{"metadata":{"trusted":true,"_uuid":"5fb914548b686894197a5766f3638262e4a212f8"},"cell_type":"code","source":"card_id_to_idx = dict((card_id, i) for i, card_id in enumerate(hist_df[\"card_id\"].unique()))\nmerchant_id_to_idx = dict((merchant_id, i) for i, merchant_id in enumerate(hist_df[\"merchant_id\"].unique()))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2d7f01dcca8d3e8b9c68bdb0f01b50edc6c7fcdd"},"cell_type":"markdown","source":"Place the indexes in two columns of the dataframe"},{"metadata":{"trusted":true,"_uuid":"db0f5d0a672340062a30148da6f0d434f44f75f2"},"cell_type":"code","source":"hist_df[\"u\"] = hist_df[\"card_id\"].apply(lambda x: card_id_to_idx[x])\nhist_df[\"v\"] = hist_df[\"merchant_id\"].apply(lambda x: merchant_id_to_idx[x])\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"82045e99409616875d33341458d40ade3c0080e3"},"cell_type":"markdown","source":"# Matrix formulation\nLets see if we get any signal if we draw a graph linking cust_id and merchant_id if there was a purchase\nThe matrix **mat** represents the *adjaceny matrix* and **mat_tilde** represents the *normalized adjacency matrix* of this graph"},{"metadata":{"trusted":true,"_uuid":"4e4b5b49d5270cac3eba0bdeb53c5b717be4a2a8"},"cell_type":"code","source":"import scipy.sparse\n\nm = len(card_id_to_idx)\nn = len(merchant_id_to_idx)\n\nu = hist_df[\"u\"].values\nv = hist_df[\"v\"].values\nw = [1 for _ in u]\n\nmat = scipy.sparse.coo_matrix((w, (u, v)), shape=(m, n)).tocsr()\n\nd1 = np.squeeze(np.sum(mat, axis=1), axis=1).tolist()[0]\nd1tilde = [1. / np.sqrt(x) if x > 0 else 0. for x in d1]\n\nd2 = np.squeeze(np.sum(mat, axis=0)).tolist()[0]\nd2tilde = [1. / np.sqrt(x) if x > 0 else 0. for x in d2]\n\ndef diags(l):\n    idx = [i for i in range(len(l))]\n    return scipy.sparse.coo_matrix((l, (idx, idx))).tocsr()\n\nmat_tilde = diags(d1tilde).dot(mat).dot(diags(d2tilde))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4f79b041e04a37c57287ab19c632db6d792a4809"},"cell_type":"markdown","source":"# Singular value decomposition of normalized adjacency matrix\n\n* Lets create features using the left singular values of the *normalized adjacency matix* **mat_tilde**\n* We will use 50 singular vectors"},{"metadata":{"trusted":true,"_uuid":"7249b8302be3020123883c3c1714e9eeaf838376"},"cell_type":"code","source":"import scipy.sparse.linalg\n\nnproj = 50\n\n# proj_mat = np.random.randn(*(n, nproj))\n# feat_mat = mat_tilde.dot(proj_mat)\n# mat = mat.asfptype()\nu, s, vt = scipy.sparse.linalg.svds(mat_tilde, k=nproj)\n\nu.shape, s.shape, vt.shape","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"cf834e5a7325a1622de8d2ef5caab073d2eaa435"},"cell_type":"markdown","source":"# Lets plot the singular vectors\n* The graph is disconnected with two components (two singular vectors are equal to 1)\n* The other 48 singular vectors should give us a good embedding of card_id if there is one "},{"metadata":{"trusted":true,"_uuid":"168e02b8313e9ba4f18bd6bf619673783be6cd5e"},"cell_type":"code","source":"import matplotlib.pyplot as plt\nplt.stem(s)\nprint(np.sum(s==1))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2b3910c80481328793123b8fe4c10dd7b988944c"},"cell_type":"markdown","source":"# Embedding of card_id using merchant_id s"},{"metadata":{"trusted":true,"_uuid":"38bc8e21945e88fd273acc0f8c35fa589ff865fc"},"cell_type":"code","source":"feat_mat = u.dot(np.diag(s))\nfeat_mat.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"43ae12f6a0f90f609787a51c11d92fed043d7c5d"},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ndf = pd.read_csv(\"../input/train.csv\")\ntrain_df, val_df = train_test_split(df, test_size=0.1, random_state=2018)\ntest_df = pd.read_csv(\"../input/test.csv\")\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7e83df0892c9687bca8f36853b180eda6dfd4f2f"},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c9015e0cb3c42fff719b101c2d556207e0ee1f60"},"cell_type":"markdown","source":"# Use embedding to generate features for train, val and test data"},{"metadata":{"trusted":true,"_uuid":"e61f587d301886ef0dbf336b3de8557c330abdb5"},"cell_type":"code","source":"def get_labeled_data(df):\n    y = None\n    \n    if \"target\" in df.columns:\n        y = df[\"target\"].values\n    \n    X = np.zeros((df.shape[0], nproj))\n    \n    for i, irow in enumerate(df.iterrows()):\n        _, row = irow\n        idx = card_id_to_idx[row[\"card_id\"]]\n        X[i, :] = feat_mat[idx, :]\n    \n    valid_idx = np.sum(np.abs(X), axis=1) > 0\n    \n    return X, y, valid_idx","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2d4c7af51ff6ad12bb6ae18704468005d719822d"},"cell_type":"code","source":"train_X, train_y, train_idx = get_labeled_data(train_df)\nval_X, val_y, val_idx = get_labeled_data(val_df)\ntest_X, _, _ = get_labeled_data(test_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0e8de062835f081ba15933c517ec92f2b421d809"},"cell_type":"code","source":"from sklearn.ensemble import GradientBoostingRegressor\n\nmodel = GradientBoostingRegressor(verbose=2, n_estimators=50)\nmodel.fit(train_X[train_idx, :], train_y[train_idx])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"468bc1f0860648dfd267ce52551daff459aad6ab"},"cell_type":"markdown","source":"# We dont improve much over constant predictor"},{"metadata":{"trusted":true,"_uuid":"0370d0ba6d9ee71cf3e6ec884345de33ac98dbde"},"cell_type":"code","source":"print(\"Preformance improved to {} from the trivial value of {}\".format(np.sqrt(np.mean(np.square(model.predict(val_X[val_idx, :]) - val_y[val_idx]))), np.sqrt(np.mean(np.square(np.mean(train_y[train_idx]) - val_y[val_idx])))))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6647ce2b3476aeb4fbd49445baa048885180d031"},"cell_type":"code","source":"pd.DataFrame({\n    \"card_id\": test_df[\"card_id\"],\n    \"target\": model.predict(test_X)\n}).to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}