[
  {
    "path": ".gitignore",
    "content": "*pyc\ndata/*\njobs/*\ntrained/*\n.DS_Store\nimages/\nnul\n.ipynb_checkpoints\nmykey.json\nrun/airflow/*cfg\nrun/airflow/airflow.db\ndefault.profraw\n"
  },
  {
    "path": "CONTRIBUTING",
    "content": "Want to contribute? Great! First, read this page (including the small print at the end).\n\n## Before you contribute\nBefore we can use your code, you must sign the [Google Individual Contributor License Agreement] (https://cla.developers.google.com/about/google-individual) (CLA), which you can do online. The CLA is necessary mainly because you own the copyright to your changes, even after your contribution becomes part of our codebase, so we need your permission to use and distribute your code. We also need to be sure of various other things—for instance that you'll tell us if you know that your code infringes on other people's patents. You don't have to sign the CLA until after you've submitted your code for review and a member has approved it, but you must do it before we can put your code into our codebase. Before you start working on a larger contribution, you should get in touch with us first through the issue tracker with your idea so that we can help out and possibly guide you. Coordinating up front makes it much easier to avoid frustration later on.\n\n## Code reviews\nAll submissions, including submissions by project members, require review. We use Github pull requests for this purpose.\n\n## The small print\nContributions made by corporations are covered by a different agreement than the one above, the [Software Grant and Corporate Contributor License Agreement] (https://cla.developers.google.com/about/google-corporate)."
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "This code supports the three-part solution [Predicting Customer Lifetime Value with Cloud ML Engine](https://cloud.google.com/solutions/machine-learning/clv-prediction-with-offline-training-intro) published on cloud.google.com.\n\nThis code is also used in the updated solution [Predicting Customer Lifetime Value with AutoML Tables ](https://cloud.google.com/solutions/machine-learning/clv-prediction-with-automl-tables) published on cloud.google.com.\n\n# Customer Lifetime Value Prediction on GCP\n\nThis project shows how to use ML models to predict customer lifetime value in the following context:\n- We apply the models using [this data set](http://archive.ics.uci.edu/ml/datasets/Online+Retail) [1].\n- We provide an implementation using a TensorFlow DNN model with batch normalization and dropout.\n- We provide an implementation, using the [Lifetimes library](https://github.com/CamDavidsonPilon/lifetimes) in Python, of [statistical models](https://rdrr.io/cran/BTYD/) commonly used in industry to perform lifetime value prediction.\n- We also provide an implementation using [AutoML Tables](https://cloud.google.com/automl-tables).\n\nThe project also shows how to deploy a production-ready data processing pipeline for lifetime value prediction on Google Cloud Platform, using BigQuery and DataStore with orchestration provided by Cloud Composer.\n\n## Install\n\n### install Miniconda\n\nThe code works with python 2/3.  Using Miniconda2:\n\n```\nsudo apt-get install -y git bzip2\nwget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh\nbash Miniconda2-latest-Linux-x86_64.sh -b\nexport PATH=~/miniconda2/bin:$PATH\n```\n\n### Create dev environment\n\n```\nconda create -y -n clv\nsource activate clv\nconda install -y -n clv python=2.7 pip\npip install -r requirements.txt\n```\n\n### Enable the required APIs in your GCP Project\n- Cloud Composer API\n- Machine Learning API (for TensorFlow / Lifetimes models)\n- Dataflow API\n- AutoML Tables API (for AutoML Tables models)\n\n\n### Environment setup\nBefore running the training and Airflow scripts, you need some environment variables:\n\n```\nexport PROJECT=$(gcloud config get-value project 2> /dev/null)\nexport BUCKET=gs://${PROJECT}_data_final\nexport REGION=us-central1\nexport DATASET_NAME=ltv\n\nexport COMPOSER_NAME=\"clv-final\"\nexport COMPOSER_BUCKET_NAME=${PROJECT}_composer_final\nexport COMPOSER_BUCKET=gs://${COMPOSER_BUCKET_NAME}\nexport DF_STAGING=${COMPOSER_BUCKET}/dataflow_staging\nexport DF_ZONE=${REGION}-a\nexport SQL_MP_LOCATION=\"sql\"\n\nexport LOCAL_FOLDER=$(pwd)\n```\n\n\n### Data setup\nCreating the BigQuery workspace:\n\n```\ngcloud storage buckets create ${BUCKET} --location ${REGION} --project ${PROJECT}\ngcloud storage buckets create ${COMPOSER_BUCKET} --location ${REGION} --project ${PROJECT}\nbq --location=US mk --dataset ${PROJECT}:${DATASET_NAME}\n```\n\nCreate a datastore database as detailed in the [Datastore documentation](https://cloud.google.com/datastore/docs/quickstart)\n\n\n### Copy the raw dataset\n```\ngcloud storage cp gs://solutions-public-assets/ml-clv/db_dump.csv ${BUCKET}\ngcloud storage cp ${BUCKET}/db_dump.csv ${COMPOSER_BUCKET}\n```\n\n### Copy the dataset to be predicted. Replace with your own.\n```\ngcloud storage cp clv_mle/to_predict.json ${BUCKET}/predictions/\ngcloud storage cp ${BUCKET}/predictions/to_predict.json ${COMPOSER_BUCKET}/predictions/\ngcloud storage cp clv_mle/to_predict.csv ${BUCKET}/predictions/\ngcloud storage cp ${BUCKET}/predictions/to_predict.csv ${COMPOSER_BUCKET}/predictions/\n\n```\n\n### Create a service account\nCreating a service account is important to make sure that your Cloud Composer instance can perform the required tasks within BigQuery, AutoML Tables, ML Engine, Dataflow, Cloud Storage and Datastore.  It is also needed to run training for AutoML locally.\n\nThe following creates a service account called composer@[YOUR-PROJECT-ID].iam.gserviceaccount.com. and assigns the required roles to the service account.\n\n```\ngcloud iam service-accounts create composer --display-name composer --project ${PROJECT}\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/composer.worker\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/bigquery.dataEditor\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/bigquery.jobUser\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/storage.admin\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/ml.developer\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/dataflow.developer\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/compute.viewer\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role roles/storage.objectAdmin\n\ngcloud projects add-iam-policy-binding ${PROJECT} \\\n--member serviceAccount:composer@${PROJECT}.iam.gserviceaccount.com \\\n--role='roles/automl.editor'\n```\n\nWait until the service account has all the proper roles setup.\n\n\n### Download API Key for AutoML Tables\n\n[Create a service account API key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) and download the json keyfile to run training for AutoML locally.\n\n\n### Upload Machine Learning Engine packaged file\nIf using the TensorFlow or Lifetimes model, do this once.  If you make changes to any of the Python files in clv_mle, you need to repeat.\n\n```\ncd ${LOCAL_FOLDER}/clv_mle\nrm -rf clv_ml_engine.egg-info/\nrm -rf dist\npython setup.py sdist\ngcloud storage cp dist/* ${COMPOSER_BUCKET}/code/\n```\n\n\n## [Optional] launch Jupyter\nThe ```notebooks``` folder contains notebooks for data exploration and modeling with linear models and AutoML Tables.\n\n```\njupyter notebook\n```\n\nIf you are interested in using Jupyter with Datalab, you can do the following:\n\n```\njupyter nbextension install --py datalab.notebook --sys-prefix\njupyter notebook\n```\n\nAnd enter in the first cell of your Notebook\n\n```\n%load_ext google.datalab.kernel\n```\n\n## Train and Tune Models\n\n### AutoML Tables\nYou can train the model using the script clv_automl/clv_automl.py.  This takes several parameters.  See usage for full params and default values.\n\nMake sure you have downloaded the json API key.  By default this is assumed to be in a file ```mykey.json``` in the same directory as the script.\n\nFor example:\n\n```\ncd ${LOCAL_FOLDER}/clv_automl\npython clv_automl.py --project_id [YOUR_PROJECT]\n```\n\n### TensorFlow DNN/Lifetimes\nTo run training or hypertuning for the non-automl models you can use the mltrain.sh script.  It must be run from the top level directory, as in the examples below. For ML Engine jobs you must supply a bucket on GCS.  The job data folder will be gs://bucket/data and the job directory will be gs://bucket/jobs. So your data files must already be in gs://bucket/data.  If you use ${COMPOSER_BUCKET}, and the DAG has been run at least once, the data files will be present.  For DNN models the data should be named 'train.csv', 'eval.csv' and 'test.csv', for probablistic models the file must be 'btyd.csv'.\n\nFor example:\n\nTrain the DNN model on local data:\n\n```\ncd ${LOCAL_FOLDER}\ngcloud storage cp --recursive ${COMPOSER_BUCKET}/data .\nrun/mltrain.sh local data\n```\n\nTrain the DNN model in a Cloud ML Engine job on data in the ${COMPOSER_BUCKET}:\n\n```\nrun/mltrain.sh train ${COMPOSER_BUCKET}\n```\n\nRun hyperparameter tuning on Cloud ML Engine:\n\n```\nrun/mltrain.sh tune gs://your-bucket\n```\n\nFor statistical models:\n\n```\nrun/mltrain.sh local data --model_type paretonbd_model --threshold_date 2011-08-08 --predict_end 2011-12-12\n```\n\n\n## Automation with AirFlow\nThis code is set to run automatically using Cloud Composer, a google-managed version of Airflow. The following steps describe how to go from your own copy of the data to a deployed model with results exported both in Datastore and BigQuery.\n\nSee [part three of the solution](https://cloud.google.com/solutions/machine-learning/clv-prediction-with-offline-training-deploy) for more details.\n\n\n### Set up Cloud Composer\n\n#### Create a composer instance with the service account\nThis will take a while.  This project assumes Airflow 1.9.0, which is the default for Cloud Composer as of March 2019.\n\n```\ngcloud composer environments create ${COMPOSER_NAME} \\\n--location ${REGION}  \\\n--zone ${REGION}-f \\\n--machine-type n1-standard-1 \\\n--service-account=composer@${PROJECT}.iam.gserviceaccount.com\n```\n\n#### Make SQL files available to the DAG\nThere are various ways of calling BigQuery queries. This solutions leverages BigQuery files directly. For them to be accessible by the DAGs, they need to be in the same folder.\n\nThe following command line, copies the entire sql folder as a subfolder in the Airflow dags folder.\n\n```\ncd ${LOCAL_FOLDER}/preparation\n\ngcloud composer environments storage dags import \\\n--environment ${COMPOSER_NAME} \\\n--source  ${SQL_MP_LOCATION} \\\n--location ${REGION} \\\n--project ${PROJECT}\n```\n\n#### Other files\nSome files are important when running the DAG. They need to be placed in the composer bucket:\n\n1 - The BigQuery schema file used to load data into BigQuery\n\n```\ncd ${LOCAL_FOLDER}\ngcloud storage cp ./run/airflow/schema_source.json ${COMPOSER_BUCKET}\n```\n\n2 - A Javascript file used by the Dataflow template for processing.\n\n```\ngcloud storage cp ./run/airflow/gcs_datastore_transform.js ${COMPOSER_BUCKET}\n```\n\n#### Set Composer environment variables\n\nRegion where things happen\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\nregion ${REGION}\n```\n\nStaging location for Dataflow\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\ndf_temp_location ${DF_STAGING}\n```\n\nZone where Dataflow should run\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\ndf_zone ${DF_ZONE}\n```\n\nBigQuery working dataset\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\ndataset ${DATASET_NAME}\n```\n\nComposer bucket\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\ncomposer_bucket_name ${COMPOSER_BUCKET_NAME}\n```\n\n#### (for AutoML Tables) Composer environment variables\n\nAutoML Dataset name\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\nautoml_dataset \"clv_solution\"\n```\n\nAutoML Model name\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\nautoml_model \"clv_model\"\n```\n\nAutoML training budget\n\n```\ngcloud composer environments run ${COMPOSER_NAME} --location ${REGION} variables set \\\n-- \\\nautoml_training_budget \"1\"\n```\n\n#### (for AutoML Tables) Import AutoML libraries\n```\ngcloud composer environments storage dags import \\\n--environment ${COMPOSER_NAME} \\\n--source clv_automl \\\n--location ${REGION} \\\n--project ${PROJECT}\n\ngcloud composer environments update ${COMPOSER_NAME} \\\n--update-pypi-packages-from-file run/airflow/requirements.txt \\\n--location ${REGION} \\\n--project ${PROJECT}\n```\n\n#### Import DAGs\nYou need to run this for all your dag files. This solution has two DAGs located in the [run/airflow/dags](run/airflow/dags) folder.\n\n```\ngcloud composer environments storage dags import \\\n--environment ${COMPOSER_NAME} \\\n--source run/airflow/dags/01_build_train_deploy.py \\\n--location ${REGION} \\\n--project ${PROJECT}\n\ngcloud composer environments storage dags import \\\n--environment ${COMPOSER_NAME} \\\n--source run/airflow/dags/02_predict_serve.py \\\n--location ${REGION} \\\n--project ${PROJECT}\n```\n\n\n### Run DAGs\nYou now should have both DAGs and the SQL files in the Cloud Composer's reserved bucket. Because you probably want to run training and prediction tasks independently, you can run the following script as needed. For more automatic runs (like daily for example), refer to the Airflow documentation to setup your DAGs accordingly.\n\nAirflow can take various parameters as inputs.\n\nThe following are used within the .sql files through the syntax {{ dag_run.conf['PARAMETER-NAME'] }}\n\n- project: Project ID where the data is located\n- dataset: Dataset that is used to write and read the data\n- predict_end: When is the final date of the whole sales dataset\n- threshold_date: What is the data used to split the data\n\nOther variables are important as they depend on your environment and are passed directly to the Operators:\n\n- model_type: Name of the model that you want to use. Should be either 'automl' or one of the options from model.py\n- project: Your project id\n- dataset: Your dataset id\n- threshold_date: Date that separates features from target\n- predict_end: End date of the dataset\n- model_name: Name of the model saved to AutoML Tables or Machine Learning Engine\n- model_version: Name of the version of model_name save to Machine Learning Engine (not used for AutoML Tables)\n- tf_version: Tensorflow version to be used\n- max_monetary: Monetary cap to discard all customers that spend more than that amount\n\n```\ngcloud composer environments run ${COMPOSER_NAME} \\\n--project ${PROJECT} \\\n--location ${REGION} \\\ndags trigger \\\n-- \\\nbuild_train_deploy \\\n--conf '{\"model_type\":\"automl\", \"project\":\"'${PROJECT}'\", \"dataset\":\"'${DATASET_NAME}'\", \"threshold_date\":\"2011-08-08\", \"predict_end\":\"2011-12-12\", \"model_name\":\"clv_automl\", \"model_version\":\"v1\", \"tf_version\":\"1.10\", \"max_monetary\":\"15000\"}'\n```\n\n```\ngcloud composer environments run ${COMPOSER_NAME} \\\n--project ${PROJECT} \\\n--location ${REGION} \\\ndags trigger \\\n-- \\\npredict_serve \\\n--conf '{\"model_name\":\"clv_automl\", \"model_version\":\"v1\", \"dataset\":\"'${DATASET_NAME}'\"}'\n```\n\n\n### Disclaimer: This is not an official Google product\n\n[1]: Dua, D. and Karra Taniskidou, E. (2017). UCI Machine Learning Repository [http://archive.ics.uci.edu/ml]. Irvine, CA: University of California, School of Information and Computer Science.\n"
  },
  {
    "path": "clv_automl/__init__.py",
    "content": ""
  },
  {
    "path": "clv_automl/clv_automl.py",
    "content": "# Copyright 2019 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import division\nfrom __future__ import print_function\nfrom __future__ import absolute_import\n\nimport argparse\nfrom google.cloud.automl_v1beta1 import AutoMlClient, PredictionServiceClient\nimport sys\nimport time\n\n# parameter defaults\nKEY_FILE = 'mykey.json'\nLOCATION = 'us-central1'\nBQ_DATASET = 'ltv_edu_auto'\nBQ_TABLE = 'features_n_target'\nAUTOML_DATASET = 'clv_solution'\nTARGET_LABEL = 'target_monetary'\nAUTOML_MODEL = 'clv_model'\nBATCH_GCS_INPUT = 'gs://'\nBATCH_GCS_OUTPUT = 'gs://'\n\ndef create_automl_model(client,\n                        project_id,\n                        location,\n                        bq_dataset,\n                        bq_table,\n                        automl_dataset,\n                        automl_model,\n                        training_budget):\n  \"\"\"\n  Create an AutoML Tables dataset based on the data in BigQuery.\n  Create a model to predict CLV based on that dataset.\n\n  Returns:\n    The name of the created model.\n  \"\"\"\n  location_path = client.location_path(project_id, location)\n  dataset_display_name = automl_dataset\n\n  # create dataset\n  create_dataset_response = client.create_dataset(\n      location_path,\n      {'display_name': dataset_display_name, 'tables_dataset_metadata': {}})\n  print(\"Creating AutoML Tables dataset...\")\n  dataset_name = create_dataset_response.name\n  print(\"Done\")\n\n  # import data\n  dataset_bq_input_uri = 'bq://{}.{}.{}'.format(project_id, bq_dataset, bq_table)\n  input_config = {\n      'bigquery_source': {\n          'input_uri': dataset_bq_input_uri}}\n\n  print(\"Importing data...\")\n  import_data_response = client.import_data(dataset_name, input_config)\n  while import_data_response.done() is False:\n    time.sleep(1)\n  print(\"Done\")\n\n  # get column specs\n  list_table_specs_response = client.list_table_specs(dataset_name)\n  table_specs = [s for s in list_table_specs_response]\n  table_spec_name = table_specs[0].name\n  list_column_specs_response = client.list_column_specs(table_spec_name)\n  column_specs = {s.display_name: s for s in list_column_specs_response}\n\n  # update dataset to assign a label\n  label_column_name = TARGET_LABEL\n  label_column_spec = column_specs[label_column_name]\n  label_column_id = label_column_spec.name.rsplit('/', 1)[-1]\n  update_dataset_dict = {\n      'name': dataset_name,\n      'tables_dataset_metadata': {\n          'target_column_spec_id': label_column_id\n      }\n  }\n  print(\"Setting label...\")\n  update_dataset_response = client.update_dataset(update_dataset_dict)\n  print(\"Done\")\n\n  # define the features used to train the model\n  feat_list = list(column_specs.keys())\n  feat_list.remove('target_monetary')\n  feat_list.remove('customer_id')\n  feat_list.remove('monetary_btyd')\n  feat_list.remove('frequency_btyd')\n  feat_list.remove('frequency_btyd_clipped')\n  feat_list.remove('monetary_btyd_clipped')\n  feat_list.remove('target_monetary_clipped')\n\n  # create and train the model\n  model_display_name = automl_model\n  model_training_budget = training_budget * 1000\n  model_dict = {\n    'display_name': model_display_name,\n    'dataset_id': dataset_name.rsplit('/', 1)[-1],\n    'tables_model_metadata': {\n        'target_column_spec': column_specs['target_monetary'],\n        'input_feature_column_specs': [\n            column_specs[x] for x in feat_list],\n        'train_budget_milli_node_hours': model_training_budget,\n        'optimization_objective': 'MINIMIZE_MAE'\n    }\n  }\n  print(\"Creating AutoML Tables model...\")\n  create_model_response = client.create_model(location_path, model_dict)\n  while create_model_response.done() is False:\n    time.sleep(10)\n  print(\"Done\")\n\n  create_model_result = create_model_response.result()\n  model_name = create_model_result.name\n\n  return model_name\n\n\ndef deploy_model(client, model_name):\n  \"\"\"\n  Deploy model for predictions.\n  \"\"\"\n  print(\"Deploying AutoML Tables model...\")\n  deploy_model_response = client.deploy_model(model_name)\n  api = client.transport._operations_client\n  while deploy_model_response.done is False:\n    deploy_model_response = api.get_operation(deploy_model_response.name)\n    time.sleep(10)\n  print(\"Done\")\n\n\ndef get_model_evaluation(client, model_name):\n  \"\"\"\n  Get the evaluation stats for the model.\n  \"\"\"\n  model_evaluations = [e for e in client.list_model_evaluations(model_name)]\n  model_evaluation = model_evaluations[0]\n  print(\"Model evaluation:\")\n  print(model_evaluation)\n  return model_evaluation\n\n\ndef do_batch_prediction(prediction_client,\n                        model_name,\n                        gcs_input_uri,\n                        gcs_output_uri_prefix):\n\n  # Define input source.\n  batch_prediction_input_source = {\n    'gcs_source': {\n      'input_uris': [gcs_input_uri]\n    }\n  }\n  # Define output target.\n  batch_prediction_output_target = {\n      'gcs_destination': {\n        'output_uri_prefix': gcs_output_uri_prefix\n      }\n  }\n\n  # initiate batch predict\n  print('Performing AutoML Tables batch predict...')\n  batch_predict_response = prediction_client.batch_predict(\n      model_name, batch_prediction_input_source, batch_prediction_output_target)\n\n  # Wait until batch prediction is done.\n  while batch_predict_response.done() is False:\n    time.sleep(1)\n  print('Done')\n\n  batch_predict_result = batch_predict_response.result()\n  return batch_predict_result\n\n\ndef create_parser():\n  \"\"\"Initialize command line parser using argparse.\n\n  Returns:\n    An argparse.ArgumentParser.\n  \"\"\"\n  parser = argparse.ArgumentParser()\n\n  # required args\n  parser.add_argument('--project_id',\n                      help='Project id for project containing BQ data',\n                      default=KEY_FILE,\n                      type=str,\n                      required=True)\n\n  # data and model args\n  parser.add_argument('--training_budget',\n                      help='Training budget in hours',\n                      default=1,\n                      type=int)\n  parser.add_argument('--key_file',\n                      help='JSON key file for API access',\n                      default=KEY_FILE,\n                      type=str)\n  parser.add_argument('--location',\n                      help='GCP region to run',\n                      default=LOCATION,\n                      type=str)\n  parser.add_argument('--automl_dataset',\n                      help='Name of AutoML dataset',\n                      default=AUTOML_DATASET,\n                      type=str)\n  parser.add_argument('--automl_model',\n                      help='Name of AutoML model',\n                      default=AUTOML_MODEL,\n                      type=str)\n  parser.add_argument('--bq_dataset',\n                      help='BigQuery dataset to import from',\n                      default=BQ_DATASET,\n                      type=str)\n  parser.add_argument('--bq_table',\n                      help='BigQuery table to import from',\n                      default=BQ_TABLE,\n                      type=str)\n  parser.add_argument('--batch_gcs_input',\n                      help='GCS URI for batch predict CSV',\n                      default=BATCH_GCS_INPUT,\n                      type=str)\n  parser.add_argument('--batch_gcs_output',\n                      help='GCS URI for batch predict output',\n                      default=BATCH_GCS_OUTPUT,\n                      type=str)\n  return parser\n\n\ndef main(argv=None):\n  \"\"\"Create and train the CLV model on AutoML Tables.\"\"\"\n  argv = sys.argv if argv is None else argv\n  args = create_parser().parse_args(args=argv[1:])\n\n  # create and configure client\n  keyfile_name = args.key_file\n  client = AutoMlClient.from_service_account_file(keyfile_name)\n\n  # create and deploy model\n  model_name = create_automl_model(client,\n                                   args.project_id,\n                                   args.location,\n                                   args.bq_dataset,\n                                   args.bq_table,\n                                   args.automl_dataset,\n                                   args.automl_model,\n                                   args.training_budget)\n\n  # deploy model\n  deploy_model(client, model_name)\n\n  # get model evaluations\n  model_evaluation = get_model_evaluation(client, model_name)\n\n  # make predictions\n  prediction_client = PredictionServiceClient.from_service_account_file(\n      keyfile_name)\n  do_batch_prediction(prediction_client,\n                      model_name,\n                      args.batch_gcs_input,\n                      args.batch_gcs_output)\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "clv_automl/to_predict.csv",
    "content": "customer_id,monetary,recency,frequency,avg_basket_value,avg_basket_size,date_range,cnt_orders,cnt_returns,has_returned\n0123456789,1000.0,10,2,240,4.0,240.0,13.0,1,1\n1234567890,500.0,20,3,250,5.0,250.0,15.0,1,1"
  },
  {
    "path": "clv_mle/__init__.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License."
  },
  {
    "path": "clv_mle/clv_ml_engine.egg-info/PKG-INFO",
    "content": "Metadata-Version: 1.0\nName: clv-ml-engine\nVersion: 0.1\nSummary: A trainer application package for CLV prediction on ML Engine\nHome-page: UNKNOWN\nAuthor: UNKNOWN\nAuthor-email: UNKNOWN\nLicense: UNKNOWN\nDescription: UNKNOWN\nPlatform: UNKNOWN\n"
  },
  {
    "path": "clv_mle/clv_ml_engine.egg-info/SOURCES.txt",
    "content": "setup.py\nclv_ml_engine.egg-info/PKG-INFO\nclv_ml_engine.egg-info/SOURCES.txt\nclv_ml_engine.egg-info/dependency_links.txt\nclv_ml_engine.egg-info/requires.txt\nclv_ml_engine.egg-info/top_level.txt\ntrainer/__init__.py\ntrainer/btyd.py\ntrainer/context.py\ntrainer/model.py\ntrainer/task.py"
  },
  {
    "path": "clv_mle/clv_ml_engine.egg-info/dependency_links.txt",
    "content": "\n"
  },
  {
    "path": "clv_mle/clv_ml_engine.egg-info/requires.txt",
    "content": "sh\nlifetimes==0.9.0.0\nnumpy==1.14.5\ntensorflow==1.10\n"
  },
  {
    "path": "clv_mle/clv_ml_engine.egg-info/top_level.txt",
    "content": "trainer\n"
  },
  {
    "path": "clv_mle/config.yaml",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ntrainingInput:\n  masterType: standard"
  },
  {
    "path": "clv_mle/config_tune.json",
    "content": "{\n  \"trainingInput\": {\n    \"scaleTier\": \"CUSTOM\",\n    \"masterType\": \"complex_model_m\",\n    \"hyperparameters\": {\n        \"goal\": \"MINIMIZE\",\n        \"hyperparameterMetricTag\": \"rmse\",\n        \"params\": [\n          {\n            \"parameterName\": \"learning_rate\",\n            \"type\": \"DOUBLE\",\n            \"minValue\": \"0.0001\",\n            \"maxValue\": \"0.1\",\n            \"scaleType\": \"UNIT_REVERSE_LOG_SCALE\"\n          },\n          {\n            \"parameterName\": \"l1_regularization\",\n            \"type\": \"DOUBLE\",\n            \"minValue\": \"0.0001\",\n            \"maxValue\": \"0.1\",\n            \"scaleType\": \"UNIT_REVERSE_LOG_SCALE\"\n          },\n          {\n            \"parameterName\": \"l2_regularization\",\n            \"type\": \"DOUBLE\",\n            \"minValue\": \"0.0001\",\n            \"maxValue\": \"0.1\",\n            \"scaleType\": \"UNIT_REVERSE_LOG_SCALE\"\n          },\n          {\n            \"parameterName\": \"dropout\",\n            \"minValue\": 0.01,\n            \"maxValue\": 0.99,\n            \"type\": \"DOUBLE\",\n            \"scaleType\": \"UNIT_REVERSE_LOG_SCALE\"\n          },\n          {\n            \"parameterName\": \"learning_decay_rate\",\n            \"minValue\": 0.6,\n            \"maxValue\": 0.99,\n            \"type\": \"DOUBLE\",\n            \"scaleType\": \"UNIT_LINEAR_SCALE\"\n          },\n          {\n            \"parameterName\": \"hidden_units\",\n            \"type\": \"CATEGORICAL\",\n            \"categoricalValues\": [\"256 128 64 32\", \"128 64 32 16\", \"256 128 64 32 16\", \"128 64 32 16 16 8\"]\n          },\n          {\n            \"parameterName\": \"batch_size\",\n            \"type\": \"DISCRETE\",\n            \"discreteValues\": [\"5\", \"10\", \"15\", \"20\", \"25\", \"30\"],\n          }\n        ],\n        \"maxTrials\": 1000,\n        \"maxParallelTrials\": 10\n    }\n  }\n}\n"
  },
  {
    "path": "clv_mle/setup.py",
    "content": "# Copyright 2017 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom setuptools import find_packages\nfrom setuptools import setup\n\nREQUIRED_PACKAGES = ['sh', 'lifetimes==0.9.0.0', 'numpy==1.14.5', 'tensorflow==1.10']\n\nsetup(\n    name='clv_ml_engine',\n    version='0.1',\n    install_requires=REQUIRED_PACKAGES,\n    packages=find_packages(),\n    include_package_data=True,\n    description='A trainer application package for CLV prediction on ML Engine'\n)"
  },
  {
    "path": "clv_mle/to_predict.csv",
    "content": "0123456789,1000.0,10,2,240,4.0,240.0,13.0,1,1\n1234567890,500.0,20,3,250,5.0,250.0,15.0,1,1"
  },
  {
    "path": "clv_mle/to_predict.json",
    "content": "{\"customer_id\":\"abc\", \"monetary\": 1000.0, \"recency\": 20.0, \"frequency\": 3.0, \"avg_basket_value\": 250.0, \"avg_basket_size\": 5.0, \"date_range\": 250, \"cnt_orders\": 15, \"cnt_returns\": 1, \"has_returned\": 1}\n{\"customer_id\":\"cde\", \"monetary\": 500.0, \"recency\": 20.0, \"frequency\": 3.0, \"avg_basket_value\": 250.0, \"avg_basket_size\": 5.0, \"date_range\": 250, \"cnt_orders\": 15, \"cnt_returns\": 1, \"has_returned\": 1}"
  },
  {
    "path": "clv_mle/trainer/README.md",
    "content": ""
  },
  {
    "path": "clv_mle/trainer/__init__.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License."
  },
  {
    "path": "clv_mle/trainer/btyd.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Core functions for Probabilistic (BTYD) models.\"\"\"\n\nfrom __future__ import print_function\nfrom __future__ import absolute_import\n\nfrom datetime import datetime\nfrom lifetimes import BetaGeoFitter, ParetoNBDFitter, GammaGammaFitter\nimport math\nimport numpy as np\nimport os\nimport pandas as pd\nimport tensorflow as tf\n\nfrom .model import PARETO, BGNBD\n\nPENALIZER_COEF = 0.01\nDISCOUNT_RATE = 0.01\n\nTRAINING_DATA_FILE = 'btyd.csv'\nOUTPUT_FILE = 'predictions.csv'\n\n\ndef load_data(datapath):\n  \"\"\"Loads data from CSV data file.\n\n  Args:\n    datapath: Location of the training file\n  Returns:\n    summary dataframe containing RFM data for btyd models\n    actuals_df containing additional data columns for calculating error\n  \"\"\"\n  # Does not used the summary_data_from_transaction_data from the Lifetimes\n  # library as it wouldn't scale as well. The pre-processing done in BQ instead.\n  tf.logging.info('Loading data...')\n\n  ft_file = '{0}/{1}'.format(datapath, TRAINING_DATA_FILE)\n#[START prob_selec]\n  df_ft = pd.read_csv(ft_file)\n\n  # Extracts relevant dataframes for RFM:\n  # - summary has aggregated values before the threshold date\n  # - actual_df has values of the overall period.\n  summary = df_ft[['customer_id', 'frequency_btyd', 'recency', 'T',\n                   'monetary_btyd']]\n#[END prob_selec]\n  summary.columns = ['customer_id', 'frequency', 'recency', 'T',\n                     'monetary_value']\n  summary = summary.set_index('customer_id')\n\n  # additional columns needed for calculating error\n  actual_df = df_ft[['customer_id', 'frequency_btyd', 'monetary_dnn',\n                     'target_monetary']]\n  actual_df.columns = ['customer_id', 'train_frequency', 'train_monetary',\n                       'act_target_monetary']\n\n  tf.logging.info('Data loaded.')\n\n  return summary, actual_df\n\n\ndef bgnbd_model(summary):\n  \"\"\"Instantiate and fit a BG/NBD model.\n\n  Args:\n    summary: RFM transaction data\n  Returns:\n    bgnbd model fit to the data\n  \"\"\"\n  bgf = BetaGeoFitter(penalizer_coef=PENALIZER_COEF)\n  bgf.fit(summary['frequency'], summary['recency'], summary['T'])\n  return bgf\n\n\ndef paretonbd_model(summary):\n  \"\"\"Instantiate and fit a Pareto/NBD model.\n\n  Args:\n    summary: RFM transaction data\n  Returns:\n    bgnbd model fit to the data\n  \"\"\"\n  #[START run_btyd]\n  paretof = ParetoNBDFitter(penalizer_coef=PENALIZER_COEF)\n  paretof.fit(summary['frequency'], summary['recency'], summary['T'])\n  return paretof\n  #[END run_btyd]\n\ndef run_btyd(model_type, data_src, threshold_date, predict_end):\n  \"\"\"Run selected BTYD model on data files located in args.data_src.\n\n  Args:\n    model_type:                 model type (PARETO, BGNBD)\n    data_src:                   path to data\n    threshold_date:             end date for training data 'YYYY-mm-dd'\n    predict_end:                end date for predictions 'YYYY-mm-dd'\n  \"\"\"\n  train_end_date = datetime.strptime(threshold_date, '%Y-%m-%d')\n  predict_end_date = datetime.strptime(predict_end, '%Y-%m-%d')\n\n  # load training transaction data\n  summary, actual_df = load_data(data_src)\n\n  # train fitter for selected model\n  tf.logging.info('Fitting model...')\n\n  if model_type == PARETO:\n    fitter = paretonbd_model(summary)\n  elif model_type == BGNBD:\n    fitter = bgnbd_model(summary)\n\n  tf.logging.info('Done.')\n\n  #\n  # use trained fitter to compute actual vs predicted ltv for each user\n  #\n\n  # compute the number of days in the prediction period\n  time_days = (predict_end_date - train_end_date).days\n  time_months = int(math.ceil(time_days / 30.0))\n\n  # fit gamma-gamma model\n  tf.logging.info('Fitting GammaGamma model...')\n\n  ggf = GammaGammaFitter(penalizer_coef=0)\n  ggf.fit(summary['frequency'], summary['monetary_value'])\n\n  tf.logging.info('Done.')\n\n  ltv, rmse = predict_value(summary,\n                            actual_df,\n                            fitter,\n                            ggf,\n                            time_days,\n                            time_months)\n\n  # output results to csv\n  output_file = os.path.join(data_src, OUTPUT_FILE)\n  ltv.to_csv(output_file, index=False)\n\n  # log results\n  tf.logging.info('BTYD RMSE error for %s model: %.2f', model_type, rmse)\n  print('RMSE prediction error: %.2f' % rmse)\n\n\ndef predict_value(summary, actual_df, fitter, ggf, time_days, time_months):\n  \"\"\"Predict lifetime values for customers.\n\n  Args:\n    summary:      RFM transaction data\n    actual_df:    dataframe containing data fields for customer id,\n                  actual customer values\n    fitter:       lifetimes fitter, previously fit to data\n    ggf:          lifetimes gamma/gamma fitter, already fit to data\n    time_days:    time to predict purchases in days\n    time_months:  time to predict value in months\n  Returns:\n    ltv:  dataframe with predicted values for each customer, along with actual\n      values and error\n    rmse: root mean squared error summed over all customers\n  \"\"\"\n  # setup dataframe to hold results\n  ltv = pd.DataFrame(data=np.zeros([actual_df.shape[0], 6]),\n                     columns=['customer_id',\n                              'actual_total',\n                              'predicted_num_purchases',\n                              'predicted_value',\n                              'predicted_total',\n                              'error'], dtype=np.float32)\n\n  predicted_num_purchases = fitter.predict(time_days,\n                                           summary['frequency'],\n                                           summary['recency'],\n                                           summary['T'])\n\n  predicted_value = ggf.customer_lifetime_value(fitter,\n                                                summary['frequency'],\n                                                summary['recency'],\n                                                summary['T'],\n                                                summary['monetary_value'],\n                                                time=time_months,\n                                                discount_rate=DISCOUNT_RATE)\n\n  ltv['customer_id'] = actual_df['customer_id']\n  ltv['actual_total'] = actual_df['act_target_monetary']\n  ltv['predicted_num_purchases'] = predicted_num_purchases.values\n  ltv['predicted_value'] = predicted_value.values\n  ltv['predicted_total'] = actual_df['train_monetary'] + ltv['predicted_value']\n  ltv['error'] = ltv['actual_total'] - ltv['predicted_total']\n\n  mse = pd.Series.sum(ltv['error'] * ltv['error']) / ltv.shape[0]\n  rmse = math.sqrt(mse)\n\n  return ltv, rmse\n"
  },
  {
    "path": "clv_mle/trainer/context.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Feature definition and processing.\"\"\"\n\nfrom tensorflow import feature_column as tfc\nfrom six import iteritems\n\nclass CLVFeatures(object):\n  \"\"\"Encapulates the features for Estimator models.\"\"\"\n\n  # Columns\n  HEADERS = ['customer_id', 'monetary_dnn', 'monetary_btyd', 'frequency_dnn',\n             'frequency_btyd', 'recency', 'T', 'time_between',\n             'avg_basket_value', 'avg_basket_size', 'cnt_returns',\n             'has_returned', 'frequency_btyd_clipped', 'monetary_btyd_clipped',\n             'target_monetary_clipped', 'target_monetary']\n\n  HEADERS_DEFAULT = [[''], [0.0], [0.0], [0],\n                     [0], [0], [0], [0.0],\n                     [0.0], [0.0], [0],\n                     [-1], [0], [0.0],\n                     [0.0], [0.0]]\n\n  NUMERICS = {\n      'monetary_dnn': [],\n      'recency': [],\n      'frequency_dnn': [],\n      'T': [],\n      'time_between': [],\n      'avg_basket_value': [],\n      'avg_basket_size': [],\n      'cnt_returns': []}\n\n  CATEGORICALS_W_LIST = {\n      'has_returned': [0, 1]}\n\n  # Columns to cross (name, bucket_size, boundaries)\n  # Note that boundaries is None if we have all the values. This will helps\n  # using between categorical_column_with_identity vs bucketized_column\n  # max(recency)=33383\n  # max(frequency) = 300\n  # max(monetary) = 3809291.2\n  CROSSED = []\n\n  KEY = 'customer_id'\n\n  UNUSED = [KEY, 'monetary_btyd', 'frequency_btyd', 'frequency_btyd_clipped',\n            'monetary_btyd_clipped', 'target_monetary_clipped']\n\n  TARGET_NAME = 'target_monetary'\n\n  def __init__(self, ignore_crosses=False, is_dnn=None):\n    \"\"\"Initialize CLVFeatures.\n\n    Args:\n        ignore_crosses: Whether to apply crosses or not\n        is_dnn: Whether the model is a dnn one or not.\n    \"\"\"\n    if not is_dnn:\n      return\n\n    self.ignore_crosses = ignore_crosses\n\n    # Initializes features names that will be used.\n    (self.headers, self.numerics_names,\n     self.categoricals_names) = self._keep_used()\n\n    # Creates the base continuous and categorical features\n    self.continuous, self.categorical = self._make_base_features()\n\n    # Creates the crossed features for both wide and deep.\n    if not self.ignore_crosses:\n      self.crossed_for_wide, self.crossed_for_deep = self._make_crossed()\n\n  def _keep_used(self):\n    \"\"\"Returns only the used headers names.\n\n    Returns:\n        used_headers names\n    \"\"\"\n    headers = [h for h in self.HEADERS if h not in self.UNUSED]\n    numerics_names = {\n        k: v for k, v in iteritems(self.NUMERICS)\n        if (k not in self.UNUSED) and (k != self.TARGET_NAME)\n    }\n    categoricals_names = {\n        k: v for k, v in iteritems(self.CATEGORICALS_W_LIST)\n        if k not in self.UNUSED\n    }\n\n    return headers, numerics_names, categoricals_names\n\n  def get_key(self):\n    return self.KEY\n\n  def get_used_headers(self, with_key=False, with_target=False):\n    \"\"\"Returns headers that are useful to the model.\n\n    Possibly includes the key and the target.\n\n    Args:\n        with_key: include KEY column\n        with_target: include target column\n    Returns:\n        used_headers\n    \"\"\"\n    used_headers = [h for h in self.headers if h != self.TARGET_NAME]\n\n    if with_key:\n      used_headers.insert(0, self.KEY)\n\n    if with_target:\n      used_headers.append(self.TARGET_NAME)\n\n    return used_headers\n\n  def get_defaults(self, headers_names=None, with_key=False):\n    \"\"\"Returns default values based on indexes taken from the headers to keep.\n\n    If key and target are to keep, it is decided in get_used_headers.\n\n    Args:\n        headers_names: column header names\n        with_key: include KEY column\n    Returns:\n        default values\n    \"\"\"\n    if headers_names is None:\n      headers_names = self.get_used_headers(with_key)\n\n    keep_indexes = [self.HEADERS.index(n) for n in headers_names]\n    return [self.HEADERS_DEFAULT[i] for i in keep_indexes]\n\n  def get_all_names(self):\n    return self.HEADERS\n\n  def get_all_defaults(self):\n    return self.HEADERS_DEFAULT\n\n  def get_unused(self):\n    return self.UNUSED\n\n  def get_target_name(self):\n    return self.TARGET_NAME\n\n  #####################\n  # Features creation #\n  #####################\n  # dense columns = numeric columns + embedding columns\n  # categorical columns = vocabolary list columns + bucketized columns\n  # sparse columns = hashed categorical columns + crossed columns\n  # categorical columns => indicator columns\n  # deep columns = dense columns + indicator columns\n  # wide columns = categorical columns + sparse columns\n\n  def _make_base_features(self):\n    \"\"\"Make base features.\n\n    Returns:\n      base features\n    \"\"\"\n    # Continuous columns\n    continuous = {key_name: tfc.numeric_column(key_name)\n                  for key_name in self.numerics_names.keys()}\n\n    # Categorical columns (can contain all categorical_column_with_*)\n    categorical = {\n        key_name: tfc.categorical_column_with_vocabulary_list(\n            key=key_name,\n            vocabulary_list=voc)\n        for key_name, voc in self.categoricals_names.items()\n    }\n\n    return continuous, categorical\n\n  def get_base_features(self):\n    # Could create bucket or/and hash here before return\n    return self.continuous, self.categorical\n\n  def _prepare_for_crossing(self, key_name, num_bck, boundaries):\n    \"\"\"Prepares features for crossing.\n\n    Whether they're continuous or categorical matters, and\n    whether we have the whole dictionary or not.\n\n    Args:\n      key_name: A string representing the name of the feature\n      num_bck: How many buckets to use when we know # of distinct values\n      boundaries: Range used for boundaries when bucketinizing\n    Returns:\n      key name\n    \"\"\"\n    key = None\n    if key_name in self.continuous.keys():\n      if boundaries is not None:\n        # Note that cont[key_name] is a source column\n        key = tfc.bucketized_column(self.continuous[key_name], boundaries)\n      else:\n        # We can count all the values in the dataset. Ex: boolean.\n        # Note that key_name is a string\n        key = tfc.categorical_column_with_identity(key_name, num_bck)\n    elif key_name in self.categorical.keys():\n      # It is also possible to use the categorical column instead of the\n      # column name. i.e key = cat[key_name]\n      key = key_name\n    else:\n      key = key_name\n\n    return key\n\n  def _make_crossed(self):\n    \"\"\"Makes crossed features for both Wide or Deep network.\n\n    Returns:\n      Tuple (crossed columns for Wide, its dimension)\n    \"\"\"\n    # Crossed columns\n    f_crossed_for_wide = []\n    f_crossed_for_deep = []\n    for to_cross in self.CROSSED:\n      keys = []\n      bck_size = 1\n      for (key, bck, bnd) in to_cross:\n        keys.append(self._prepare_for_crossing(key, bck, bnd))\n        bck_size *= bck\n\n      # We can't go crazy on the dim for crossed_column so use a min\n      # **0.25 is a rule of thumb for bucket size vs dimension\n      t_crossed = tfc.crossed_column(keys, min(bck_size, 10000))\n      t_dimension = int(bck_size**0.25)\n      f_crossed_for_wide.append(t_crossed)\n      f_crossed_for_deep.append(tfc.embedding_column(t_crossed, t_dimension))\n\n    return f_crossed_for_wide, f_crossed_for_deep\n\n  def get_wide_features(self):\n    \"\"\"Creates wide features.\n\n    Sparse (ie. hashed categorical + crossed) + categorical.\n\n    Returns:\n      A list of wide features\n    \"\"\"\n    # Base sparse (ie categorical) feature columns + crossed\n    wide_features = self.categorical.values()\n\n    if not self.ignore_crosses:\n      wide_features += self.crossed_for_wide\n\n    return  wide_features\n\n  def get_deep_features(self, with_continuous=True):\n    \"\"\"Creates deep features: dense(ie numeric + embedding) + indicator.\n\n    Args:\n      with_continuous: include continuous columns\n    Returns:\n        features for DNN\n    \"\"\"\n    # Multi-hot representation of categories. We know all the values so use\n    # indicator_column. If the vocabulary could be bigger in the outside\n    # world, we'd use embedding_column\n    deep_features = [tfc.indicator_column(f) for f in self.categorical.values()]\n\n    # Creates deep feature lists\n    if with_continuous:\n      deep_features += self.continuous.values()\n\n    if not self.ignore_crosses:\n      deep_features += self.crossed_for_deep\n\n    return deep_features\n"
  },
  {
    "path": "clv_mle/trainer/model.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"DNN Estimator model code.\"\"\"\n\nfrom __future__ import print_function\nfrom __future__ import absolute_import\n\nimport tensorflow as tf\n\nfrom .context import CLVFeatures\n\n# Possible estimators:\n# Canned: https://www.tensorflow.org/api_docs/python/tf/estimator or custom ones\nCANNED_MODEL_TYPES = ['DNNRegressor', 'Linear']\nMODEL_TYPES = CANNED_MODEL_TYPES[:] + ['dnn_model', 'paretonbd_model',\n                                       'bgnbd_model']\nCANNED_DEEP, LINEAR, DEEP, PARETO, BGNBD = MODEL_TYPES\nPROBABILISTIC_MODEL_TYPES = [PARETO, BGNBD]\n\n# Either a custom function or a canned estimator name\n# Used as default it not passed as an argument when calling the task\nMODEL_TYPE = DEEP\n\n# Features\nclvf = CLVFeatures(\n    ignore_crosses=True, is_dnn=MODEL_TYPE not in PROBABILISTIC_MODEL_TYPES)\n\n\ndef parse_csv(csv_row):\n  \"\"\"Parse CSV data row.\n\n  tf.data.Dataset.map takes a function as an input so need to call parse_fn\n  using map(lamba x: parse_fn(x)) or do def parse_fn and return the function\n  as we do here.\n  Builds a pair (feature dictionary, label) tensor for each example.\n\n  Args:\n    csv_row: one example as a csv row coming from the Dataset.map()\n  Returns:\n    features and targets\n  \"\"\"\n  columns = tf.decode_csv(csv_row, record_defaults=clvf.get_all_defaults())\n  features = dict(zip(clvf.get_all_names(), columns))\n\n  # Remove the headers that we don't use\n  for column_name in clvf.get_unused():\n    features.pop(column_name)\n\n  target = features.pop(clvf.get_target_name())\n\n  return features, target\n\n\ndef dataset_input_fn(data_folder, prefix=None, mode=None, params=None, count=None):\n  \"\"\"Creates a dataset reading example from filenames.\n\n  Args:\n    data_folder: Location of the files finishing with a '/'\n    prefix: Start of the file names\n    mode: tf.estimator.ModeKeys(TRAIN, EVAL)\n    params: hyperparameters\n  Returns:\n    features and targets\n  \"\"\"\n  shuffle = True if mode == tf.estimator.ModeKeys.TRAIN else False\n\n  # Read CSV files into a Dataset\n  filenames = tf.matching_files('{}{}*.csv'.format(data_folder, prefix))\n  dataset = tf.data.TextLineDataset(filenames)\n\n  # Parse the record into tensors.\n  dataset = dataset.map(parse_csv)\n\n  # Shuffle the dataset\n  if shuffle:\n    dataset = dataset.shuffle(buffer_size=params.buffer_size)\n\n  # Repeat the input indefinitely if count is None\n  dataset = dataset.repeat(count=count)\n\n  # Generate batches\n  dataset = dataset.batch(params.batch_size)\n\n  # Create a one-shot iterator\n  iterator = dataset.make_one_shot_iterator()\n\n  # Get batch X and y\n  features, target = iterator.get_next()\n\n  return features, target\n\n\ndef read_train(data_folder, params):\n  \"\"\"Returns a shuffled dataset for training.\"\"\"\n  return dataset_input_fn(\n      data_folder=data_folder,\n      prefix='train',\n      params=params,\n      mode=tf.estimator.ModeKeys.TRAIN)\n\n\ndef read_eval(data_folder, params):\n  \"\"\"Returns a dataset for evaluation.\"\"\"\n  return dataset_input_fn(data_folder=data_folder,\n                          prefix='eval',\n                          params=params)\n\n\ndef read_test(data_folder, params):\n  \"\"\"Returns a dataset for test.\"\"\"\n  return dataset_input_fn(data_folder=data_folder,\n                          prefix='test',\n                          params=params,\n                          count=1)\n\n#####################\n# Model Definitions #\n#####################\ndef dnn_model(features, mode, params):\n  \"\"\"Creates a DNN regressor model.\n\n  Args:\n    features: list of feature_columns\n    mode: tf.estimator.ModeKeys(TRAIN, EVAL)\n    params: hyperparameters\n\n  Returns:\n    output tensor\n  \"\"\"\n  # Make features\n  feature_columns = clvf.get_deep_features()\n\n  # Creates the input layers from the features.\n  h = tf.feature_column.input_layer(features=features,\n                                    feature_columns=feature_columns)\n\n  # Loops through the layers.\n  for size in params.hidden_units:\n    h = tf.layers.dense(h, size, activation=None)\n    h = tf.layers.batch_normalization(h, training=(\n        mode == tf.estimator.ModeKeys.TRAIN))\n    h = tf.nn.relu(h)\n    if (params.dropout is not None) and (mode == tf.estimator.ModeKeys.TRAIN):\n      h = tf.layers.dropout(h, params.dropout)\n\n  logits = tf.layers.dense(h, 1, activation=None)\n  return logits\n\n\ndef model_fn(features, labels, mode, params):\n  \"\"\"Model function for custom Estimator.\n\n  Args:\n    features: given by dataset_input_fn() tuple\n    labels: given by dataset_input_fn() tuple\n    mode: given when calling the estimator.train/predict/evaluate function\n    params: hyperparameters\n  Returns:\n      EstimatorSpec that can be used by tf.estimator.Estimator.\n  \"\"\"\n  # Build the dnn model and get output logits\n  logits = dnn_model(features, mode, params)\n\n  # Reshape output layer to 1-dim Tensor to return predictions\n  output = tf.squeeze(logits)\n\n  # Returns an estimator spec for PREDICT.\n  if mode == tf.estimator.ModeKeys.PREDICT:\n\n    #[START prediction_output_format]\n    predictions = {\n        'customer_id': tf.squeeze(features[clvf.get_key()]),\n        'predicted_monetary': output\n    }\n    export_outputs = {\n        'predictions': tf.estimator.export.PredictOutput(predictions)\n    }\n\n    return tf.estimator.EstimatorSpec(mode=mode,\n                                      predictions=predictions,\n                                      export_outputs=export_outputs)\n    #[END prediction_output_format]\n\n  # Calculates loss using mean squared error between the given labels\n  # and the calculated output.\n  loss = tf.losses.mean_squared_error(labels, output)\n\n  # Create Optimizer and thhe train operation\n  optimizer = get_optimizer(params)\n\n  # add update ops for batch norm stats\n  update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)\n  with tf.control_dependencies(update_ops):\n    train_op = optimizer.minimize(loss=loss,\n                                  global_step=tf.train.get_global_step())\n\n  # Root mean square error eval metric\n  eval_metric_ops = {\n      'rmse': tf.metrics.root_mean_squared_error(labels, output)\n  }\n\n  # Returns an estimator spec for EVAL and TRAIN modes.\n  return tf.estimator.EstimatorSpec(mode=mode,\n                                    loss=loss,\n                                    train_op=train_op,\n                                    eval_metric_ops=eval_metric_ops)\n\n\ndef rmse_evaluator(labels, predictions):\n  \"\"\"Metric for RMSE.\n\n  Args:\n    labels: Truth provided by the estimator when adding the metric\n    predictions: Predicted values. Provided by the estimator silently\n  Returns:\n    metric_fn that can be used to add the metrics to an existing Estimator\n  \"\"\"\n  pred_values = predictions['predictions']\n  return {'rmse': tf.metrics.root_mean_squared_error(labels, pred_values)}\n\n\ndef get_learning_rate(params):\n  \"\"\"Get learning rate given hyperparams.\n\n  Args:\n    params: hyperparameters\n\n  Returns:\n    learning_rate tensor if params.learning_rate_decay,\n    else a constant.\n  \"\"\"\n  if params.learning_rate_decay:\n    global_step = tf.train.get_global_step()\n    learning_rate = tf.train.exponential_decay(\n        learning_rate=params.learning_rate,\n        global_step=global_step,\n        decay_steps=params.checkpoint_steps,\n        decay_rate=params.learning_decay_rate,\n        staircase=True\n    )\n  else:\n    learning_rate = params.learning_rate\n  return learning_rate\n\n\ndef get_optimizer(params):\n  \"\"\"Get optimizer given hyperparams.\n\n  Args:\n    params: hyperparameters\n\n  Returns:\n    optimizer object\n\n  Raises:\n    ValueError: if params.optimizer is not supported.\n  \"\"\"\n  if params.optimizer == 'ProximalAdagrad':\n    optimizer = tf.train.ProximalAdagradOptimizer(\n        learning_rate=get_learning_rate(params),\n        l1_regularization_strength=params.l1_regularization,\n        l2_regularization_strength=params.l2_regularization\n    )\n  elif params.optimizer == 'SGD':\n    optimizer = tf.train.GradientDescentOptimizer(get_learning_rate(params))\n  elif params.optimizer == 'Adam':\n    optimizer = tf.train.AdamOptimizer(learning_rate=get_learning_rate(params))\n  elif params.optimizer == 'RMSProp':\n    optimizer = tf.train.RMSPropOptimizer(\n        learning_rate=get_learning_rate(params))\n  else:\n    raise ValueError('Invalid optimizer: %s' % params.optimizer)\n  return optimizer\n\n\ndef get_estimator(estimator_name, config, params, model_dir):\n  \"\"\"Return one of the TF-provided canned estimators defined by MODEL_TYPE.\n\n  Args:\n    estimator_name:     estimator model type\n    config:             run config\n    params:             hyperparams\n    model_dir:          model directory\n\n  Returns:\n    Estimator object\n  \"\"\"\n  print('-- Running training with estimator {} --'.format(estimator_name))\n\n  if estimator_name not in CANNED_MODEL_TYPES:\n    estimator = tf.estimator.Estimator(model_fn=model_fn,\n                                       config=config,\n                                       params=params,\n                                       model_dir=model_dir)\n  else:\n    if estimator_name == CANNED_DEEP:\n      estimator = tf.estimator.DNNRegressor(\n          feature_columns=clvf.get_deep_features(),\n          hidden_units=params.hidden_units,\n          config=config,\n          model_dir=model_dir,\n          optimizer=lambda: get_optimizer(params),\n          batch_norm=True,\n          dropout=params.dropout)\n    else:\n      estimator = tf.estimator.LinearRegressor(\n          feature_columns=clvf.get_wide_features(),\n          config=config,\n          model_dir=model_dir)\n\n    # Add RMSE for metric for canned estimators\n    estimator = tf.contrib.estimator.add_metrics(estimator, rmse_evaluator)\n  return estimator\n"
  },
  {
    "path": "clv_mle/trainer/task.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Entry point for CMLE jobs for CLV.\"\"\"\n\nfrom __future__ import division\nfrom __future__ import print_function\nfrom __future__ import absolute_import\n\nimport sys\n\nimport argparse\nimport json\nimport os\nimport shutil\nimport tensorflow as tf\n\nfrom .btyd import run_btyd\nfrom .context import CLVFeatures\nfrom .model import get_estimator, read_train, read_eval, read_test\nfrom .model import MODEL_TYPE, MODEL_TYPES, PROBABILISTIC_MODEL_TYPES\n\n# Training defaults\n\n# 100000 is the approximate size of our training set (to nearest 1000).\n#[START hyperparams]\nTRAIN_SIZE = 100000\nNUM_EPOCHS = 70\nBATCH_SIZE = 5\nNUM_EVAL = 20\n\nLEARNING_DECAY_RATE = 0.7\nHIDDEN_UNITS = '128 64 32 16'\nLEARNING_RATE = 0.00135\nL1_REGULARIZATION = 0.0216647\nL2_REGULARIZATION = 0.0673949\nDROPOUT = 0.899732\nSHUFFLE_BUFFER_SIZE = 10000\n#[END hyperparams]\n# TRAIN_SIZE = 100000\n# NUM_EPOCHS = 70\n# BATCH_SIZE = 20\n# NUM_EVAL = 20\n# HIDDEN_UNITS = '128 64 32 16'\n# LEARNING_RATE = 0.096505\n# L1_REGULARIZATION = 0.0026019\n# L2_REGULARIZATION = 0.0102146\n# DROPOUT = 0.843251\n# SHUFFLE_BUFFER_SIZE = 10000\n\n\ndef create_parser():\n  \"\"\"Initialize command line parser using arparse.\n\n  Returns:\n    An argparse.ArgumentParser.\n  \"\"\"\n  parser = argparse.ArgumentParser()\n\n  parser.add_argument('--model_type',\n                      help='Model type to train on',\n                      choices=MODEL_TYPES,\n                      default=MODEL_TYPE)\n\n  parser.add_argument('--job-dir', type=str, required=True)\n  parser.add_argument('--data-src', type=str, required=True)\n\n  # The following parameters are required for BTYD.\n  parser.add_argument('--predict_end', type=str, required=False,\n                      help='Predict end date YYYY-mm-dd')\n  parser.add_argument('--threshold_date', type=str, required=False,\n                      help='Threshold date YYYY-mm-dd')\n\n  # hyper params\n  parser.add_argument('--hidden_units',\n                      help='List of hidden units per fully connected layer.',\n                      default=HIDDEN_UNITS,\n                      type=str)\n  parser.add_argument('--learning_rate',\n                      help='Learning rate for the optimizer',\n                      default=LEARNING_RATE,\n                      type=float)\n  parser.add_argument('--learning_rate_decay',\n                      type=str,\n                      help='Use learning rate decay [True|False]',\n                      default='True')\n  parser.add_argument('--learning_decay_rate',\n                      help='Learning decay rate',\n                      type=float,\n                      default=LEARNING_DECAY_RATE)\n  parser.add_argument('--train_size',\n                      help='(Approximate) size of training set',\n                      default=TRAIN_SIZE,\n                      type=int)\n  parser.add_argument('--batch_size',\n                      help='Number of input records used per batch',\n                      default=BATCH_SIZE,\n                      type=int)\n  parser.add_argument('--buffer_size',\n                      help='Size of the buffer for training shuffle.',\n                      default=SHUFFLE_BUFFER_SIZE,\n                      type=float)\n  parser.add_argument('--train_set_size',\n                      help='Number of samples on the train dataset.',\n                      type=int)\n  parser.add_argument('--l1_regularization',\n                      help='L1 Regularization (for ProximalAdagrad)',\n                      type=float,\n                      default=L1_REGULARIZATION)\n  parser.add_argument('--l2_regularization',\n                      help='L2 Regularization (for ProximalAdagrad)',\n                      type=float,\n                      default=L2_REGULARIZATION)\n  parser.add_argument('--dropout',\n                      help='Dropout probability, 0.0 = No dropout layer',\n                      type=float,\n                      default=DROPOUT)\n  parser.add_argument('--hypertune',\n                      action='store_true',\n                      help='Perform hyperparam tuning',\n                      default=False)\n  parser.add_argument('--optimizer',\n                      help='Optimizer: [Adam, ProximalAdagrad, SGD, RMSProp]',\n                      type=str,\n                      default='ProximalAdagrad')\n  parser.add_argument('--num_epochs',\n                      help='Number of epochs',\n                      default=NUM_EPOCHS,\n                      type=int)\n  parser.add_argument('--ignore_crosses',\n                      action='store_true',\n                      default=False,\n                      help='Whether to ignore crosses (linear model only).')\n  parser.add_argument('--verbose-logging',\n                      action='store_true',\n                      default=False,\n                      help='Turn on debug logging')\n  parser.add_argument('--labels',\n                      type=str,\n                      default='',\n                      help='Labels for job')\n  parser.add_argument('--resume',\n                      action='store_true',\n                      default=False,\n                      help='Resume training on saved model.')\n  return parser\n\n\ndef csv_serving_input_fn():\n  \"\"\"Defines how the model gets exported and the required prediction inputs.\n\n  Required to have a saved_model.pdtxt file that can be used for prediction.\n\n  Returns:\n    ServingInputReceiver for exporting model.\n  \"\"\"\n  #[START csv_serving_fn]\n  clvf = CLVFeatures(ignore_crosses=True,\n                     is_dnn=MODEL_TYPE not in PROBABILISTIC_MODEL_TYPES)\n  used_headers = clvf.get_used_headers(with_key=True, with_target=False)\n  default_values = clvf.get_defaults(used_headers)\n\n  rows_string_tensor = tf.placeholder(dtype=tf.string, shape=[None],\n                                      name='csv_rows')\n  receiver_tensor = {'csv_rows': rows_string_tensor}\n\n  row_columns = tf.expand_dims(rows_string_tensor, -1)\n  columns = tf.decode_csv(row_columns, record_defaults=default_values)\n\n  features = dict(zip(used_headers, columns))\n\n  return tf.estimator.export.ServingInputReceiver(features, receiver_tensor)\n  #[END csv_serving_fn]\n\n\ndef main(argv=None):\n  \"\"\"Run the CLV model.\"\"\"\n  argv = sys.argv if argv is None else argv\n  args = create_parser().parse_args(args=argv[1:])\n\n  # Set logging mode\n  tf.logging.set_verbosity(tf.logging.INFO)\n\n  # execute non-estimator models\n  if args.model_type in PROBABILISTIC_MODEL_TYPES:\n    run_btyd(args.model_type, args.data_src, args.threshold_date,\n             args.predict_end)\n    return\n\n  if args.hypertune:\n    # if tuning, join the trial number to the output path\n    config = json.loads(os.environ.get('TF_CONFIG', '{}'))\n    trial = config.get('task', {}).get('trial', '')\n    model_dir = os.path.join(args.job_dir, trial)\n  else:\n    model_dir = args.job_dir\n\n  print('Running training with model {}'.format(args.model_type))\n\n  # data path\n  data_folder = '{}/'.format(args.data_src)\n\n  # Calculate train steps and checkpoint steps based on approximate\n  # training set size, batch size, and requested number of training\n  # epochs.\n  train_steps = (args.train_size/args.batch_size) * args.num_epochs\n  checkpoint_steps = int((args.train_size/args.batch_size) * (\n      args.num_epochs/NUM_EVAL))\n\n  # create RunConfig\n  config = tf.estimator.RunConfig(\n      save_checkpoints_steps=checkpoint_steps\n  )\n\n  hidden_units = [int(n) for n in args.hidden_units.split()]\n\n  # Hyperparameters\n  params = tf.contrib.training.HParams(\n      num_epochs=args.num_epochs,\n      train_steps=train_steps,\n      batch_size=args.batch_size,\n      hidden_units=hidden_units,\n      learning_rate=args.learning_rate,\n      ignore_crosses=args.ignore_crosses,\n      buffer_size=args.buffer_size,\n      learning_rate_decay=(\n          args.learning_rate_decay == 'True'),\n      learning_decay_rate=args.learning_decay_rate,\n      l1_regularization=args.l1_regularization,\n      l2_regularization=args.l2_regularization,\n      optimizer=args.optimizer,\n      dropout=(\n          None if args.dropout == 0.0 else args.dropout),\n      checkpoint_steps=checkpoint_steps)\n\n  print(params)\n  print('')\n  print('Dataset Size:', args.train_size)\n  print('Batch Size:', args.batch_size)\n  print('Steps per Epoch:', args.train_size/args.batch_size)\n  print('Total Train Steps:', train_steps)\n  print('Required Evaluation Steps:', NUM_EVAL)\n  print('Perform evaluation step after each', args.num_epochs/NUM_EVAL,\n        'epochs')\n  print('Save Checkpoint After', checkpoint_steps, 'steps')\n  print('**********************************************')\n\n  # Creates the relevant estimator (canned or custom)\n  estimator = None\n\n  # get model estimator\n  #[START choose_model]\n  estimator = get_estimator(estimator_name=args.model_type,\n                            config=config,\n                            params=params,\n                            model_dir=model_dir)\n  #[END choose_model]\n  # Creates the training and eval specs by reading the relevant datasets\n  # Note that TrainSpec needs max_steps otherwise it runs forever.\n  train_spec = tf.estimator.TrainSpec(\n      input_fn=lambda: read_train(data_folder, params),\n      max_steps=train_steps)\n\n  eval_spec = tf.estimator.EvalSpec(\n      input_fn=lambda: read_eval(data_folder, params),\n      exporters=[\n          tf.estimator.LatestExporter(\n              name='estimate',\n              serving_input_receiver_fn=csv_serving_input_fn,\n              exports_to_keep=1,\n              as_text=True\n          )\n      ],\n      steps=1000,\n      throttle_secs=1,\n      start_delay_secs=1\n  )\n\n  if not args.resume:\n    print('Removing previous trained model...')\n    shutil.rmtree(model_dir, ignore_errors=True)\n  else:\n    print('Resuming training...')\n\n  # Runs the training and evaluation using the chosen estimator.\n  # Saves model data into export/estimate/1234567890/...\n  tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)\n\n  # Evaluate the test set for final metrics\n  estimator.evaluate(lambda: read_test(data_folder, params), name=\"Test Set\")\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "linear.py",
    "content": "from tensorflow.python.lib.io import file_io\nimport pandas\nfrom pandas.compat import StringIO\nimport json\nimport math\nimport numpy as np\nfrom sklearn.linear_model import LinearRegression\n\nc_names =['customer_id', 'monetary_dnn', 'monetary_btyd', 'frequency_dnn',\n       'frequency_btyd', 'recency', 'T', 'time_between', 'avg_basket_value',\n       'avg_basket_size', 'cnt_returns', 'has_returned',\n       'frequency_btyd_clipped', 'monetary_btyd_clipped',\n       'target_monetary_clipped', 'target_monetary']\n\n\ntrain_df = file_io.FileIO(\n            'data/train.csv',\n            mode='r').read()\ntrain_df = pandas.read_csv(\n            StringIO(train_df),\n            header = None,\n            names = c_names,\n            delimiter=',',\n            na_filter=True)\n\ntest_df = file_io.FileIO(\n            'data/eval.csv',\n            mode='r').read()\ntest_df = pandas.read_csv(\n            StringIO(test_df),\n            header = None,\n            names = c_names,\n            delimiter=',',\n            na_filter=True)\n\nreg = LinearRegression().fit(\n    train_df.values[:, [1,3,5,6,7,8,9,10,11]],\n    train_df.values[:, -1])\n\nerror = 0\ni = 0\nfor p in reg.predict(test_df.values[:, [1,3,5,6,7,8,9,10,11]]):\n    error = error + math.pow(p - test_df.values[i, -1], 2)\n    i = i +1\n\nprint \"RMSE = \", math.sqrt(error/test_df.values.shape[0])"
  },
  {
    "path": "notebooks/Exploration.ipynb",
    "content": "{\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0,\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"Exploration.ipynb\",\n      \"version\": \"0.3.2\",\n      \"provenance\": []\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"language\": \"python\",\n      \"name\": \"python3\"\n    }\n  },\n  \"cells\": [\n    {\n      \"metadata\": {\n        \"id\": \"0jifCBTzSnwO\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"a17521de-b813-4125-b238-5b656578db25\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 17\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# %%javascript\\n\",\n        \"# IPython.OutputArea.prototype._should_scroll = function(lines) {\\n\",\n        \"#     return false;\\n\",\n        \"# }\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"application/javascript\": [\n              \"IPython.OutputArea.prototype._should_scroll = function(lines) {\\n\",\n              \"    return false;\\n\",\n              \"}\"\n            ],\n            \"text/plain\": [\n              \"<IPython.core.display.Javascript object>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          }\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"GqvIibXtSnwT\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"import pandas as pd\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"from mpl_toolkits.mplot3d import Axes3D\\n\",\n        \"import seaborn as sns\\n\",\n        \"from google.cloud import bigquery\\n\",\n        \"import calendar\\n\",\n        \"import time\\n\",\n        \"import os\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"metadata\": {\n        \"id\": \"l2EbQxk8SnwV\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"PROJECT_ID = '[YOUR-PROJECT-ID]'\\n\",\n        \"DATASET = '[YOUR-DATASET]'\\n\",\n        \"TABLE = 'data_source'\\n\",\n        \"\\n\",\n        \"from google.colab import auth\\n\",\n        \"auth.authenticate_user()\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"metadata\": {\n        \"id\": \"IRZwAmVtWAW4\",\n        \"colab_type\": \"text\"\n      },\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"### Look at source data\"\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"sO2ttiq6V_6M\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"692906c7-e8c2-4303-a253-bda403fb2022\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 416\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"qry_original = \\\"\\\"\\\"\\n\",\n        \"SELECT\\n\",\n        \"  DATETIME_DIFF(\\n\",\n        \"    PARSE_DATETIME(\\\"%m/%d/%y %H:%M\\\", InvoiceDate), mm.mind, DAY) AS day_number,\\n\",\n        \"  *\\n\",\n        \"FROM\\n\",\n        \"  `{p}.{d}.{t}`,\\n\",\n        \"  (\\n\",\n        \"  SELECT\\n\",\n        \"    MIN(PARSE_DATETIME(\\\"%m/%d/%y %H:%M\\\", InvoiceDate)) mind,\\n\",\n        \"    MAX(PARSE_DATETIME(\\\"%m/%d/%y %H:%M\\\", InvoiceDate)) maxd\\n\",\n        \"  FROM\\n\",\n        \"    `{p}.{d}.{t}`) mm {w}\\n\",\n        \"\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"where_sample = \\\"\\\"\\\"\\n\",\n        \"WHERE\\n\",\n        \"  -- 1 in 50 so we get about 500000 records out of 10000\\n\",\n        \"  MOD(ABS(FARM_FINGERPRINT( \\n\",\n        \"    CONCAT(\\n\",\n        \"      CAST(InvoiceNo AS STRING),\\n\",\n        \"      CAST(Quantity AS STRING),\\n\",\n        \"      CAST(InvoiceDate AS STRING),\\n\",\n        \"      CAST(UnitPrice AS STRING),\\n\",\n        \"      CAST(CustomerID AS STRING)\\n\",\n        \"    ) )), 50) = 1\\n\",\n        \"\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"df_original = pd.io.gbq.read_gbq(qry_original.format(p=PROJECT_ID, d=DATASET, t=TABLE, w=where_sample), \\n\",\n        \"                                 project_id=PROJECT_ID, \\n\",\n        \"                                 dialect='standard')\\n\",\n        \"\\n\",\n        \"print(df_original.head(2))\\n\",\n        \"df_original.describe()\"\n      ],\n      \"execution_count\": 88,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"   day_number InvoiceNo StockCode Description  Quantity     InvoiceDate  \\\\\\n\",\n            \"0          90    545332         M      Manual         1  03/01/11 03:52   \\n\",\n            \"1         337    574252         M      Manual         1  11/03/11 01:24   \\n\",\n            \"\\n\",\n            \"   UnitPrice CustomerID Country                mind                maxd  \\n\",\n            \"0     183.75      12352  Norway 2010-12-01 01:04:00 2011-12-09 12:50:00  \\n\",\n            \"1       0.00      12437  France 2010-12-01 01:04:00 2011-12-09 12:50:00  \\n\"\n          ],\n          \"name\": \"stdout\"\n        },\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/html\": [\n              \"<div>\\n\",\n              \"<style scoped>\\n\",\n              \"    .dataframe tbody tr th:only-of-type {\\n\",\n              \"        vertical-align: middle;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe tbody tr th {\\n\",\n              \"        vertical-align: top;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe thead th {\\n\",\n              \"        text-align: right;\\n\",\n              \"    }\\n\",\n              \"</style>\\n\",\n              \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n              \"  <thead>\\n\",\n              \"    <tr style=\\\"text-align: right;\\\">\\n\",\n              \"      <th></th>\\n\",\n              \"      <th>day_number</th>\\n\",\n              \"      <th>Quantity</th>\\n\",\n              \"      <th>UnitPrice</th>\\n\",\n              \"    </tr>\\n\",\n              \"  </thead>\\n\",\n              \"  <tbody>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>count</th>\\n\",\n              \"      <td>7974.000000</td>\\n\",\n              \"      <td>7974.000000</td>\\n\",\n              \"      <td>7974.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>mean</th>\\n\",\n              \"      <td>221.630298</td>\\n\",\n              \"      <td>12.244043</td>\\n\",\n              \"      <td>3.088569</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>std</th>\\n\",\n              \"      <td>112.506385</td>\\n\",\n              \"      <td>43.805342</td>\\n\",\n              \"      <td>7.036266</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>min</th>\\n\",\n              \"      <td>0.000000</td>\\n\",\n              \"      <td>-144.000000</td>\\n\",\n              \"      <td>0.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>25%</th>\\n\",\n              \"      <td>130.000000</td>\\n\",\n              \"      <td>2.000000</td>\\n\",\n              \"      <td>1.250000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>50%</th>\\n\",\n              \"      <td>240.000000</td>\\n\",\n              \"      <td>5.000000</td>\\n\",\n              \"      <td>1.950000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>75%</th>\\n\",\n              \"      <td>323.000000</td>\\n\",\n              \"      <td>12.000000</td>\\n\",\n              \"      <td>3.750000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>max</th>\\n\",\n              \"      <td>373.000000</td>\\n\",\n              \"      <td>1500.000000</td>\\n\",\n              \"      <td>295.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"  </tbody>\\n\",\n              \"</table>\\n\",\n              \"</div>\"\n            ],\n            \"text/plain\": [\n              \"        day_number     Quantity    UnitPrice\\n\",\n              \"count  7974.000000  7974.000000  7974.000000\\n\",\n              \"mean    221.630298    12.244043     3.088569\\n\",\n              \"std     112.506385    43.805342     7.036266\\n\",\n              \"min       0.000000  -144.000000     0.000000\\n\",\n              \"25%     130.000000     2.000000     1.250000\\n\",\n              \"50%     240.000000     5.000000     1.950000\\n\",\n              \"75%     323.000000    12.000000     3.750000\\n\",\n              \"max     373.000000  1500.000000   295.000000\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 88\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"nPxHxgGoaUH5\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"ef6a3b05-e26f-4f21-bf81-1b23c0934716\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 609\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(20, 10))\\n\",\n        \"\\n\",\n        \"# distplot not as easy to clip\\n\",\n        \"# sns.distplot( df_original['Quantity'], ax=axs[0], label='quantity')\\n\",\n        \"# sns.distplot(df_original['UnitPrice'], ax=axs[1], label='price')\\n\",\n        \"\\n\",\n        \"sns.kdeplot(df_original['Quantity'], ax=axs[0, 0], label='quantity', clip=(-50, 100))\\n\",\n        \"sns.kdeplot(df_original['UnitPrice'], ax=axs[0, 1], label='price', clip=(-50, 5000))\\n\",\n        \"sns.kdeplot(df_original['day_number'], ax=axs[1, 0], label='days')\"\n      ],\n      \"execution_count\": 89,\n      \"outputs\": [\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/plain\": [\n              \"<matplotlib.axes._subplots.AxesSubplot at 0x7f687124c550>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 89\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAABJEAAAI/CAYAAADHiEgWAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBo\\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3X1823d97/33T3e+k+xIjuQkTdOG\\n0BIIV1bCwihmSdkSwsKBDega00forgOHQx8LMFiyq314sOQckmzto3QHQg/ltOkuxk3nrcvFuo0R\\naElZ15gGCktpoEkT2tRJGlu+t2zr7iddf/wk2U7sRPpZsmTr9fwHyz9L/upL+pD81ufz+RrpdDot\\nAAAAAAAA4Aoc5V4AAAAAAAAAKh8hEgAAAAAAAK6KEAkAAAAAAABXRYgEAAAAAACAqyJEAgAAAAAA\\nwFURIgEAAAAAAOCqXOVewJWEwyMle2y/v14DA2Mle/z5gn2wsA8W9sHCPljYBwv7YCnlPgSDvpI8\\nLuwr5XuwLP7bKhx7Zg/7Zg/7Vjj2zB72zZ7Z7pvd919VW4nkcjnLvYSKwD5Y2AcL+2BhHyzsg4V9\\nsLAPKDb+TRWOPbOHfbOHfSsce2YP+2ZPufatakMkAAAAAAAA5I8QCQAAAAAAAFdFiAQAAAAAAICr\\nIkQCAAAAAADAVREiAQAAAAAA4KoIkQAAAAAAAHBVhEgAAAAAAAC4KkIkAACQl6eeelKS9N3v/rN+\\n9KMjkqQjR54o55IAAAAWtG984//VCy88X+5l5BAiATNImin95MUemalUuZcCAGV37tw5PfHEYUnS\\n1q3v08aN71IikVBHx7fLvDIsFPGEqWd/2a1E0iz3UgAAqBgf+cj/rTe/eW25l5HjKvcCgEr1k1/1\\n6KF/+aX++/vepLevWVLu5QBA3kZHI2pv/38Uj8f01reu1+HD31U6ndbf/m2H6uvr9ZWv/C+97nWr\\ntHHju/Q//sfnND4+rmg0qs9+9s/0pje9Wdu2/YF+//c/qGeeeVrxeFxf+tL/1r59/1PHjx/X3/zN\\nQ0qlUlq0aJFeeeUVnTlzWvfd91c6efJX2rNnn665Zrl6erp199079cgj3yz3VixI+/fv1/Hjx2UY\\nhtrb27V27cQby6NHj+r++++X0+nUhg0btGPHDknS448/rocfflgul0uf/vSndcstt5Rp9TP72Uth\\n/Z/Hf6k7f3+N3vbGlnIvBwCAOfHd7/6znn32qEZHRxUO9+i2227XN77xN3r721vl9/t17lyXbrnl\\nd/Vbv3Wz9u7dre7u1+Tx1Oiv//qLSqdrdO+9+3Thwnklk0n9t/92p9761vUlXS+VSMAMwkPjkqSe\\nwfEyrwQACvO9731XN9xwo7761YO6/vqVSqfT0/5cX1+f/st/+QMdOPA13XnnJ/Wtb31dkmSaplas\\nuF4PPPCQli1bpp/+9Cf62Mc+pptuWqf/+l8/nrv/7bd/RCtWXKddu+7We96zVU8++X1J0n/8x79r\\n06YtpX+iVejYsWM6e/asOjo6tG/fPu3bt2/K9b179+rAgQN69NFH9cwzz+j06dMaGBjQAw88oG9/\\n+9t68MEH9eSTT5Zp9VcWjVsVSOOxZJlXAgDA3Hr55V/rr/7qfn3pSw/qoYe+qng8rre//R36oz/6\\nWO5n/u3f/kXNzc366lcf0fve9wd68skn9YMffE/NzYt14MDX9Jd/+UV9+ctfLPlaqUQCZjA0Gpck\\nDYzEyrwSAPPZ3//wtH7yYk9RH3P96pBu+53Xz3j97NmX9Za3vFWScv87nUCgWV//+sN69NFvKJFI\\nqLa2NnftN37jLZKkYLBFo6MRSaErrmnTpi360z/9lO6446M6evRp3XXX5wp4RshXZ2enNm3aJEla\\ntWqVhoaGFIlE5PV61dXVpaamJi1dulSStHHjRnV2dqq5uVk333yzvF6vvF6vvvCFL5TzKcwolbLC\\nzqQ5fegJAECpleN9myTddNM6uVwuLVq0SD6fTxcunNeb3rRmys+cPPmifvM3rSqjTZu2KBj06a67\\n2nX8+M/1/PP/KUmKxWJKJBJyu91FfQ6TESIBMxgmRAIwT6XTkmEYkiSn03qpz96WpGTSqvT4+7//\\nthYvDunzn/+CXnzxl/rKV/5X7mecTuekx7v6H/VNTYsUCoX0q1+dUCqVVjB45dAJ9vT29mrNmok3\\nlYFAQOFwWF6vV+FwWIFAYMq1rq6uXLvinXfeqeHhYX3qU5/SzTffXI7lX5GZC5GYRQgAqC7ZD1Kk\\nifdxLtfUIMjpdEz5OUlyudy6446PavPm98zJOiVCJGBG2Uqk/mFCJAD23fY7r7/qp0/Fdt111+mX\\nv3xBt9zyu/rpT5+VJNXXN6ivr1c1NdfoxIlf6MYb36ChoUGtWnWDJOlHPzqSC5em43A4ZJpTBx4b\\nxtTvbdmyVffff4/e//4PluBZYTr5BHySNDg4qK985Su6cOGC7rjjDh05cmRKsHgpv79eLpdzxuvF\\nEgz6cl/X13skSTW17infx1TsjT3smz3sW+HYM3sqZd92bHvLnP9On69WJ0+eUCBQr6GhIcVi4/L7\\n/Vq82KuGhgbV1rrV1FSnt73trfr5z3+ubds+oCNHjujkyZN6+9t/Uz/84Q91++1/qL6+Pn3961/X\\nn/7pn5Z0vYRIwAyGI9lKpGiZVwIAhdmy5b1qb9+lHTs+rrVrb5IkfehDt+muuz6rFSuu08qVr5Mk\\nvec979Xevbt15MgT+tCHbtMTT3xf//qvj0/7mKtWrdLJky/qy1/+ohoavJKkxYsXK5lM6HOfu0t7\\n996j1tYNuueefbrllt+dmydahUKhkHp7e3O3e3p6FAwGp73W3d2tUCikuro6veUtb5HL5dKKFSvU\\n0NCg/v5+NTc3z/h7BgbGSvckMoJBn8LhkdztoeFo7n8nfx8TLt0z5Id9s4d9Kxx7Zk+179vISFSL\\nF7fozjt36Pz5Ln3sY3fq4YcfVG9vRGNjKUWjCQ0Njettb9ugH/7wR9q27cNyOl3667++T6mUR089\\n9bQ+9KE/lGma+uhH/3vee2k3uCNEAmaQrUQajSYVS5iqcZf+E1kAKAafz6cDB74mSRobG9MPfvA9\\nvf/9H9D73/+By372W996LPf1O9+5UZL03ve+P/e9T37yM5KkQMCnQ4f+9bL7f/Ob/5D7+he/OK7W\\n1t+Wz1cZnyYuRK2trTpw4IDa2tp04sQJhUIheb1WqLd8+XJFIhGdO3dOS5Ys0ZEjR3Tfffepvr5e\\nd999tz7+8Y9raGhIY2Nj8vv9ZX4ml6OdDQBQra65ZnnuPZdkfdCX9ed/vif39ec//z9zX2fDt7vv\\n/vycrDGLEAmYRjRuBUdZgyMxtQTqy7giAKhsBw9+Tc8+26l9++4t91IWtHXr1mnNmjVqa2uTYRja\\nvXu3Dh06JJ/Pp82bN2vPnj3auXOnJGnr1q1auXKlJGnLli267bbbJEmf+9zn5HBU3gG92TkPJoO1\\nAQCoWIRIwDSyQ7Wz+oejhEgA5qX6+no99tg/l/z3fOxjn9DHPvaJkv8eSLt27Zpye/Xq1bmv169f\\nr46Ojsvu09bWpra2tpKvbTaylUgJKpEAAFVk69b3lXsJBam8j6GACpBtZWuotXLWfk5oAwCgpCYq\\nkQiRAACoVIRIwDSGMkO1r1/aKEkaIEQCAKCkUrmZSLSzAQBQqQiRgGlkK5GuX2INhyVEAgCgtHKD\\ntVNUIgEAUKkIkYBpDOdCJCqRAACYC2YmPEomCZEAAKhUhEjANLKVSMsW18vjcqh/JFrmFQEAsLDR\\nzgYAQOUjRAKmka1EamrwyO+roRIJAIASo50NAIDKR4gETGNoNCaX06G6Gpf8vhqNjCWUSJrlXhYA\\nAAtWrhKJdjYAACoWIRIwjaHRuJoaPDIMQ4HGWknSQObENgAAUHxmOluJRDsbAACVihAJuEQ6ndbw\\naFxNXo8kye+rkSQNDDMXCQCAUslWIpkmlUgAAFQqQiTgEmOxpJJmWo31VogUyIZIzEUCAKBkTAZr\\nAwBQ8QiRgEsMZdrWJiqRMu1shEgAAJSMaWZDJCqRAACoVLZDpP3792vbtm1qa2vT888/P+VaLBbT\\nXXfdpQ9+8IN53weoFEOTTmaTJtrZ+gmRAAAomVSaEAkAgEpnK0Q6duyYzp49q46ODu3bt0/79u2b\\ncv3ee+/VG9/4xoLuA1SK4UtDpEba2QAAKDXa2QAAqHy2QqTOzk5t2rRJkrRq1SoNDQ0pEonkrn/2\\ns5/NXc/3PkClyFYiNWZCJF+dWy6noYERBmsDAFAqqRSVSAAAVDpbIVJvb6/8fn/udiAQUDgczt32\\ner0F3weoFEOjVsVRU4NVgWQYhvy+GtrZAAAoISqRAACofK5iPEg6XfiLfT738fvr5XI57SwpL8Gg\\nr2SPPZ+wD5bsPsSS1r/NlSv8CjY3SJJCgQb98uU++QMNcjkX9jx6/j1Y2AcL+2BhHyzsA0opW4lk\\nUokEAEDFshUihUIh9fb25m739PQoGAwW/T4DA2N2lpeXYNCncHikZI8/X7APlsn70NM3KklKRhO5\\n7/lqXUqnpdMv96m5qbZs6yw1/j1Y2AcL+2BhHyyl3AfCKUhUIgEAMB/YKqlobW3V4cOHJUknTpxQ\\nKBSatoVttvcBymF4NK5aj1M1nokquOwJbQzXBgCgNLKVSKl0Ovc1AACoLLYqkdatW6c1a9aora1N\\nhmFo9+7dOnTokHw+nzZv3qxPf/rTunjxol5++WV95CMf0W233ab3ve99l90HqERDo/HcUO2sbIjU\\nPxKV1FSGVQEAsLCZqYk2tqSZksdRupEGAADAHtszkXbt2jXl9urVq3Nff/nLX87rPkClSaXSGh6L\\n6/X+qUGR32e1sPUPU4kEAEApmJOqj5JmWh53GRcDAACmtbAnBAMFGhlPKJ2Wmi6pRAo00s4GAEAp\\npaaESAzXBgCgEhEiAZMMj8YlSU0NNVO+PzETKTrnawIAoBqYhEgAAFQ8QiRgkqFRq9KosWFqDX1j\\nvUdOh0ElEgAAJZJKTwqRGKwNAEBFIkQCJhmKZCqRvFMrkRwOQ4u8HvUTIgEAUBKTK5FMKpEAAKhI\\nhEjAJNl2tktPZ5OkRb4aDUZiUz4pBQAAxTF5JlIiSYgEAEAlIkQCJhnKzUS6PETy1XmUTktj0eRc\\nLwsAgAXPNCdVItHOBgBARSJEAiYZvkKI5K2z5iSNjifmdE0AAFQDM81gbQAAKh0hEjBJthLJVz9z\\niBQhRAIAoOgmt7MlaWcDAKAiucq9AKCSDI3G1VDrktt1eb7aUGf950KIBAAop/379+v48eMyDEPt\\n7e1au3Zt7trRo0d1//33y+l0asOGDdqxY4eeffZZ/cmf/IluuOEGSdKNN96oz3/+8+Va/oymhEi0\\nswEAUJEIkYBJhiKxy05my6ISCQBQbseOHdPZs2fV0dGhM2fOqL29XR0dHbnre/fu1cGDB9XS0qLt\\n27dry5YtkqS3ve1t+vKXv1yuZV9VOp2eMgeJdjYAACoT7WxAhplKaTSaVGO9e9rrzEQCAJRbZ2en\\nNm3aJElatWqVhoaGFIlEJEldXV1qamrS0qVL5XA4tHHjRnV2dpZzuXm79ODTyUO2AQBA5SBEAjLi\\nCetTz1rP9AV6uUqkKCESAKA8ent75ff7c7cDgYDC4bAkKRwOKxAITHvt9OnTuvPOO/XhD39Yzzzz\\nzNwuOg+XnsaWoBIJAICKRDsbkBFPmJIkj3v6bLUh186WnLM1AQBwJelLS3imcf311+uTn/ykfu/3\\nfk9dXV2644479P3vf18ez+WHSGT5/fVyuZzFXOq0gkGfJGk8NvW1tb7ek7uGqdgXe9g3e9i3wrFn\\n9rBv9pRj3wiRgIxY5iQYzwxvmn3MRAIAlFkoFFJvb2/udk9Pj4LB4LTXuru7FQqF1NLSoq1bt0qS\\nVqxYocWLF6u7u1vXXnvtjL9nYGCsRM9gQjDoUzg8Ikkau6TKd2BwPHcNEybvGfLHvtnDvhWOPbOH\\nfbNntvtmN4CinQ3IiMfzq0RiJhIAoFxaW1t1+PBhSdKJEycUCoXk9XolScuXL1ckEtG5c+eUTCZ1\\n5MgRtba26vHHH9fBgwclWS1vfX19amlpKdtzmA7tbAAAzA9UIgEZsWQ2RJq+EsnldKjW46QSCQBQ\\nNuvWrdOaNWvU1tYmwzC0e/duHTp0SD6fT5s3b9aePXu0c+dOSdLWrVu1cuVKBYNB7dq1S08++aQS\\niYT27NlzxVa2ckhlQiSP26F4IsVgbQAAKhQhEpCRHaztcc1coOetcxMiAQDKateuXVNur169Ovf1\\n+vXr1dHRMeW61+vVgw8+OCdrsytbieRxORVPpJSkEgkAgIpEOxuQkR2sXeOZeZBoQ52bdjYAAIos\\nW4lUk6kGJkQCAKAyESIBGfGrDNaWrEqkeDKVC5wAAMDsmemJdjZJStLOBgBARSJEAjKywdBMg7Ul\\nK0SSOKENAIBiys5AohIJAIDKRogEZOTa2WYYrC1J3lpCJAAAiu3SdjYGawMAUJkIkYCMWOLq7WwN\\nddYseuYiAQBQPLnB2pkQKUElEgAAFYkQCcgoqJ0tmpyTNQEAUA1Sl8xEMgmRAACoSIRIQEYsmQ2R\\nrjxYW5IiY/E5WRMAANUgV4mUqQZOpmhnAwCgEhEiARnxTDvbFWciMVgbAICiy81E8jBYGwCASkaI\\nBGTk087WkAuRaGcDAKBYJiqRrNfgZJIQCQCASkSIBGTEk1cfrE0lEgAAxZe6ZLA27WwAAFQmQiQg\\nI1uJVJPHYO3RKCESAADFYqayLeUM1gYAoJIRIgEZscTVB2vXepxyOgwqkQAAKKJsO5vL6ZDTYShB\\niAQAQEUiRAIy4omUHIYhp8OY8WcMw1BDnZsQCQCAIsq2szkchpxOQ0mTdjYAACoRIRKQEU+Y8rgd\\nMoyZQyTJamkbJUQCAKBospVITochl8NBOxsAABXKZfeO+/fv1/Hjx2UYhtrb27V27drctaNHj+r+\\n+++X0+nUhg0btGPHDo2Ojuquu+7S0NCQEomEduzYod/+7d8uypMAiiGWTKnmCq1sWd5al17rHVUq\\nlZbjClVLAAAgP5MrkVwuB5VIAABUKFsh0rFjx3T27Fl1dHTozJkzam9vV0dHR+763r17dfDgQbW0\\ntGj79u3asmWLfvzjH2vlypXauXOnuru79Ud/9Ef63ve+V7QnAsxWthLpahrq3ErLGq7tq/eUfmEA\\nACxwuUokw5DLaShJJRIAABXJVjtbZ2enNm3aJElatWqVhoaGFIlEJEldXV1qamrS0qVL5XA4tHHj\\nRnV2dsrv92twcFCSNDw8LL/fX6SnABSHFSLlUYmUOaGNuUgAABTHlEokh4MQCQCACmUrROrt7Z0S\\nAgUCAYXDYUlSOBxWIBC47Np73/teXbhwQZs3b9b27dt11113zXLpQHHFkyl5XPmHSKPjyVIvCQCA\\nqjBlJhLtbAAAVCzbM5EmS6ev/kL/T//0T1q2bJkOHjyoF198Ue3t7Tp06NAV7+P318uVxx/1dgWD\\nvpI99nzCPlhvXhPJlLz1nqvuR8tiryTJ6XEtyL1biM/JDvbBwj5Y2AcL+4BSyYVITodcDtrZAACo\\nVLZCpFAopN7e3tztnp4eBYPBaa91d3crFArpZz/7md75zndKklavXq2enh6Zpimnc+aQaGBgzM7y\\n8hIM+hQOj5Ts8ecL9sHibayTJBlKX30/UtYb2/MXhxUONZR6aXOKfw8W9sHCPljYB0sp94FwCrl2\\nNsOQ00klEgAAlcpWO1tra6sOHz4sSTpx4oRCoZC8Xqs6Y/ny5YpEIjp37pySyaSOHDmi1tZWXXfd\\ndTp+/Lgk6fz582poaLhigATMpVjclCR5XFf/T4KZSAAAFNeUdjanIdNM5VXpDgAA5patSqR169Zp\\nzZo1amtrk2EY2r17tw4dOiSfz6fNmzdrz5492rlzpyRp69atWrlypUKhkNrb27V9+3Ylk0nt2bOn\\nmM8DmJVYIhMiFTBYezRKiAQAQDGk0pMGazsdSme+5zSM8i4MAABMYXsm0q5du6bcXr16de7r9evX\\nq6OjY8r1hoYGfelLX7L764CSisWtIdk1eYRIDVQiAQBQVFMrkayq4GQyLaennKsCAACXstXOBiw0\\nE5VIV/9PwkeIBABAUeVmImXa2SQpmWK4NgAAlYYQCdDkmUj5VCJZBXyjhEgAABSFmQmMplQiMVwb\\nAICKQ4gESIrG869EcjocqqtxUYkEAECRmNNVIiWpRAIAoNIQIgEqbLC2JHnrCJEAACiW1KSZSM5s\\nJRLtbAAAVBxCJEAT7Wz5DNaWrBPaIuNJjh8GAKAIph2sTTsbAAAVhxAJ0KRKJFd+/0k01LmVNFOK\\nJ/iUFAAwt/bv369t27apra1Nzz///JRrR48e1a233qpt27bpgQcemHItGo1q06ZNOnTo0FwuNy/T\\nDdY2TV5jAQCoNIRIgOxVIkmc0AYAmFvHjh3T2bNn1dHRoX379mnfvn1Tru/du1cHDhzQo48+qmee\\neUanT5/OXfvqV7+qpqamuV5yXlLTVCIlCJEAAKg4hEiApFgiKamAmUi1hEgAgLnX2dmpTZs2SZJW\\nrVqloaEhRSIRSVJXV5eampq0dOlSORwObdy4UZ2dnZKkM2fO6PTp07rlllvKtfQrmm6wtkk7GwAA\\nFYcQCdBEJVI+p7NJkyqRooRIAIC509vbK7/fn7sdCAQUDoclSeFwWIFAYNpr99xzj+6+++65XWwB\\ncjORjMkzkahEAgCg0rjKvQCgEuRCJFd+lUgNmRBplEokAEAZ5XPAw3e+8x3ddNNNuvbaa/N+XL+/\\nXq48XxNnIxj0SZLcblfudlPjkCSpwVubu44J7Ik97Js97Fvh2DN72Dd7yrFvhEiAJg3WLrQSiRAJ\\nADCHQqGQent7c7d7enoUDAanvdbd3a1QKKSnnnpKXV1deuqpp3Tx4kV5PB4tWbJE73jHO2b8PQMD\\nY6V7EhnBoE/h8IgkaWw8LkkaHBxTLPN1X/9o7josk/cM+WPf7GHfCsee2cO+2TPbfbMbQBEiAWKw\\nNgBgfmhtbdWBAwfU1tamEydOKBQKyev1SpKWL1+uSCSic+fOacmSJTpy5Ijuu+8+bd++PXf/AwcO\\n6JprrrligFQOkwdrO2lnAwCgYhEiAZpciUSIBACoXOvWrdOaNWvU1tYmwzC0e/duHTp0SD6fT5s3\\nb9aePXu0c+dOSdLWrVu1cuXKMq84P7nB2oYht8sKkRisDQBA5SFEAjS5EqnAdrYxQiQAwNzatWvX\\nlNurV6/Ofb1+/Xp1dHTMeN9PfepTJVvXbKTSkyqRHNbpbAkqkQAAqDiczgbIqkQypNyJMFfT5PXI\\nkDQYiZV0XQAAVINcJZJj4nQ2kxAJAICKQ4gESIrFk/K4nTIMI6+fdzkd8jV41D9CiAQAwGxNnonk\\nys1Eop0NAIBKQ4gESIrGzbxPZsvye2s0OBLL63hlAAAws2zVkWFILqf1gQ6DtQEAqDyESICsdjaP\\nK7+h2ll+X43iyZRGo8kSrQoAgOpgptNyOgwZxuRKJEIkAAAqDSESIGuwdsGVSL4aSdIgLW0AAMxK\\nKpXODdSeqESi0hcAgEpDiAQoU4nkLqwSaVEmRBpguDYAALNiptJy5EKkTCVSikokAAAqDSESql46\\nnVYsbqqmwBApkA2RqEQCAGBWplYiZUKkJJVIAABUGkIkVL1E0vqks9B2tkWESAAAFMXkSiRntp2N\\nSiQAACoOIRKqXjwTItUUOljbmw2RokVfEwAA1SQ1KURyZyqRTAZrAwBQcQiRUPVicVNS4ZVI/lwl\\nUrzoawIAoJqYk9rZspVICdrZAACoOIRIqHrxZDZEKqwSqa7GpVqPk3Y2AABmyUyl5TCmzkQyaWcD\\nAKDiECKh6sUTmZlIBbazSVY1Eu1sAADMTiqVljMTHuUGa5tUIgEAUGkIkVD1Ygl77WySFSKNRpOK\\nZx4DAAAUzpxyOltmsDYzkQAAqDiESKh6dtvZpInh2oMRWtoAALArNU07GyESAACVhxAJVS/bzlZj\\nJ0RqzA7XJkQCAMAuMz1psLYjW4lEOxsAAJWGEAlVb1btbF5CJAAAZiuVSsuRCY8Mw5DLacikEgkA\\ngIpDiISql51nVGNjsPYiHyESAACzZZoTlUiS5HQ6lCBEAgCg4tgOkfbv369t27apra1Nzz///JRr\\nR48e1a233qpt27bpgQceyH3/8ccf1/vf/3598IMf1FNPPWV70UAx5U5ns1GJFPDVSiJEAgDArnQ6\\nrVR6ohJJklwOQybtbAAAVByXnTsdO3ZMZ8+eVUdHh86cOaP29nZ1dHTkru/du1cHDx5US0uLtm/f\\nri1btqi5uVkPPPCA/vEf/1FjY2M6cOCAbrnllmI9D8C22QzWzlUiMVgbAABbUmkrLJpcieRyOhis\\nDQBABbIVInV2dmrTpk2SpFWrVmloaEiRSERer1ddXV1qamrS0qVLJUkbN25UZ2enmpubdfPNN8vr\\n9crr9eoLX/hC8Z4FMAuxbCWSq/BKJF+9W06HQSUSAAA2pVKESAAAzBe22tl6e3vl9/tztwOBgMLh\\nsCQpHA4rEAhcdu3cuXOKRqO68847dfvtt6uzs3OWSweKI56wX4nkMAwt8tYQIgEAYJOZCZGmtLM5\\nDU5nAwCgAtmqRLpUOp3fi/zg4KC+8pWv6MKFC7rjjjt05MgRGYYx48/7/fVy2Rh2nK9g0Feyx55P\\nqn0fnJl/Y0tbGm3tRShQr5OvDijQ7J3yKep8Ve3/HrLYBwv7YGEfLOwDSmHmSqREuZYEAABmYCtE\\nCoVC6u3tzd3u6elRMBic9lp3d7dCoZDq6ur0lre8RS6XSytWrFBDQ4P6+/vV3Nw84+8ZGBizs7y8\\nBIM+hcMjJXv8+YJ9kAaHo5KkyMi4wjZq87y1LqVSaZ15pU/+zIyk+Yp/Dxb2wcI+WNgHSyn3gXCq\\nuk1fieRQMkUlEgAAlcZWO1tra6sOHz4sSTpx4oRCoZC8Xq8kafny5YpEIjp37pySyaSOHDmi1tZW\\nvfOd79SPf/xjpVIpDQwMaGxsbEpLHFAusxmsLSkXHA0yXBsAgIKZ01YiGUommYkEAEClsVWJtG7d\\nOq1Zs0ZtbW0yDEO7d+/WoUPDuJqgAAAgAElEQVSH5PP5tHnzZu3Zs0c7d+6UJG3dulUrV66UJG3Z\\nskW33XabJOlzn/ucHA5bGRZQVPHMYO0am62Ti7xWiNQ/HNPKpUVbFgAAVSE1TSWS0+mQmUornU5f\\ncfQBAACYW7ZnIu3atWvK7dWrV+e+Xr9+vTo6Oi67T1tbm9ra2uz+SqAksoO13W57oWagkUokAADs\\nylUiGVMrkbLXsl8DAIDyoxQIVS+eNOVxOeSw+UlnrhJpJFrMZQEAUBVyg7WdU2ciSVKCljYAACoK\\nIRKqXjyRUo3H/imAuZlII1QiAQBQqInB2hNvS7MhkslwbQAAKortdjZgoYglTNV47P+nkK1EGiBE\\nAgDMgf379+v48eMyDEPt7e1au3Zt7trRo0d1//33y+l0asOGDdqxY4fGx8d19913q6+vT7FYTH/8\\nx3+sd73rXWV8BlOlrtDOljSpRAIAoJIQIqHqxROmfA01tu/vdjnkq3cTIgEASu7YsWM6e/asOjo6\\ndObMGbW3t0+ZQ7l3714dPHhQLS0t2r59u7Zs2aJTp07pzW9+sz7+8Y/r/Pnz+uhHP1pRIZI5zWDt\\nbCUSIRIAAJWFEAlVL5ZMafEs2tkkye+t0cWBMU6RAQCUVGdnpzZt2iRJWrVqlYaGhhSJROT1etXV\\n1aWmpiYtXWodFbpx40Z1dnbqIx/5SO7+r732mlpaWsqy9pmk0plKJMd0lUi0swEAUEkIkVDV0um0\\n4glTNe5Zhki+Gr3aE9F4LKn6WneRVgcAwFS9vb1as2ZN7nYgEFA4HJbX61U4HFYgEJhyraurK3e7\\nra1NFy9e1IMPPjina74a07y8EslJJRIAABWJEAlVLWmmlU5rVoO1pYnh2v0jMUIkAMCcSafzr9T5\\nu7/7O/3qV7/Sn/3Zn+nxxx+/YuWs318vl2t2r435CAZ96h622sF93hoFgz5JUqO31vpeY13ue7Cw\\nH/awb/awb4Vjz+xh3+wpx74RIqGqxZOmJM2+EqnRerPbPxzV8qB31uvKOnzsVZ14uV+f+cPfmPIJ\\nLQCgOoVCIfX29uZu9/T0KBgMTnutu7tboVBIL7zwgpqbm7V06VK98Y1vlGma6u/vV3Nz84y/Z2Bg\\nrHRPIiMY9CkcHlF//6gkKRZNKBwekSTF4wlJUrg3oqYap9LptP7l6Ctas7JZr1vWWPK1VarsnqEw\\n7Js97Fvh2DN72Dd7ZrtvdgMox9V/BFi44gmrTH62lUgt/jpJUnf/+KzXNNm/H7+gF17uZ2g3AECS\\n1NraqsOHD0uSTpw4oVAoJK/X+vBi+fLlikQiOnfunJLJpI4cOaLW1lb99Kc/1SOPPCLJaocbGxuT\\n3+8v23O4lJmeZrC2w3qLamba2S72j+n/e/plPflc1+UPAAAA5gyVSKhq8URxKpFa/PWSpItF/OQ2\\nFjd1sc96vIGRmJqbaov22ACA+WndunVas2aN2traZBiGdu/erUOHDsnn82nz5s3as2ePdu7cKUna\\nunWrVq5cqaVLl+rP//zPdfvttysajeov/uIv5HBUzueIqdTVB2v3DUUlTXz4AwAAyoMQCVUtlgmR\\namtm959CKFOJ1NNfvBCpqyei7KSL/pGopKaiPTYAYP7atWvXlNurV6/Ofb1+/Xp1dHRMuV5bW6sv\\nfvGLc7I2O8zUNJVIrqmDtfszFbnxJCESAADlVDkfQwFlkGtnm2UlUl2NS01ejy4WsZ3tbPdEf2v/\\nMO1sAICFKTVdiOSYGiJlK5ESmVmGAACgPAiRUNVi2cHas5yJJFktbf3D0aK9wT17cVKINBItymMC\\nAFBpzDza2fqHsyESlUgAAJQTIRKqWrFmIknSkkCd0pJ6BopTjfTKxRFlT18eoBIJALBATdvO5ryk\\nEikTItHOBgBAeREioaoV63Q2aWK4dncRQqRE0tSF3lGtXNoop8PIzYIAAGChyQ3WNmYOkbKvg1Qi\\nAQBQXgzWRlUrZiVSSyATIhVhuPa58KhS6bSuX+LT8GicdjYAwIKVa2dzToRIzkntbKl0OjcbkJlI\\nAACUF5VIqGrZsvjiVCJZJ7R1D8w+RMrOQ7quxaeAr0bDkXju01gAABaSaQdrZyqRTDOlkbFE7jWQ\\ndjYAAMqLEAlVLZapRKr1zL4oL+SvkyGpuwgntGVPZrtuiU+BxlqlJQ1GaGkDACw8E4O1J96W5trZ\\nUuncUG2JdjYAAMqNEAlVrZjtbG6XU4HGWl0sQiXSKxdH5HIaWra4Qf7GGknSAHORAAALUK4SyZjm\\ndLZkSn1DhEgAAFQKQiRUtWIO1pasE9qGInFF40nbj5E0Uzofjmh50CuX06GAr1aScvMgAABYSCYq\\nkaYZrJ1KTTlcwkylZaYIkgAAKBdCJFS1WLJ4lUiSFMoN17bf0nahd1RJM63rlvgkSQEflUgAgIUr\\nGwpNNxMpaU60szXWuyVRjQQAQDkRIqGq5drZilWJ5M+ESLNoaZs8VFtSrp1t8kwIAAAWitS0lUiT\\n2tkyr3/ZU1AZrg0AQPkQIqGqxeLFDZFaApkT2vpnESJNGqotaaKdjUokAMACNF07m/OSwdoup0PN\\njdbrYZIQCQCAsiFEQlUbj1mzixpq3UV5vOynpN0D9tvZzl4ckdNhaHmwQZLkq3fL5TQ0MEIlEgBg\\n4UmlM4O1p6tEMlPqG44p0Fgjj9t620olEgAA5UOIhKo2FjPlchryFGkm0uKmWjkdhu1KJDOVUldP\\nRMsWN8jtstZkGIb8vhoGawMAFqTpKpHcmUqkaNzU8GhczY21udfFbCs6AACYe4RIqGrReFK1HlfR\\nHs/pcGjxojrblUgX+8YUT6Zy85CyAr5aDY/GlTT59BUAsLBkZyI5pmlnC2deTwONNXK7rO8leC0E\\nAKBsCJFQ1cZiSdXXFC9EkqQWf50i4wlFxhMF37crHJEkXdvinfJ9f2ON0pIGmYsEAFhgTHPmwdo9\\ng1Zlb8BXK082REoQIgEAUC6ESKhq0ZipuiKHSEsC9k9o6xuy5h6FFtVN+T7DtQEAC5U57Uwk6y3q\\neMxqXWtuqqUSCQCACkCIhKplplKKJUzV1RRnHlJWi9/+CW3ZEKm5qXbK9/2+GklSP8O1AQALTGqa\\nmUjZSqQsq50tOxOJEAkAgHIhRELVyn66WexKpNwJbf2Fz0XqHc6ESI1TQ6RAoxUiDTBcGwCwwJjT\\nzERyGIYmx0jNjZPa2ZIM1gYAoFwIkVC1orGkpBKESP7ZtbM11LouWxPtbACAhWq6SiTDMHLDtSXr\\ndTDXzpakEgkAgHIhRELVGsuGSEU8nU2yhmC7XY6CK5HS6bT6hqKXVSFlH1OS+odpZwMALCwTIdLU\\nt6VulxUqeevcqvE4cyFSnBAJAICysR0i7d+/X9u2bVNbW5uef/75KdeOHj2qW2+9Vdu2bdMDDzww\\n5Vo0GtWmTZt06NAhu78aKIrxbIhUW9yZSA7DUMhfp4sDY0pnhoXmY2Q8oXgyddk8JEny1bnlcjo0\\nQCUSAGCBma6dTZoIlQKZuYCezEwkKpEAACgfWyHSsWPHdPbsWXV0dGjfvn3at2/flOt79+7VgQMH\\n9Oijj+qZZ57R6dOnc9e++tWvqqmpaXarBopgPF6amUiStMRfr1jc1PBoPO/75IZqT1OJZBiGAr4a\\n2tkAAAvOdO1s0sRw7UDmddHNTCQAAMrOVojU2dmpTZs2SZJWrVqloaEhRSIRSVJXV5eampq0dOlS\\nORwObdy4UZ2dnZKkM2fO6PTp07rllluKs3pgFsZL1M4mSaGAdULbxQJOaMuGSIunqUSSrOHaw6Nx\\nJTnaGACwgOQqkYxLQyTrbWrzJSES7WwAAJSPrRCpt7dXfr8/dzsQCCgcDkuSwuGwAoHAtNfuuece\\n3X333bNZL1A04yUarC1ZlUiS1D2Q/1ykvuzJbDOESP5MOT8tbQCAhcScsRIp087WlGlnczNYGwCA\\ncivKX8/5zH35zne+o5tuuknXXntt3o/r99fL5SruvJrJgkFfyR57PqnWfXBk/m0tCVnPv5j78IbX\\nLZYkjUSTeT/uWMJ6U/z665qnvc/yJY3SiW6lnc6S/n9Wrf8eLsU+WNgHC/tgYR9QCqmU9fp36Uyk\\nbDtbrhLJSYgEAEC52QqRQqGQent7c7d7enoUDAanvdbd3a1QKKSnnnpKXV1deuqpp3Tx4kV5PB4t\\nWbJE73jHO2b8PQM2jkjPVzDoUzg8UrLHny+qeR96M61m8ag1t6iY+1CTqfF7+fxQ3o977uKwJMmR\\nMqe9T23mzfSvX+1XyOcpzkIvUc3/HiZjHyzsg4V9sJRyHwinqluuEsl5yWDtbCWSLxMiua0Pf+LM\\nRAIAoGxshUitra06cOCA2tradOLECYVCIXm9XknS8uXLFYlEdO7cOS1ZskRHjhzRfffdp+3bt+fu\\nf+DAAV1zzTVXDJCAUitlO1tjvVu1Hqe6C5yJ5HE75K1zT3vdn3kTzXBtAKhu+/fv1/Hjx2UYhtrb\\n27V27drctaNHj+r++++X0+nUhg0btGPHDknSvffeq+eee07JZFKf+MQn9O53v7tcy79MKpWWoctn\\nImUrjwKN2dPZqEQCAKDcbP31vG7dOq1Zs0ZtbW0yDEO7d+/WoUOH5PP5tHnzZu3Zs0c7d+6UJG3d\\nulUrV64s6qKBYhiPl26wtmEYagnU63x4VKl0+rI3xtPpHYqqubFWxgw/m30TPTBMiAQA1WryCbln\\nzpxRe3u7Ojo6ctf37t2rgwcPqqWlRdu3b9eWLVvU29url156SR0dHRoYGNAHPvCBigqRzHT6slY2\\nyTpoIjw4rkVe6/XPTYgEAEDZ2f7redeuXVNur169Ovf1+vXrp7yhudSnPvUpu78WKJrxaOkqkSSp\\nxV+nsxdH1D8c1eKmuiuvJZbUWCyp113TOOPPLMoM1u4fiRZ1nQCA+WOmE3K9Xu+UE3Il5U7Ivf32\\n23PVSo2NjRofH5dpmnI6Szd3shCpVPqyodqSdMd73qB4IpULmKhEAgCg/GydzgYsBNl2ttqa0ryJ\\nXhLI/4S2viErGLpS2OStc8swpMh4ojgLBADMO3ZOyHU6naqvt16THnvsMW3YsKFiAiRJMs3pK5Fq\\nPS41NkzMAMye1hYnRAIAoGxKU4IBzAPjcVO1HmderWZ2tPgzIVL/mNZcH7jiz/YOWyFSc6ZlbToO\\nw5C3zq2RMUIkAIAlnxNys5544gk99thjeuSRR676s6U+ITcrGPTJcBhyOR15DVj3uBxKq7qHsVfz\\nc58N9s0e9q1w7Jk97Js95dg3QiRUrfFYsmStbJLUkq1E6s+/Eqm5qfaKP0eIBADVzc4JuZL09NNP\\n68EHH9TDDz8sn+/qbzhLeUJuVvbEv3jClGHkd0qq2+XQeDRRtScmclqkPeybPexb4dgze9g3e2a7\\nb3YDKNrZULXGY0nVlzREslrTuvN4I96XqURa3Hjl2Um+eo9GxxNKpfL/5BkAsHC0trbq8OHDknTF\\nE3KTyaSOHDmi1tZWjYyM6N5779XXvvY1LVq0qJzLn5Y5w0yk6bhcDtrZAAAoIyqRUJXS6bTGY6aW\\nNJeuVL+h1i1vnVvd/XmESHlWIvnq3UrLmos0eU4EAKA62DkhN3sq22c+85nc49xzzz1atmxZuZ7G\\nFDMN1p6Ox+VgsDYAAGVEiISqFE+klEqnS9rOJlnVSC9fGFHSTOUGgk6nbzgqp8NQk/fKwZCvzi1J\\nGiFEAoCqVegJudu2bdO2bdvmZG12mKm0XM78QiS3y6nxWLzEKwIAADOhnQ1VaSxzMlsp29kkaYm/\\nXql0OldpNJO+oagCjTVXHfLtrbeCo8gYb6ABAAtDKpWWw5HfW1K3y6F40izxigAAwEwIkVCVonEr\\nRKr1lDZECmWGa1+8QktbImlqaDSuxU1XnockTapEYrg2AGCBKGQmUradrZBT6QAAQPEQIqEqzVkl\\nUvaEtoGZT2jrG45JkpobrzwPSbJmIklWOxsAAAuBmUpftRI3y+1yKJ227gMAAOYeIRKq0ngmRKqr\\nKd1gbUlq8WdOaLtCJVK+Q7UlyZsJkWhnAwAsFIUN1rZet+MJhmsDAFAOhEioStGYNU+htsSVSKFs\\niDRwhRBpOBMi5VOJVGfNRKKdDQCwUJiptBx5hkhul/XWNWESIgEAUA6ESKhKc9XOVutxaZHXc8VK\\npN4CKpGy7WwR2tkAAAtEKpWWM+/T2TIhUoLh2gAAlAMhEqrSRDtbaUMkSWrx16t/OKb4DG94C2pn\\nyw3Wpp0NADD/pdNppdJpOfOcieTJhEjxJJVIAACUAyESqlIuRPKUdiaSJLUE6pWW1DM4/XDtvuGo\\nDEkBX81VH8vjdqrG7WSwNgBgQUhlTlnLt53Nla1EIkQCAKAsCJFQlcYzM5HqauegEimQHa49Q4g0\\nFNUiX41czvz+c/TVu5mJBABYEEzTCpEKHaxNiAQAQHkQIqEqzWU72xJ/vSTpYv/oZdeGx+LqH47m\\nTnHLh7fOrch4Quk0xxsDAOY3M1VYJZInV4nETCQAAMqBEAlVaTyebWcrfYh0/dJGSdILv+6/7Np/\\nvtSrtKS1qxbn/Xi+eo8SyZRiDBUFAMxz2Xa2fCuR3MxEAgCgrAiRUJXmshLJ76vR65c36VTXoAYj\\nsSnXfnYqLEla94Zg3o83MVybljYAwPxWaCWSm5lIAACUFSESqtJ4zJTL6ci9GS219atDSkt67mR4\\n0hqS+uUr/Voe9Cq0KP92Nl+9FSJFGK4NAJjnUqkCZyK5mYkEAEA5ESKhKo3HkqqrKf3JbFm/+YaQ\\nDEk/ebEn971f/LpPSTOtdTfm38omTYRII2PxYi4RAIA5V2iI5HZm29lo6QYAoBwIkVCVrBCp9K1s\\nWX5fjW5Y3qSXugY1MGK1tOVa2W7Mv5VNsmYiSbSzAQDmP9rZAACYXwiRUJXG43MbIknS+je2ZFra\\nepRIpvT8mT4tbqrVtSFvQY/jYyYSAGCBKLgSiRAJAICyIkRC1UmaKcUTKdV55q6dTZLe+oZgrqXt\\nV2f7FY2bWndjUIaR3xvnLC8zkQAAC0QyV4mU31tSD6ezAQBQVoRIqDrRuDVHYa4rkRZ5a3TjtYv0\\n0rkhPfnceUmFt7JJk9vZmIkEAJjfcpVIeX6g4mawNgAAZUWIhKozHktKkurnOESSpPVvDEmyhmo3\\n1rv1+muaCn4Mbx2VSACAhSFV6EwkBmsDAFBWhEioOtkQqbYMIdJbbwwq+2HrTTcE837TPFl9rUsO\\nw2AmEgBg3jMLnInkcTMTCQCAciJEQtXJhkhz3c4mSU3eGr3h2kWS7LWySZLDMOStc2mESiQAwDyX\\na2dzFlaJRIgEAEB5zP1f0UCZjcesEvhytLNJ0m2/83r9/FSv1qz0234MX71Hg5FYEVcFAMDcM1NW\\nGOQocCYSg7UBACgPQiRUnYl2trk9nS3r+iWNun5J46wew1vn1vneUZmplJx5nmgDAEClSRXazpY5\\nnS1JiAQAQFnw1yeqzlgZB2sXi68+O1w7WeaVAABgn1noYG0Xg7UBACgnQiRUnWi8fDORisVb75Ek\\njYzFy7wSAADsK3SwttNhyDBoZwMAoFxs/xW9f/9+HT9+XIZhqL29XWvXrs1dO3r0qO6//345nU5t\\n2LBBO3bskCTde++9eu6555RMJvWJT3xC7373u2f/DIACZSuR6jzzN0Ty1WUqkTihDQAwj6UKrEQy\\nDEMel5PB2gAAlImtv6KPHTums2fPqqOjQ2fOnFF7e7s6Ojpy1/fu3auDBw+qpaVF27dv15YtW9Tb\\n26uXXnpJHR0dGhgY0Ac+8AFCJJRFdrB2XZlmIhWDN9POxgltAID5rNB2NslqaSNEAgCgPGy1s3V2\\ndmrTpk2SpFWrVmloaEiRSESS1NXVpaamJi1dulQOh0MbN25UZ2en1q9fry996UuSpMbGRo2Pj8s0\\n6WfH3IvG5n87W24mUpnb2X7x6z49+dy5sq4BADB/pdJWiOQqMESKJ3gPCQBAOdgKkXp7e+X3TxxP\\nHggEFA6HJUnhcFiBQOCya06nU/X19ZKkxx57TBs2bJDTOX8rQTB/jS2EEKkuOxOpvJVI3/7BKX3r\\nB6d0Phwp6zoAoJrs379f27ZtU1tbm55//vkp144ePapbb71V27Zt0wMPPJD7/qlTp7Rp0yZ985vf\\nnOvlXpHtSiSTSiQAAMqhKH9FpzOfIuXjiSee0GOPPaZHHnnkqj/r99fL5Spd0BQM+kr22PNJte1D\\nMpWWYUjLly2a8qZ1Pu3Dirj15jmp4q8738frH46qe2BckvSTU7266U1Li7qOcptP/x5KiX2wsA8W\\n9qH87IwUWLZsmb7whS/o5ptvLuPKp1foTCRJ8rgczAQEAKBMbIVIoVBIvb29uds9PT0KBoPTXuvu\\n7lYoFJIkPf3003rwwQf18MMPy+e7+hvRgYExO8vLSzDoUzg8UrLHny+qcR+GI3HVepzq65uonplv\\n+5CIWm1sPX2jRV13Iftw7Ffdua+f/Mmr2vq2a+VxL4zqwvn276FU2AcL+2Ap5T4QTuVvppECXq93\\nykgBSbmRAh/+8If10EMP6aGHHirn0qdV6OlskuR2OalEAgCgTGy1s7W2turw4cOSpBMnTigUCsnr\\n9UqSli9frkgkonPnzimZTOrIkSNqbW3VyMiI7r33Xn3ta1/TokWLivcMgAKNx5LzupVNmjQTqYyD\\ntU++OihJeuN1fo1Gk3ruZLhsawGAamFnpIDL5VJtbe2crzUfsxmsXUglPAAAKA5bf0mvW7dOa9as\\nUVtbmwzD0O7du3Xo0CH5fD5t3rxZe/bs0c6dOyVJW7du1cqVK3Onsn3mM5/JPc4999yjZcuWFeeZ\\nAHkajyXlb6wp9zJmxe1yqsbjLOtMpFNdg/K4Hdr+7hv15w89qx/953nd/OYlZVsPAFSjUgUppR4p\\nkFVfb8348y+qz7sizZu5T5O/QTWZCljrtLa03HOw5nKjcs8e9s0e9q1w7Jk97Js95dg32+UYu3bt\\nmnJ79erVua/Xr18/pT9fkrZt26Zt27bZ/XVAUaTTaY3Hk1rmaSj3UmbNV+cuWyXSyFhc53tH9abr\\n/Vra3KA3Xe/XL18Z0IXeUS1bPP/3FgAqld2RAoUq5UiBrGDQp6HhqCQpMhLLu10ynbJa2V67OKSG\\nWqsy94t/93PFkim1b39raRZbIWivtYd9s4d9Kxx7Zg/7Zs9s981uAGWrnQ2Yr2IJU+n0/D6ZLctX\\n79HIWLws5fynuoYkSW+41mpN3XjTNZKkfz9+Yc7XAgDVxM5IgUqWsjUTyXr7Gk9MzEX69WvDevnC\\nsFK0uAEAUFLz/y9poADjMVOSVFcz/8vdffVuJc20onFzzkOxU13WPKQbMyHSW25YLF+9W8/84jV9\\naOPrqqKdAADKwc5IgRdeeEH33HOPzp8/L5fLpcOHD+vAgQMVMaPSzFQVOZ2FnM6WaWHLDNeOxpO5\\n1/fIeEKNmXY3AABQfIRIqCrjsaSkBVKJVGeV8I+MJ+b8+ZzsGpDL6dDrljVKklxOh975fy3Vvz37\\nqp47Gdbb1zAbCQBKpdCRAm9+85v1jW98Y07WVqiUzcHakpRIWMHRUCSeuzY4EiNEAgCghGhnQ1VZ\\nSCGSN3NC28hY/Co/WVxj0aS6uiN63bLGKRVH71xrHSn9s1Oc0gYAmN7PToX1v7/zQq4CKXs6m9Ow\\n0c6WtB5jYCSWuzYYiU17HwAAUByESKgqo1FrEHX9AgiRfJlPWodH5zZEOn1+UGlNtLJlLQnUq6HW\\npVe7I3O6HgDA/PHCy/366Ys9uthnDe42bVQiedyZSqRMiDQ5OBqMzO1rIgAA1YYQCVXltcyb1pZA\\nfZlXMnstfus5XOgdndPfe/JVax7SGy4JkQzD0IoWn3oGxzUWTc7pmgAA80PAVyNJ6hu2gh9bg7Wd\\nl4ZIU9vZAABA6RAioaqcD1uByzUL4Bj661qs03jmuvLnVNegnA5Dr7+m6fI1LbGOiezq4YhOAMDl\\nmhtrJUn9w1FJ9iqRsq3U8aQ1E4l2NgAA5g4hEqrK+d5RuZyGQv66ci9l1pqbalVf49Kr3XMX2MTi\\npl65OKLrlvhU47n8BLYVmWDrLC1tAIBpBBqzlUhWiGSrEsk1czvbAJVIAACUFCESqkYqndaF3lEt\\nCTTI5Zz///St9jGvugfGcwPDS+30hSGZqfRlrWxZ17VYlUhzGWwBAOaPyyqR0oWHSJ5pQiTDsL7P\\nTCQAAEpr/v8lDeSpbyiqWMLU8uD8b2XLWtGSbR+bm8qf7DykS4dqZ7X46+VxOwiRAADTWuSrkaHL\\nZyIV1M52yWDtgZGYGhs8WuSroZ0NAIASI0RC1cjOQ1q2AOYhZWXbx+YqRDrVNShD0g3LL5+HJFl/\\nBKwI+XShd0yJzKwKAACyXE6HFvlqLpuJVNhg7exMpJTS6bQGI3H5vTXye2s0PBpX0kwVf+EAAEAS\\nIRKqyPleK2i5ZgFWIp2dg8qfRNLUry8M69oWr+pr3VdYk1epdFrnwnN7ahwAYH4INNZoYCQmM5WW\\naRZeieTJVSKZGo0mlTRTWuSt0SJfjdKShkdpaQMAoFQIkVA1ciezBb1lXknxLG2ul9s1N+1jv74w\\nrKSZmrGVLWsugy0AwPwT8NXKTKU1OBJVKjcTKf+3pJNnIg1mBmkv8tVokdcjScxFAgCghFzlXgAw\\nV86FR+VxO7S4qbbcSykap8Oh5cEGvdodUdJMlXRg+Kkuax7STEO1syaGa3NCGwDgctnh2uHB8Vw7\\nWyGVSK5MiBRPpnIzkPxejzxuq82NE9oAACgdKpFQFZJmShf7R3XN4gY5jPzfqM4HK1p8MlPWyXOl\\ndDITIt1wlRBp2eIGOR2Gzl6kEgkAcLlAY40kKTwwnhusXdjpbFZYlEikNJAJkRZ5a+T3WY/LcG0A\\nAEqHEAlVoWdgXEkzrWsWL5xWtqwVIes5lbLyJ2mmdPr8kJYtblBjveeKP+t2OXTN4gadC0dkphhu\\nCgCYKleJZDNEcmfb2aa+56oAACAASURBVEwz17pmtbMRIgEAUGqESKgK53uz85AWzlDtrBW59rHS\\nVf6c7R5RPJG6aivb5DUlkild7Bsr2ZoAAPNTINfONmarnc0zuZ1tJNvONmkm0iXtbF09ET3+zMu5\\n+UsAAMA+QiRUhfPhhXcyW9bykFeGUdoQ6dSrVivb1YZqZ123hLlIAIDpNTdNrkSyKlZtVSJNmol0\\npUqkf37mZX3n6Zd15vzQrNcOAEC1I0RCVchVIi3AdrYat1NLAvV6tSdSsk9Zs/OQ8g2RVrRY+8wJ\\nbQCASzXUuuRxO2wP1r40RHI5HZnHdKqh1nXZ6WyvZGb0vfIar0kAAMwWIRKqwvnwqBpqXblS94Xm\\nuhafonFT4cHxoj92KpXWS+cGFfLX5YaWXs21Ia8MlbY6CgAwPxmGoebGWoUHrBDJkAo69CIbIsUT\\npgZGYlrk9cjI3H+Rt2ZKJdLIWFy9Q1FJ0isXh4v3JAAAKLJ0Oq0nnzunrp7K7uYgRMKCl0ia6h4Y\\n0zWLG3JvMhea7FykrhK0j3X1RDQeM/OuQpKkWo9LoUC9Xu2OKM0MCgDAJQKNtRoZi2s8liyoCkmS\\nnA6HnA5DsURKQ6NxLZr0Accir0ej0aTiCVOSppwU+gqnhgIAKtir3RF96wen9A9PnS73Uq6IEAkL\\n3mt9Y0qnpWuCC6+VLauU7WOnMq1s+Q7VzrquxauxWFLhzCfAAABkNTdawU94KFrQPKQst8uhvuGo\\n0mnlZiFJygVK2WqklzPBkctp6GLfmMZjydkuHQCAknjx1QFJ0kvnhir6lGtCJCx458ML92S2rIkT\\n2opfiXTSZoi0almTJOkXZ/qKviYAwPyWPaEtFjcLrkSSrBPahket2Uf+ySFSbri2de2V16wWtvWr\\nQ0pramUSAACV5GTmMKNY3KzoA4oIkbDgnevNnMy2eOGGSN46twKNNTp9flCv9Y0W7XG7eiJ68eyA\\nAo01udN08vW2N7XIYRh6+vkLRVsPAGBhaG6ceE2xW4mUtcg3Me8wGyINjFiVSK9cHFGT16Obbgjm\\nbgMAUGlSaWsObVY2UKpEhEhY8CYqkRZuO5skvfft12k8Zuovv/mzWX/SGoub+ocjp/U//uYnGosl\\ntfGmawqeJ9XU4NFvvL5Zr3ZHGLANAJgiMOsQyZn7enI7m39SO9tgJKaBkZhWLmnU9Uusil2GawMA\\nKtG5nohGo0m96Xq/JOlkprWtErnKvQCglGJxU6+8NqymBo+8de5yL6ek3rVuuRwOQ3/7vZO699Gf\\n6U9u/Y2rDsMeHo3re8++qqefvyC3y6GW5gb56tx65bVh9Q5FtbipVtvf/QatXdVsa03vXLtUP3+p\\nV//x/Gu6fbPP1mMsVL1D4/rm90/JkPSRLW+Y8gcVACx02ZlIkmy3s2UtmradLZarOrp+iU+Lm2rV\\nUOvSK6/xoQYAoHwSSVPHftWjt72xZUpVbXYO7W+9qUW9g1GdOvf/s3fn8VHW997/X9es2Sb7ZAcS\\nwr6DoiIKqCiKrW2tC/WmdLE97dHW3q1WPdbf0fNra097Wu/et7a3S+mmHoWiVU+1xYNIcQEsImDY\\nEyAbZF8nsyQzc91/TDIQAgYiZBLyfj4eGjLXLJ/5zJXv9Z3P9f1+r1bCYXNAx8hzTUUkOa/9aUMp\\nbd4ull4yJtahDIqFs/KJd9p4+r928+iq7Vx/aSFzJ2WRk57Q637N7QHWfVDJmx9U0dkVJjnRgc1q\\nobSyhVDYxGIYXHfJaG6YX4TTbj3Fq/Vv+tgMkhMdbNpVw81XjOvVUI5UpmnyzkdHeX7dAfydkasH\\nHVj5PiuunchFk7NjHJ2IyOBIc5296WxpJ1ydDSLHuZ71kApzXRiGQWFuMrsONdHh7yIx7vw+sSQi\\nIkPTX7dU8PLbh2hq8/Pp+UXR26Pr0I5O40BVK+/sPEpVvSe69u1QoiKSnLd2H25i/bZq8jIT+cxl\\nhbEOZ9BcNDmbOIeVX79cwp83HuTPGw+S705kfH4Ktc0+qus9tHm7gEhn++ZFhSyYmYfdZiE9I4my\\nw41YLQbJiY5+Xql/NquFS6fl8LctFWwvbWDupKxP/JxnIhw2gYGd5T4X2r2d/O71vWwvbSDeaeWr\\nSycTDId54c0DPPHKLnaUNrL8mgnEO9U0i8j5zW6zkOZy0tweGFAbfXwRKeW441VyogODyMLaPYX6\\nMTnJQGRE0q5DTRw+2s7UonQAPL4uXt9UzuILCzQiVEREzqlgKMyGD6sB2LD9CEvnjcFqsWCaJvsr\\nW0hzOXGnxDFxVCrv7DzKvooWFZFEBosvEOR3r+/BYhjcfv3kXmsnjAQzijP5xZ3z2X6ggQ/21VNy\\nqCm6NlRmShyzxqUwpTCNBTPzcBw30shqMXqd0T0bLpueG50yN1hFJF8gyPptVbzxj0pCIZNPXVrI\\nVRcUxHQklL8zyKOrd1Be087kMWl8denk6GLlk0an8dSru9i0q4baZi/fu2UWCXFqnkXk/OZOi+8u\\nIp1529xzXI9zWHsV3m1WC8mJDlraA/i7QqQnO6NFpsLuYtLhmrZoEenPGw/y1ofV1Lf4uPPG6Z/0\\nLYmIiFDT5KWsupVLp+X0Wld2+4EGWjydOOwWmtsDbD/QwAUTszjS6KXd28UlU7IxDCN6Vex9lS1c\\nPXdUrN7GKelbipyXVq0/QGNbgE9dWkhRbnKsw4mJxDg786fnMn96Lr5AkLpmH1lp8YM+yiUvM5Hi\\n/GR2HWyiqc1/Ts/0+gJB3vhHJeu2VtLhDxLvtGEAq98q5a0Pq7hp0TgunOg+40XCP6lgKMyv/1xC\\neU07l8/I5UvXTcJyXAw56Qk88MUL+N3re9m0q4ZfrPqQu2+dRYKmW4jIecydmsD+ipYBTWfrWRPp\\nZCc+UpOcVNS1Y5pwwQR39Pai3O7FtbvXRapt9rJxR+QKoh/sr+fgkTbG5vXuM9S1+EhJcOB0jKyT\\nUSIiMjCBzhCPrtpOQ6ufsGly+Yy86LY3P6gC4J8+PZXHX/qINz+o4oKJWezvXkR7wuhI8SgzNZ6M\\nZCf7K1sIm2av7w1DgRYokfOKLxDk9c3lbNxxlAJ3EjfML4x1SENCvNPGmBxXzKZJXT4jDxN4t6Tm\\nnDy/aZps3VvHg7/ZwivvHMIwDD63YCz/8c/z+PdvzuOauaNoagvwf18u4ecvbKeuxXdO4jhVbL//\\n615KDjUxsziDFddOPOmBwGa1cPv1k5k/PYdDR9v5jxe24/F1DVqcw4lpmnj9QY42duALBGMdjogM\\nkDstHhjgmkj2SBf2+EW1e6QmOTAjs5kpzD02DSDN5SQ50RG9QtvLbx8iFDa5ak4BAGs2lGL2PBDY\\ndbiJB57czI+f2Yq/s29b0xUMn/R2ERE5v3UFwzzxSgm/fW0PoXC417ZX3jlEQ6sfgNXrS2nr6ASg\\nqt7DvsoWphSmMWeCm8lj0thb0UJ1Q8ex9ZCOuyjShFFpeHxdHGnoGKR3dfoG/I3ykUceYceOHRiG\\nwQMPPMCMGTOi29577z0effRRrFYrCxYs4M477+z3MSIDZZom9a1+3tpWxcYdR/AFQjgdVr72qcnY\\nrKqTDgVzJ2Xxn+v289qmwwQ6Qyy5aBSuhN5rLvV8juU17ZTXtNPaESDQGcLfFSIYDJOVFk9+ZhIF\\n7kQyUuOjFXBvIMiLfz/IRwcbsVkNbphfyLUXjybOcax5W3bVeK6Yk88L6w6wo6yRf125hRsXFLP4\\ngoJzul5S2DRZs6GM90pqGJuXzDc/Mw3rx0zbsFgMvrJ0MlaLwcYdR/nZf37IiiUTGVeQcs5iHC7q\\nmr3899YqSg420uLpJNAVWevEYbMwd1IWl8/MY3xByqCPMhtOAl0hmtsD2KwGdqsFu81KvNOqnA1D\\n50sfzJ0aKSINaE0ka08Rqe/6fanHjU7qmcIGRBbXznGxs6yRkkONbNldy5hsF1+4ejx1LT4+OtjI\\nrsNNTCvKoK7ZyxMvlxA2TarqO1j52h7u+Oy06N9LXYuPR1/YjjcQ5H/ePLPPCKaGFh97K1q4eEp2\\nn6nUDS0+/vp+BZdOzaE4X+27iEishE2Txu4rUp/YH6qs8/DMG/uYWZzB0kvGRLeHTZPf/GU3/9hb\\nB4BhwJevm4RhGJTXtLP2HxW4U+NYOCufNRvKeP7NA3zjhqms3xZZC+nK7hMXV84pYE95M+u3VbGv\\nooXkREeviyFNHJ3Kpl017KtoocCdNBjpOG0DKiK9//77lJeXs2rVKsrKynjggQdYtWpVdPuPfvQj\\nVq5cSXZ2NsuXL2fJkiU0NTV97GPk/BToDFHb7KXFE6DF00lLewCAxHg7iXE2kuLtpLqcZCTHnXKU\\njGma+DtDtHgCNLcHos9V3+LjSEMHRxo66PBHzgQmJzpYctFoFs3OJznhky8MLWdHvNPGV5dO5vk3\\nD/D65nLe/KCKRbPziHfaqGv2Udfs42jjsc/xZPZWtHzsa0wpTGP5NRP7XImuR3ZaAnfdNIMte2r5\\nz/8+wAtvHuCdnUcZm+ciPTmOdFccSfF27DYL7vZOOjx+stLiB3QFH9M02VHayEsbD1JV7yE7LZ67\\nbppxWtMhLIbBimsnYbNaWL+tmkee/YAZxRl87vKxjMkZegvrnUvhsMm+yhbWba1k+4EGTCDBaSM7\\nPZ7UJCfJCQ72V7bwbkkN75bUkJOewIKZeVw6LeesLAx/Pmho9bGzrJEdpY3sKW8mGOp9tiwzJY5Z\\n4zOZPd7NhFEpH1vklKHhfOqDfZKRSI7uNZFSTzKdLe240Ukntps9RaTf/NduAG5aVIzFMPj8wrF8\\ndLCRFzccpDgvhcde/IgOf5AVSyayeXctH+yr5y/vHebT84uorPPw6KrttHZ0YgD/8fyHfOvz05la\\nGFlnacvuWv64di++QIh1H1Tyzc9Mix6bSg418uQru+jwB9m4/Qi3LR7Potn50S8nXn+Q9/fU4kqw\\nM3u8u1eBzTRNduyvp7q2jVnjMvsUp0zTJBgyT7n+n8fXRUKcbchNixAROR0eXxcJTlufEw9h02R/\\nRQtOh5XCHFevYpAvEOTND6poavNz1QUF5B9XjKlt9vL71/eyr7KF2eMz+eKSidHRrdtLG3jy1V0E\\nOkOUVrVS3dDBV66bhN1mZfX6Uv6xt47xBSl0BsO8vfMoaS4nn55fyO//uhfThBXXTmLy6DS27a9n\\ny+5aZo/PZFNJDRnJTmaOywBg1vgM0pOdbNx+hFDYZO6krF6xTxx9bF2kqy4oOGd5HYgBFZE2bdrE\\n4sWLASguLqa1tRWPx0NSUhKVlZWkpKSQm5sLwMKFC9m0aRNNTU2nfMxwETbN6NWerBZjRJy9NU0T\\ns/tnONz9s+ffRPIRNiN/oM1tfpraAzS1+amq76CizkNdkxez31eJiHdaSU6IXGreajGwWg28/mCv\\nUQcnMgzISktgwqhU5kxwc9Hkvmf8ZGi4aHI2s8Zl8vcdR3h9czlr36+MbrMYBu60eKYWpVOYk8yY\\n7CQyUuOJc1iJs1uxWAxqmrxU1Xuoru+IFiN7TC/O6NPwnoxhGFwyJYcphek8v+4AW3bXUlXv+djH\\npLmc5GcmkpeZiDs1nqy0eLJS40mIs2G1GFgsBqYJLZ4Aja1+Glr9vPvRUcqOtGEYcOm0HD6/sPiM\\nipoWw2D5NRO5aHI2f954kJ1ljewsa6Q4L5mxeSkU5boYk+MiJdFBnPPMvxCYpklXMIwvEMQbCNLS\\nHqCxLfK329LRiS8QxBcIEgybhIJhEuJsJDhtxDttkb9Pq4HFMDCJLBju7wwR6AxhGJErJtltVpx2\\nCymJTtJckf9SkhwkxtlP+QXGNE0CXSHavF0cPNLKR2WNfHSwKTqlryg3mWvmjuKCie5eIwzDpsm+\\nihbe3nGErfvqWf1WKS/+vYxZ4zOZOymL7LQE3Klx53R9qWPtokko3PunxWJgt1lw2KzndNRbT7G9\\n3dvJwaNt7C1vYW9FM3XNx6ZuFriTKMx1EQ6b3dNwQpRWt7BuaxXrtlaR4LQxriCF4vwUxuenkO9O\\n7N7P1aYOJedTH8ydGimsfJKrs510Olt3YcmdGjk5cLzC7nUS27xdTB6TxpTCNABGZ7u4eEo2W3bX\\n8sM/bKWmyctVFxSwaHY+cya6+eHvt/Lntw9hmrD2H5X4AkFuWzye9OQ4nnhlF79cvYOvLJ0UaY92\\nHsVptzJrXCbbSxv4t9/9g+XXTKCpPcDLGw9itRpcP28Mf99+hGfe2M/BI2189vKxbNhezfpt1dFp\\nutnpCSy9eDQXTc5m67461r5fQVX3RTJSEh1cdUEBV8zJp8MfZMvuWrbsruVoQwcTRqVy8dRsLpyY\\nhWmavL+njvdKjnLoaDvpyU7mTc1h3tQccjMSONLQwe7yZvZVtOCwW5g8Oo3JhWlkpsTT4e+irLqN\\n0upWfP4gRXkuxuWn4E6NJ2yaHGnwcvhoG/WtfvIyEhiT4yI7PQEDaOvopLLew9EGL65EO6OyXOSk\\nx2O1WOjsCnG00cuRxg6sFoO8jESy0xOw2yyETZOmNj81TV78gRBZafFkpyfg7L4IiNcfpK7FS0t7\\nJ2kuZ6+1HoOhMI1tkeNwgtOGOzWexDgbhmFgmiatnkB0iklmSlzkSn7dxyNfIEhTmx9fZ4h0l5PU\\nJGd0vwyGwjS3B2jr6MSV6CDd5Yweh8KmSXtHJ03tARx2KxnJzuhIaNM08fi6aGzzEw5DRkocyQl2\\nDCPSvy2tbmFfRQuNbX6KcpOZODqV0VkuLBaDYChMU3uAVk+ApHg7Gclx0QuhmKZJhz9y7LbbLKS6\\nnNH8QGTUaUt7gGDYJC3JEVkf0jAImyYNLb5IP6qjk+y0ePLdSb2ubhjoCtHh6+qzYD1AVzBEW0cX\\nVquBK8He69gQCofxeLvoCoVJTnD0umhLT7y+QJCkeDtxjt4jYP2dQTzeLpwOK4nx9l59hGAoTFtH\\nJ4YRec0Tj/8eXxfBYBhXgr3PRXR6ppv29F2OFwyF6fAHcdgsfeIJdJ+4xoDURGf0BGAoHKau2Rc9\\neZ2TnkC+OzF6srErGKapzU9deydmV5D0ZGc0pnDYpLk9QFO7H6fdSmbKsX6JaZq0dnTS0BJZP8ed\\nGk9KkiPSz+p+j3UtPrz+IBnJcbhT46LP6/UHqW320tTmJyXJSU56QrTNC3SGqGnycrSpgzi7jdzM\\nBNwp8VgsBl3ByN9gdX0HXaEw+e5E8jMTiXPYCIXDHG3wUl7bTosnQF5GIqOzXaQnOzGBmkYvh462\\nUV3fQUZKHGPzkhmVlYTVYlDX7GN/ZQul1a04HVYmFKQyYVQqyYkOGlp97D7czK5DTfgCQSaNSWNq\\nYTqjspNo6+hka2kjb2+roqrew4RRqcwen8n0sRmEwibv76llU0kNZUfaSHM5uWRqNpdOyyXd5eS9\\nkhrWba2ktru/U5TrYvGFo5g1LpN3dh7lL5sO0959Veq/bz/CvGk53DC/kO0HGnhp40E6g2HSk518\\neKCBfRUtfGHxeDp8XaxaX4rdZuHL103i7R1H2LyrloYWP1MK03jjH5XkZSZy100zCIZMfvzHrbz6\\n7mHKjrRRXtvOpdNyoicVvnTtJP7/3/+Dp17dTdg0+dSlY6J/O1aLhUWz8nlp40HgWNGoR1ZqPKlJ\\nDvZXNGOa5pCqPQyoiNTQ0MDUqVOjv6enp1NfX09SUhL19fWkp6f32lZZWUlzc/MpHzPY6lp8/Oz5\\nD2n1BLqLIMeKIT2df9Ps+2XgxGKIYUQ+fKvFiH6ZtFqN4363YEDkcd3FGMxI8eW4Kffd/+4p1vTc\\ndvz9j20wez2mN/MkN/Z6nRP+ZZqR93CsOHTsOcKmedLXOBMJThsTRqWS506MHpBTk5wYBnT4g3T4\\numj3dUUa1TY/jW1+2r1dhEJdkS+vIZOEuGOjDiL/OUh1OUlLioxe6ul0yPDgsFu5+sJRLJqVx47S\\nRpwOK9lp8aQnx/U79bDAnXTWhnImJzj4xg1T+dK1E2lqixzUm9oCeP1BukJhHA4bjS1eapoiB9iS\\nQ02UHGo6o9e4YKKbz14+lvzMxAHHOWFUKvfeNps95c28+u5hSqtaKTvS1us+BhDntOKwWaMF3+Pb\\nkcjP6F89phnp6ITC/f+BG8bJ25pPwgAS4iIdOovFiL5Gu7erzyiZ1CQHC2bmctn0PIrzk0968LQY\\nBpPHpDF5TBq3+brYvKuGjTuO8MG+ej7YVx+9X7zTRlx3R9AwwCDy2se/z7AZaXeOb/tD3ceIU1XD\\nT2zPP441WlCyYLdZIp0II5KTnkCM7niO3WRE89ZzRwOix6twd0HwZPmLc1iZUZzBjOIMZhZnRq8G\\neLxgKMzeimY+PNBAycHGaMHyePFOG64EO3fdOpu8VF0CPdaGex/seNGRSAPoGPcc+9NOsSYS9J7K\\n1qPwuJFJNy0q7tWufO7yIrburaOmycvkMWnceuU4IHLM+Pbnp/PIsx/w8juHsFoMvv7pKcybmgPA\\nd2+ZyWMv7uQ3f9kDwOjspOjoo827a/jj3/ax8rXItvRkJ3d8djpj85JZNCufX7/8UXQ0JYArwc7n\\nLi+iodXPeyU1/O6ve/nD3/ZFF1ZdMDufeLuFjTuO8NLGg7z67uHo377NaqEgK4l9lS3sq2zhuTf2\\nAxAKmxgGjC9Ioarew2ubynltUzkJThveE9aV27yrNvKeEx3RdTyithGNMdAZojPYu82BSLtjt1mi\\nX9qOZ7NaSEm009QW6NOkWgyD9GQnbR2dJ33e9GQnnV3hk64V6Eqw47BZaGoP9GmP451WXPEOWjyB\\nPs9rt1lIcznp8HX1GQndc7XaYChMq6ezV7wGkJzkwGGLXF0pGOr9oolxNhLj7bSe5CSo3WYhJdFB\\nY5u/V6zv76mLxhvnsNHS3jdHyQn2yLaTvJee0f3t3q4+n6nDbiE10UlLR4DOrr65dSVECjttHV29\\n4rVZDVKTnFgtBm3erl5rEBp0zyqIt+P1d+HxdvWKt+fEcGcwUgQ6vs9ht1lITnBgGNDm7ewVk9Vi\\nkNT9Pj3ezj6fS1L3a/oCQdq9nb1yGO+04kpwEAqZePxdBDpDvbYldReoTsyRzWqJ7kNt3k58gd6f\\nWbzThiveTlN7oM9xFiClu71p9XT23ZbowN69n5zY7+p53mZPgK4TPk+b1UKay4HH19UnHoNIoTwU\\nCtN2kr+zxDgbToeVprZAn202q0FKopPm9kCkb3OCyN9g3/5Ez/OGTbNPPD3PG++09fm7X7c1soh0\\ncoK9T6wlh5pYQ1mfdigp3h4tituskRO1PW3YuIIUqus9/HVzBX/dXIHNaiEYCmOzWpg/PQevP8j2\\nAw08/V+7sXQXTuMcVj57WRH57iReeecQ75XU8F53e5sUb+er10/mwklZ/P3DalZvKIu21SlJDu76\\n/AyKcpOZNzWb376+ly27aymtbiU1ycF3b54ZLSB+79ZZPPLMB+w61ERSvD167AAYlZXEtReP5rVN\\n5disBpfPzON4C2bm8eq7hwiGzF7rIUGkDzhxdBpbdtdS0+QlN2Pg3ynOtrOyyu7Jihdn4zFpaQnY\\nzsGl2b3ByFmOQFcIi9FT/LFgP74YZLF0/+z+r3t0TE91PBQ2CYbChMMmwXC4+4tH5GcwbBIOhSMN\\nsUn0S4LR/S3A6PnSYBz7t4HR6wuE0f2gEx8Hx3/BONbxMYwTb+GkX7iO3e/YAyxG5L6W7mAsPXF1\\n/+x5z5bu3Fi672uxdD/OYpDgtEVWkU+JJzM1jlFZLtxp8UOqYtoft3tkTRU6lcHKQ15uav93GgSj\\n8vu/j8fXRVVdOzWNXmoaO6jpXtC5p+BgmibpyXFkpSfgTo1nXEEqY87iVQGzspJZOHcM/s4gB6tb\\nOVDZwsHqVjzeLjr8XXi7O0tG9G8Xjm9rooWI7rbFbrNERhfF20mMs5OeHIc7LR53ajzpKZEz9wlx\\nkU5l2ASfvwuPr4sOX1eksBKKtHsGkU5QfJyNeIcNE+jsCtHZFcLfGaKx1U9jq4/GVj/N7X483sjz\\ntHs7CQbDkY6BaWIxIlehSE50kJLkJN+dxIWTsynKO3nh6FTcQNHodJZdO5kDlS3sOthIXVOkGFjX\\n7KWzK3SsoNZdOO/5aRC5ZHic41h7b7Ua2LqPBR/n2MmD444b3aO1Qt2jfnry0tkVpjMY+Xfki8dx\\nRb/o/3oXp8zjNvTEbzGOHa8cditFefGkJEUWDR6V7WLGuEyK81Ownsa6cLk5KVxxUSEAzW1+9pY3\\nsftQE7VNXtq9nXi8kS8WXcGw2skhaLj1wU6MY970XCYXpp/xvnXprHz2VDRzyax80ly9i5tzE5yM\\n21TO0svG9nlet9vFpTNyyUyJ56IZ+X22ffG6yXywt477VlxIynEFKrfbxT3/w+DZv+3hy9dPYe6U\\nnF7b8rKT+eUL25g9MYsVSydHRwp8eqGLudPyeGz1duIcNu66dVb0ed1uFz//zkJWvlrCztIGrp9f\\nxOKLjq3p19Di489/L+WDPbVcMDmbGy4vJrt7WtxXbpjOG1vK+e/3K8hIiWPh7ALmTc8lMd5OXbOX\\nd7ZX8/b2yPobC+cUsGB2AenJcQS6QrxfUsObWyuoqvMwd0oOM8dnMmO8G38gyI7SenYeaKC0qoUZ\\n4zKZXJjO5KJ0kuLt7CtvZs/hJvZXNJPZfawbNyqVnIxEKmvbKa1qobSyha5gmClFGRTlpTAqO4mW\\n9gCHjrRx6GgrzW1+phZnMCrbxejsyMjIitp2KmraqWnsoCDbRYE7ifysJBLi7Bxp8FBd5+FIvYfk\\nRAeTCtPJzUwkPTmOxhYfRxo7OFrfQVcwxJSiDLLTE8hKS8Ab6KK2+5jd2tHJ6BwXWd3bAOqbfdQ2\\nddDQ6ictOY6JYxJwp8WTEGenscVHXbOXumYfDoeNqcWu7tEhkSJXfbOP+pbIMWVsfgru1AQyUuMI\\ndIaob/FR3+yjvaOTPHciWWkJZKUnYDEM6pq91Dd7aWz1M7kwnWnFmUwbm0F2RgL7ypspKWtk18GG\\nSP7GZpDVfZKtL3zM1gAAIABJREFUraMzGo8/EGRUjovMlMi2rmCYhu7jbFtHgIzUeCamxJGREo/V\\natDY6qep1U9Tu598dxJjcpIZneMiIyWO6voOyo+2UVHTTqArRH5WEqlJTlwJDnyBIK2eyBISgc7I\\niLA0VxwpSU5C4XBkiYm2AB5fJ8mJDkbnJJPqcmK3WiLLTnQvP+GwWxlXkEqqy0lCXKTI0NLup6U9\\nQNiEgiwXqd0L3vu7R0a3eAL4AyEyUuMpTnKS6nISDpvR5233dpIYbyffnRR5TZuFNk9n93IXAWwW\\ng/zMJFyJkb5Mh6+Lto7OSN8jFCYrPYHkRAeuBEdkBHRHgFZPJ/7OINnpkX2r50rCTW2Rfkyrp5PC\\nXBejc5IZk+PCleCgqs5DeU0bFbXtWAyD0cXJuNPiSXM5afEEqG/2Rfse40elRvuIPVdMrmv20uaJ\\n7JvZ6QlkpydiMaC2yUttk5eGFh/utARyMxLJyUjElWCntslLTaOXo40dOB1Wxo9OI8+dhDs1nqY2\\nP0fqOzjS4MEfCDJzfCYFWS7y3Un4O4NU1rZTVeehocXHxDFpFOZG3ovNZqWipo3ymjaq6jyMyXUx\\nNi8yKjk9JZ7K2nYOVrdy8EgrVovBhNFpTBiVSmFeCjWNHeyvaGZ/ZQttngAzx7uZOjaDKUUZeP1d\\n7DrUyO6DTVTUtHHRlAxmT3Qza4KbpHgHOw7U8+H+OnYdbGRsQQrzpuVy8bRcstLiOXy0jc0fHWXL\\n7hpMExbOLmDhnHwyUuIjbdiuGtZvraSmsYOFcwq49pLC6AjUmsYO/vLOIbbuqWXulGxuunJ8tM29\\n5tIiNm6vZs2b+ynKS+Frn5kW3XbLkmQWzR3DUy9/hMfXxT3/4wIyu9ftA/jBVy9m9Zv7eWf7Eb53\\n2xyK8o6tZ+d2u3j465fw6zU7uW3JRMaOyeh1bPnKZ6ZTWd/B5MJ0ik/Y5nbD568cT1lVKzMm5fTp\\ncy69bCw1TV7yc1PISInnZGLRNzPMAfQ+HnvsMdxuN8uWLQPgqquu4pVXXiEpKYmqqiruvvvu6Fz7\\nxx9/nNTUVJqbm0/5mFOpr28fyHs6LW6365w+/3ChPEQoDxHKQ4TyEKE8RCgPEecyDypOnb7zoQ/W\\nQ39bZ045GxjlbWCUtzOnnA2M8jYwnzRvA+1/DWge0Pz581m7di0Au3btIisrK9oRKSgowOPxUFVV\\nRTAY5K233mL+/Pkf+xgRERER6Z/6YCIiIhJLA5rONmfOHKZOncqyZcswDIOHHnqIl156CZfLxdVX\\nX83DDz/M3XffDcDSpUspKiqiqKioz2NERERE5PSpDyYiIiKxNKDpbINF09nOPeUhQnmIUB4ilIcI\\n5SFCeYjQdLaRRdPZhiblbGCUt4FR3s6ccjYwytvADKvpbCIiIiIiIiIiMrKoiCQiIiIiIiIiIv1S\\nEUlERERERERERPqlIpKIiIiIiIiIiPRLRSQREREREREREemXikgiIiIiIiIiItIvwzRNM9ZBiIiI\\niIiIiIjI0KaRSCIiIiIiIiIi0i8VkUREREREREREpF8qIomIiIiIiIiISL9URBIRERERERERkX6p\\niCQiIiIiIiIiIv1SEUlERERERERERPpli3UAsdDQ0MB1113H448/zsUXX8zevXt5+OGHAZg4cSL/\\n9m//FtsAz7FgMMgPfvADKioqCIVC3HvvvVx44YUjLg89HnnkEXbs2IFhGDzwwAPMmDEj1iENmp/9\\n7Gd88MEHBINBvvGNbzB9+nTuvfdeQqEQbreb//iP/8DhcMQ6zEHh9/v51Kc+xR133MG8efNGZB5e\\nffVVfvOb32Cz2bjrrruYOHHiiMtDR0cH9913H62trXR1dXHnnXfidrtHTNu4f/9+7rjjDr785S+z\\nfPlyjh49etJ94NVXX+UPf/gDFouFW265hZtvvjnWocswMpKPu2diy5YtfOc732H8+PEATJgwga99\\n7Wsjrl0+XWq/BubEvN1///3s2rWL1NRUAG6//XYWLVqkvB3ndPvPyllvJ+Zt/fr12tf64fP5uP/+\\n+2lsbCQQCHDHHXcwadKk2O9v5gj0/e9/3/zc5z5nbt682TRN01y+fLm5Y8cO0zRN83vf+565YcOG\\nWIZ3zq1Zs8Z86KGHTNM0zf3795uf//znTdMceXkwTdPcsmWL+U//9E+maZpmaWmpecstt8Q4osGz\\nadMm82tf+5ppmqbZ1NRkLly40Lz//vvN119/3TRN0/zFL35hPvfcc7EMcVA9+uij5o033mi++OKL\\n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\\nv9/PD37wA44ePYrf7+db3/oW06ZNO+lxYDDzNmKLSCIiIiIiIiIicvpG1HQ2EREREREREREZGBWR\\nRERERERERESkXyoiiYiIiIiIiIhIv1REEhERERERERGRfqmIJCIiIiIiIiIi/VIRSURERERERERE\\n+qUikoiIiIiIiIiI9EtFJBERERERERER6ZeKSCIiIiIiIiIi0i8VkUREREREREREpF8qIomIiIiI\\niIiISL9URBIRERERERERkX6piCQiIiIiIiIiIv1SEUlERERERERERPqlIpKIiIiIiIiIiPRLRSQR\\nEREREREREemXikgiIiIiIiIiItIvFZFERERERERERKRfKiKJiIiIiIiIiEi/VEQSEREREREREZF+\\nqYgkIiIiIiIiIiL9UhFJRERERERERET6pSKSiIiIiIiIiIj0S0UkERERERERERHpl4pIIiIiIiIi\\nIiLSLxWRRERERERERESkXyoiiYiIiIiIiIhIv1REEhERERERERGRfqmIJCIiIiIiIiIi/VIRSURE\\nRERERERE+qUikoiIiMh5YP/+/SxevJhnn322z7b33nuPm266iVtvvZVf/epXMYhOREREzgcqIomI\\niIgMc16vlx/+8IfMmzfvpNt/9KMf8dhjj/H888/z7rvvUlpaOsgRioiIyPlARSQRERGRYc7hcPD0\\n00+TlZXVZ1tlZSUpKSnk5uZisVhYuHAhmzZtikGUIiIiMtypiCQiIiIyzNlsNuLi4k66rb6+nvT0\\n9Ojv6enp1NfXD1ZoIiIich6xxTqAj1Nf3x7rEIattLQEmpu9sQ5jxFHeY0N5jw3lffCdrzl3u12x\\nDkFOYJomhmHEOgwREREZYoZ0EUkGzmazxjqEEUl5jw3lPTaU98GnnMtAZGVl0dDQEP29trb2pNPe\\njmcYhk7mDUFut0ufyxCjz2Ro0ucy9OgzGXoGehJP09lEREREzmMFBQV4PB6qqqoIBoO89dZbzJ8/\\nP9ZhiYiIyDCkkUgiIiIiw1xJSQk//elPqa6uxmazsXbtWq688koKCgq4+uqrefjhh7n77rsBWLp0\\nKUVFRTGOWERERIYjFZFEREREhrlp06bxzDPPnHL73LlzWbVq1SBGJCIiIucjTWcTEREREREREZF+\\nqYgkIiIiIiIiIiL9UhFJRERERERERET6pSKSiIiIiIiIiIj0S0UkERER6cPr9XLTTZ+OdRgiIiIi\\n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\\nSWG4c7mHHnpIX/rSlzR37lyzIiadE/XJVVddpb/+9a965pln9LOf/UzFxcVavHixmXGTxon6xW63\\nKz8/X/v27Rvaz9SpkXeiPpkwYYJ279499MHYtm3bNGnSJLOi4rDTea23GIxbTjgrVqzQxo0bZbFY\\ntGTJEk2fPt3sSAlj27Zt+tGPfqSGhgbZ7Xbl5ORoxYoVuv/++9XX16fx48frhz/8oRwOh1588UU9\\n/vjjslgsuu222/TZz37W7Phxq7KyUitXrjzqxf+hhx7S9773Pdp9hPT29upf//Vf1djYqN7eXt19\\n990699xz9d3vfpc2HyUrV67UhAkTdMkll9DuGFEfPW9477335PF4tGDBAm3YsEErVqyQJC1cuFBf\\n/epXTU6bHI7XJ5dccolmz56tCy64YOi21157rcrLy01MmxxO9P/kiPr6ej3wwAN64oknTEyaXE7U\\nL7W1tbr//vtlGIamTZumpUuXymplDMVIO1GfPP3006qqqpLNZtMFF1yg++67z+y4SeFY72Hnz5+v\\nvLy803qtp4gEAAAAAACAYVGKBQAAAAAAwLAoIgEAAAAAAGBYFJEAAAAAAAAwLIpIAAAAAAAAGBZF\\nJAAAAAAAAAyLIhIAAAAAAACGRREJAAAAAAAAw6KIBAAAAAAAgGH9P+YfQiLiqdoTAAAAAElFTkSu\\nQmCC\\n\",\n            \"text/plain\": [\n              \"<matplotlib.figure.Figure at 0x7f687067fd68>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          }\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"NShPt-5udbOg\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"f42df0c4-ed4d-4fbc-dfec-fbc5e7b75777\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 402\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"figv, axsv = plt.subplots(nrows=2, ncols=2, figsize=(20, 5))\\n\",\n        \"\\n\",\n        \"axq = sns.violinplot(x=df_original[\\\"Quantity\\\"], ax=axsv[0, 0])\\n\",\n        \"axq.set_xlim(-100, 100)\\n\",\n        \"\\n\",\n        \"axp = sns.violinplot(x=df_original[\\\"UnitPrice\\\"], ax=axsv[0, 1])\\n\",\n        \"axp.set_xlim(-10, 40)\\n\",\n        \"\\n\",\n        \"axd = sns.violinplot(x=df_original[\\\"day_number\\\"], ax=axsv[1, 0])\"\n      ],\n      \"execution_count\": 93,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"/usr/local/lib/python3.6/dist-packages/seaborn/categorical.py:588: FutureWarning: remove_na is deprecated and is a private function. Do not use.\\n\",\n            \"  kde_data = remove_na(group_data)\\n\",\n            \"/usr/local/lib/python3.6/dist-packages/seaborn/categorical.py:816: FutureWarning: remove_na is deprecated and is a private function. Do not use.\\n\",\n            \"  violin_data = remove_na(group_data)\\n\"\n          ],\n          \"name\": \"stderr\"\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\nlZVkZGQwb9481q9fz7JlywC4+uqr+eY3v+lxtYPDJ/Vk9uzZXHTRRUydOrXvvl/4wheYP3++h9UO\\nHsf6WelVU1PDQw89xAsvvOBhpYPHsXqyd+9eHnzwQVzXZeLEiTz66KOYpsYmnAnH6suLL75IUVER\\nlmUxdepUHnjgAa/LHfCO9jvuVVddxciRI0/otV6Bj4iIiIiIiIjIAKPYVERERERERERkgFHgIyIi\\nIiIiIiIywCjwEREREREREREZYBT4iIiIiIiIiIgMMAp8REREREREREQGGAU+IgPQfffdR1FRkddl\\nHNNVV13F3r17vS5DRERERERkQFLgIyIiIiIiIiIywNheFyAiJ89xHB5++GF27NhBYWEhXV1dADzz\\nzDMUFxcDMGzYMH72s5/xi1/8Atu2ufPOOwH49a9/TWtrKw888MBRH7uoqIg1a9bgOA67d++msLCQ\\n5cuXU1JSws9//nP++Mc/AvDggw8yffp0Lr30Ur7zne8wa9YsNmzYQE5ODl/84hf561//Sm1tLc88\\n8wznnnsuAH/+858pKyujqamJf/3Xf+Xiiy9m//79/PSnPyUYDNLV1cWPfvQjLrvsMh588EH8fj+7\\nd+9m2bJlFBQUnO4vq4iIiIiISL+lET4iA8CaNWv44IMPePnll3nyySfZsWMHsViMlJQU/ud//ocX\\nX3yRjo4OVq1axY033sgrr7yC67oAvPrqq9xwww3HfPzNmzezZMkSioqK2L59O9u2bTvm/Xfv3s3N\\nN99MUVERu3fvZt++ffzud7/jC1/4Ai+//HLf/bKzs/nv//5vHn74YZ544gkAHn30UW677Taef/55\\nfvnLX/LII48QjUYB6Orq4oUXXlDYIyIiIiIichwa4SMyAOzcuZOpU6diGAYpKSlMmTIFy7IwTZNb\\nbrkF27b54IMPaGlpYeTIkYwZM4aSkhKGDx9OSkoK48aNO+bjT5kyheTkZACGDx9OW1sbpvnJeXFO\\nTg5jx44FoKCggGnTpgHxUUb79+/vu9+sWbMAmDp1KlVVVQCsW7eOzs5Onn32WQBs26apqanvfiIi\\nIiIiInJ8CnxEBgDXdTEMo++64zgcPHiQV155hZdffpnU1FTuuuuuvuMLFizgr3/9K2PGjDnu6B4A\\ny7KO+XwAkUjkE+9/+PXekUVA32O4rtsXIPn9fpYvX05ubu7H6vD7/cetVURERERERDSlS2RAmDBh\\nAqWlpbiuSyAQoLS0lOTkZAoLC0lNTaW2tpYtW7YQDocBuOKKKygrK+Ptt9/mmmuuOaHnTE9P5+DB\\ng7iuSzAYpLS09DM/xtq1awHYtGkTZ599NgDTp0/nf//3fwFobm7mscceO6H6REREREREBjON8BEZ\\nAGbPns0rr7zCjTfeyIgRI7jwwgvx+XwEAgFuvvlmzj77bO68806effZZLr74YsaOHcvnPvc5AoEA\\nKSkpJ/Sc5557Lueccw5f+tKXGD169AlNt2ptbeU73/kO+/fvZ/HixQA8/PDD/OQnP+Fvf/sb4XCY\\n7373uydUn4iIiIiIyGBmuIfPrxCRQSEcDnPLLbewdOlSJkyY4HU5IiIiIiIicopphI/IIPPuu++y\\nbNky5s+f3xf2vPHGGzz//PNHvf8LL7xwJssTERERERGRU0AjfEREREREREREBhgt2iwiIiIiIiIi\\nMsAo8BERERERERERGWAU+IiIiIiIiIiIDDAKfEREREREREREBhgFPiIiIiIiIiIiA4wCHxERERER\\nERGRAeb/B9Cc9AVFoVkKAAAAAElFTkSuQmCC\\n\",\n            \"text/plain\": [\n              \"<matplotlib.figure.Figure at 0x7f687061d550>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          }\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"QWzOIIq_Gpui\",\n        \"colab_type\": \"text\"\n      },\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"Day 250 seems to represent a decent threshold data (more than 50% of the orders happened before that day but less than 75%\\n\",\n        \"\\n\",\n        \"Would use this query:\"\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"Ur3i5h1qJx4Z\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"threshold_qry = \\\"\\\"\\\"\\n\",\n        \"SELECT\\n\",\n        \"  DATETIME_ADD(mind, INTERVAL 250 DAY)\\n\",\n        \"FROM\\n\",\n        \"  (\\n\",\n        \"  SELECT\\n\",\n        \"    MIN(PARSE_DATETIME(\\\"%m/%d/%y %H:%M\\\", InvoiceDate)) mind,\\n\",\n        \"    MAX(PARSE_DATETIME(\\\"%m/%d/%y %H:%M\\\", InvoiceDate)) maxd\\n\",\n        \"  FROM\\n\",\n        \"    `{}.{}.{}`) mm\\n\",\n        \"\\\"\\\"\\\"\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"metadata\": {\n        \"id\": \"wuzX4obrSnwb\",\n        \"colab_type\": \"text\"\n      },\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"### RFMT\"\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"qwKjxIeNSnwc\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"2e0538c8-9f00-4c69-eb6d-a1500441738b\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 348\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# Frequency of Repeat transaction\\n\",\n        \"repeat_transactions_query = \\\"\\\"\\\"\\n\",\n        \"SELECT\\n\",\n        \"  customer_id,\\n\",\n        \"  COUNT(DISTINCT order_date) -1 AS num_transactions\\n\",\n        \"FROM\\n\",\n        \"  `{}.{}.data_cleaned`\\n\",\n        \"GROUP BY\\n\",\n        \"  customer_id\\\"\\\"\\\"\\n\",\n        \"repeat_transactions_query = repeat_transactions_query.format(PROJECT_ID, DATASET)\\n\",\n        \"\\n\",\n        \"df_repeat_transactions = pd.io.gbq.read_gbq(query=repeat_transactions_query, dialect ='standard', project_id=PROJECT_ID)\\n\",\n        \"print(df_repeat_transactions.head(2))\\n\",\n        \"df_repeat_transactions.describe()\"\n      ],\n      \"execution_count\": 52,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"  customer_id  num_transactions\\n\",\n            \"0       16915                 5\\n\",\n            \"1       15349                 1\\n\"\n          ],\n          \"name\": \"stdout\"\n        },\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/html\": [\n              \"<div>\\n\",\n              \"<style scoped>\\n\",\n              \"    .dataframe tbody tr th:only-of-type {\\n\",\n              \"        vertical-align: middle;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe tbody tr th {\\n\",\n              \"        vertical-align: top;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe thead th {\\n\",\n              \"        text-align: right;\\n\",\n              \"    }\\n\",\n              \"</style>\\n\",\n              \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n              \"  <thead>\\n\",\n              \"    <tr style=\\\"text-align: right;\\\">\\n\",\n              \"      <th></th>\\n\",\n              \"      <th>num_transactions</th>\\n\",\n              \"    </tr>\\n\",\n              \"  </thead>\\n\",\n              \"  <tbody>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>count</th>\\n\",\n              \"      <td>1650.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>mean</th>\\n\",\n              \"      <td>7.436364</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>std</th>\\n\",\n              \"      <td>9.308011</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>min</th>\\n\",\n              \"      <td>1.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>25%</th>\\n\",\n              \"      <td>3.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>50%</th>\\n\",\n              \"      <td>5.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>75%</th>\\n\",\n              \"      <td>9.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>max</th>\\n\",\n              \"      <td>141.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"  </tbody>\\n\",\n              \"</table>\\n\",\n              \"</div>\"\n            ],\n            \"text/plain\": [\n              \"       num_transactions\\n\",\n              \"count       1650.000000\\n\",\n              \"mean           7.436364\\n\",\n              \"std            9.308011\\n\",\n              \"min            1.000000\\n\",\n              \"25%            3.000000\\n\",\n              \"50%            5.000000\\n\",\n              \"75%            9.000000\\n\",\n              \"max          141.000000\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 52\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"v3pFNR9mSnwf\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"5c19fd34-8d3e-41d4-c1cc-23ba6ce96e57\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 337\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"fig, axs = plt.subplots(figsize=(10, 5))\\n\",\n        \"sns.kdeplot(df_repeat_transactions['num_transactions'])\"\n      ],\n      \"execution_count\": 60,\n      \"outputs\": [\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/plain\": [\n              \"<matplotlib.axes._subplots.AxesSubplot at 0x7f688c044908>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 60\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\nQmCC\\n\",\n            \"text/plain\": [\n              \"<matplotlib.figure.Figure at 0x7f68955abd68>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          }\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"hs55GnZbSnwh\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"c64a274b-9ce9-48a5-a30a-7981f3f5d6e5\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 348\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"recency_query = \\\"\\\"\\\"\\n\",\n        \"SELECT\\n\",\n        \"  customer_id,\\n\",\n        \"  DATE_DIFF(MAX(order_date), MIN(order_date), DAY) recency\\n\",\n        \"FROM\\n\",\n        \"  `{}.{}.data_cleaned`\\n\",\n        \"GROUP BY\\n\",\n        \"  customer_id\\\"\\\"\\\"\\n\",\n        \"recency_query = recency_query.format(PROJECT_ID, DATASET)\\n\",\n        \"\\n\",\n        \"df_recency = pd.io.gbq.read_gbq(query=recency_query, dialect ='standard', project_id=PROJECT_ID)\\n\",\n        \"print(df_recency.head(2))\\n\",\n        \"df_recency.describe()\"\n      ],\n      \"execution_count\": 55,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"  customer_id  recency\\n\",\n            \"0       16676      304\\n\",\n            \"1       12901      262\\n\"\n          ],\n          \"name\": \"stdout\"\n        },\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/html\": [\n              \"<div>\\n\",\n              \"<style scoped>\\n\",\n              \"    .dataframe tbody tr th:only-of-type {\\n\",\n              \"        vertical-align: middle;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe tbody tr th {\\n\",\n              \"        vertical-align: top;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe thead th {\\n\",\n              \"        text-align: right;\\n\",\n              \"    }\\n\",\n              \"</style>\\n\",\n              \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n              \"  <thead>\\n\",\n              \"    <tr style=\\\"text-align: right;\\\">\\n\",\n              \"      <th></th>\\n\",\n              \"      <th>recency</th>\\n\",\n              \"    </tr>\\n\",\n              \"  </thead>\\n\",\n              \"  <tbody>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>count</th>\\n\",\n              \"      <td>1650.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>mean</th>\\n\",\n              \"      <td>256.879394</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>std</th>\\n\",\n              \"      <td>90.692561</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>min</th>\\n\",\n              \"      <td>1.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>25%</th>\\n\",\n              \"      <td>197.250000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>50%</th>\\n\",\n              \"      <td>272.500000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>75%</th>\\n\",\n              \"      <td>337.750000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>max</th>\\n\",\n              \"      <td>373.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"  </tbody>\\n\",\n              \"</table>\\n\",\n              \"</div>\"\n            ],\n            \"text/plain\": [\n              \"           recency\\n\",\n              \"count  1650.000000\\n\",\n              \"mean    256.879394\\n\",\n              \"std      90.692561\\n\",\n              \"min       1.000000\\n\",\n              \"25%     197.250000\\n\",\n              \"50%     272.500000\\n\",\n              \"75%     337.750000\\n\",\n              \"max     373.000000\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 55\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"AlykZLJvSnwk\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"21a069d0-e3e3-4cef-e866-808905d3559e\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 337\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"fig, axs = plt.subplots(figsize=(10, 5))\\n\",\n        \"sns.kdeplot(df_recency['recency'])\"\n      ],\n      \"execution_count\": 59,\n      \"outputs\": [\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/plain\": [\n              \"<matplotlib.axes._subplots.AxesSubplot at 0x7f6890f883c8>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 59\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAAAlwAAAEvCAYAAACQQh9CAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBo\\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3Xl81PW97/HXLNkne2ayQ0JYDYRN\\nVEzZBETRulWFemhve2s3tef0lNZabs+Bnov0VM/x1u61Rz1trRWP4latWBEVIYIssoQ1IQlJyDLZ\\nMyHrzO/+EUhdgATI5DfJvJ+PBw+YzHwznx9fh7z9/r6LxTAMAxERERHxG6vZBYiIiIiMdApcIiIi\\nIn6mwCUiIiLiZwpcIiIiIn6mwCUiIiLiZwpcIiIiIn5mN7uA83G7W80uIWjEx0fS2HjK7DLkI9Qn\\ngUd9EljUH4En2PvE6Yw+53Ma4RIA7Hab2SXIJ6hPAo/6JLCoPwKP+uTcFLhERERE/EyBS0RERMTP\\nFLhERERE/EyBS0RERMTPFLhERERE/EyBS0RERMTPFLhERERE/EyBS0RERMTPFLhERERE/Cygj/YR\\nEREZiYoqmimqbGZUsoPs1BgiwvTj+Fxee+0V3n9/G3V1bq68cjbvv78Vi8XKnDnz+fznV9Da2sq/\\n/dsPaWtrw+FwsGbNOsBg3bof0draitfr5dvf/h5jx45j2bJbuPnm29i6dQtdXV08+uivCA0NY+3a\\n1dTUVBEaGsYPf/gjVq36HmvWPEh6ega1tTU88MBKnnjiqUu6DvWwiIjIEGk91cX/bC7mvf1VfV+z\\nAGlJUeTlJHLr3DHYbbr59Ek1NdWsXr2WH//43/jVrx4H4Jvf/AoLFizi5Zc3cMUVs7njjuWsX/8n\\ndu7cwfHjRVx55dV89rO3UFJynEcf/Q9++tNf4fV6GTUqi7vu+iKrV/+AnTs/oLm5icTERNaseZA3\\n39zIe++9y3XXLWXTpjf44hf/N++99y6LFi255GtQ4BIREfEzn2GwdV8Vz24uoq2jh0yXg2tnZVJV\\nf4rjJ5spqWrlr9tP0N7l5QvXjsdisZhd8qc8+1YRHxyuPe9rbDYLXq8x4O85a6KLO68Z2+/rJk26\\njEOHCqmoKOdb3/o6AKdOtVFdfZKjRw9z993fBGDZsn8A4KWXNtDU1MjGja8B0NnZ0fe9pk6dDoDT\\nmUxbm4cjRw5z+eWzAPqCVXNzE9/5zrf44hf/N9u2beH73//hgK/pXAYUuNatW8fevXuxWCysWrWK\\nvLy8vue2bdvGI488gs1mY+7cudx7773nbPPAAw9QWFhIXFwcAF/5yleYP3/+JV+EiIhIoDIMgyde\\nPcS2A9WEhdpYvnAcC2emY7P+fSSro6uHdX/czdt7KklNjGTx5ZkmVhx47PYQ7PYQZs/O5/77/8/H\\nnnv66T9iGL6PfS0kxM4///P3mDw5j0+y2f5+wLZhGNhsVny+j4fE2Ng4XC4Xhw4V4vMZOJ2uS7+G\\n/l6wY8cOysrKWL9+PcXFxaxatYr169f3Pb927Voef/xxkpOTWbFiBUuWLKGhoeGcbb7zne+wYMGC\\nSy5cRERkOHhzZwXbDlSTnRrNvbdOISEm/FOvCQ+180+35/F//7CTZzYdIzk+krycRBOqPbc7rxnb\\n72iU0xmN293ql/efMGESv/71z+no6CAsLIxHH/1PvvnN+5g06TJ27fqASZNyefHF5wkLC+Oyyybz\\n7rtvM3lyHiUlx9m+fRvLl6846/edOPEydu/+gGuuWcTWrVsoLj7GF7/4v1myZCmPPPITbrrptkGp\\nv98bxQUFBSxatAiAnJwcmpub8Xg8AJSXlxMbG0tqaipWq5V58+ZRUFBw3jYiIiLB4siJRta/VURM\\nVCj33ZZ31rB1RmJsON/63BRsViu/eekAlW793PyolJQU7rzz89x771f52te+RGJiImFh4dxxx+c5\\ncGAf9933NbZte4958xZw++3LqKws55577uYnP1nLtGkzzvl9Fy1aQnt7O/fd9zWeffbPXH/9jQDk\\n58+loqKC+fMXDkr9/Y5w1dXVkZub2/c4ISEBt9uNw+HA7XaTkJDwsefKy8tpbGw8axuAp556iief\\nfJLExET+5V/+5WPtRURERorG1k5+/eIBLBa455bJxEeH9dsmJy2Wr9wwid++XMijz+1j7d1XEhpi\\n67fdSLZ06Wf7/nzbbXdw2213fOx5h8PBv//7I59q9+CDD3/qa88990rfn++779t9f/6Xf/m3T712\\n//695OfPITo6+qLq/qQLnjRvGAOfDPfJNjfffDNxcXFMmjSJxx57jF/84hf867/+6znbxcdHYrcH\\n939oQ8npHJz/qGTwqE8Cj/oksARqf3T3ePnJn/fQcqqbr94ymfwZA5+TdeO8aKqa2nn53ePsK2vi\\n+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\\n\",\n            \"text/plain\": [\n              \"<matplotlib.figure.Figure at 0x7f6888cbc710>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          }\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"-QzWVNOISnwn\",\n        \"colab_type\": \"text\"\n      },\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"### Monetary\"\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"V8XjN--xSnwn\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"086e0800-8a7d-408a-a483-ef0dacd9a068\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 348\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"monetary_query = \\\"\\\"\\\"\\n\",\n        \"SELECT\\n\",\n        \"  customer_id,\\n\",\n        \"  SUM(order_value) AS monetary\\n\",\n        \"FROM\\n\",\n        \"  `{}.{}.data_cleaned`\\n\",\n        \"GROUP BY\\n\",\n        \"  customer_id\\\"\\\"\\\"\\n\",\n        \"monetary_query = monetary_query.format(PROJECT_ID, DATASET)\\n\",\n        \"\\n\",\n        \"df_monetary = pd.io.gbq.read_gbq(query=monetary_query, dialect ='standard', project_id=PROJECT_ID)\\n\",\n        \"print(df_monetary.head(2))\\n\",\n        \"df_monetary.describe()\"\n      ],\n      \"execution_count\": 57,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"  customer_id  monetary\\n\",\n            \"0       16676   1558.72\\n\",\n            \"1       12901  16293.10\\n\"\n          ],\n          \"name\": \"stdout\"\n        },\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/html\": [\n              \"<div>\\n\",\n              \"<style scoped>\\n\",\n              \"    .dataframe tbody tr th:only-of-type {\\n\",\n              \"        vertical-align: middle;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe tbody tr th {\\n\",\n              \"        vertical-align: top;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe thead th {\\n\",\n              \"        text-align: right;\\n\",\n              \"    }\\n\",\n              \"</style>\\n\",\n              \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n              \"  <thead>\\n\",\n              \"    <tr style=\\\"text-align: right;\\\">\\n\",\n              \"      <th></th>\\n\",\n              \"      <th>monetary</th>\\n\",\n              \"    </tr>\\n\",\n              \"  </thead>\\n\",\n              \"  <tbody>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>count</th>\\n\",\n              \"      <td>1650.000000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>mean</th>\\n\",\n              \"      <td>3973.304164</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>std</th>\\n\",\n              \"      <td>12993.748834</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>min</th>\\n\",\n              \"      <td>36.560000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>25%</th>\\n\",\n              \"      <td>935.362500</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>50%</th>\\n\",\n              \"      <td>1767.770000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>75%</th>\\n\",\n              \"      <td>3429.125000</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>max</th>\\n\",\n              \"      <td>279489.020000</td>\\n\",\n              \"    </tr>\\n\",\n              \"  </tbody>\\n\",\n              \"</table>\\n\",\n              \"</div>\"\n            ],\n            \"text/plain\": [\n              \"            monetary\\n\",\n              \"count    1650.000000\\n\",\n              \"mean     3973.304164\\n\",\n              \"std     12993.748834\\n\",\n              \"min        36.560000\\n\",\n              \"25%       935.362500\\n\",\n              \"50%      1767.770000\\n\",\n              \"75%      3429.125000\\n\",\n              \"max    279489.020000\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 57\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"BpyOyOIfSnwq\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"b7d45413-777a-495f-b2f8-3eff30a52c3c\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 337\n        }\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"fig, axs = plt.subplots(figsize=(10, 5))\\n\",\n        \"sns.kdeplot(df_monetary['monetary'], clip=(-1000, 15000))\"\n      ],\n      \"execution_count\": 76,\n      \"outputs\": [\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/plain\": [\n              \"<matplotlib.axes._subplots.AxesSubplot at 0x7f688b4d99e8>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          },\n          \"execution_count\": 76\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n9u2b0nl8JmYld85mILVajV6vNx+3t7eb7/DtTU1NDbdu3aKwsBA3NzciIiIICgoCYOvWrTQ1NVnM\\nS61Wo1arzXdTw8PDCCHMOwC2xMfHh927d6NQKPDz82PevHn09PQwODgIQFtbmzkPe87pZzqdzvxh\\nsGPHDvz8/IDf19TPGY5lNdZm61Qq1aRqydvbe9wjPlvOLSsri0WLFpGcnGxui4uLY/bs2ahUKsLD\\nw831Zc85TXYet4Ws5OJsBlq/fj3Pnz8H4P3796jVaubMmWPlXv19fX19XLx4kdu3b5t/1ZOSkkJL\\nSwvwnw/YgIAAQkJCePfuHb29vRgMBurr6wkLC2P9+vVUVVUB8Pr1a9auXWu1sfxJFRUVFBcXA9DR\\n0UFnZycxMTHmGnrx4gUbNmyw+5zGtLW1MXv2bJRKJUIIEhIS6O3tBf5bU+Hh4VRXV2M0Gmlra6O9\\nvZ2lS5eOy2osV1u3bt26SdWSs7MzS5Ysoa6ubtx72JqKigqcnZ05duyYue3Tp0+kpaUhhGBkZIT6\\n+noCAgLsOieY/DxuC1kpxEzc75O4dOkSdXV1KBQKcnJyWL58ubW79Nc9evSI69ev888//5jbYmJi\\nKC0txdXVFZVKRV5eHl5eXlRVVVFcXGx+dBAVFYXJZOLMmTN8/vwZpVLJ+fPn8fX1teKI/oz+/n5O\\nnTpFb28vw8PDJCcnExQUREZGBkNDQ8yfP5+8vDycnZ3tOqcxjY2NXLlyhaKiIgAqKyspKirC1dUV\\nHx8fzp07h6urKyUlJTx9+hSFQsHx48eJiIjAYDBw+vRpuru7cXd3Jz8/Hzc3NyuPaOo0NjZy4cIF\\nWltbcXJywsfHh0uXLpGZmTmpWmpubiY7O5vR0VFCQkLIysqy9lAnxVJOnZ2dzJo1y3wj7e/vT25u\\nLvn5+dTW1uLg4MDWrVs5evSo3eQElrPSaDQUFBRMah6f6VnJxZkkSZIkSdI0Ih9rSpIkSZIkTSNy\\ncSZJkiRJkjSNyMWZJEmSJEnSNCIXZ5IkSZIkSdOIXJxJkiRJkiRNI3JxJkmSJEmSNI3IxZkkSZIk\\nSdI0IhdnkiRJkiRJ08i/pIg3viLLsbkAAAAASUVORK5CYII=\\n\",\n            \"text/plain\": [\n              \"<matplotlib.figure.Figure at 0x7f688be1bb70>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          }\n        }\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"N0icT7seXwu1\",\n        \"colab_type\": \"text\"\n      },\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"We can clip the monetary data at 15000\"\n      ]\n    },\n    {\n      \"metadata\": {\n        \"id\": \"Zr3JO8KXTQm-\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    }\n  ]\n}"
  },
  {
    "path": "notebooks/clv_automl.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from google.cloud import automl_v1beta1\\n\",\n    \"import os\\n\",\n    \"import time\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Create and authenticate clients \"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"keyfile_name = 'mykey.json'\\n\",\n    \"client = automl_v1beta1.AutoMlClient.from_service_account_file(keyfile_name)\\n\",\n    \"prediction_client = automl_v1beta1.PredictionServiceClient.from_service_account_file(keyfile_name)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Initialize some variables\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"project_id = 'ml-clv'\\n\",\n    \"location = 'us-central1'\\n\",\n    \"location_path = client.location_path(project_id, location)\\n\",\n    \"\\n\",\n    \"dataset_display_name = 'clv_solution_test'\\n\",\n    \"model_display_name = 'clv_model_test2'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"location_path\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Create AutoML Dataset\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"create_dataset_response = client.create_dataset(\\n\",\n    \"  location_path,\\n\",\n    \"  {'display_name': dataset_display_name, 'tables_dataset_metadata': {}})\\n\",\n    \"dataset_name = create_dataset_response.name\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## ... or alternatively, use an existing Dataset\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"dataset_list_response = client.list_datasets(location_path)\\n\",\n    \"dataset_list = [d for d in dataset_list_response]\\n\",\n    \"dataset = [d for d in dataset_list if d.display_name == dataset_display_name][0]\\n\",\n    \"dataset_name = dataset.name\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"dataset_name\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Import data from BigQuery\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"dataset_bq_input_uri = 'bq://ml-clv.clv_auto.features_n_target'\\n\",\n    \"input_config = {\\n\",\n    \"  'bigquery_source': {\\n\",\n    \"      'input_uri': dataset_bq_input_uri}}\\n\",\n    \"import_data_response = client.import_data(dataset_name, input_config)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"while import_data_response.done() is False:\\n\",\n    \"    time.sleep(1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Get column specs for Dataset\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"list_table_specs_response = client.list_table_specs(dataset_name)\\n\",\n    \"table_specs = [s for s in list_table_specs_response]\\n\",\n    \"table_spec_name = table_specs[0].name\\n\",\n    \"list_column_specs_response = client.list_column_specs(table_spec_name)\\n\",\n    \"column_specs = {s.display_name: s for s in list_column_specs_response}\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Example of updating column spec...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# update column spec for 'has_returned'\\n\",\n    \"update_column_spec_dict = {\\n\",\n    \"  \\\"name\\\": column_specs['has_returned'].name,\\n\",\n    \"  \\\"data_type\\\": {\\n\",\n    \"      \\\"type_code\\\": \\\"CATEGORY\\\"\\n\",\n    \"  }\\n\",\n    \"}\\n\",\n    \"update_column_response = client.update_column_spec(update_column_spec_dict)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Assign a training label\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"label_column_name = 'target_monetary'\\n\",\n    \"label_column_spec = column_specs[label_column_name]\\n\",\n    \"label_column_id = label_column_spec.name.rsplit('/', 1)[-1]\\n\",\n    \"print('Label column ID: {}'.format(label_column_id))\\n\",\n    \"update_dataset_dict = {\\n\",\n    \"  'name': dataset_name,\\n\",\n    \"  'tables_dataset_metadata': {\\n\",\n    \"      'target_column_spec_id': label_column_id\\n\",\n    \"  }\\n\",\n    \"}\\n\",\n    \"update_dataset_response = client.update_dataset(update_dataset_dict)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Select features for training\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"feat_list = list(column_specs.keys())\\n\",\n    \"feat_list.remove('target_monetary')\\n\",\n    \"feat_list.remove('customer_id')\\n\",\n    \"feat_list.remove('monetary_btyd')\\n\",\n    \"feat_list.remove('frequency_btyd')\\n\",\n    \"feat_list.remove('frequency_btyd_clipped')\\n\",\n    \"feat_list.remove('monetary_btyd_clipped')\\n\",\n    \"feat_list.remove('target_monetary_clipped')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"feat_list\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Train the model\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"model_dict = {\\n\",\n    \"  'display_name': model_display_name,\\n\",\n    \"  'dataset_id': dataset_name.rsplit('/', 1)[-1],\\n\",\n    \"  'tables_model_metadata': {\\n\",\n    \"      'target_column_spec': column_specs['target_monetary'],\\n\",\n    \"      'input_feature_column_specs': [\\n\",\n    \"          column_specs[x] for x in feat_list],\\n\",\n    \"      'train_budget_milli_node_hours': 10000,\\n\",\n    \"      'optimization_objective': 'MINIMIZE_MAE'\\n\",\n    \"  }\\n\",\n    \"}\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"create_model_response = client.create_model(location_path, model_dict)\\n\",\n    \"while create_model_response.done() is False:\\n\",\n    \"    time.sleep(10)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"create_model_result = create_model_response.result()\\n\",\n    \"model_name = create_model_result.name\\n\",\n    \"create_model_result.name\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## ... or alternatively get an existing trained Model \"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"model_list_response = client.list_models(location_path)\\n\",\n    \"model_list = [m for m in model_list_response]\\n\",\n    \"model = [m for m in model_list if m.display_name == model_display_name][0]\\n\",\n    \"model_name = model.name\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Get evalutions for model\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"model_evaluations = [e for e in client.list_model_evaluations(model_name)]\\n\",\n    \"model_evaluations[0]\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Deploy the model\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"scrolled\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"deploy_model_response = client.deploy_model(model_name)\\n\",\n    \"api = client.transport._operations_client\\n\",\n    \"while deploy_model_response.done is False:\\n\",\n    \"    deploy_model_response = api.get_operation(deploy_model_response.name)\\n\",\n    \"    time.sleep(1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Run batch predictions\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"gcs_input_uri = \\\"gs://ml-clv_composer_final/predictions/to_predict.csv\\\"\\n\",\n    \"gcs_output_uri_prefix = \\\"gs://ml-clv_composer_final/predictions\\\"\\n\",\n    \"\\n\",\n    \"# Define input source.\\n\",\n    \"batch_prediction_input_source = {\\n\",\n    \"  'gcs_source': {\\n\",\n    \"    'input_uris': [gcs_input_uri]\\n\",\n    \"  }\\n\",\n    \"}\\n\",\n    \"\\n\",\n    \"# Define output target.\\n\",\n    \"batch_prediction_output_target = {\\n\",\n    \"    'gcs_destination': {\\n\",\n    \"      'output_uri_prefix': gcs_output_uri_prefix\\n\",\n    \"    }\\n\",\n    \"}\\n\",\n    \"\\n\",\n    \"batch_predict_response = prediction_client.batch_predict(\\n\",\n    \"  model_name, batch_prediction_input_source, batch_prediction_output_target)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"while batch_predict_response.done() is False:\\n\",\n    \"    time.sleep(1)\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 2\",\n   \"language\": \"python\",\n   \"name\": \"python2\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 2\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython2\",\n   \"version\": \"2.7.13\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "notebooks/linear_model.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from tensorflow.python.lib.io import file_io\\n\",\n    \"import pandas\\n\",\n    \"from pandas.compat import StringIO\\n\",\n    \"import json\\n\",\n    \"import math\\n\",\n    \"import numpy as np\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Requirement already satisfied: sklearn in /Users/lramsey/miniconda2/envs/clv/lib/python2.7/site-packages\\n\",\n      \"Requirement already satisfied: scikit-learn in /Users/lramsey/miniconda2/envs/clv/lib/python2.7/site-packages (from sklearn)\\n\",\n      \"Requirement already satisfied: scipy>=0.13.3 in /Users/lramsey/miniconda2/envs/clv/lib/python2.7/site-packages (from scikit-learn->sklearn)\\n\",\n      \"Requirement already satisfied: numpy>=1.8.2 in /Users/lramsey/miniconda2/envs/clv/lib/python2.7/site-packages (from scikit-learn->sklearn)\\n\",\n      \"\\u001b[33mYou are using pip version 9.0.1, however version 19.0.1 is available.\\n\",\n      \"You should consider upgrading via the 'pip install --upgrade pip' command.\\u001b[0m\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"!pip install sklearn\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from sklearn.linear_model import LinearRegression\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"c_names =['customer_id', 'monetary_dnn', 'monetary_btyd', 'frequency_dnn',\\n\",\n    \"       'frequency_btyd', 'recency', 'T', 'time_between', 'avg_basket_value',\\n\",\n    \"       'avg_basket_size', 'cnt_returns', 'has_returned',\\n\",\n    \"       'frequency_btyd_clipped', 'monetary_btyd_clipped',\\n\",\n    \"       'target_monetary_clipped', 'target_monetary']\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"train_df = file_io.FileIO(\\n\",\n    \"            '../data/train.csv',\\n\",\n    \"            mode='r').read()\\n\",\n    \"train_df = pandas.read_csv(\\n\",\n    \"            StringIO(train_df),\\n\",\n    \"            header = None,\\n\",\n    \"            names = c_names,\\n\",\n    \"            delimiter=',',\\n\",\n    \"            na_filter=True)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"eval_df = file_io.FileIO(\\n\",\n    \"            '../data/eval.csv',\\n\",\n    \"            mode='r').read()\\n\",\n    \"eval_df = pandas.read_csv(\\n\",\n    \"            StringIO(eval_df),\\n\",\n    \"            header = None,\\n\",\n    \"            names = c_names,\\n\",\n    \"            delimiter=',',\\n\",\n    \"            na_filter=True)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 7,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"test_df = file_io.FileIO(\\n\",\n    \"            '../data/test.csv',\\n\",\n    \"            mode='r').read()\\n\",\n    \"test_df = pandas.read_csv(\\n\",\n    \"            StringIO(test_df),\\n\",\n    \"            header = None,\\n\",\n    \"            names = c_names,\\n\",\n    \"            delimiter=',',\\n\",\n    \"            na_filter=True)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 8,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"reg = LinearRegression().fit(\\n\",\n    \"    train_df.values[:, [1,3,5,6,7,8,9,10,11]],\\n\",\n    \"    train_df.values[:, -1])\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 9,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"1399.0002542993266\"\n      ]\n     },\n     \"execution_count\": 9,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"error = 0\\n\",\n    \"i = 0\\n\",\n    \"for p in reg.predict(eval_df.values[:, [1,3,5,6,7,8,9,10,11]]):\\n\",\n    \"    error = error + math.pow(p - eval_df.values[i, -1], 2)\\n\",\n    \"    i = i +1\\n\",\n    \"math.sqrt(error/eval_df.values.shape[0])\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 10,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"961.9763923040274\"\n      ]\n     },\n     \"execution_count\": 10,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"error = 0\\n\",\n    \"i = 0\\n\",\n    \"for p in reg.predict(test_df.values[:, [1,3,5,6,7,8,9,10,11]]):\\n\",\n    \"    error = error + math.pow(p - test_df.values[i, -1], 2)\\n\",\n    \"    i = i +1\\n\",\n    \"math.sqrt(error/test_df.values.shape[0])\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 2\",\n   \"language\": \"python\",\n   \"name\": \"python2\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 2\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython2\",\n   \"version\": \"2.7.13\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "preparation/sql/common/benchmark.sql",
    "content": "-- Copyright 2018 Google Inc. All Rights Reserved.\n--\n-- Licensed under the Apache License, Version 2.0 (the \"License\");\n-- you may not use this file except in compliance with the License.\n-- You may obtain a copy of the License at\n--\n--     http://www.apache.org/licenses/LICENSE-2.0\n--\n-- Unless required by applicable law or agreed to in writing, software\n-- distributed under the License is distributed on an \"AS IS\" BASIS,\n-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-- See the License for the specific language governing permissions and\n-- limitations under the License.\n\n--[START benchmark]\nSELECT\n  ROUND(SQRT( SUM(POW(predicted_monetary - target_monetary, 2)) / COUNT(1) ), 2) as rmse\nFROM (\n  SELECT\n    tf.customer_id,\n    avg_basket_value * ( cnt_orders * (1 + target_days/feature_days) ) AS predicted_monetary,\n    ROUND(tt.target_monetary, 2) AS target_monetary\n--[END benchmark]\n  FROM (\n      -- This SELECT takes records that are used for features later\n    SELECT\n      customer_id,\n      AVG(order_value) avg_basket_value,\n      COUNT(DISTINCT order_date) AS cnt_orders\n    FROM\n      `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_cleaned`\n    WHERE\n      order_date <= DATE('{{ dag_run.conf['threshold_date'] }}')\n    GROUP BY\n      customer_id) tf,\n    (\n      -- This SELECT takes records that are used for target later\n    SELECT\n      customer_id,\n      SUM(order_value) target_monetary\n    FROM\n      `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_cleaned`\n      --WHERE order_date > '2013-01-31'\n    GROUP BY\n      customer_id) tt,\n    (\n    SELECT\n      DATE_DIFF(DATE('{{ dag_run.conf['threshold_date'] }}'), MIN(order_date), DAY) feature_days,\n      DATE_DIFF(DATE('{{ dag_run.conf['predict_end'] }}'), DATE('{{ dag_run.conf['threshold_date'] }}'), DAY) target_days\n    FROM\n      `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_cleaned` ) AS days\n  WHERE\n    tf.customer_id = tt.customer_id )"
  },
  {
    "path": "preparation/sql/common/clean.sql",
    "content": "SELECT\n  customer_id,\n  order_date,\n  order_value,\n  order_qty_articles\nFROM\n(\n  SELECT\n    CustomerID AS customer_id,\n    PARSE_DATE(\"%m/%d/%y\", SUBSTR(InvoiceDate, 0, 8)) AS order_date,\n    ROUND(SUM(UnitPrice * Quantity), 2) AS order_value,\n    SUM(Quantity) AS order_qty_articles,\n    (\n      SELECT\n        MAX(PARSE_DATE(\"%m/%d/%y\", SUBSTR(InvoiceDate, 0, 8)))\n      FROM\n        `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_source` tl\n      WHERE\n        tl.CustomerID = t.CustomerID\n    ) latest_order\n  FROM\n    `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_source` t\n  GROUP BY\n      CustomerID,\n      order_date\n) a\n\nINNER JOIN (\n  -- Only customers with more than one positive order values before threshold.\n  SELECT\n    CustomerID\n  FROM (\n    -- Customers and how many positive order values  before threshold.\n    SELECT\n      CustomerID,\n      SUM(positive_value) cnt_positive_value\n    FROM (\n      -- Customer with whether order was positive or not at each date.\n      SELECT\n        CustomerID,\n        (\n          CASE\n            WHEN SUM(UnitPrice * Quantity) > 0 THEN 1\n            ELSE 0\n          END ) positive_value\n      FROM\n        `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_source`\n      WHERE\n        PARSE_DATE(\"%m/%d/%y\", SUBSTR(InvoiceDate, 0, 8)) < DATE(`{{ dag_run.conf['threshold_date'] }}`)\n      GROUP BY\n        CustomerID,\n        SUBSTR(InvoiceDate, 0, 8) )\n    GROUP BY\n      CustomerID )\n  WHERE\n    cnt_positive_value > 1\n  ) b\nON\n  a.customer_id = b. CustomerID\n--[START common_clean]\nWHERE\n  -- Bought in the past 3 months\n  DATE_DIFF(DATE(`{{ dag_run.conf['predict_end'] }}`), latest_order, DAY) <= 90\n  -- Make sure returns are consistent.\n  AND (\n    (order_qty_articles > 0 and order_Value > 0) OR\n    (order_qty_articles < 0 and order_Value < 0)\n  )\n--[END common_clean]"
  },
  {
    "path": "preparation/sql/common/features_n_target.sql",
    "content": "-- Copyright 2018 Google Inc. All Rights Reserved.\n--\n-- Licensed under the Apache License, Version 2.0 (the \"License\");\n-- you may not use this file except in compliance with the License.\n-- You may obtain a copy of the License at\n--\n--     http://www.apache.org/licenses/LICENSE-2.0\n--\n-- Unless required by applicable law or agreed to in writing, software\n-- distributed under the License is distributed on an \"AS IS\" BASIS,\n-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-- See the License for the specific language governing permissions and\n-- limitations under the License.\n\n-- Keep all records before a threshold date for Features\n-- And all records before a threshold date for Target\n-- Threshold taken at {{ dag_run.conf['threshold_date'] }} ex: 2013-01-31\n-- {{ dag_run.conf['threshold_date'] }} is understood by Airflow\nSELECT\n  tf.customer_id,\n  -- For training period\n  -- Copying the calculations from Lifetimes where first orders are ignored\n  -- See https://github.com/CamDavidsonPilon/lifetimes/blob/master/lifetimes/utils.py#L246\n--[START features_target]\n  tf.monetary_dnn,\n  tf.monetary_btyd,\n  tf.cnt_orders AS frequency_dnn,\n  tf.cnt_orders - 1 AS frequency_btyd,\n  tf.recency,\n  tf.T,\n  ROUND(tf.recency/cnt_orders, 2) AS time_between,\n  ROUND(tf.avg_basket_value, 2) AS avg_basket_value,\n  ROUND(tf.avg_basket_size, 2) AS avg_basket_size,\n  tf.cnt_returns,\n  (CASE\n      WHEN tf.cnt_returns > 0 THEN 1\n      ELSE 0 END) AS has_returned,\n\n  -- Used by BTYD mainly, potentially DNN if clipped improve results\n  (CASE\n      WHEN tf.cnt_orders - 1 > 600 THEN 600\n      ELSE tf.cnt_orders - 1 END) AS frequency_btyd_clipped,\n  (CASE\n      WHEN tf.monetary_btyd > 100000 THEN 100000\n      ELSE ROUND(tf.monetary_btyd, 2) END) AS monetary_btyd_clipped,\n  (CASE\n      WHEN tt.target_monetary > 100000 THEN 100000\n      ELSE ROUND(tt.target_monetary, 2) END) AS target_monetary_clipped,\n\n  -- Target calculated for overall period\n  ROUND(tt.target_monetary, 2) as target_monetary\n--[END features_target]\nFROM\n  -- This SELECT uses only data before threshold to make features.\n  (\n    SELECT\n      customer_id,\n      SUM(order_value) AS monetary_dnn,\n      (CASE\n        WHEN COUNT(DISTINCT order_date) = 1 THEN 0\n        ELSE SUM(order_value_btyd) / (COUNT(DISTINCT order_date) -1) END) AS monetary_btyd,\n      DATE_DIFF(MAX(order_date), MIN(order_date), DAY) AS recency,\n      DATE_DIFF(DATE('{{ dag_run.conf['threshold_date'] }}'), MIN(order_date), DAY) AS T,\n      COUNT(DISTINCT order_date) AS cnt_orders,\n      AVG(order_qty_articles) avg_basket_size,\n      AVG(order_value) avg_basket_value,\n      SUM(CASE\n          WHEN order_value < 1 THEN 1\n          ELSE 0 END) AS cnt_returns\n    FROM\n      -- Makes the order value = 0 if it is the first one\n      (\n        SELECT\n          a.*,\n          (CASE\n              WHEN a.order_date = c.order_date_min THEN 0\n              ELSE a.order_value END) AS order_value_btyd\n--[START airflow_params]\n        FROM\n          `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_cleaned` a\n--[END airflow_params]\n        INNER JOIN (\n          SELECT\n            customer_id,\n            MIN(order_date) AS order_date_min\n          FROM\n            `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_cleaned`\n          GROUP BY\n            customer_id) c\n        ON\n          c.customer_id = a.customer_id\n      )\n    WHERE\n--[START threshold_date]\n      order_date <= DATE('{{ dag_run.conf['threshold_date'] }}')\n--[END threshold_date]\n    GROUP BY\n      customer_id) tf,\n\n  -- This SELECT uses all records to calculate the target (could also use data after threshold )\n  (\n    SELECT\n      customer_id,\n      SUM(order_value) target_monetary\n    FROM\n      `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.data_cleaned`\n      --WHERE order_date > DATE('{{ dag_run.conf['threshold_date'] }}')\n    GROUP BY\n      customer_id) tt\nWHERE\n  tf.customer_id = tt.customer_id\n  AND tf.monetary_dnn > 0\n  AND tf.monetary_dnn <= {{ dag_run.conf['max_monetary'] }}\n  AND tf.monetary_btyd > 0"
  },
  {
    "path": "preparation/sql/dnn/split_eval.sql",
    "content": "-- Copyright 2018 Google Inc. All Rights Reserved.\n--\n-- Licensed under the Apache License, Version 2.0 (the \"License\");\n-- you may not use this file except in compliance with the License.\n-- You may obtain a copy of the License at\n--\n--     http://www.apache.org/licenses/LICENSE-2.0\n--\n-- Unless required by applicable law or agreed to in writing, software\n-- distributed under the License is distributed on an \"AS IS\" BASIS,\n-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-- See the License for the specific language governing permissions and\n-- limitations under the License.\n\nSELECT\n  *\nFROM\n  `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.features_n_target`\nWHERE\n  -- TRAIN\n  MOD(ABS(FARM_FINGERPRINT(CAST(customer_id AS STRING))), 100000) > 70000 AND\n  MOD(ABS(FARM_FINGERPRINT(CAST(customer_id AS STRING))), 100000) < 85000\n"
  },
  {
    "path": "preparation/sql/dnn/split_test.sql",
    "content": "-- Copyright 2018 Google Inc. All Rights Reserved.\n--\n-- Licensed under the Apache License, Version 2.0 (the \"License\");\n-- you may not use this file except in compliance with the License.\n-- You may obtain a copy of the License at\n--\n--     http://www.apache.org/licenses/LICENSE-2.0\n--\n-- Unless required by applicable law or agreed to in writing, software\n-- distributed under the License is distributed on an \"AS IS\" BASIS,\n-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-- See the License for the specific language governing permissions and\n-- limitations under the License.\n\nSELECT\n  *\nFROM\n  `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.features_n_target`\nWHERE\n  -- TRAIN\n  MOD(ABS(FARM_FINGERPRINT(CAST(customer_id AS STRING))), 100000) > 85000 AND\n  MOD(ABS(FARM_FINGERPRINT(CAST(customer_id AS STRING))), 100000) < 100000\n"
  },
  {
    "path": "preparation/sql/dnn/split_train.sql",
    "content": "-- Copyright 2018 Google Inc. All Rights Reserved.\n--\n-- Licensed under the Apache License, Version 2.0 (the \"License\");\n-- you may not use this file except in compliance with the License.\n-- You may obtain a copy of the License at\n--\n--     http://www.apache.org/licenses/LICENSE-2.0\n--\n-- Unless required by applicable law or agreed to in writing, software\n-- distributed under the License is distributed on an \"AS IS\" BASIS,\n-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-- See the License for the specific language governing permissions and\n-- limitations under the License.\n\n-- Save to table 'dnn_train'\n--[START split_train]\nSELECT\n  *\nFROM\n  `{{ dag_run.conf['project'] }}.{{ dag_run.conf['dataset'] }}.features_n_target`\nWHERE\n  -- TRAIN\n  MOD(ABS(FARM_FINGERPRINT(CAST(customer_id AS STRING))), 100000) <= 70000\n--[END split_train]"
  },
  {
    "path": "requirements.txt",
    "content": "google-cloud-automl>=0.1.2\ntensorflow==1.13.1\njupyter\napache-beam==2.2.0\nipdb\nlifetimes==0.9.0.0\ngoogle-api-python-client==1.7.3\ngoogle-cloud-bigquery==0.32.0\nseaborn==0.8.1\npandas-gbq==0.5.0\nmatplotlib==2.2.3"
  },
  {
    "path": "run/airflow/dags/01_build_train_deploy.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime, json, re, logging\nfrom airflow import models\nfrom airflow.operators.python_operator import PythonOperator, BranchPythonOperator\nfrom airflow.hooks.base_hook import BaseHook\nfrom airflow.contrib.operators import bigquery_operator\nfrom airflow.contrib.operators import bigquery_get_data\nfrom airflow.contrib.operators import gcs_to_bq\nfrom airflow.contrib.operators import bigquery_to_gcs\nfrom airflow.contrib.operators import mlengine_operator\nfrom airflow.contrib.operators import mlengine_operator_utils\nfrom airflow.contrib.hooks.gcp_mlengine_hook import MLEngineHook\nfrom airflow.contrib.hooks.gcs_hook import GoogleCloudStorageHook\nfrom airflow.operators import bash_operator\nfrom airflow.operators.dummy_operator import DummyOperator\nfrom airflow.utils import trigger_rule\n\nfrom google.cloud.automl_v1beta1 import AutoMlClient\nfrom clv_automl import clv_automl\n\ndef _get_project_id():\n  \"\"\"Get project ID from default GCP connection.\"\"\"\n\n  extras = BaseHook.get_connection('google_cloud_default').extra_dejson\n  key = 'extra__google_cloud_platform__project'\n  if key in extras:\n    project_id = extras[key]\n  else:\n    raise ('Must configure project_id in google_cloud_default '\n           'connection from Airflow Console')\n  return project_id\n\nPROJECT = _get_project_id()\nREGION = models.Variable.get('region')\nDATASET = models.Variable.get('dataset')\nCOMPOSER_BUCKET_NAME = models.Variable.get('composer_bucket_name')\nGCS_SQL = 'sql'\nDB_DUMP_FILENAME = 'db_dump.csv'\n\nLOCATION_TRAINING_DATA = '{}/data'.format(COMPOSER_BUCKET_NAME)\n\nPREFIX_JOBS_EXPORT = 'jobs/clv-composer'\nPREFIX_FINAL_MODEL = '{}/final'.format(PREFIX_JOBS_EXPORT)\n\nMODEL_PACKAGE_NAME = 'clv_ml_engine-0.1.tar.gz' # Matches name in setup.py\n\nAUTOML_DATASET = models.Variable.get('automl_dataset')\nAUTOML_MODEL = models.Variable.get('automl_model')\nAUTOML_TRAINING_BUDGET = int(models.Variable.get('automl_training_budget'))\n\n\n#[START dag_build_train_deploy]\ndefault_dag_args = {\n    'start_date': datetime.datetime(2050, 1, 1),\n    'schedule_interval': None,\n    'provide_context': True\n}\n\ndag = models.DAG(\n    'build_train_deploy',\n    default_args = default_dag_args)\n#[END dag_build_train_deploy]\n\n\n# Loads the database dump from Cloud Storage to BigQuery\nt1 = gcs_to_bq.GoogleCloudStorageToBigQueryOperator(\n    task_id=\"db_dump_to_bigquery\",\n    bucket=COMPOSER_BUCKET_NAME,\n    source_objects=[DB_DUMP_FILENAME],\n    schema_object=\"schema_source.json\",\n    source_format=\"CSV\",\n    skip_leading_rows=1,\n    destination_project_dataset_table=\"{}.{}.{}\".format(PROJECT,\n                                                        DATASET,\n                                                        'data_source'),\n    create_disposition=\"CREATE_IF_NEEDED\",\n    write_disposition=\"WRITE_TRUNCATE\",\n    dag=dag\n)\n\n# Clean the data from BigQuery to BigQuery\nt2 = bigquery_operator.BigQueryOperator(\n    task_id='bq_from_source_to_clean',\n    bql='{}/common/clean.sql'.format(GCS_SQL),\n    use_legacy_sql=False,\n    allow_large_results=True,\n    destination_dataset_table=\"{}.{}.{}\".format(PROJECT,\n                                                DATASET,\n                                                'data_cleaned'),\n    create_disposition=\"CREATE_IF_NEEDED\",\n    write_disposition=\"WRITE_TRUNCATE\",\n    dag=dag\n)\n\n# Creates split between features and targets and also aggregates both sides.\n# The threshold date is passed as an arg when calling the Airflow job and\n# dynamically understood within the .sql file.\n# We should pass query_params but we run into various problems:\n# - if using BigQueryOperator, we can not pass dag_run.conf['threshold_date']\n# - if using hooks, run_query does not accept a .sql file and needs the string\n# So the way is to add directly {{ dag_run.conf['threshold_date'] }} into the\n# .sql file which Airflow can ping up when running the operator.\nt3 = bigquery_operator.BigQueryOperator(\n    task_id='bq_from_clean_to_features',\n    bql='{}/common/features_n_target.sql'.format(GCS_SQL),\n    use_legacy_sql=False,\n    allow_large_results=True,\n    destination_dataset_table=\"{}.{}.{}\".format(PROJECT,\n                                                DATASET,\n                                                'features_n_target'),\n    create_disposition=\"CREATE_IF_NEEDED\",\n    write_disposition=\"WRITE_TRUNCATE\",\n    dag=dag\n)\n\n\ndef get_model_type(**kwargs):\n  model_type = kwargs['dag_run'].conf.get('model_type')\n  if model_type == 'automl':\n    model_train_task = 'train_automl'\n  else:\n    model_train_task = 'train_ml_engine'\n  return model_train_task\n\nt4_train_cond = BranchPythonOperator(task_id='train_branch', dag=dag, python_callable=get_model_type)\n\n#\n# Train the model using AutoML\n#\ndef do_train_automl(**kwargs):\n    \"\"\"\n    Create, train and deploy automl model.\n    \"\"\"\n    # instantiate automl client\n    automl_client = AutoMlClient()\n\n    model_name = clv_automl.create_automl_model(automl_client,\n                                                PROJECT,\n                                                REGION,\n                                                DATASET,\n                                                'features_n_target',\n                                                AUTOML_DATASET,\n                                                AUTOML_MODEL,\n                                                AUTOML_TRAINING_BUDGET)\n    clv_automl.deploy_model(automl_client, model_name)\n\nt4_automl = PythonOperator(\n    task_id='train_automl', dag=dag, python_callable=do_train_automl)\n\n\nt4_ml_engine = DummyOperator(task_id='train_ml_engine', dag=dag)\n\n# Split the data into a training set and evaluation set within BigQuery\nt4a = bigquery_operator.BigQueryOperator(\n    task_id='bq_dnn_train',\n    bql='{}/dnn/split_train.sql'.format(GCS_SQL),\n    use_legacy_sql=False,\n    allow_large_results=True,\n    destination_dataset_table=\"{}.{}.{}\".format(PROJECT,\n                                                DATASET,\n                                                'dnn_train'),\n    create_disposition=\"CREATE_IF_NEEDED\",\n    write_disposition=\"WRITE_TRUNCATE\",\n    dag=dag\n)\n\nt4b = bigquery_operator.BigQueryOperator(\n    task_id='bq_dnn_eval',\n    bql='{}/dnn/split_eval.sql'.format(GCS_SQL),\n    use_legacy_sql=False,\n    allow_large_results=True,\n    destination_dataset_table=\"{}.{}.{}\".format(PROJECT,\n                                                DATASET,\n                                                'dnn_eval'),\n    create_disposition=\"CREATE_IF_NEEDED\",\n    write_disposition=\"WRITE_TRUNCATE\",\n    dag=dag\n)\n\nt4c = bigquery_operator.BigQueryOperator(\n    task_id='bq_dnn_test',\n    bql='{}/dnn/split_test.sql'.format(GCS_SQL),\n    use_legacy_sql=False,\n    allow_large_results=True,\n    destination_dataset_table=\"{}.{}.{}\".format(PROJECT,\n                                                DATASET,\n                                                'dnn_test'),\n    create_disposition=\"CREATE_IF_NEEDED\",\n    write_disposition=\"WRITE_TRUNCATE\",\n    dag=dag\n)\n\n# TODO: Currently all data steps are done whether BTYD or DNN are used. It would\n# be better to have a condition to call only one task or the other using 'model_type'\ndata_btyd_location = ['gs://{}/{}'.format(LOCATION_TRAINING_DATA, 'btyd.csv')]\ndata_train_locations = ['gs://{}/{}'.format(LOCATION_TRAINING_DATA, 'train.csv')]\ndata_eval_locations = ['gs://{}/{}'.format(LOCATION_TRAINING_DATA, 'eval.csv')]\ndata_test_locations = ['gs://{}/{}'.format(LOCATION_TRAINING_DATA, 'test.csv')]\n\nt5a = bigquery_to_gcs.BigQueryToCloudStorageOperator(\n    task_id='bq_dnn_train_to_gcs',\n    source_project_dataset_table=\"{}.{}.{}\".format(PROJECT, DATASET, 'dnn_train'),\n    destination_cloud_storage_uris=data_train_locations,\n    print_header=False,\n    dag=dag\n)\n\nt5b = bigquery_to_gcs.BigQueryToCloudStorageOperator(\n    task_id='bq_dnn_eval_to_gcs',\n    source_project_dataset_table=\"{}.{}.{}\".format(PROJECT, DATASET, 'dnn_eval'),\n    destination_cloud_storage_uris=data_eval_locations,\n    print_header=False,\n    dag=dag\n)\n\nt5c = bigquery_to_gcs.BigQueryToCloudStorageOperator(\n    task_id='bq_dnn_test_to_gcs',\n    source_project_dataset_table=\"{}.{}.{}\".format(PROJECT, DATASET, 'dnn_test'),\n    destination_cloud_storage_uris=data_test_locations,\n    print_header=False,\n    dag=dag\n)\n\nt5d = bigquery_to_gcs.BigQueryToCloudStorageOperator(\n    task_id='bq_btyd_to_gcs',\n    source_project_dataset_table=\"{}.{}.{}\".format(PROJECT, DATASET, 'features_n_target'),\n    destination_cloud_storage_uris=data_btyd_location,\n    print_header=True,\n    dag=dag\n)\n\n\n#\n# Train the model using ML Engine (TensorFlow DNN or Lifetimes BTYD)\n#\ndef do_train_ml_engine(**kwargs):\n    \"\"\"\n    \"\"\"\n    job_id = 'clv-{}'.format(datetime.datetime.now().strftime('%Y%m%d%H%M'))\n\n    mlengine_operator.MLEngineTrainingOperator(\n        task_id='train_ml_engine_job',\n        project_id=PROJECT,\n        job_id=job_id,\n        package_uris=['gs://{}/code/{}'.format(COMPOSER_BUCKET_NAME, MODEL_PACKAGE_NAME)],\n        training_python_module='trainer.task',\n        region=REGION,\n        training_args=['--job-dir', 'gs://{}/{}/{}'.format(COMPOSER_BUCKET_NAME, PREFIX_JOBS_EXPORT, job_id),\n                       '--data-src', 'gs://{}'.format(LOCATION_TRAINING_DATA),\n                       '--model_type', kwargs['dag_run'].conf.get('model_type')],\n        dag=dag\n    ).execute(kwargs)\n\nt6 = PythonOperator(\n    task_id='train_ml_engine_task', dag=dag, python_callable=do_train_ml_engine)\n\n#\n# Copies the latest model to a consistent 'final' bucket\n#\ndef do_copy_model_to_final(**kwargs):\n    gcs = GoogleCloudStorageHook()\n\n    # Returns all the objects within the bucket. All sub-buckets are considered\n    # as prefix of the leaves. List does not differentiate files from subbuckets\n    all_jobs_files = gcs.list(\n        bucket=COMPOSER_BUCKET_NAME,\n        prefix='{}/export/estimate'.format(PREFIX_JOBS_EXPORT)\n    )\n\n    # Extract the latest model bucket parent of variables/ and saved_model.pbtxt\n    # The max() string contains the latest model folders in 1234567, we need to\n    # extract that using regex\n    # ex: jobs/clv-composer/export/estimate/1234567890/variables/variables.index\n    # returns /1234567890/\n    latest_model_bucket = re.findall(r'/\\d+/', max(all_jobs_files))[0]\n\n    # List all the files that needs to be copied (only files in the latest bucket\n    # and skip the ones that are not files but sub buckets)\n    for c in [f for f in all_jobs_files\n              if latest_model_bucket in f and f[-1] != '/']:\n\n        # The model used for training is saved into a 'final' sub bucket of the\n        # export bucket.\n        dest_object = c.split(latest_model_bucket)[1]\n        dest_object = '{}/{}'.format(PREFIX_FINAL_MODEL, dest_object)\n\n        logging.info(\"Copying {} to {} ...\".format(dest_object, COMPOSER_BUCKET_NAME))\n\n        gcs.copy(\n            source_bucket=COMPOSER_BUCKET_NAME,\n            source_object=c,\n            destination_object=dest_object\n        )\n\n# Note that this could be done as well in Tensorflow using tf.gFile aftet the\n# model is created but for reasons of flexibility, it was decided to do this in the\n# wider workflow. This way, it is also possible pick other models.\nt7 = PythonOperator(\n    task_id='copy_model_to_final',\n    python_callable=do_copy_model_to_final,\n    dag=dag)\n\n#\n# Model Creation\n#\n\ndef do_check_model(**kwargs):\n    \"\"\" Check if a model with the name exists using Hooks instead of operators.\n    Uses xcom_push to pass it to the next step. Could use return too if no key.\n    \"\"\"\n    # pushes an XCom without a specific target, just by returning it\n    mle = MLEngineHook()\n    model_name = kwargs['dag_run'].conf.get('model_name')\n    # return bool(mle.get_model(PROJECT, MODEL_DNN_NAME))\n    project = mle.get_model(PROJECT, model_name)\n    kwargs['ti'].xcom_push(key='is_project', value=bool(project))\n\n\ndef do_create_model(**kwargs):\n    \"\"\" Creates a model only if one with the same name did not exist.\n    It leverages the check from the previous task pushed using xcom.\n    \"\"\"\n    model_params = {\n      'name': kwargs['dag_run'].conf.get('model_name'),\n      'description': 'A custom DNN regressor model',\n      'regions': [REGION]\n    }\n\n    ti = kwargs['ti']\n\n    is_model = ti.xcom_pull(key='is_project', task_ids='check_model')\n    if not is_model:\n        mle = MLEngineHook()\n        mle.create_model(PROJECT, model_params)\n\n# Checks if model exists using Hook instead of GCP operators due to conditional.\nt8 = PythonOperator(\n    task_id='check_model', dag=dag, python_callable=do_check_model)\n\n# Creates model if it does not exist using Hook instead of GCP operators\nt9 = PythonOperator(\n    task_id='create_model', dag=dag, python_callable=do_create_model)\n\n#\n# Version Creation\n#\n\ndef do_list_versions(**kwargs):\n    \"\"\" Check if a version with the name exists using Hooks instead of operators.\n    Uses xcom_push to pass it to the next step. Could use return too if no key.\n    \"\"\"\n    mle = MLEngineHook()\n    model_name = kwargs['dag_run'].conf.get('model_name')\n    model_versions = mle.list_versions(PROJECT, model_name)\n    kwargs['ti'].xcom_push(key='model_versions', value=model_versions)\n\n\ndef do_create_version(**kwargs):\n    \"\"\" Creates a new version or overwrite if existing one. It leverages the\n    check from the previous task pushed using xcom.\n    \"\"\"\n    version_params = {\n      \"name\": kwargs['dag_run'].conf.get('model_version'),\n      \"description\": 'Version 1',\n      \"runtimeVersion\": kwargs['dag_run'].conf.get('tf_version'),\n      \"deploymentUri\": 'gs://{}/{}'.format(COMPOSER_BUCKET_NAME, PREFIX_FINAL_MODEL)\n    }\n\n    ti = kwargs['ti']\n\n    mle = MLEngineHook()\n\n    model_name = kwargs['dag_run'].conf.get('model_name')\n    model_versions = ti.xcom_pull(key='model_versions', task_ids='list_versions')\n\n    version_path = 'projects/{}/models/{}/versions/{}'.format(PROJECT,\n                                                              model_name,\n                                                              version_params['name'])\n\n    if version_path in [v['name'] for v in model_versions]:\n        logging.info(\"Delete previously version of the model to overwrite.\")\n        mle.delete_version(PROJECT, model_name, version_params['name'])\n\n    mle.create_version(PROJECT, model_name, version_params)\n\n# Checks if model exists using Hook instead of GCP operators due to conditional.\nt10 = PythonOperator(\n    task_id='list_versions', dag=dag, python_callable=do_list_versions)\n\n# Creates model if it does not exist using Hook instead of GCP operators\nt11 = PythonOperator(\n    task_id='create_version', dag=dag, python_callable=do_create_version)\n\n# Create task graph\nt1.set_downstream(t2)\nt2.set_downstream(t3)\nt3.set_downstream(t4_train_cond)\nt4_train_cond.set_downstream([t4_ml_engine, t4_automl])\nt4_ml_engine.set_downstream([t4a, t4b, t4c])\nt4_ml_engine.set_downstream(t5d)\nt4a.set_downstream(t5a)\nt4b.set_downstream(t5b)\nt4c.set_downstream(t5c)\nt6.set_upstream([t5a, t5b, t5c, t5d])\nt6.set_downstream(t7)\nt7.set_downstream(t8)\nt9.set_upstream(t8)\nt9.set_downstream(t10)\nt10.set_downstream(t11)\n"
  },
  {
    "path": "run/airflow/dags/02_predict_serve.py",
    "content": "# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#            http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime, json, logging\nfrom airflow import models\nfrom airflow.operators.python_operator import PythonOperator, BranchPythonOperator\nfrom airflow.hooks.base_hook import BaseHook\nfrom airflow.contrib.operators import mlengine_operator\nfrom airflow.contrib.operators import mlengine_operator_utils\nfrom airflow.contrib.operators import dataflow_operator\nfrom airflow.contrib.operators import gcs_to_bq\n# TODO Add when Composer on v2.0 and more Hook\n# from airflow.contrib.operators import gcs_list_operator\nfrom airflow.contrib.hooks.gcs_hook import GoogleCloudStorageHook\nfrom airflow.utils import trigger_rule\n\nfrom google.cloud.automl_v1beta1 import AutoMlClient, PredictionServiceClient\nfrom clv_automl import clv_automl\n\n\ndef _get_project_id():\n  \"\"\"Get project ID from default GCP connection.\"\"\"\n\n  extras = BaseHook.get_connection('google_cloud_default').extra_dejson\n  key = 'extra__google_cloud_platform__project'\n  if key in extras:\n    project_id = extras[key]\n  else:\n    raise ('Must configure project_id in google_cloud_default '\n           'connection from Airflow Console')\n  return project_id\n\nPROJECT = _get_project_id()\nREGION = models.Variable.get('region')\nDF_ZONE = models.Variable.get('df_zone')\nDF_TEMP = models.Variable.get('df_temp_location')\nCOMPOSER_BUCKET_NAME = models.Variable.get('composer_bucket_name')\n\n#[START dag_predict_serve]\ndefault_dag_args = {\n    'start_date': datetime.datetime(2050, 1, 1),\n    'schedule_interval': None,\n    'provide_context': True,\n    'dataflow_default_options': {\n        'project': PROJECT,\n        'zone': DF_ZONE,\n        'tempLocation': DF_TEMP\n    }\n}\n\ndag = models.DAG(\n    'predict_serve',\n    default_args = default_dag_args)\n#[END dag_predict_serve]\n\n#\n# Runs prediction.\n#\n\ndef get_model_type(**kwargs):\n  model_type = kwargs['dag_run'].conf.get('model_type')\n  if model_type == 'automl':\n    model_train_task = 'predict_automl'\n  else:\n    model_train_task = 'predict_ml_engine'\n  return model_train_task\n\nt0_predict_cond = BranchPythonOperator(task_id='predict_branch', dag=dag, python_callable=get_model_type)\n\n\ndef do_predict_mle(**kwargs):\n    \"\"\" Runs a batch prediction on new data and saving the results as CSV into\n    output_path.\n    \"\"\"\n    job_id = 'clv-{}'.format(datetime.datetime.now().strftime('%Y%m%d%H%M'))\n    gcs_prediction_input = 'gs://{}/predictions/to_predict.csv'.format(COMPOSER_BUCKET_NAME)\n    gcs_prediction_output = 'gs://{}/predictions/output'.format(COMPOSER_BUCKET_NAME)\n    model_name = kwargs['dag_run'].conf.get('model_name')\n    model_version = kwargs['dag_run'].conf.get('model_version')\n\n    logging.info(\"Running prediction using {}:{}...\".format(model_name,\n                                                            model_version))\n\n    mlengine_operator.MLEngineBatchPredictionOperator(\n        task_id='predict_dnn',\n        project_id=PROJECT,\n        job_id=job_id,\n        region=REGION,\n        data_format='TEXT',\n        input_paths=gcs_prediction_input,\n        output_path=gcs_prediction_output,\n        model_name=model_name,\n        version_name=model_version,\n        #uri=gs://WHERE_MODEL_IS_IF_NOT_ML_ENGINE\n        #runtime_version=TF_VERSION,\n        dag=dag\n    ).execute(kwargs)\n\n\ndef do_predict_automl(**kwargs):\n  # get automl clients\n  automl_client = AutoMlClient()\n  automl_predict_client = PredictionServiceClient()\n\n  # get model resource name\n  automl_model = models.Variable.get('automl_model')\n  location_path = automl_client.location_path(PROJECT, REGION)\n  model_list_response = automl_client.list_models(location_path)\n  model_list = [m for m in model_list_response]\n  model = [m for m in model_list if m.display_name == automl_model][0]\n\n  # run batch prediction\n  gcs_prediction_input = 'gs://{}/predictions/to_predict.csv'.format(COMPOSER_BUCKET_NAME)\n  gcs_prediction_output = 'gs://{}/predictions/output'.format(COMPOSER_BUCKET_NAME)\n  clv_automl.do_batch_prediction(automl_predict_client,\n                                 model.name,\n                                 gcs_prediction_input,\n                                 gcs_prediction_output)\n\nt1a = PythonOperator(\n          task_id='predict_ml_engine', dag=dag, python_callable=do_predict_mle)\n\nt1b = PythonOperator(\n          task_id='predict_automl', dag=dag, python_callable=do_predict_automl)\n\n\n#\n# Load the predictions from GCS to Datastore.\n#\n\ndef do_load_to_datastore(**kwargs):\n    \"\"\" Saves the predictions results into Datastore. Because there is no way to\n    directly load a CSV to Datastore, we use Apache Beam on Dataflow with\n    templates gs://dataflow-templates/latest/GCS_Text_to_Datastore.\n    https://cloud.google.com/dataflow/docs/templates/provided-templates#gcstexttodatastore\n    \"\"\"\n    gcs_prediction_output = 'gs://{}/predictions/output'.format(COMPOSER_BUCKET_NAME)\n    template = 'gs://dataflow-templates/latest/GCS_Text_to_Datastore'\n\n    df_template_params = {\n        'textReadPattern': '{}/prediction.results*'.format(gcs_prediction_output),\n        'javascriptTextTransformGcsPath': 'gs://{}/gcs_datastore_transform.js'.format(COMPOSER_BUCKET_NAME),\n        'javascriptTextTransformFunctionName': 'from_prediction_output_to_datastore_object',\n        'datastoreWriteProjectId': PROJECT,\n        'errorWritePath': 'gs://{}/errors/serving_load'.format(COMPOSER_BUCKET_NAME)\n    }\n\n    dataflow_operator.DataflowTemplateOperator(\n        task_id='gcs_predictions_df_transform',\n        project_id=PROJECT,\n        template=template,\n        parameters=df_template_params,\n        dag=dag\n    ).execute(kwargs)\n\nt2 = PythonOperator(\n    task_id='load_to_datastore', dag=dag, python_callable=do_load_to_datastore)\n\n#\n# Loads the database dump from Cloud Storage to BigQuery\n#\n\ndef do_list_predictions_files(**kwargs):\n    \"\"\" Retrieves all the predictions files that should be loaded to BigQuery.\n    Can not do a GoogleCloudStorageToBigQueryOperator directly due to the possible\n    multiple files.\n    \"\"\"\n    # List all relevant files\n    # TODO Add when Composer is on Airflow 2.0\n    # predictions_files = gcs_list_operator.GoogleCloudStorageListOperator(\n    #     task_id='predictions_files',\n    #     bucket=COMPOSER_BUCKET_NAME,\n    #     prefix='predictions/output/prediction.results-'\n    # )\n    # TODO Remove when Composer on Airflow 2.0\n    gcs = GoogleCloudStorageHook()\n    predictions_files = gcs.list(\n        bucket=COMPOSER_BUCKET_NAME,\n        prefix='predictions/output/prediction.results-'\n    )\n\n    logging.info(\"Predictions files are: {}\".format(predictions_files))\n\n    # Create a variable that can be used in the next task\n    kwargs['ti'].xcom_push(key='predictions_files', value=predictions_files)\n\n\ndef do_load_to_bq(**kwargs):\n    \"\"\" Loads the prediction files to BigQuery using the list output from\n    do_list_predictions_files.\n    \"\"\"\n    dataset = kwargs['dag_run'].conf.get('dataset')\n\n    # Reads files from the variables saved in the previous task\n    ti = kwargs['ti']\n    predictions_files = ti.xcom_pull(key='predictions_files',\n                                     task_ids='list_predictions_files')\n\n    gcs_to_bq.GoogleCloudStorageToBigQueryOperator(\n        task_id=\"load_gcs_predictions_to_bigquery\",\n        bucket=COMPOSER_BUCKET_NAME,\n        source_objects=predictions_files,\n        schema_fields=[{\n            'name':'customer_id',\n            'type':'STRING'\n        },{\n            'name':'predicted_monetary',\n            'type':'FLOAT'\n        },{\n            'name':'predictions',\n            'type':'FLOAT'\n        }],\n        source_format=\"NEWLINE_DELIMITED_JSON\",\n        skip_leading_rows=1,\n        destination_project_dataset_table=\"{}.{}.{}\".format(PROJECT,\n                                                            dataset,\n                                                            'predictions'),\n        create_disposition=\"CREATE_IF_NEEDED\",\n        write_disposition=\"WRITE_TRUNCATE\",\n        dag=dag\n    ).execute(kwargs)\n\nt3 = PythonOperator(\n    task_id='list_predictions_files', dag=dag, python_callable=do_list_predictions_files)\n\nt4 = PythonOperator(\n    task_id='load_to_bq', dag=dag, python_callable=do_load_to_bq)\n\n# How to link them\nt0_predict_cond.set_downstream([t1a, t1b])\nt2.set_upstream([t1a, t1b])\nt3.set_upstream([t1a, t1b])\nt3.set_downstream(t4)\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "run/airflow/gcs_datastore_transform.js",
    "content": "// Copyright 2018 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//            http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// To be copied to GCS so it can be uses with Dataflow template\nfunction from_prediction_output_to_datastore_object(prediction_row, entity){\n  /*\n   * prediction_row looks like what is given by the export function in model.py\n   * {\"properties\": 427.7606201171875, \"key\": [\"'abc'\"]}\n   * entity should match https://cloud.google.com/datastore/docs/reference/data/rest/v1/Entity\n   */\n\n//[START row_to_ds]\n  var prediction_object = JSON.parse(prediction_row);\n\n  to_write = {\n    key: {\n      path: [{\n        //id: prediction_object.key,\n        kind: 'clv',\n        name: prediction_object.customer_id\n      }]\n    },\n    properties: {\n      predicted_monetary: {doubleValue: prediction_object.predicted_monetary}\n    }\n  };\n\n  return JSON.stringify(to_write);\n//[END row_to_ds]\n}"
  },
  {
    "path": "run/airflow/requirements.txt",
    "content": "google-cloud-automl>=0.2.0\n"
  },
  {
    "path": "run/airflow/schema_source.json",
    "content": "[\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"InvoiceNo\",\n    \"type\": \"STRING\"\n  },\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"StockCode\",\n    \"type\": \"STRING\"\n  },\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"Description\",\n    \"type\": \"STRING\"\n  },\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"Quantity\",\n    \"type\": \"INTEGER\"\n  },\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"InvoiceDate\",\n    \"type\": \"STRING\"\n  },\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"UnitPrice\",\n    \"type\": \"FLOAT\"\n  },\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"CustomerID\",\n    \"type\": \"STRING\"\n  },\n  {\n    \"mode\": \"NULLABLE\",\n    \"name\": \"Country\",\n    \"type\": \"STRING\"\n  }\n]"
  },
  {
    "path": "run/mltrain.sh",
    "content": "#!/bin/bash\n\n# Copyright 2018 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nusage () {\n  echo \"usage: run/mltrain.sh [local | train | tune] [gs://]data_folder_or_bucket [args]\n\nUse 'local' to train locally with a local data folder, and 'train' and 'tune' to\nrun on ML Engine.\n\nFor ML Engine jobs you must supply a bucket on GCS.  The job data\nfolder will be gs://bucket/data and the job directory will be gs://bucket/jobs.\nSo your data files must already be in gs://bucket/data.  For DNN models the\ndata should be named 'train.csv', 'eval.csv' and 'test.csv, for probablistic\nmodels the file must be 'btyd.csv'.\n\nFor probabilistic models, specify '--model_type paretonbd_model' and include\n--threshold_date and --predict_end args.\n\nExamples:\n\n   # train locally\n   run/mltrain.sh local data\n\n   # train on ML Engine\n   run/mltrain.sh train gs://your_bucket\n\n   # tune hyperparams on ML Engine:\n   run/mltrain.sh tune gs://your_bucket\n\n   # train using btyd models\n   run/mltrain.sh local data --model_type paretonbd_model --threshold_date 2011-08-08 --predict_end 2011-12-12\n\"\n\n}\n\ndate\n\nTIME=`date +\"%Y%m%d_%H%M%S\"`\n\n\nif [[ $# == 0 || $# == 1 ]]; then\n  usage\n  exit 1\nfi\n\n# set job vars\nTRAIN_JOB=\"$1\"\nDATA_DIR=\"$2\"\nBUCKET=\"$2\"\nJOB_NAME=clv_${TRAIN_JOB}_${TIME}\nREGION=us-central1\n\n# queue additional args\nshift; shift\n\nif [[ ${TRAIN_JOB} == \"local\" ]]; then\n\n  ARGS=\"--data-src ${DATA_DIR} --verbose-logging $@\"\n\n  mkdir -p jobs/${JOB_NAME}\n\n  gcloud ml-engine local train \\\n    --job-dir jobs/${JOB_NAME} \\\n    --module-name clv_mle.trainer.task \\\n    --package-path trainer \\\n    -- \\\n    ${ARGS}\n\nelif [[ ${TRAIN_JOB} == \"train\" ]]; then\n\n  ARGS=\"--data-src ${BUCKET}/data --verbose-logging $@\"\n\n  gcloud beta ml-engine jobs submit training ${JOB_NAME} \\\n    --job-dir ${BUCKET}/jobs/${JOB_NAME} \\\n    --region $REGION \\\n    --scale-tier=CUSTOM \\\n    --module-name trainer.task \\\n    --package-path clv_mle/trainer \\\n    --config clv_mle/config.yaml \\\n    --runtime-version 1.10 \\\n    -- \\\n    ${ARGS}\n\nelif [[ $TRAIN_JOB == \"tune\" ]]; then\n\n  ARGS=\"--data-src ${BUCKET}/data --verbose-logging $@\"\n\n  # set configuration for tuning\n  CONFIG_TUNE=\"clv_mle/config_tune.json\"\n\n  gcloud beta ml-engine jobs submit training ${JOB_NAME} \\\n    --job-dir ${BUCKET}/jobs/${JOB_NAME} \\\n    --region ${REGION} \\\n    --scale-tier=CUSTOM \\\n    --module-name trainer.task \\\n    --package-path clv_mle/trainer \\\n    --config ${CONFIG_TUNE} \\\n    --runtime-version 1.10 \\\n    -- \\\n    --hypertune \\\n    ${ARGS}\n\nelse\n  usage\nfi\n\ndate\n"
  }
]