From 9eb5f2a36b4cbe3edd352cb0b001ebcda0c0906d Mon Sep 17 00:00:00 2001 From: sinemypolat Date: Wed, 11 Jul 2018 22:39:26 +0100 Subject: [PATCH 1/6] Adding files for challenge --- README.md | 22 +-------------- future_work.txt | 21 ++++++++++++++ get_scores.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++ orders.zip | 3 -- utils.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 24 deletions(-) create mode 100644 future_work.txt create mode 100644 get_scores.py delete mode 100644 orders.zip create mode 100644 utils.py diff --git a/README.md b/README.md index b0367eb..ad5e1f9 100644 --- a/README.md +++ b/README.md @@ -1,21 +1 @@ -## Hiring Challenge for Data Scientists - -### Introduction - -Two of our "clients" have a similar problem: they each have data on which of their customers made purchases at which times, and they each want to assign a value to each of their customers (there could be different commercial reasons for this -- for instance you may want to use the information to target an email campaign at customers that are at risk of never purchasing again, or simply to quantify customer health over time). -Your task is to build a solution that can take either client's data, and returns a "health" score for each customer. - -### Instructions - -Build a standalone command-line application in the language of your choice that takes the path of a single input CSV datafile as a command-line argument and - - loads and validates the input dataset of customer transaction data - - trains a model that predicts a customer's health as a float from `0.0 - 1.0` given their transaction history - - prints a CSV file containing the customer ID and health score per row to `stdout` - -The `orders.zip` archive contains two sample transaction datasets that can each be used as input to generate customer predictions. The files come from two different domains and are independent, with their own schema and consist of "messy" real world data - your solution is expected to be able to work with each sample dataset individually to output predictions. - -You can use any 1st- or 3rd-party existing library functions, packages, frameworks, models, and solvers you like, or can build a solution/model from scratch if you prefer. - -There is no right answer as such, we will mainly be looking at code quality, data preprocessing skills, completeness of the solution from a software engineering perspective, and clarity of thought. - -To get started, we recommend forking and cloning this repo, and then either point us to your fork or submit a PR - thanks! (Note, you'll need `git-lfs` installed to pull down the datasets, or just download direct from the [GitHub source browser](https://github.com/nstack/hiring-ds/blob/master/orders.zip)) + diff --git a/future_work.txt b/future_work.txt new file mode 100644 index 0000000..ccd6071 --- /dev/null +++ b/future_work.txt @@ -0,0 +1,21 @@ +ScoreUp is a simple scoring model where you may use to score customer 'health' based on their previous purchases. It takes a dataset file path as input and the dataset must include 3 arguments in order: + +1. date_col: Name of the column for purchase dates in any format. +2. id_col: Name of the column for customer id. +3. price_col: Name of the column for the purchase amount. + +ScoreUp prepare/preprocess dataset, splits it into training and test sets and then uses ordinary linear regression from scikit-learn library. + +Future Improvements: + +- More data can be provided in addition to purchase data and model can be improved in the light of the new data. + +- Data processing functions are written based on 2 example input datasets. More general rules can be followed to provide the flexibility of working with any dataset. + +- More independent modules can be added so that: + - a model can be trained with a training set then downloaded to local disk + - there would be ready-to-use models to make score predictions + - a downloaded model can be read and used as a model to make score predictions + + + diff --git a/get_scores.py b/get_scores.py new file mode 100644 index 0000000..4209ea4 --- /dev/null +++ b/get_scores.py @@ -0,0 +1,72 @@ + +import argparse +import os +from sklearn.linear_model import LinearRegression +from sklearn.metrics import mean_squared_error,r2_score,mean_absolute_error,accuracy_score + +from utils import * + +def main(args): + + assert os.path.isfile(args.filepath),'File path not found... must provide a valid file path...' + + print('Preprocessing data...') + + processed_df = prepare_data(args.filepath, args.date_col, args.id_col, args.price_col) + + print('Splitting data into training and test sets...') + + X_train, X_test, y_train, y_test = split_data(processed_df, args.date_col, args.id_col, args.price_col) + + print('Training set size : ', len(X_train)) + print('Test set size : ', len(X_test)) + + y_actual = y_test + + model = LinearRegression() + model.fit(X_train, y_train) + y_pred = model.predict(X_test) + + print('Mean Absolute Error: ', mean_absolute_error(y_actual,y_pred)) + print('Mean Squared Error:', np.sqrt(mean_squared_error(y_actual,y_pred))) + print('R2 Score: ', r2_score(y_actual,y_pred)) + + y_pred = min_max_scaler(pd.DataFrame(y_pred)) + + ids = pd.DataFrame(X_test.index) + scores = pd.concat([ids, y_pred], axis=1, join_axes=[y_pred.index]) + scores.columns = ['id','score'] + with pd.option_context('display.max_rows', None, 'display.max_columns', 3): + print(scores) + + scores.to_csv('score_report.csv') + print('Score report is saved to local disk.') + +if __name__=="__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + '-f', '--filepath', + help="Dataset file path to use as input; example: your_file_path.csv", + required=True, type=str) + parser.add_argument( + '-d', '--date_col', + help="Date column name in dataset file", + required=True, type=str) + parser.add_argument( + '-id', '--id_col', + help="Customer ID column name in dataset file", + required=True, type=str) + parser.add_argument( + '-p', '--price_col', + help="Price column name in dataset file", + required=True, type=str) + + args = parser.parse_args() + + if len(args) != 4: + print("This application expects 4 arguments") + + main(args) + + + \ No newline at end of file diff --git a/orders.zip b/orders.zip deleted file mode 100644 index 6c30209..0000000 --- a/orders.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ecaddc9abc1caab60674492bc35d5af9ceb32da09b073873f68ec6611cb050f2 -size 13171540 diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..ef43eb3 --- /dev/null +++ b/utils.py @@ -0,0 +1,75 @@ +#utils.py + +import pandas as pd +import numpy as np +import datetime as dt +from sklearn.preprocessing import MinMaxScaler, Imputer, StandardScaler +from sklearn.model_selection import train_test_split + +def prepare_data(file_dir, date_col, id_col, price_col): + df = pd.read_csv(file_dir, date_parser=True, usecols=[date_col, id_col, price_col]) + + df[price_col] = df[price_col].astype(str) + df[price_col] = df[price_col].str.replace(',','') + + df.replace([-1, "null","nan","NaN",'NaT', 'nat'], np.nan, inplace = True) + + df[price_col] = pd.to_numeric(df[price_col]) + negative_values = df[price_col] <= 0.0 + df.loc[negative_values, price_col] = np.nan + + mean_imputer = Imputer(missing_values=np.nan, strategy='mean', axis=0) + df[price_col] = mean_imputer.fit_transform(np.array(df[price_col]).reshape(-1,1)) + + df.dropna(subset=[id_col, date_col], inplace=True) + df[date_col] = pd.to_datetime(df[date_col]) + + return df + + +def pivot_df_feature(df, date_col, id_col, price_col): + df_pivot_1 = pd.pivot_table(df, values=price_col, index=[id_col], columns=df[date_col].dt.month , aggfunc=np.sum) + df_pivot_1 = df_pivot_1.add_suffix('_price') + + df_pivot_2 = pd.pivot_table(df, values=price_col, index=[id_col], columns=df[date_col].dt.month , aggfunc=np.count_nonzero) + df_pivot_2 = df_pivot_2.add_suffix('_count') + + df_pivot = pd.concat([df_pivot_1,df_pivot_2], axis=1, join_axes=[df_pivot_1.index]) + df_pivot.fillna(0, inplace=True) + + return df_pivot + +def pivot_df_target(df, date_col, id_col, price_col): + df_pivot = pd.pivot_table(df, values=price_col, index=[id_col], columns=df[date_col].dt.month , aggfunc=np.sum) + df_pivot = df_pivot.add_suffix('_price') + + df_pivot.fillna(0, inplace=True) + + return df_pivot + +def split_data(df, date_col, id_col, price_col): + df = df.sort_values(by=date_col) + #getting the last month as our target data + target_data = df[df[date_col].dt.strftime("%m-%y")==df[date_col].iloc[-1].strftime("%m-%y")] + feature_data = df[df[date_col].dt.strftime("%m-%y")!=df[date_col].iloc[-1].strftime("%m-%y")] + + #pivoting target and feature data + target_data = pivot_df_target(target_data, date_col, id_col, price_col) + feature_data = pivot_df_feature(feature_data, date_col, id_col, price_col) + + #get mutual customers and split data into train and test + idx = feature_data.index.intersection(target_data.index) + target_data = (target_data.loc[idx]).sort_index() + feature_data = (feature_data.loc[idx]).sort_index() + + X_train, X_test, y_train, y_test = train_test_split(feature_data, target_data, test_size=0.30) + + return X_train, X_test, y_train, y_test + +def min_max_scaler(x): + float_array = x.values.astype(float) + min_max_scaler = MinMaxScaler() + scaled = pd.DataFrame(min_max_scaler.fit_transform(float_array.reshape(-1,1))) + + return scaled + From 823f6908ee6376d22a9a0ea5de1478a738043957 Mon Sep 17 00:00:00 2001 From: sinemypolat Date: Wed, 11 Jul 2018 22:42:31 +0100 Subject: [PATCH 2/6] Remove future_work.txt --- README.md | 22 +++++++++++++++++++++- Score UP.png | Bin 0 -> 38521 bytes future_work.txt | 21 --------------------- 3 files changed, 21 insertions(+), 22 deletions(-) create mode 100644 Score UP.png delete mode 100644 future_work.txt diff --git a/README.md b/README.md index ad5e1f9..044fdaa 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ - + + +ScoreUp is a simple scoring model where you may use to score customer 'health' based on their previous purchases. It takes a dataset file path as input and the dataset must include 3 arguments in order: + +1. date_col: Name of the column for purchase dates in any format. +2. id_col: Name of the column for customer id. +3. price_col: Name of the column for the purchase amount. + +ScoreUp prepare/preprocess dataset, splits it into training and test sets and then uses ordinary linear regression from scikit-learn library. + +Future Improvements: + +- More data can be provided in addition to purchase data and model can be improved in the light of the new data. + +- Data processing functions are written based on 2 example input datasets. More general rules can be followed to provide the flexibility of working with any dataset. + +- More independent modules can be added so that: + - a model can be trained with a training set then downloaded to local disk + - there would be ready-to-use models to make score predictions + - a downloaded model can be read and used as a model to make score predictions + diff --git a/Score UP.png b/Score UP.png new file mode 100644 index 0000000000000000000000000000000000000000..1b6536dea5dc57ab5e0c7c4629a0bb01ac123a08 GIT binary patch literal 38521 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 c-NuuF7Sn)U?!I)ab Date: Wed, 11 Jul 2018 22:44:48 +0100 Subject: [PATCH 3/6] Update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 044fdaa..699639c 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,7 @@ Future Improvements: - there would be ready-to-use models to make score predictions - a downloaded model can be read and used as a model to make score predictions +How to Run: +python get_scores.py -f -d -id -p + + From 33faa70eb35aeac5e0fa0e133b9afd35c812a2cc Mon Sep 17 00:00:00 2001 From: Sinem Yekbun Polat <30531582+sinemypolat@users.noreply.github.com> Date: Wed, 11 Jul 2018 22:45:53 +0100 Subject: [PATCH 4/6] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 699639c..d39aa63 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -ScoreUp is a simple scoring model where you may use to score customer 'health' based on their previous purchases. It takes a dataset file path as input and the dataset must include 3 arguments in order: +**ScoreUp** is a simple scoring model where you may use to score customer 'health' based on their previous purchases. It takes a dataset file path as input and the dataset must include 3 arguments in order: 1. date_col: Name of the column for purchase dates in any format. 2. id_col: Name of the column for customer id. @@ -8,7 +8,7 @@ ScoreUp is a simple scoring model where you may use to score customer 'health' b ScoreUp prepare/preprocess dataset, splits it into training and test sets and then uses ordinary linear regression from scikit-learn library. -Future Improvements: +**Future Improvements:** - More data can be provided in addition to purchase data and model can be improved in the light of the new data. @@ -19,7 +19,8 @@ Future Improvements: - there would be ready-to-use models to make score predictions - a downloaded model can be read and used as a model to make score predictions -How to Run: +**How to Run:** + python get_scores.py -f -d -id -p From daa58c73d820a3176ef45b7a42d33d4efe4eb4b1 Mon Sep 17 00:00:00 2001 From: Sinem Yekbun Polat <30531582+sinemypolat@users.noreply.github.com> Date: Thu, 12 Jul 2018 16:48:28 +0100 Subject: [PATCH 5/6] Update utils.py --- utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/utils.py b/utils.py index ef43eb3..56b9c64 100644 --- a/utils.py +++ b/utils.py @@ -1,4 +1,3 @@ -#utils.py import pandas as pd import numpy as np From eda4172a27d3890bceb46d5f51c0ca8db4ff366f Mon Sep 17 00:00:00 2001 From: Sinem Yekbun Polat <30531582+sinemypolat@users.noreply.github.com> Date: Tue, 17 Jul 2018 13:34:22 +0100 Subject: [PATCH 6/6] Update get_scores.py --- get_scores.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/get_scores.py b/get_scores.py index 4209ea4..1bcaa3e 100644 --- a/get_scores.py +++ b/get_scores.py @@ -63,10 +63,7 @@ def main(args): args = parser.parse_args() - if len(args) != 4: - print("This application expects 4 arguments") - main(args) - \ No newline at end of file +