{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Assignment 3\n",
"\n",
"We have information about several auctions at different times. The goal of the assignment is to predict whether a certain auction will be succesfull or not and then optimize a certain auction in order to get the most profit. The assignment consists of the following steps: \n",
" \n",
"1) *Descriptive Analysis* \n",
"Here we do some basic exploratory analysis in order to get a feeling for the data and what we can do with it. We are allowed to also use correlation, plots and some simple inferences. Moreover, this is a nice way to start thinking about creating new features if neccesary. \n",
" \n",
"2) *Prediction Model* \n",
"The goal is to build a prediction model that can either classify or predict the performance of a new auction. We are free to choose either classification or prediction as long as we are accurate since the model is used in step 3. Moreover, it should also be a relatively fast method in order to use it in our optimization in step 3. \n",
" \n",
"3) *Optimization* \n",
"Given a new auction, optimize its features in such a way that you will maximize the final output. In other words, optimize the different variables and their values in a way to get the most money out of your auction. For example, if you have an item/lot that you expect will be worth 200 euros, then setting the starting bid at 50 euros or 100 euros might make a big difference on the end price. Each time we change a value, we check with our prediction model whether that change is a good one or not in order to eventually get the best \"lay-out\" of an auction. \n",
"\n",
"**Features** \n",
"We have the following features in the dataset: \n",
"* **Multiplier** This is defined as (Winning Bid / Estimated Value)\n",
"* **LotNr** An items number, the lower the number the earlier the item is shown on the site\n",
"* **Allocate** Whether the seller of the lot has set a price which he want as minimum for the lot \n",
"* **EstValue**: estimated value of the lot (by auction experts)\n",
"* **StartPrice**: starting bidding price of the lot\n",
"* **Followers**: nr. of people following the lot\n",
"* **Bank, Dealer, Liquidator, Volunteer**: type of sales\n",
"* **LotsSale**: amount of lots of one sale\n",
"* **LotsCtgry**: amount of lots within a sale with the same category\n",
"* **Forced**: whether a sale is forced or not (due to bankruptcy)\n",
"* **SP.EV**: starting price/estimated value\n",
"* **Duration**: duration of auctions in hours on a lot\n",
"* **Morning, Evening, Afternoon**: last bid on the lot "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Table of Contents \n",
"\n",
"1. [Functions](#functions)\n",
"\n",
"2. [Preprocessing](#preprocessing)\n",
"\n",
"3. [Exploration](#exploration)\n",
"\n",
"4. [Visualization](#visual) \n",
"\n",
" 4.1 [Plots](#plots) \n",
" \n",
" 4.2 [Correlations](#correlation) \n",
" \n",
"5. [Feature Engineering](#engineering) \n",
"\n",
" 5.1 [Outlier Removal](#outliers) \n",
" \n",
" 5.2 [Resolving Skewedness](#skewedness) \n",
" \n",
"6. [Feature Importance](#importance) \n",
"\n",
" 6.1 [Decision Tree](#importancetree) \n",
" \n",
" 6.2 [Random Forest](#importanceforest) \n",
" \n",
" 6.3 [Feature Selection](#selection) \n",
" \n",
"7. [Prediction](#prediction) \n",
"\n",
" 7.1 [Classification with K-Fold](#resultclassifiers) \n",
" \n",
" 7.2 [Classification with LOO](#loo) \n",
" \n",
" 7.3 [Voting Classifier with LOO](#voting) \n",
" \n",
" 7.4 [Regression with K-Fold](#regression) \n",
" \n",
"8. [Grid Search](#grid) \n",
"\n",
" 8.1 [Classification](#gridclassification) \n",
"\n",
" 8.2 [Regression](#gridregression) \n",
"\n",
"9. [Optimization - Description](#optimization)\n",
"\n",
"10. [Optimization - Regression](#regressionoptimization)\n",
"\n",
"11. [Optimization - Classification](#optimizationfull)\n",
"\n",
" 11.1 [Genetic Algorithm](#geneticalgorithm) \n",
"\n",
" 11.2 [Heuristic](#heuristic) \n",
"\n",
" 11.3 [Validation](#validation) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1. Functions \n",
"[Back to Table of Contents](#table)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import graphviz \n",
"import warnings\n",
"import random\n",
"\n",
"import pandas as pd\n",
"import numpy as np\n",
"import seaborn as sns\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from deap import base\n",
"from deap import creator\n",
"from deap import tools\n",
"\n",
"from scipy.stats import skew\n",
"from sklearn.tree import DecisionTreeClassifier, export_graphviz\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"from sklearn.svm import SVC\n",
"from sklearn.ensemble import RandomForestRegressor,VotingClassifier,AdaBoostClassifier, \\\n",
" GradientBoostingClassifier, RandomForestClassifier, \\\n",
" GradientBoostingRegressor\n",
"from sklearn.naive_bayes import GaussianNB\n",
"from sklearn.linear_model import LogisticRegression, Lasso, Ridge, ElasticNet\n",
"from sklearn.kernel_ridge import KernelRidge\n",
"\n",
"from xgboost import XGBClassifier, XGBRegressor\n",
"from catboost import CatBoostClassifier, CatBoostRegressor\n",
"from lightgbm import LGBMClassifier, LGBMRegressor\n",
"\n",
"from sklearn.feature_selection import VarianceThreshold\n",
"from sklearn.model_selection import StratifiedShuffleSplit, train_test_split, GridSearchCV,\\\n",
" cross_val_score\n",
"from sklearn.metrics import accuracy_score, log_loss, mean_squared_error\n",
"from sklearn.preprocessing import StandardScaler, MinMaxScaler\n",
"\n",
"from mlxtend.classifier import StackingClassifier, StackingCVClassifier\n",
"\n",
"%matplotlib inline\n",
"warnings.simplefilter(\"ignore\", category=PendingDeprecationWarning) # LightGBM\n",
"warnings.simplefilter(\"ignore\", category=DeprecationWarning) # LightGBM\n",
"warnings.simplefilter(\"ignore\", category=UserWarning) # LightGBM\n",
"\n",
"def preprocess_data(file_1_location = 'data2014.csv', file_2_location = 'data2015.csv'):\n",
" \"\"\" Loading and preprocessing the data\n",
" \n",
" Parameters:\n",
" -----------\n",
" file_1_location (str): The location of the first csv file\n",
" file_2_location (str): The location of the first csv file\n",
" \n",
" Returns:\n",
" --------\n",
" df (dataframe): Preprocessed dataframe\n",
" \n",
" \"\"\"\n",
" \n",
" # Load in Data\n",
" df_2014 = pd.read_csv(file_1_location)\n",
" df_2015 = pd.read_csv(file_2_location)\n",
"\n",
" # Merge two files\n",
" df_2014['Year'] = 2014\n",
" df_2015['Year'] = 2015\n",
" df = df_2014.append(df_2015)\n",
" df = df.set_index(np.arange(len(df)))\n",
"\n",
" # Extract part of day as a single variable and remove columns\n",
" df['Part_of_Day'] = df.apply(lambda row: get_part_of_day(row), axis = 1)\n",
" df['multiplier_binary'] = df.apply(lambda row: get_bin_multiplier(row['multiplier']), axis = 1)\n",
"\n",
" df = df.drop(['Morning', 'Afternoon', 'Evening'], 1)\n",
"\n",
" # Since we have the number of lots in a category, we can also say that each number of lots in a category \n",
" # is associated with a single category\n",
" # This is an assumption that might not hold, but it does give more information about having several categories\n",
" categories = {value: index+1 for index, value in enumerate(list(df['LotsCtgry'].unique()))}\n",
" df['Category'] = df.apply(lambda row: categories[row['LotsCtgry']], axis = 1)\n",
"\n",
" # Test for consecutive rows\n",
" test_consecutive_sale(df)\n",
"\n",
" # Create new column with sale number as the number of lots in a sale can be used as a proxy to which sale a lot belongs\n",
" sale_number = {value: index+1 for index, value in enumerate(list(df['LotsSale'].unique()))}\n",
" df['Sale_nr'] = df.apply(lambda row: sale_number[row['LotsSale']], axis = 1)\n",
"\n",
" # # Get number of categories in a sale\n",
" df = get_categories_per_sale(df)\n",
" \n",
" # Ordered lot numbers by category\n",
" ctgry_lot_nr_map = {(sales_nr,ctgry_nr,lot_nr): idx + 1 \n",
" for (sales_nr,ctgry_nr),lot_nr_list \n",
" in df.groupby(['Sale_nr','Category']).LotNr.unique().items()\n",
" for idx, lot_nr in enumerate(lot_nr_list)}\n",
"\n",
" df['CtgryLotNr'] = list(zip(*[df.Sale_nr,df.Category,df.LotNr]))\n",
" df['CtgryLotNr'] = df['CtgryLotNr'].map(ctgry_lot_nr_map)\n",
"\n",
" # Binarize Year\n",
" year_map={2014:0,2015:1}\n",
" df.Year = df.Year.map(year_map)\n",
" \n",
" # Create winning bid and the difference between winning bid and est. value\n",
" df['WinningBid'] = df.apply(lambda row: row['EstValue'] * row['multiplier'], axis = 1)\n",
" df['ResultDifference'] = df.apply(lambda row: row['EstValue'] * row['multiplier'] - \n",
" row['EstValue'], axis = 1)\n",
" \n",
" # Create 3 classes\n",
" df['multiplier_three'] = df.apply(lambda row: three_classes(row), axis = 1)\n",
" \n",
" return df\n",
"\n",
"def get_part_of_day(row):\n",
" if row['Morning'] == 1:\n",
" return 0\n",
" elif row['Afternoon'] == 1:\n",
" return 1\n",
" elif row['Evening'] == 1:\n",
" return 2\n",
" else:\n",
" return 'Error!'\n",
" \n",
"def get_bin_multiplier(row):\n",
" if row < 1:\n",
" return 0\n",
" elif row >= 1:\n",
" return 1\n",
" else:\n",
" return 'Error'\n",
" \n",
"def test_consecutive_sale(df):\n",
" \"\"\" We want to know if the number of lots in a sale can be used\n",
" to indicate whether it belongs to a certain sale. This function\n",
" checks whether a consecutive series of LotsSale belong to each\n",
" other by keeping a set in which we store the LotsSale values\n",
" and check whether a new series of LotsSale has the same amount as\n",
" a series we have seen previously. If it doesn't return an error, \n",
" we can use the column \"LotsSale\" as a proxy for the lotnumber\n",
" \"\"\"\n",
" consecutive_check = set()\n",
" previous = ''\n",
"\n",
" for i in df['LotsSale']:\n",
" if len(consecutive_check) == 0:\n",
" consecutive_check.update([i])\n",
" previous = i\n",
"\n",
" if i == previous:\n",
" continue\n",
" else:\n",
" if i in consecutive_check:\n",
" print('Problem!')\n",
" break\n",
" else:\n",
" consecutive_check.update([i])\n",
" previous = i\n",
" \n",
"def get_categories_per_sale(df):\n",
" \"\"\" Count for each sale how many categories there are in it\n",
" \"\"\"\n",
" test = df.groupby(by = ['Sale_nr', 'Category']).count().reset_index()\n",
" df['Categories_per_sale'] = 0\n",
" \n",
" for i in df['Sale_nr'].unique():\n",
" df.loc[df['Sale_nr'] == i, 'Categories_per_sale'] = len(test[test['Sale_nr'] == i]['Category'].unique())\n",
" \n",
" return df\n",
"\n",
"def correlation_matrix(df):\n",
" \"\"\" Shows a correlation matrix\n",
" \n",
" Parameters:\n",
" -----------\n",
" df (dataframe): Insert a dataframe with only the\n",
" columns that you want to visualize\n",
" \"\"\"\n",
" sns.set(style=\"white\")\n",
"\n",
" # Compute the correlation matrix\n",
" corr = df.corr()\n",
"\n",
" # Generate a mask for the upper triangle\n",
" mask = np.zeros_like(corr, dtype=np.bool)\n",
" mask[np.triu_indices_from(mask)] = True\n",
"\n",
" # Set up the matplotlib figure\n",
" f, ax = plt.subplots(figsize=(11, 9))\n",
"\n",
" sns.heatmap(corr, mask=mask,cmap=\"RdBu_r\", linewidths=.5, cbar_kws={\"shrink\": .5});\n",
"\n",
"def scatter_plot(df, x, y, figsize = (10, 5), xlabel=None, ylabel=None, title=None):\n",
" \"\"\" Plots a scatterplot\n",
" \n",
" Parameters:\n",
" -----------\n",
" df (dataframe): Data to be plotted\n",
" x (str): Name of column to be plotted\n",
" y (str): Name of column to be plotted\n",
" figsize (tup): Tuple of size \n",
" xlabel (str): Label x axis\n",
" ylabel (str): Label y axis\n",
" title (str): Title \n",
" \"\"\"\n",
" \n",
" if not xlabel:\n",
" xlabel = x\n",
" if not ylabel:\n",
" ylabel = y\n",
" if not title:\n",
" title = x + ' vs. ' + y\n",
" \n",
" plt.figure(figsize=figsize)\n",
" xseries = df[x]\n",
" yseries = df[y]\n",
" plt.xlabel(xlabel)\n",
" plt.ylabel(ylabel)\n",
" plt.title(title)\n",
" plt.plot(xseries,yseries, 'ro')\n",
" plt.show()\n",
" \n",
"def multiple_scatter_plots(df, to_plot, figsize=(10, 5), markersize=2, max_row=2):\n",
" \"\"\" Plots a scatterplot\n",
" \n",
" Parameters:\n",
" -----------\n",
" df (dataframe): Data to be plotted\n",
" to_plot (list of tuples): Each tuples contains the column name of what you\n",
" want to plot on the x and y axis respectively\n",
" figsize (tup): Width and length of figures\n",
" marketsize (int): Size of dots\n",
" max_row (int): Number of plots per row\n",
" \n",
" \"\"\"\n",
" # Initialize values and create figure\n",
" number_of_plots = len(to_plot)\n",
" max_column = math.ceil(number_of_plots / max_row)\n",
" fig, axs = plt.subplots(nrows=max_row, ncols=max_column, figsize=figsize)\n",
" \n",
" # Flatten list if possible\n",
" try:\n",
" axs = [item for sublist in axs for item in sublist]\n",
" except:\n",
" pass\n",
" \n",
" # Plot subplots\n",
" for i in range(len(to_plot)):\n",
" x = to_plot[i][0]\n",
" y = to_plot[i][1]\n",
" axs[i].plot(df[x], df[y], 'ro', markersize=markersize)\n",
" axs[i].set_xlabel(x)\n",
" axs[i].set_ylabel(y)\n",
" axs[i].set_title(x + ' vs. ' + y)\n",
" \n",
" # Remove plots that are empty\n",
" empty_plots = abs(number_of_plots - (max_column * max_row))\n",
" if empty_plots > 0:\n",
" for i in range(1, empty_plots + 1, 1):\n",
" axs[-1].remove()\n",
" \n",
" plt.tight_layout(pad=2, h_pad=5)\n",
" plt.show()\n",
" \n",
"def plot_feature_importance(dataset, X, y):\n",
" \"\"\" Plot the importance of features in X\n",
" \n",
" Parameters:\n",
" -----------\n",
" dataset (type): Dataset only containing relevant columns for X and y\n",
" X (numpy array): Matrix of features\n",
" y (numpy array): Target feature\n",
" \n",
" \"\"\"\n",
" \n",
" # Create random forest classifier\n",
" forest = RandomForestClassifier(n_estimators=250, random_state=42)\n",
" forest.fit(X, y)\n",
"\n",
" # Calculate the relative importances of reatures\n",
" importances = forest.feature_importances_\n",
" std = np.std([tree.feature_importances_ for tree in forest.estimators_],axis=0)\n",
" indices = np.argsort(importances)[::-1]\n",
"\n",
" # Plot the feature importances of the forest\n",
" plt.figure(figsize=(10, 4))\n",
" plt.title(\"Feature importances\")\n",
" plt.bar(range(X.shape[1]), importances[indices],\n",
" color=\"r\", yerr=std[indices], align=\"center\")\n",
" plt.xticks(range(X.shape[1]), dataset.columns[indices], rotation=70)\n",
" plt.xlim([-1, X.shape[1]])\n",
" plt.ylim([0, 0.4])\n",
" plt.show()\n",
" \n",
"def plot_decision_tree(dataset, X, y, max_depth=3):\n",
" \"\"\" Plot decision tree to gain insight to feature importances\n",
" \n",
" Parameters:\n",
" -----------\n",
" dataset (type): Dataset only containing relevant columns for X and y\n",
" X (numpy array): Matrix of features\n",
" y (numpy array): Target feature\n",
" max_depth (int): Depth of the decision tree\n",
" \n",
" \"\"\"\n",
"\n",
" dtree=DecisionTreeClassifier(max_depth=max_depth)\n",
" dtree.fit(X,y)\n",
"\n",
" dot_data = export_graphviz(dtree, out_file=None, \n",
" feature_names=dataset.columns[:-1], \n",
" class_names=[\"0\", \"1\"], \n",
" filled=True, rounded=True, \n",
" special_characters=True) \n",
" graph = graphviz.Source(dot_data) \n",
" return graph\n",
"\n",
"def cv_multiple_classifiers(X, y, classifiers = [RandomForestClassifier()], n_splits = 5, \n",
" print_process = False):\n",
" \"\"\"\n",
" \n",
" Parameters:\n",
" -----------\n",
" X (numpy array): Matrix of features\n",
" y (numpy array): Target feature\n",
" n_splits (int): Number of splits for cross validation\n",
" \n",
" Returns:\n",
" --------\n",
" log (dataframe): Dataframe of the results\n",
" \n",
" \"\"\"\n",
" \n",
" # Dataframe to track results\n",
" log_cols = [\"Classifier\", \"Accuracy\"]\n",
" log = pd.DataFrame(columns=log_cols)\n",
" acc_dict = {name.__class__.__name__: 0 for name in classifiers}\n",
"\n",
" # Method of cross validation\n",
" sss = StratifiedShuffleSplit(n_splits=n_splits, test_size=0.3, random_state=42)\n",
" fold = 1\n",
"\n",
" # Cross-validation for each classifier \n",
" for train_index, test_index in sss.split(X, y):\n",
" if print_process:\n",
" print('Fold {}'.format(fold))\n",
" X_train, X_test = X[train_index], X[test_index]\n",
" y_train, y_test = y[train_index], y[test_index]\n",
"\n",
" for clf in classifiers:\n",
" name = clf.__class__.__name__\n",
" clf.fit(X_train, y_train)\n",
" train_predictions = clf.predict(X_test)\n",
" acc = accuracy_score(y_test, train_predictions)\n",
" acc_dict[name] += acc\n",
"\n",
" fold += 1\n",
"\n",
" # Average out accuracies over number of splits\n",
" for clf in acc_dict:\n",
" acc_dict[clf] = acc_dict[clf] / n_splits\n",
" log_entry = pd.DataFrame([[clf, acc_dict[clf]]], columns=log_cols)\n",
" log = log.append(log_entry)\n",
"\n",
" # Plot results\n",
" plt.xlabel('Accuracy')\n",
" plt.title('Classifier Accuracy')\n",
" sns.set_color_codes(\"muted\")\n",
" sns.barplot(x='Accuracy', y='Classifier', data=log, color=\"b\")\n",
" \n",
" return log\n",
"\n",
"def select_features(df, features = [], target = 'multiplier_binary'):\n",
" \"\"\"\n",
" \n",
" Parameters:\n",
" -----------\n",
" df (dataframe): Dataframe that you want to select features from\n",
" features (list): List of the names (str) of the features you want\n",
" to extract as your X feature matrix\n",
" target (str): The column name of the feature that you want to extract\n",
" \n",
" Returns:\n",
" --------\n",
" dataset (dataframe): Dataframe containig the features and target variables\n",
" X (numpy matrix): Numpy matrix of feature values\n",
" y (numpy array): Numpy array of the target values\n",
" \n",
" \"\"\"\n",
" \n",
" # Select a standard set of features if no other are given\n",
" if len(features) == 0:\n",
" features = ['LotNr', 'Allocate', 'EstValue', 'StartPrice','Followers', 'Bank', 'Dealer', \n",
" 'Liquidator', 'Volunteer', 'LotsSale','LotsCtgry', 'Forced','SP.EV','Duration', \n",
" 'Year', 'Category', 'Sale_nr', 'Categories_per_sale']\n",
" features.append(target)\n",
" dataset = df[features]\n",
"\n",
" # Get X and y matrices\n",
" X = dataset.iloc[:, list(range(len(dataset.columns)-1))].values\n",
" y = dataset.iloc[:, -1].values\n",
" \n",
" return dataset, X, y\n",
"\n",
"def loo_cv(dataset, clf=RandomForestClassifier(n_estimators=100), adjust_folds=True):\n",
" \"\"\" Leave one-out cross validation\n",
" \n",
" An implementation of leave one-out cross validation in which the folds\n",
" are determined by the sale number as we want to predict the multiplier of\n",
" sales we have not previously seen. Thereby creating an average accuracy score\n",
" that better represents reality. Folds in which the y_test shows very little\n",
" rows are removed as those are likely outliers. \n",
" \n",
" Parameters:\n",
" -----------\n",
" dataset (dataframe): Dataframe with only the features used\n",
" in the classification task including the target.\n",
" clf (classifier): Classifier to be used for the Leave-one out \n",
" cross validation\n",
" \n",
" Returns:\n",
" --------\n",
" results (list): List of accuracies per fold\n",
" info_folds (dict): Dictionary of lengths of y_test per fold with the intent\n",
" to remove folds that with less than 50 rows\n",
" \"\"\"\n",
" \n",
" nr_folds = len(dataset['Sale_nr'].unique())\n",
" info_folds = {fold: '' for fold in range(1, nr_folds+1)}\n",
" results = []\n",
"\n",
" for sale_nr in range(1, nr_folds+1):\n",
" X_train = dataset[dataset['Sale_nr']!= sale_nr].iloc[:,0:-1].values\n",
" X_test = dataset[dataset['Sale_nr']== sale_nr].iloc[:,0:-1].values\n",
"\n",
" y_train = dataset[dataset['Sale_nr']!= sale_nr].iloc[:,-1].values\n",
" y_test = dataset[dataset['Sale_nr']== sale_nr].iloc[:,-1].values\n",
"\n",
" name = clf.__class__.__name__\n",
" clf.fit(X_train, y_train)\n",
" train_predictions = clf.predict(X_test)\n",
" acc = accuracy_score(y_test, train_predictions)\n",
"\n",
" results.append(acc)\n",
" info_folds[sale_nr] = len(y_test)\n",
" \n",
" if adjust_folds:\n",
" folds_to_remove = [fold[0]-1 for fold in info_folds.items() if fold[1]<50]\n",
" results = [value for index, value in enumerate(results) if index not in folds_to_remove]\n",
" for fold in folds_to_remove:\n",
" del info_folds[fold+1]\n",
" \n",
" return results, info_folds\n",
"\n",
"def three_classes(row):\n",
" \"\"\" Create 3 classes of the multiplier variable:\n",
" 0 for multiplier < 1\n",
" 1 for multiplier between 1 and 2\n",
" 2 for multiplier larger than 2\n",
" \"\"\"\n",
" if row['multiplier'] < 1:\n",
" return 0\n",
" elif row['multiplier'] > 1.5:\n",
" return 2\n",
" else:\n",
" return 1\n",
"\n",
"def plot_multiplier_distribution():\n",
" \"\"\" A quick plot to demonstrate outliers\n",
" in the data with respect to the variable\n",
" multipliers. \n",
" \"\"\"\n",
" \n",
" plt.figure()\n",
" plt.subplot(121)\n",
" plt.hist(df['multiplier'], 60, facecolor='g', alpha=0.75)\n",
" plt.xlabel('Multiplier')\n",
" plt.ylabel('Count')\n",
" plt.title('Histogram of Multiplier')\n",
" plt.grid(True)\n",
"\n",
" plt.subplot(122)\n",
" plt.hist(df['multiplier'], 60, facecolor='g', alpha=0.75)\n",
" plt.xlabel('Multiplier')\n",
" plt.ylabel('Count')\n",
" plt.title('Zoomed in Version')\n",
" plt.axis([0, 60, 0, 10])\n",
" plt.grid(True)\n",
" plt.tight_layout(pad=2, h_pad=5)\n",
"\n",
" plt.show()\n",
" \n",
"def resolve_skewedness(df):\n",
" \"\"\" Resolving the skewedness with features \n",
" that show a skew over .65 and normalizing\n",
" those features with a log(1+p) transform. \n",
" \n",
" \"\"\"\n",
" # # Selecting only the numeric features\n",
" numeric_feature_names = df.dtypes[df.dtypes != \"object\"].index\n",
"\n",
" # # Calculates the skewedness of the features and then gets the features with a skewedness above a certain threshold\n",
" skewed_features = df[numeric_feature_names].apply(lambda x: skew(x.dropna()))\n",
" skewed_features = list(skewed_features[skewed_features > 0.65].index)\n",
" skewed_features.remove('multiplier')\n",
" skewed_features.remove('multiplier_three')\n",
" skewed_features.remove('WinningBid')\n",
" skewed_features.remove('ResultDifference')\n",
"\n",
" df[skewed_features] = np.log1p(df[skewed_features])\n",
" \n",
" return df\n",
"\n",
"def rmse_cross_validation(model, X, y):\n",
" \"\"\" Cross validation using RMSE as error measure\n",
" \"\"\"\n",
" rmse= np.sqrt(-cross_val_score(model, X, y, scoring=\"neg_mean_squared_error\", cv = 5))\n",
" return(rmse)\n",
"\n",
"def rmse(y_true, y_pred):\n",
" \"\"\" Calculate RMSE between predicted and true values\n",
" \"\"\"\n",
" return np.sqrt(mean_squared_error(y_true, y_pred))\n",
"\n",
"def grid_search(param_grid, estimator):\n",
" \"\"\" Gridsearch implementation\n",
" \"\"\"\n",
" grid = GridSearchCV(estimator,param_grid,refit=True,verbose=2)\n",
" grid.fit(X, y)\n",
" print(grid.best_params_)\n",
" print(grid.best_estimator_)\n",
" \n",
"def prepare_data_optimization(target = 'multiplier_binary'):\n",
" \"\"\" All preprocessing steps that are necessary for optimization\n",
" \n",
" This function preprocesses the data, extract features, outlier removal, \n",
" feature engineering etc. Basically, it does all steps seen in this\n",
" notebook until the actual optimization. Thus, this also includes\n",
"\n",
" \n",
" Returns:\n",
" --------\n",
" df (dataframe): Preprocessed full data\n",
" to_optimize (dataframe): Only the data from sale_nr 10 \n",
" which we want to optimize\n",
" baseline (int): The sum of all 1's in the to_optimize dataset\n",
" so we know what to beat. \n",
" X (numpy array): nD Array of features for training\n",
" y (numpy array): 1D Array of features for training\n",
" \n",
" \"\"\"\n",
" # Preprocess data\n",
" df = preprocess_data()\n",
" \n",
" # Remove outliers\n",
" df = df[df['multiplier'] <= 10]\n",
" df = df[df['EstValue'] < 15000]\n",
" \n",
" # We choose to optimize sale_nr 10 as that sale has sufficient lots available to optimize\n",
" to_optimize = df.loc[df.Sale_nr == 10, :].copy()\n",
" \n",
" if target == 'multiplier':\n",
" baseline = np.sum(to_optimize.multiplier.values * to_optimize.EstValue.values)\n",
" else:\n",
" baseline = np.sum(to_optimize.multiplier_binary)\n",
" \n",
" # Initialize Decision variables\n",
" to_optimize.LotNr = range(1, len(to_optimize)+1, 1)\n",
" to_optimize.Duration = 50\n",
" to_optimize.Part_of_Day = 1\n",
" to_optimize.StartPrice = to_optimize.EstValue / 10\n",
" \n",
" # Select only features for prediction, sale_nr is removed to prevent overfitting\n",
" features = ['LotNr', 'StartPrice','Duration','Part_of_Day','Allocate','EstValue','Followers', \n",
" 'Bank','Dealer','Liquidator','Volunteer','LotsSale','LotsCtgry','Forced','Year',\n",
" 'Category', 'Categories_per_sale','CtgryLotNr', 'RelStartPriceCtgry', \n",
" 'RelDurationCtgry', 'RelLotNr','RelCtgryLotNr']\n",
" to_optimize = to_optimize[features]\n",
"\n",
" # Create X and y for training the model by selecting all sales except sale_nr 10\n",
" df_train = df.loc[df.Sale_nr != 10, :].copy()\n",
" df_train = df[features + [target]]\n",
" dataset, X, y = select_features(df_train, features, target)\n",
" \n",
" return df, to_optimize, baseline, X, y\n",
"\n",
"def heuristic_probabilities(row, feature, interval):\n",
" \"\"\" Heuristic to optimize individual features\n",
" \n",
" The heuristic optimizes the value of the feature variable\n",
" by iterating over values as defined by the interval. Thus,\n",
" a starting value is selected and added by the interval until\n",
" it improves. If after 10 iterations there has been no improvement,\n",
" it resets back to the starting value and goes the other direction. \n",
" For example, if adding 5 to the starting price of 40 doesn't yield \n",
" any improvement, reset back to the starting price of 40 and substract\n",
" 5 instead. \n",
" \n",
" Parameters:\n",
" -----------\n",
" row (numpy array): Array of features to optimize. \n",
" feature (string): The feature to optimize\n",
" interval (float): Intervals in which to optimize the feature\n",
" \n",
" Returns:\n",
" --------\n",
" row (numpy array): Array of features that are optimized. \n",
" \n",
" \"\"\" \n",
" best_probability = float(clf.predict_proba(row.reshape(1, -1)).flatten()[1])\n",
" feature_index = to_optimize.columns.get_loc(feature) # Global, change this!\n",
" est_value = row[to_optimize.columns.get_loc('EstValue')]\n",
"\n",
" start_row = row.copy()\n",
" temp_row = row.copy()\n",
" \n",
" k = 0\n",
" count_consecutive = 0\n",
" list_probabilities = []\n",
" \n",
" while k < 20: \n",
" temp_row[feature_index] += interval\n",
" \n",
" # If feature to optimize is startprice, then it cannot be higher than EstValue\n",
" # In all other cases it only needs to be higher than zero as negative are impossible\n",
" if ((feature == 'StartPrice') and (temp_row[feature_index] < est_value)\n",
" and (temp_row[feature_index] > 0)):\n",
" new_probability = float(clf.predict_proba(temp_row.reshape(1, -1)).flatten()[1]) \n",
" elif (feature != 'StartPrice') and (temp_row[feature_index] > 0):\n",
" new_probability = float(clf.predict_proba(temp_row.reshape(1, -1)).flatten()[1]) \n",
" else:\n",
" return row, list_probabilities\n",
" \n",
" # Changes direction of interval if there hasn't been improvement\n",
" # after 10 consecutive iterations. \n",
" if new_probability > best_probability:\n",
" row = temp_row.copy()\n",
" list_probabilities.append(new_probability - best_probability)\n",
" best_probability = new_probability\n",
" count_consecutive = 0\n",
" else:\n",
" count_consecutive += 1\n",
" if count_consecutive == 10:\n",
" interval = interval * -1\n",
" temp_row = start_row.copy()\n",
" k = 0\n",
" k += 1\n",
" return row, list_probabilities\n",
"\n",
"def evalOneMax(individual, temp_to_optimize, target):\n",
" \"\"\" Evaluation Function to be used with DEAP.\n",
" It returns the sum of 1s for the target. \n",
" \"\"\"\n",
" for index, value in enumerate(individual):\n",
" temp_to_optimize.loc[index, target] = value\n",
" return np.sum(clf.predict(temp_to_optimize.values)),\n",
"\n",
"def initialize_deap(to_optimize, target, min_val, max_val):\n",
" \"\"\" \n",
" Registrates the following:\n",
" attr_bool: Values (genes) taken between 0 and 300\n",
" individual: a set of genes that correspond to all starting prices for the dataframe to optimize\n",
" population: a list of individuals\n",
" evaluate: register the goal / fitness function\n",
" mate: the crossover operator\n",
" mutate: a mutation operator with a probability to flip each attribute/gene of 0.05\n",
" select: selecting individuals for breeding next generation\n",
" \"\"\"\n",
" temp_to_optimize = to_optimize.copy()\n",
" temp_to_optimize = temp_to_optimize.reset_index(drop=True)\n",
" \n",
" creator.create(\"FitnessMax\", base.Fitness, weights=(1.0,))\n",
" creator.create(\"Individual\", list, fitness=creator.FitnessMax)\n",
" toolbox = base.Toolbox()\n",
" toolbox.register(\"attr_bool\", random.randint, min_val, max_val)\n",
" toolbox.register(\"individual\", tools.initRepeat, creator.Individual, toolbox.attr_bool, len(to_optimize))\n",
" toolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)\n",
" toolbox.register(\"evaluate\", evalOneMax, temp_to_optimize = temp_to_optimize, target = target)\n",
" toolbox.register(\"mate\", tools.cxTwoPoint)\n",
" toolbox.register(\"mutate\", tools.mutFlipBit, indpb=0.05)\n",
" toolbox.register(\"select\", tools.selTournament, tournsize=3)\n",
" return toolbox, temp_to_optimize\n",
"\n",
"def heuristic_ga(to_optimize, target, min_val=0, max_val=300):\n",
" \"\"\" A genetic algorithm for optimization of the target. \n",
" \n",
" Code was adopted from:\n",
" https://github.com/DEAP/deap/blob/master/examples/ga/onemax.py\n",
" \n",
" Parameters:\n",
" -----------\n",
" to_optimize (dataframe): The dataframe to be optimized\n",
" target (str): The target column to optimize\n",
" min_val (float): Minimum value (>= 0) the target can take\n",
" max_val (float): Maximum value (> min_val) the target can take\n",
" \n",
" Returns\n",
" to_optimize (dataframe): Dataframe with the target optimized \n",
" \"\"\"\n",
" random.seed(64)\n",
" \n",
" # create an initial population of 300 individuals\n",
" toolbox, temp_to_optimize = initialize_deap(to_optimize, target, min_val, max_val)\n",
" pop = toolbox.population(n=300)\n",
"\n",
" # CXPB is probability with which two individuals are crossed\n",
" # MUTPB is the probability for mutating an individual\n",
" CXPB, MUTPB = 0.5, 0.2\n",
" \n",
" print(\"Start of evolution\")\n",
" \n",
" # Evaluate the entire population\n",
" fitnesses = list(map(toolbox.evaluate, pop))\n",
" for ind, fit in zip(pop, fitnesses):\n",
" ind.fitness.values = fit\n",
" \n",
" print(\" Evaluated %i individuals\" % len(pop))\n",
"\n",
" # Extracting all the fitnesses of \n",
" fits = [ind.fitness.values[0] for ind in pop]\n",
"\n",
" # Variable keeping track of the number of generations\n",
" g = 0\n",
" \n",
" # Begin the evolution\n",
" while max(fits) < 100 and g < 1000:\n",
" g = g + 1\n",
" print(\"-- Generation %i --\" % g)\n",
" \n",
" # Select and clone the next generation individuals\n",
" offspring = toolbox.select(pop, len(pop))\n",
" offspring = list(map(toolbox.clone, offspring))\n",
" \n",
" # Apply crossover and mutation on the offspring\n",
" for child1, child2 in zip(offspring[::2], offspring[1::2]):\n",
"\n",
" # cross two individuals with probability CXPB\n",
" if random.random() < CXPB:\n",
" toolbox.mate(child1, child2)\n",
"\n",
" # fitness values of the children\n",
" # must be recalculated later\n",
" del child1.fitness.values\n",
" del child2.fitness.values\n",
"\n",
" for mutant in offspring:\n",
"\n",
" # mutate an individual with probability MUTPB\n",
" if random.random() < MUTPB:\n",
" toolbox.mutate(mutant)\n",
" del mutant.fitness.values\n",
" \n",
" # Evaluate the individuals with an invalid fitness\n",
" invalid_ind = [ind for ind in offspring if not ind.fitness.valid]\n",
" fitnesses = map(toolbox.evaluate, invalid_ind)\n",
" for ind, fit in zip(invalid_ind, fitnesses):\n",
" ind.fitness.values = fit\n",
" \n",
" print(\" Evaluated %i individuals\" % len(invalid_ind))\n",
" \n",
" # The population is entirely replaced by the offspring\n",
" pop[:] = offspring\n",
" \n",
" # Gather all the fitnesses in one list and print the stats\n",
" fits = [ind.fitness.values[0] for ind in pop]\n",
" \n",
" length = len(pop)\n",
" mean = sum(fits) / length\n",
" sum2 = sum(x*x for x in fits)\n",
" std = abs(sum2 / length - mean**2)**0.5\n",
" \n",
" print(\" Min %s\" % min(fits))\n",
" print(\" Max %s\" % max(fits))\n",
" print(\" Avg %s\" % mean)\n",
" print(\" Std %s\" % std)\n",
" \n",
" print(\"-- End of (successful) evolution --\")\n",
" \n",
" best_ind = tools.selBest(pop, 1)[0]\n",
" temp_to_optimize.loc[:, target] = best_ind\n",
" return temp_to_optimize\n",
"\n",
"\n",
"def print_results(data, clf):\n",
" \"\"\" Print final result of heuristics\n",
" \n",
" It prints both the sum of 1s as well as the sum of \n",
" belonging to the first class.\n",
" \n",
" Parameters:\n",
" -----------\n",
" data (list of tuples): Each tuple indicates the df with the corresponding name\n",
" E.g. results(data = [(df, 'Heuristic'), (df2, 'Heuristic 2'), clf])\n",
" clf (classifier): Classifier to be used for calculating results\n",
" \n",
" \"\"\"\n",
" for df, name in data:\n",
" print(name)\n",
" sum_1s = np.sum(clf.predict(df.values))\n",
" sum_probabilities = np.sum([x[1] if x[1] > 0.5 else 0 for x in clf.predict_proba(df.values)])\n",
" print('Sum of 1s: {}'.format(sum_1s))\n",
" print('Sum of probs that class is 1: {}'.format(sum_probabilities))\n",
" print()\n",
"\n",
"def validate_heuristic_results(clf, df, baseline, percentage=30, iterations=100):\n",
" \"\"\" Validation of results after using heuristic\n",
" \n",
" The issue with using a classifier is that it is only\n",
" accurate for a certain percentage of items. For that \n",
" reason optimizing might be an issue. For example,\n",
" if a model is only accurate for 80% of a binary predictor, \n",
" then 80% of the optimized values are likely to be correct\n",
" and 20% likely incorrect. This function reverts x% of the \n",
" target (e.g. 1 to 0 and 0 to 1) as a way to simulate having\n",
" x% of the target classified incorrectly. Then the baseline\n",
" is again calculated. This is average over a set amount of \n",
" instances/iterations. \n",
" \n",
" Parameters:\n",
" -----------\n",
" clf (model): Model that is used for prediction\n",
" df (dataframe): Dataframe that is optimized\n",
" baseline (int): Baseline with which to compare (sum of 1s)\n",
" percentage (float): percentage to be swapped\n",
" iterations (int): Number of iterations to average over\n",
" \"\"\"\n",
" \n",
" temp = list(clf.predict(df.values))\n",
" indices = [np.random.choice(range(len(temp)), int(len(temp)/100*percentage), \n",
" replace=False) for i in range(iterations)]\n",
" multiple_tests = []\n",
"\n",
" for sample in indices:\n",
" # Predict scores and invert them according to the sample\n",
" result = list(clf.predict(df.values))\n",
" for index in sample:\n",
" result[index] = (result[index] * -1) + 1\n",
"\n",
" multiple_tests.append(np.sum(result))\n",
"\n",
" print('Baseline: \\t{}'.format(baseline))\n",
" print('Optimization: \\t{}'.format(np.mean(multiple_tests)))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%html\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2. Preprocess Data \n",
"[Back to Table of Contents](#table)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Steps taken:**\n",
"* Load and merge the 2014 and 2015 files\n",
"* Extract part of day as a single variable and remove columns\n",
"* Create a binary feature of the multipler variable\n",
"* Create category based on the number of lots per category (those are unique and therefore represent a category)\n",
"* Create Sale number based on the number of lost per sale (those are unique and therefore represent a lot)\n",
"* Create categories per sale"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [],
"source": [
"df = preprocess_data()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below you can see the data as was given to me without additional preprocessing of the data. The main purpose is to optimize the feature *\"multiplier\"* by finding optimal values for the decision variables. "
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
multiplier
\n",
"
LotNr
\n",
"
Allocate
\n",
"
EstValue
\n",
"
StartPrice
\n",
"
Followers
\n",
"
Bank
\n",
"
Dealer
\n",
"
Liquidator
\n",
"
Volunteer
\n",
"
LotsSale
\n",
"
LotsCtgry
\n",
"
Forced
\n",
"
SP.EV
\n",
"
Duration
\n",
"
Year
\n",
"
Part_of_Day
\n",
"
multiplier_binary
\n",
"
Category
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
1.300000
\n",
"
6
\n",
"
0
\n",
"
700
\n",
"
400
\n",
"
19
\n",
"
0
\n",
"
1
\n",
"
0
\n",
"
0
\n",
"
1177
\n",
"
436
\n",
"
0
\n",
"
0.571429
\n",
"
67
\n",
"
0
\n",
"
2
\n",
"
1
\n",
"
1
\n",
"
\n",
"
\n",
"
1
\n",
"
0.616667
\n",
"
7
\n",
"
0
\n",
"
600
\n",
"
350
\n",
"
12
\n",
"
0
\n",
"
1
\n",
"
0
\n",
"
0
\n",
"
1177
\n",
"
436
\n",
"
0
\n",
"
0.583333
\n",
"
67
\n",
"
0
\n",
"
2
\n",
"
0
\n",
"
1
\n",
"
\n",
"
\n",
"
2
\n",
"
0.600000
\n",
"
9
\n",
"
0
\n",
"
1500
\n",
"
600
\n",
"
19
\n",
"
0
\n",
"
1
\n",
"
0
\n",
"
0
\n",
"
1177
\n",
"
436
\n",
"
0
\n",
"
0.400000
\n",
"
67
\n",
"
0
\n",
"
2
\n",
"
0
\n",
"
1
\n",
"
\n",
"
\n",
"
3
\n",
"
0.833333
\n",
"
10
\n",
"
0
\n",
"
300
\n",
"
150
\n",
"
20
\n",
"
0
\n",
"
1
\n",
"
0
\n",
"
0
\n",
"
1177
\n",
"
436
\n",
"
0
\n",
"
0.500000
\n",
"
67
\n",
"
0
\n",
"
2
\n",
"
0
\n",
"
1
\n",
"
\n",
"
\n",
"
4
\n",
"
0.600000
\n",
"
11
\n",
"
0
\n",
"
500
\n",
"
300
\n",
"
9
\n",
"
0
\n",
"
1
\n",
"
0
\n",
"
0
\n",
"
1177
\n",
"
436
\n",
"
0
\n",
"
0.600000
\n",
"
68
\n",
"
0
\n",
"
2
\n",
"
0
\n",
"
1
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" multiplier LotNr Allocate EstValue StartPrice Followers Bank Dealer \\\n",
"0 1.300000 6 0 700 400 19 0 1 \n",
"1 0.616667 7 0 600 350 12 0 1 \n",
"2 0.600000 9 0 1500 600 19 0 1 \n",
"3 0.833333 10 0 300 150 20 0 1 \n",
"4 0.600000 11 0 500 300 9 0 1 \n",
"\n",
" Liquidator Volunteer LotsSale LotsCtgry Forced SP.EV Duration \\\n",
"0 0 0 1177 436 0 0.571429 67 \n",
"1 0 0 1177 436 0 0.583333 67 \n",
"2 0 0 1177 436 0 0.400000 67 \n",
"3 0 0 1177 436 0 0.500000 67 \n",
"4 0 0 1177 436 0 0.600000 68 \n",
"\n",
" Year Part_of_Day multiplier_binary Category \n",
"0 0 2 1 1 \n",
"1 0 2 0 1 \n",
"2 0 2 0 1 \n",
"3 0 2 0 1 \n",
"4 0 2 0 1 "
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df[df.columns[:-10]].head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Newly created features** \n",
"Several new features were created based on the initial set of features:\n",
"\n",
"| Feature | Description \n",
"|-|------|\n",
"| Multiplier_binary | The multiplier variable in binary format (0 if value < 1 and 1 if value >= 1)|\n",
"| Category | The number of the category for a certain item|\n",
"| Sale_nr | The number of the sale|\n",
"| Categories_per_sale | The number of categories per sale|\n",
"| CtgryLotNr | Ordering of Lots within a category |\n",
"| RelStartPriceCtgry | Relative starting price with regards to the category |\n",
"| RelDurationCtgry | Relative duration with regards to the category |\n",
"| RelLotNr | LotNr divided by LotsSale |\n",
"| RelCtrgyLotNr | Relative amount of lots per category (CtgryLotNr / LotsCtgry) |\n",
"| WinningBid | The winning bid (EstValue * multiplier) |\n",
"| ResultsDifference | Difference between winning bid and est. value | "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3. Exploration \n",
"[Back to Table of Contents](#table)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We have no missing values that we have to worry about. "
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.isnull().values.any()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It turns out there is a lot of data missing. The number of lots in sale indicate how many lots there should be in a sale. \n",
"However, when you actually count the number of lots, there seem to be some missing. "
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Lots Expected: 1177, \tLots in df: 783, \t Lots missing: 394\n",
"Lots Expected: 1411, \tLots in df: 979, \t Lots missing: 432\n",
"Lots Expected: 95, \tLots in df: 89, \t Lots missing: 6\n",
"Lots Expected: 1223, \tLots in df: 765, \t Lots missing: 458\n",
"Lots Expected: 355, \tLots in df: 36, \t Lots missing: 319\n",
"Lots Expected: 905, \tLots in df: 767, \t Lots missing: 138\n",
"Lots Expected: 219, \tLots in df: 157, \t Lots missing: 62\n",
"Lots Expected: 295, \tLots in df: 226, \t Lots missing: 69\n",
"Lots Expected: 955, \tLots in df: 730, \t Lots missing: 225\n",
"Lots Expected: 422, \tLots in df: 378, \t Lots missing: 44\n",
"Lots Expected: 460, \tLots in df: 243, \t Lots missing: 217\n",
"Lots Expected: 191, \tLots in df: 158, \t Lots missing: 33\n",
"Lots Expected: 68, \tLots in df: 34, \t Lots missing: 34\n",
"Lots Expected: 17, \tLots in df: 14, \t Lots missing: 3\n",
"Lots Expected: 1426, \tLots in df: 1152, \t Lots missing: 274\n",
"Lots Expected: 462, \tLots in df: 390, \t Lots missing: 72\n",
"Lots Expected: 646, \tLots in df: 507, \t Lots missing: 139\n",
"Lots Expected: 1053, \tLots in df: 877, \t Lots missing: 176\n",
"Lots Expected: 721, \tLots in df: 526, \t Lots missing: 195\n",
"Lots Expected: 1329, \tLots in df: 996, \t Lots missing: 333\n",
"Lots Expected: 89, \tLots in df: 62, \t Lots missing: 27\n",
"Lots Expected: 1079, \tLots in df: 771, \t Lots missing: 308\n",
"Lots Expected: 161, \tLots in df: 101, \t Lots missing: 60\n",
"Lots Expected: 291, \tLots in df: 3, \t Lots missing: 288\n",
"Lots Expected: 326, \tLots in df: 252, \t Lots missing: 74\n",
"Lots Expected: 280, \tLots in df: 201, \t Lots missing: 79\n",
"Lots Expected: 162, \tLots in df: 119, \t Lots missing: 43\n"
]
}
],
"source": [
"for i in df['LotsSale'].unique():\n",
" nr_lots_suggested = i\n",
" nr_lots_actual = len(df[df['LotsSale'] == i])\n",
" print('Lots Expected: {}, \\tLots in df: {}, \\t Lots missing: {}'.format(nr_lots_suggested, nr_lots_actual, \n",
" nr_lots_suggested - nr_lots_actual))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Most likely the data that is missing are lots that couldn't be sold as nobody would place a bid. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4. Visualizations \n",
"[Back to Table of Contents](#table)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 4.1 Plots \n",
"[Back to Table of Contents](#table)\n",
"\n",
"After some playing around, I found the following (possibly) interesting plots to show some form of relationship, \n",
"at least to the human eye. Further exploration seems to be neccesary with regards to the Estimated value \n",
"which is why next you can see the correlations. "
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\nSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJ\nkjTwTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmS\nNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI0\n8ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJkjTw\nTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBMaiVJkjTwTGolSZI08ExqJUmSNPBM\naiVJkjTwTGolSZI08ExqJUmSNPBMakVEbIuI55b3T4qIT/e7TcOi+t42ef7CiDiuzX1tjohXl/df\nEREXdaqdkgaXMVwqLO53A9Q5EbENOAC4u7L4UZl5Y39apKqIOAl4ZGb+SW1ZZq6azb4y8zPAZzrU\nNEnzgDF87iJiBfDpzDxwhtutAV6dmU+vLNsAHAc8JTMvLZc9EvifzFzUqTarc0xqh88LM/Pr/W7E\nbEXEXpl59/RrarZ8j6V5zRg+v/wC+CDw/OlWjIhFwKLMHO96q9SQSe0CEREvAv4aCOBK4PWZuWW2\n20XEq4A/yswXlutdDXw3M48pH19PEZyvjIhHAx8Dngj8HPiLzDyvXG8D8BvgocCzgKMiYgnwIeAg\n4HbgI5n5obp23QO4CXh6Zn6/XPYA4KflvsaBDcDTy/s/AJ41XbCpfcsH/g74U4oek9cDO4GPAvcH\nPpSZf1Vp/w2Z+Z7q9vW9BBHxAuDdwKKIOBq4JjMfHxGby/XPKHsKXgN8F1gNjAFvzMyLG7RzDZVe\nhZm+x8DAfmhKC5ExvL0YPs17cd/ydawCdgD/APwVcCjwSWDviPgVsCszl5WbnQm8PCKelZnfaLDP\nzcB/ACuA3wV+B7h6tm3U3FhTuwBExKOAs4G3AQ8Avgx8oQw8s93uG8AzImIkIkaBvYGnlds9HLgX\ncFVE3BP4GnAW8EDgWOC0iHhs5VAvB04G7g38O/Ap4LWZeW/gcGBTfdsy8y7gc+X+ao4BvpGZNwPv\nAG4o230ARUK5e9o3q/AgYB+KD4G/pAh8f0IR0J8B/GX5GtuWmV+hCJ7nZua9MvPxTVZ9CnAtRfL8\n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\n",
"text/plain": [
"