customeranalytics 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- customeranalytics/__init__.py +319 -0
- customeranalytics/configs.py +480 -0
- customeranalytics/data_storage_configurations/__init__.py +245 -0
- customeranalytics/data_storage_configurations/data_access.py +165 -0
- customeranalytics/data_storage_configurations/data_works_pipeline.py +207 -0
- customeranalytics/data_storage_configurations/es_create_index.py +544 -0
- customeranalytics/data_storage_configurations/logger.py +36 -0
- customeranalytics/data_storage_configurations/query_es.py +157 -0
- customeranalytics/data_storage_configurations/reports.py +684 -0
- customeranalytics/data_storage_configurations/schedule_data_integration.py +355 -0
- customeranalytics/docs/queries.yaml +245 -0
- customeranalytics/docs/web_configs.yaml +2 -0
- customeranalytics/exploratory_analysis/__init__.py +109 -0
- customeranalytics/exploratory_analysis/churn.py +232 -0
- customeranalytics/exploratory_analysis/cohorts.py +535 -0
- customeranalytics/exploratory_analysis/descriptive_statistics.py +470 -0
- customeranalytics/exploratory_analysis/funnels.py +407 -0
- customeranalytics/exploratory_analysis/product_analytics.py +340 -0
- customeranalytics/exploratory_analysis/promotion_analytics.py +299 -0
- customeranalytics/exploratory_analysis/rfm.py +202 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_avg_order_count_per_promo_per_cust.csv +99 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_chart_1_search.csv +2 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_chart_2_search.csv +46 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_chart_3_search.csv +2 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_chart_4_search.csv +2 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_churn.csv +2 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_churn_weekly.csv +10 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_client_feature_predicted.csv +1590 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_client_kpis.csv +7060 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_clvrfm_anomaly.csv +561 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_clvsegments_amount.csv +8 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_customer_journey.csv +18 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_clv.csv +103 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_downloads.csv +68 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_1_to_2.csv +66 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_2_to_3.csv +64 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_3_to_4.csv +55 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_dimension_values.csv +199 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_funnel.csv +36 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_funnel_downloads.csv +68 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_inorganic_ratio.csv +6070 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_orders.csv +57 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_organic_orders.csv +64 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_products_of_sales.csv +12574 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_promotion_discount.csv +64 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_daily_promotion_revenue.csv +6070 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_dcohort_anomaly.csv +6 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_dcohort_anomaly_2.csv +118 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_deliver.csv +47 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_deliver_kpis.csv +2 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_deliver_weekday_hour.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_dfunnel_anomaly.csv +118 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_dimension_kpis.csv +199 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_dorders_anomaly.csv +85 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_frequency_clusters.csv +6 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_frequency_recency.csv +3740 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_funnel.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_funnel_downloads.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_inorganic_ratio.csv +2353 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_orders.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_organic_orders.csv +2353 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_inorganic_orders_per_promotion_per_day.csv +6070 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_kpis.csv +2 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_monetary_clusters.csv +6 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_monetary_frequency.csv +3740 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_funnel.csv +5 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_funnel_downloads.csv +5 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_orders.csv +4 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_most_combined_products.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_most_ordered_categories.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_most_ordered_products.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_order_and_payment_amount_differences.csv +91 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_prepare.csv +101 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_prepare_weekday_hour.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_product_kpis.csv +201 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_amount_accept.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_amount_reject.csv +7 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_orders_accept.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_orders_reject.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_comparison.csv +91 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_kpis.csv +99 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_number_of_customer.csv +99 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_amount_accept.csv +81 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_amount_reject.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_orders_accept.csv +55 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_orders_reject.csv +37 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_purchase_amount_distribution.csv +22 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_recency_clusters.csv +6 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_recency_monetary.csv +3740 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_rfm.csv +2001 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_ride.csv +101 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_ride_weekday_hour.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_segmentation.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_daily_before_after_amount.csv +10 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_daily_before_after_orders.csv +9 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_monthly_before_after_amount.csv +10 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_monthly_before_after_orders.csv +11 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_weekly_before_after_amount.csv +10 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_weekly_before_after_orders.csv +10 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_user_counts_per_order_seq.csv +47 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_order_per_user.csv +13 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_payment_amount.csv +13 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_session_per_user.csv +13 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_downloads.csv +13 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_1_to_2.csv +13 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_2_to_3.csv +13 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_3_to_4.csv +12 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_funnel.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_funnel_downloads.csv +25 -0
- customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_orders.csv +10 -0
- customeranalytics/ml_process/__init__.py +64 -0
- customeranalytics/ml_process/ab_test.py +615 -0
- customeranalytics/ml_process/anomaly_detection.py +456 -0
- customeranalytics/ml_process/clv_prediction.py +301 -0
- customeranalytics/ml_process/customer_segmentation.py +354 -0
- customeranalytics/ml_process/delivery_analytics.py +754 -0
- customeranalytics/utils.py +186 -0
- customeranalytics/web/app/__init__.py +37 -0
- customeranalytics/web/app/base/__init__.py +9 -0
- customeranalytics/web/app/base/forms.py +16 -0
- customeranalytics/web/app/base/models.py +43 -0
- customeranalytics/web/app/base/routes.py +126 -0
- customeranalytics/web/app/base/static/assets/css/app.css +20 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Black.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Black.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-BlackItalic.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-BlackItalic.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Bold.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Bold.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-BoldItalic.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-BoldItalic.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBold.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBold.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBoldItalic.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBoldItalic.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLight-BETA.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLight-BETA.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLightItalic-BETA.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLightItalic-BETA.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Italic.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Italic.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Light-BETA.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Light-BETA.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-LightItalic-BETA.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-LightItalic-BETA.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Medium.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Medium.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-MediumItalic.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-MediumItalic.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Regular.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Regular.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBold.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBold.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBoldItalic.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBoldItalic.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Thin-BETA.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-Thin-BETA.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ThinItalic-BETA.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-ThinItalic-BETA.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-italic.var.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter-upright.var.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/Inter.var.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.eot +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.svg +3570 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.ttf +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.eot +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.svg +801 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.ttf +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.eot +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.svg +5028 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.ttf +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.woff +0 -0
- customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.woff2 +0 -0
- customeranalytics/web/app/base/static/assets/img/avatars/avatar-1.png +0 -0
- customeranalytics/web/app/base/static/assets/img/avatars/avatar-2.png +0 -0
- customeranalytics/web/app/base/static/assets/img/avatars/avatar-3.png +0 -0
- customeranalytics/web/app/base/static/assets/img/avatars/avatar-4.png +0 -0
- customeranalytics/web/app/base/static/assets/img/avatars/avatar-5.png +0 -0
- customeranalytics/web/app/base/static/assets/img/avatars/info.jpeg +0 -0
- customeranalytics/web/app/base/static/assets/img/icons/customanality.png +0 -0
- customeranalytics/web/app/base/static/assets/img/icons/icon-48x48.png +0 -0
- customeranalytics/web/app/base/static/assets/js/app.js +2 -0
- customeranalytics/web/app/base/static/assets/js/app.js.LICENSE +566 -0
- customeranalytics/web/app/base/static/assets/js/app.js.LICENSE.txt +556 -0
- customeranalytics/web/app/base/static/assets/js/modules/bootstrap.js +15 -0
- customeranalytics/web/app/base/static/assets/js/modules/chartjs.js +4 -0
- customeranalytics/web/app/base/static/assets/js/modules/feather.js +8 -0
- customeranalytics/web/app/base/static/assets/js/modules/flatpickr.js +4 -0
- customeranalytics/web/app/base/static/assets/js/modules/moment.js +4 -0
- customeranalytics/web/app/base/static/assets/js/modules/sidebar.js +23 -0
- customeranalytics/web/app/base/static/assets/js/modules/theme.js +16 -0
- customeranalytics/web/app/base/templates/accounts/login.html +81 -0
- customeranalytics/web/app/base/templates/accounts/register.html +80 -0
- customeranalytics/web/app/base/templates/includes/footer.html +28 -0
- customeranalytics/web/app/base/templates/includes/navigation.html +51 -0
- customeranalytics/web/app/base/templates/includes/scripts.html +3 -0
- customeranalytics/web/app/base/templates/includes/sidebar.html +146 -0
- customeranalytics/web/app/base/templates/layouts/base-fullscreen.html +36 -0
- customeranalytics/web/app/base/templates/layouts/base.html +84 -0
- customeranalytics/web/app/base/util.py +30 -0
- customeranalytics/web/app/home/__init__.py +9 -0
- customeranalytics/web/app/home/forms.py +1039 -0
- customeranalytics/web/app/home/models.py +438 -0
- customeranalytics/web/app/home/profiles.py +310 -0
- customeranalytics/web/app/home/routes.py +386 -0
- customeranalytics/web/app/home/search.py +288 -0
- customeranalytics/web/app/home/static/assets/img/avatars/avatar-1.png +0 -0
- customeranalytics/web/app/home/static/assets/img/avatars/avatar-2.png +0 -0
- customeranalytics/web/app/home/static/assets/img/avatars/avatar-3.png +0 -0
- customeranalytics/web/app/home/static/assets/img/avatars/avatar-4.png +0 -0
- customeranalytics/web/app/home/static/assets/img/avatars/avatar-5.png +0 -0
- customeranalytics/web/app/home/static/assets/img/icons/customanality.png +0 -0
- customeranalytics/web/app/home/static/assets/img/icons/icon-48x48.png +0 -0
- customeranalytics/web/app/home/templates/abtest-product.html +280 -0
- customeranalytics/web/app/home/templates/abtest-promotion.html +362 -0
- customeranalytics/web/app/home/templates/abtest-segments.html +298 -0
- customeranalytics/web/app/home/templates/add-data-delivery.html +184 -0
- customeranalytics/web/app/home/templates/add-data-product.html +177 -0
- customeranalytics/web/app/home/templates/add-data-purchase.html +392 -0
- customeranalytics/web/app/home/templates/anomaly.html +261 -0
- customeranalytics/web/app/home/templates/clv.html +211 -0
- customeranalytics/web/app/home/templates/cohorts.html +308 -0
- customeranalytics/web/app/home/templates/customer-segmentation.html +308 -0
- customeranalytics/web/app/home/templates/data-es.html +152 -0
- customeranalytics/web/app/home/templates/data-execute.html +397 -0
- customeranalytics/web/app/home/templates/delivery.html +404 -0
- customeranalytics/web/app/home/templates/funnel-customer.html +240 -0
- customeranalytics/web/app/home/templates/funnel-session.html +236 -0
- customeranalytics/web/app/home/templates/index.html +524 -0
- customeranalytics/web/app/home/templates/index2.html +515 -0
- customeranalytics/web/app/home/templates/page-403.html +46 -0
- customeranalytics/web/app/home/templates/page-404.html +45 -0
- customeranalytics/web/app/home/templates/page-500.html +45 -0
- customeranalytics/web/app/home/templates/pages-blank.html +33 -0
- customeranalytics/web/app/home/templates/pages-sign-in.html +67 -0
- customeranalytics/web/app/home/templates/pages-sign-up.html +61 -0
- customeranalytics/web/app/home/templates/product.html +203 -0
- customeranalytics/web/app/home/templates/profile.html +1009 -0
- customeranalytics/web/app/home/templates/rfm.html +254 -0
- customeranalytics/web/app/home/templates/search.html +167 -0
- customeranalytics/web/app/home/templates/settings.html +205 -0
- customeranalytics/web/app/home/templates/stats-desc.html +230 -0
- customeranalytics/web/app/home/templates/stats-purchase.html +221 -0
- customeranalytics/web/config.py +30 -0
- customeranalytics/web/db.sqlite3 +0 -0
- customeranalytics/web/run.py +30 -0
- customeranalytics-0.1.dist-info/METADATA +1102 -0
- customeranalytics-0.1.dist-info/RECORD +254 -0
- customeranalytics-0.1.dist-info/WHEEL +4 -0
- customeranalytics-0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import sys, os, inspect
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import numpy as np
|
|
4
|
+
from itertools import product
|
|
5
|
+
|
|
6
|
+
from ab_test_platform.executor import ABTest
|
|
7
|
+
|
|
8
|
+
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
|
9
|
+
parentdir = os.path.dirname(currentdir)
|
|
10
|
+
sys.path.insert(0, parentdir)
|
|
11
|
+
|
|
12
|
+
from customeranalytics.configs import default_es_port, default_es_host, default_query_date, time_periods
|
|
13
|
+
from customeranalytics.utils import *
|
|
14
|
+
from customeranalytics.data_storage_configurations.query_es import QueryES
|
|
15
|
+
from customeranalytics.ml_process.customer_segmentation import CustomerSegmentation
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ABTests:
|
|
19
|
+
"""
|
|
20
|
+
AB Test;
|
|
21
|
+
For more information pls check; https://pypi.org/project/abtest/
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self,
|
|
26
|
+
temporary_export_path,
|
|
27
|
+
has_product_connection=True,
|
|
28
|
+
has_promotion_connection=True,
|
|
29
|
+
host=None,
|
|
30
|
+
port=None,
|
|
31
|
+
download_index='downloads',
|
|
32
|
+
order_index='orders'):
|
|
33
|
+
"""
|
|
34
|
+
******* ******** *****
|
|
35
|
+
Dimensional AB Test:
|
|
36
|
+
AB Tests must be created individually for dimensions.
|
|
37
|
+
For instance, the Data set contains locations dimension.
|
|
38
|
+
In this case, each location of 'orders' and 'downloads' indexes must be created individually.
|
|
39
|
+
by using 'download_index' and 'order_index' dimension can be assigned in order to create the AB Test.
|
|
40
|
+
|
|
41
|
+
download_index; downloads_location1 this will be the location dimension of
|
|
42
|
+
parameters in order to query downloads indexes; 'location1'.
|
|
43
|
+
download_index; orders_location1 this will be the location dimension of
|
|
44
|
+
parameters in order to query orders indexes; 'location1'.
|
|
45
|
+
******* ******** *****
|
|
46
|
+
|
|
47
|
+
IF THERE IS NO PROMOTION CONNECTION ON ORDERS INDEX, IT IS NOT STARTED !!
|
|
48
|
+
IF THERE IS NO PRODUCT CONNECTION ON ORDERS INDEX, IT IS NOT STARTED !!
|
|
49
|
+
|
|
50
|
+
:param has_product_connection: Has order index data for products?
|
|
51
|
+
:param has_promotion_connection: Has order index data for promotions?
|
|
52
|
+
:param host: elasticsearch host
|
|
53
|
+
:param port: elasticsearch port
|
|
54
|
+
:param download_index: elasticsearch port
|
|
55
|
+
:param order_index: elasticsearch port
|
|
56
|
+
"""
|
|
57
|
+
self.port = default_es_port if port is None else port
|
|
58
|
+
self.host = default_es_host if host is None else host
|
|
59
|
+
self.download_index = download_index
|
|
60
|
+
self.order_index = order_index
|
|
61
|
+
self.has_product_connection = has_product_connection
|
|
62
|
+
self.has_promotion_connection = has_promotion_connection
|
|
63
|
+
self.test = None
|
|
64
|
+
self.query_es = QueryES(port=port, host=host)
|
|
65
|
+
self.cs = CustomerSegmentation(port=self.port, host=self.host, order_index=self.order_index,
|
|
66
|
+
download_index=self.download_index)
|
|
67
|
+
self.data = pd.DataFrame()
|
|
68
|
+
self.products = pd.DataFrame()
|
|
69
|
+
self.promotions = []
|
|
70
|
+
self.u_products = []
|
|
71
|
+
self.max_date = current_date_to_day()
|
|
72
|
+
self.dates = {}
|
|
73
|
+
self.results = {}
|
|
74
|
+
self.time_periods = time_periods[1:]
|
|
75
|
+
self.path = temporary_export_path
|
|
76
|
+
self.features = ["orders", "amount"]
|
|
77
|
+
self.fields = ["client", "session_start_date", "payment_amount", "id"]
|
|
78
|
+
self.fields += ["promotion_id"] if has_promotion_connection else []
|
|
79
|
+
self.fields_products = ["id", "client", "basket", "session_start_date", "payment_amount"]
|
|
80
|
+
self.test_groups = ['segments', 'promotions', "products", "payment_amount"]
|
|
81
|
+
self.confidence_level = [0.01, 0.05]
|
|
82
|
+
self.boostrap_ratio = [0.1, 0.2, 0.3]
|
|
83
|
+
self.decision = pd.DataFrame()
|
|
84
|
+
self.promotion_combinations = []
|
|
85
|
+
self.promotion_comparison = pd.DataFrame()
|
|
86
|
+
|
|
87
|
+
def dimensional_query(self, boolean_query=None):
|
|
88
|
+
if dimension_decision(self.order_index):
|
|
89
|
+
if boolean_query is None:
|
|
90
|
+
boolean_query = [{"term": {"dimension": self.order_index}}]
|
|
91
|
+
else:
|
|
92
|
+
boolean_query += [{"term": {"dimension": self.order_index}}]
|
|
93
|
+
return boolean_query
|
|
94
|
+
|
|
95
|
+
def get_orders_data(self, end_date):
|
|
96
|
+
"""
|
|
97
|
+
Purchased orders are collected from Orders Index.
|
|
98
|
+
Session_start_date, client, payment_amount are needed for initializing CLv Prediciton.
|
|
99
|
+
:param end_date: final date to start clv prediction
|
|
100
|
+
"""
|
|
101
|
+
self.query_es = QueryES(port=self.port, host=self.host)
|
|
102
|
+
self.query_es.date_queries_builder({"session_start_date": {"lt": end_date}})
|
|
103
|
+
self.query_es.query_builder(fields=self.fields, boolean_queries=self.dimensional_query())
|
|
104
|
+
self.data = pd.DataFrame(self.query_es.get_data_from_es())
|
|
105
|
+
|
|
106
|
+
def dimensional_query(self, boolean_query=None):
|
|
107
|
+
if dimension_decision(self.order_index):
|
|
108
|
+
if boolean_query is None:
|
|
109
|
+
boolean_query = [{"term": {"dimension": self.order_index}}]
|
|
110
|
+
else:
|
|
111
|
+
boolean_query += [{"term": {"dimension": self.order_index}}]
|
|
112
|
+
return boolean_query
|
|
113
|
+
|
|
114
|
+
def get_products(self, end_date):
|
|
115
|
+
"""
|
|
116
|
+
Products for purchased orders are collected for Product Type A/B Test.
|
|
117
|
+
1. Fetch data from orders with filter; actions.purchased: True
|
|
118
|
+
2. Example of basket dictionary on order document;
|
|
119
|
+
'basket': {'p_10': {'price': 6.12, 'category': 'p_c_8', 'rank': 109},
|
|
120
|
+
'p_145': {'price': 12.0, 'category': 'p_c_9', 'rank': 175},
|
|
121
|
+
'p_168': {'price': 13.12, 'category': 'p_c_10', 'rank': 82},
|
|
122
|
+
'p_9': {'price': 0.52, 'category': 'p_c_3', 'rank': 9},
|
|
123
|
+
'p_4': {'price': 3.72, 'category': 'p_c_8', 'rank': 69},
|
|
124
|
+
'p_104': {'price': 8.88, 'category': 'p_c_10', 'rank': 97},
|
|
125
|
+
'p_74': {'price': 8.395, 'category': 'p_c_10', 'rank': 35}
|
|
126
|
+
}
|
|
127
|
+
a. Keys of dictionary are products.
|
|
128
|
+
IF THERE IS NO PRODUCTS DATA IS AVAILABLE SKIP THIS PROCESS !!!
|
|
129
|
+
:param end_date: last date of data set
|
|
130
|
+
"""
|
|
131
|
+
if self.has_product_connection:
|
|
132
|
+
self.query_es = QueryES(port=self.port, host=self.host)
|
|
133
|
+
self.query_es.date_queries_builder({"session_start_date": {"lt": end_date}})
|
|
134
|
+
self.query_es.query_builder(fields=None,
|
|
135
|
+
boolean_queries=self.dimensional_query([{"term": {"actions.has_basket": True}}]),
|
|
136
|
+
_source=True)
|
|
137
|
+
self.products = self.query_es.get_data_from_es()
|
|
138
|
+
self.products = pd.DataFrame([{col: r['_source'][col] for col in self.fields_products} for r in self.products])
|
|
139
|
+
self.products = self.products.query('basket == basket')
|
|
140
|
+
self.products['products'] = self.products['basket'].apply(lambda x: list(x.keys()) if x == x else None)
|
|
141
|
+
self.products = self.products.query('products == products')
|
|
142
|
+
# get prices
|
|
143
|
+
self.products['price'] = self.products.apply(
|
|
144
|
+
lambda row: [row['basket'][i]['price'] for i in row['products']], axis=1)
|
|
145
|
+
# merge price and product id into the list
|
|
146
|
+
self.products['products'] = self.products.apply(
|
|
147
|
+
lambda row: list(zip([row['id']] * len(row['products']),
|
|
148
|
+
[row['client']] * len(row['products']),
|
|
149
|
+
[row['payment_amount']] * len(row['products']),
|
|
150
|
+
[row['session_start_date']] * len(row['products']),
|
|
151
|
+
row['products'],
|
|
152
|
+
row['price'])), axis=1)
|
|
153
|
+
# concatenate products columns and convert it to dataframe with columns produc_id and price
|
|
154
|
+
self.products = pd.DataFrame(np.concatenate(list(self.products['products']))).rename(
|
|
155
|
+
columns={0: "id", 1: "client", 2: "payment_amount", 3: "session_start_date", 4: "products", 5: "price"})
|
|
156
|
+
# subtract price from payment amount.
|
|
157
|
+
# This will works to see how product of addition affects the total basket of payment amount
|
|
158
|
+
self.products['payment_amount'] = self.products['payment_amount'].apply(lambda x: float(x))
|
|
159
|
+
self.products['price'] = self.products['price'].apply(lambda x: float(x))
|
|
160
|
+
self.products['session_start_date'] = self.products['session_start_date'].apply(lambda x: convert_to_day(x))
|
|
161
|
+
|
|
162
|
+
def get_customer_segments(self):
|
|
163
|
+
"""
|
|
164
|
+
Collecting segments of customers from the reports index.
|
|
165
|
+
:param date: given report date
|
|
166
|
+
"""
|
|
167
|
+
self.data = pd.merge(self.data, self.cs.fetch()[['client', 'segments']], on='client', how='left')
|
|
168
|
+
|
|
169
|
+
def get_time_period(self, transactions, date_column):
|
|
170
|
+
"""
|
|
171
|
+
converting date column of values into the time_periods (hourly weekly, monthly,..)
|
|
172
|
+
:param transactions: total data (orders data with actions)
|
|
173
|
+
:return: data set with time periods
|
|
174
|
+
"""
|
|
175
|
+
for p in list(zip(self.time_periods,
|
|
176
|
+
[convert_dt_to_day_str, find_week_of_monday, convert_dt_to_month_str])):
|
|
177
|
+
transactions[p[0]] = transactions[date_column].apply(lambda x: p[1](x))
|
|
178
|
+
transactions[date_column] = transactions[date_column].apply(lambda x: convert_to_date(x))
|
|
179
|
+
return transactions
|
|
180
|
+
|
|
181
|
+
def assign_organic_orders(self):
|
|
182
|
+
"""
|
|
183
|
+
fill null promotions to 'organic'
|
|
184
|
+
Check for promotion data connection
|
|
185
|
+
:return:
|
|
186
|
+
"""
|
|
187
|
+
if self.has_promotion_connection:
|
|
188
|
+
self.data['promotion_id'] = self.data['promotion_id'].fillna('organic')
|
|
189
|
+
|
|
190
|
+
def get_unique_promotions(self):
|
|
191
|
+
"""
|
|
192
|
+
list of unique promotions
|
|
193
|
+
Check for promotion data connection
|
|
194
|
+
"""
|
|
195
|
+
if self.has_promotion_connection:
|
|
196
|
+
self.promotions = list(self.data.query("promotion_id != 'organic'")['promotion_id'].unique())
|
|
197
|
+
|
|
198
|
+
def get_unique_products(self):
|
|
199
|
+
"""
|
|
200
|
+
list of unique products
|
|
201
|
+
Check for products data connection.
|
|
202
|
+
"""
|
|
203
|
+
if self.has_product_connection:
|
|
204
|
+
self.u_products = list(self.products['products'].unique())
|
|
205
|
+
|
|
206
|
+
def generate_test_groups(self):
|
|
207
|
+
"""
|
|
208
|
+
generating test groups of AB Test. These groups are not A and B assigning process,
|
|
209
|
+
It is the sub groups of the A and B samples
|
|
210
|
+
:return:
|
|
211
|
+
"""
|
|
212
|
+
self.test_groups = [("segments", tp) for tp in self.time_periods]
|
|
213
|
+
if self.has_product_connection:
|
|
214
|
+
self.test_groups += [("products", None)]
|
|
215
|
+
if self.has_promotion_connection:
|
|
216
|
+
self.test_groups += [("promotions", None)]
|
|
217
|
+
|
|
218
|
+
def get_max_order_date(self):
|
|
219
|
+
"""
|
|
220
|
+
max order date from session_start_date
|
|
221
|
+
"""
|
|
222
|
+
self.max_date = max(list(self.data['daily']))
|
|
223
|
+
|
|
224
|
+
def before_after_day_decision(self, day):
|
|
225
|
+
"""
|
|
226
|
+
Before - After Test of date part decision
|
|
227
|
+
:param day: interval date which splits dates into 2 parts before and after.
|
|
228
|
+
:return: datetime
|
|
229
|
+
"""
|
|
230
|
+
if day <= self.max_date:
|
|
231
|
+
return day
|
|
232
|
+
else:
|
|
233
|
+
return self.max_date - datetime.timedelta(days=1)
|
|
234
|
+
|
|
235
|
+
def generate_before_after_dates(self, date):
|
|
236
|
+
"""
|
|
237
|
+
There 3 types of time periods to calculate dates.
|
|
238
|
+
days, weeks, months
|
|
239
|
+
:param date: current date
|
|
240
|
+
"""
|
|
241
|
+
date = self.before_after_day_decision(date)
|
|
242
|
+
_day = convert_to_date(date)
|
|
243
|
+
_week = _day - datetime.timedelta(days=7)
|
|
244
|
+
_month = _day - datetime.timedelta(days=30)
|
|
245
|
+
_prev_day, _prev_week = [tp - datetime.timedelta(days=7) for tp in [_day, _week]]
|
|
246
|
+
_prev_month = _month - datetime.timedelta(days=30)
|
|
247
|
+
self.dates = {"daily": {"before": _prev_day, "after": _day},
|
|
248
|
+
"weekly": {"before": _prev_week, "after": _week},
|
|
249
|
+
"monthly": {"before": _prev_month, "after": _month}}
|
|
250
|
+
|
|
251
|
+
def get_aggregation_func_and_renaming(self, feature):
|
|
252
|
+
"""
|
|
253
|
+
There are 2 types of testing features;
|
|
254
|
+
1. Number of orders per customers on A - B Groups
|
|
255
|
+
2. Average Payment Amount per customer on A - B Groups
|
|
256
|
+
:param feature: amount or order_count
|
|
257
|
+
:return: aggfunc for groupby function
|
|
258
|
+
"""
|
|
259
|
+
if feature == 'orders':
|
|
260
|
+
return {'id': 'count'}, {'id': 'orders'}
|
|
261
|
+
if feature == 'amount':
|
|
262
|
+
return {'payment_amount': 'mean'}, {'payment_amount': 'amount'}
|
|
263
|
+
|
|
264
|
+
def users_promotion_usage(self):
|
|
265
|
+
self.get_unique_promotions()
|
|
266
|
+
get_promo_data = lambda x, p: x.query("promotion_id == @p")
|
|
267
|
+
_first_promo_orders = self.data.query("promotion_id != 'organic'").groupby(["client", "promotion_id"]).agg(
|
|
268
|
+
{"session_start_date": "min"}).reset_index().rename(columns={"session_start_date": "promotion_date"})
|
|
269
|
+
_promos = self.data.groupby(["client", "session_start_date", "id"]).agg(
|
|
270
|
+
{"payment_amount": "mean"}).reset_index()
|
|
271
|
+
befores, afters = [], []
|
|
272
|
+
for p in self.promotions:
|
|
273
|
+
_data = pd.merge(_promos,
|
|
274
|
+
get_promo_data(_first_promo_orders, p),
|
|
275
|
+
on='client', how='left')
|
|
276
|
+
_b = _data.query("session_start_date < promotion_date").drop(
|
|
277
|
+
'promotion_date', axis=1).rename(columns={"promotion_id": "promotions"})
|
|
278
|
+
_b_clients = list(_b['client'].unique())
|
|
279
|
+
afters.append(_data.query(
|
|
280
|
+
"session_start_date >= promotion_date and client in @_b_clients").drop('promotion_date', axis=1))
|
|
281
|
+
befores.append(_b)
|
|
282
|
+
|
|
283
|
+
return pd.concat(befores), pd.concat(afters).rename(columns={"promotion_id": "promotions"})
|
|
284
|
+
|
|
285
|
+
def users_product_usage(self):
|
|
286
|
+
"""
|
|
287
|
+
Calculating the data for product usage.
|
|
288
|
+
1. There is only payment amount for product usage AB Testing.
|
|
289
|
+
2. Each customer of transactions before their order that purchased with the related product,
|
|
290
|
+
Each customer of transactions after their order that purchased with the related product,
|
|
291
|
+
3. Filter out products data for 'before' data-frame
|
|
292
|
+
4. Collect the customers who has transaction on 'before' data-frame ('before' clients)
|
|
293
|
+
5. Filter out products data for 'after' data-frame with 'before' clients
|
|
294
|
+
:return: concatenate products pf data-frames
|
|
295
|
+
"""
|
|
296
|
+
self.get_unique_products()
|
|
297
|
+
get_product_data = lambda x, p: x.query("products == @p")
|
|
298
|
+
_first_products_orders = self.products.groupby(["client", "products"]).agg(
|
|
299
|
+
{"session_start_date": "min", "price": "first"}).reset_index().rename(
|
|
300
|
+
columns={"session_start_date": "product_date"})
|
|
301
|
+
_products = self.products.groupby(
|
|
302
|
+
["client", "session_start_date", "id"]).agg({"payment_amount": "mean"}).reset_index()
|
|
303
|
+
befores, afters = [], []
|
|
304
|
+
for p in self.u_products: # iteratively collect data for each product of before and after
|
|
305
|
+
_data = pd.merge(_products,
|
|
306
|
+
get_product_data(_first_products_orders, p),
|
|
307
|
+
on='client', how='left')
|
|
308
|
+
_data = _data.query("products == products")
|
|
309
|
+
_data['payment_amount'] = _data['payment_amount'] - _data['price']
|
|
310
|
+
_data = _data.drop('price', axis=1)
|
|
311
|
+
_b = _data.query("session_start_date < product_date").drop('product_date', axis=1)
|
|
312
|
+
_b_clients = list(_b['client'].unique())
|
|
313
|
+
if len(_b) != 0:
|
|
314
|
+
afters.append(_data.query(
|
|
315
|
+
"session_start_date >= product_date and client in @_b_clients").drop('product_date', axis=1))
|
|
316
|
+
befores.append(_b)
|
|
317
|
+
return pd.concat(befores), pd.concat(afters)
|
|
318
|
+
|
|
319
|
+
def execute_test_grouping(self, data, metric, feature=None):
|
|
320
|
+
"""
|
|
321
|
+
This allows us to trigger grouping part.
|
|
322
|
+
:param data: data-frame
|
|
323
|
+
:param metric: amount or order_count
|
|
324
|
+
:param feature: grouping column additional to client
|
|
325
|
+
:return: data-frame
|
|
326
|
+
"""
|
|
327
|
+
_agg, renaming = self.get_aggregation_func_and_renaming(metric)
|
|
328
|
+
_groups = [] if feature is None else [feature]
|
|
329
|
+
return data.groupby(["client"] + _groups).agg(_agg).reset_index().rename(columns=renaming)
|
|
330
|
+
|
|
331
|
+
def create_groups(self, metric, periods=None, feature=None, data=None, before_after_test=True):
|
|
332
|
+
"""
|
|
333
|
+
This process is the creation of AB Test of A nd B groups
|
|
334
|
+
:param metric: amount of order_count
|
|
335
|
+
:param periods: if there is periodic AB Test such as Daily, Monthly, Weekly
|
|
336
|
+
:param feature: products or promotions
|
|
337
|
+
:param data: if there is data to manipulate
|
|
338
|
+
:param before_after_test: if is before after test
|
|
339
|
+
:return: concatenate data-frame
|
|
340
|
+
"""
|
|
341
|
+
if before_after_test:
|
|
342
|
+
if periods in ['daily', 'weekly', 'monthly']:
|
|
343
|
+
_before = self.data[(self.data[periods] >= self.dates[periods]['before']) &
|
|
344
|
+
(self.data[periods] < self.dates[periods]['after'])]
|
|
345
|
+
_after = self.data[self.data[periods] >= self.dates[periods]['after']]
|
|
346
|
+
else:
|
|
347
|
+
if feature == 'promotions':
|
|
348
|
+
_before, _after = self.users_promotion_usage()
|
|
349
|
+
if feature == 'products':
|
|
350
|
+
_before, _after = self.users_product_usage()
|
|
351
|
+
|
|
352
|
+
dfs = []
|
|
353
|
+
for group in [(_before, 'before'), (_after, 'after')]:
|
|
354
|
+
print("group *******", group[1])
|
|
355
|
+
_df = self.execute_test_grouping(data=group[0], metric=metric, feature=feature)
|
|
356
|
+
_df['groups'] = group[1]
|
|
357
|
+
dfs.append(_df)
|
|
358
|
+
return pd.concat(dfs)
|
|
359
|
+
|
|
360
|
+
else:
|
|
361
|
+
data = self.execute_test_grouping(data=data, metric=metric, feature=feature)
|
|
362
|
+
data['groups'] = '-'
|
|
363
|
+
return data
|
|
364
|
+
|
|
365
|
+
def decision_of_test(self, feature, groups, time_periods, comparison=False):
|
|
366
|
+
"""
|
|
367
|
+
Creating a column of decision that any increase on A to B
|
|
368
|
+
|
|
369
|
+
:param feature:amount or order_count
|
|
370
|
+
:param groups: products, promotions
|
|
371
|
+
:param time_periods: days, weeks, months
|
|
372
|
+
:param comparison: if only comparing for before and after
|
|
373
|
+
"""
|
|
374
|
+
if groups in ['promotions', 'products', 'segments']:
|
|
375
|
+
self.decision = self.decision.groupby(groups).agg({"accept_Ratio": "mean",
|
|
376
|
+
"mean_control": "mean",
|
|
377
|
+
"mean_validation": "mean"}
|
|
378
|
+
).reset_index().sort_values('accept_Ratio',
|
|
379
|
+
ascending=False)
|
|
380
|
+
column = 'is_' + feature + '_increased_per_' + groups
|
|
381
|
+
if groups == 'segments':
|
|
382
|
+
column = column + "_per_" + time_periods
|
|
383
|
+
if groups is None or comparison:
|
|
384
|
+
self.decision = pd.DataFrame([{
|
|
385
|
+
i: np.mean(self.decision[i]) for i in ['accept_Ratio', 'mean_control', 'mean_validation']
|
|
386
|
+
}])
|
|
387
|
+
if groups is None:
|
|
388
|
+
self.decision['time_period'] = time_periods
|
|
389
|
+
column = 'is_' + feature + '_increased_per_' + time_periods
|
|
390
|
+
if comparison:
|
|
391
|
+
self.decision['promotion_comparison'] = groups[0] + '_' + groups[1]
|
|
392
|
+
column = 'promo_1st_vs_promo_2nd'
|
|
393
|
+
|
|
394
|
+
self.decision[column] = self.decision.apply(
|
|
395
|
+
lambda row: True if row['mean_validation'] > row['mean_control'] and
|
|
396
|
+
row['mean_validation'] > 0.5 else False, axis=1)
|
|
397
|
+
|
|
398
|
+
def decision_of_test_promo_comparison(self, decision, groups):
|
|
399
|
+
"""
|
|
400
|
+
Creating a column of decision that any increase on A to B (only for promotion comparison)
|
|
401
|
+
:param decision: data-frame
|
|
402
|
+
:param groups: (promotion_1, promotion_2)
|
|
403
|
+
:return:
|
|
404
|
+
"""
|
|
405
|
+
decision = pd.DataFrame([{
|
|
406
|
+
i: np.mean(decision[i]) for i in ['accept_Ratio', 'mean_control', 'mean_validation']
|
|
407
|
+
}])
|
|
408
|
+
decision['promotion_comparison'] = groups[0] + '_' + groups[1]
|
|
409
|
+
decision['1st promo'], decision['2nd promo'] = groups[0], groups[1]
|
|
410
|
+
column = 'promo_1st_vs_promo_2nd'
|
|
411
|
+
|
|
412
|
+
decision[column] = decision.apply(
|
|
413
|
+
lambda row: True if row['mean_validation'] > row['mean_control'] and
|
|
414
|
+
row['mean_validation'] > 0.5 else False, axis=1)
|
|
415
|
+
decision['total_positive_effects'] = decision['promo_1st_vs_promo_2nd'].apply(
|
|
416
|
+
lambda x: 1 if x in ['True', True] else 0)
|
|
417
|
+
return decision
|
|
418
|
+
|
|
419
|
+
def name_of_test(self, is_before_after, fetaure, group, time_period):
|
|
420
|
+
"""
|
|
421
|
+
|
|
422
|
+
:param is_before_after: True/False
|
|
423
|
+
:param fetaure: amount, order_count
|
|
424
|
+
:param group: promotion, product
|
|
425
|
+
:param time_period: day, week, month
|
|
426
|
+
:return:
|
|
427
|
+
"""
|
|
428
|
+
name = ''
|
|
429
|
+
if group == 'promotions':
|
|
430
|
+
name += 'promotion_usage_'
|
|
431
|
+
if group == 'products':
|
|
432
|
+
name += 'product_usage_'
|
|
433
|
+
if group == 'segments':
|
|
434
|
+
name += 'segments_change_'
|
|
435
|
+
if time_period is not None:
|
|
436
|
+
name += time_period + '_'
|
|
437
|
+
if is_before_after:
|
|
438
|
+
name += 'before_after_'
|
|
439
|
+
name += fetaure
|
|
440
|
+
return name
|
|
441
|
+
|
|
442
|
+
def create_before_after_test(self, date):
|
|
443
|
+
"""
|
|
444
|
+
BEFORE - AFTER TEST:
|
|
445
|
+
collect data from before the event and test with after the event.
|
|
446
|
+
|
|
447
|
+
IF THERE IS NO PROMOTION CONNECTION ON ORDERS INDEX, IT IS NOT STARTED !!
|
|
448
|
+
IF THERE IS NO PRODUCT CONNECTION ON ORDERS INDEX, IT IS NOT STARTED !!
|
|
449
|
+
|
|
450
|
+
:param date: recent date
|
|
451
|
+
:return:
|
|
452
|
+
"""
|
|
453
|
+
self.results['customer_before_after'] = {}
|
|
454
|
+
for groups in self.test_groups:
|
|
455
|
+
for f in self.features:
|
|
456
|
+
ab = ABTest(data=self.create_groups(metric=f, feature=groups[0], periods=groups[1]),
|
|
457
|
+
test_groups='groups',
|
|
458
|
+
groups=groups[0],
|
|
459
|
+
feature=f,
|
|
460
|
+
exporting_data=False,
|
|
461
|
+
export_path=self.path,
|
|
462
|
+
confidence_level=self.confidence_level,
|
|
463
|
+
boostrap_sample_ratio=self.boostrap_ratio)
|
|
464
|
+
ab.ab_test_init()
|
|
465
|
+
self.decision = ab.get_results()
|
|
466
|
+
self.decision_of_test(f, groups[0], groups[1])
|
|
467
|
+
_name = self.name_of_test(is_before_after=True, fetaure=f, group=groups[0], time_period=groups[1])
|
|
468
|
+
self.insert_into_reports_index(self.decision,
|
|
469
|
+
abtest_type=_name,
|
|
470
|
+
index=self.order_index)
|
|
471
|
+
del ab
|
|
472
|
+
|
|
473
|
+
# self.decision.to_csv(join(self.path, _name + ".csv"), index=False)
|
|
474
|
+
|
|
475
|
+
def execute_promotion_comparison_test(self, p):
|
|
476
|
+
"""
|
|
477
|
+
Comparing all combination of Promotions
|
|
478
|
+
:param p: promotion id
|
|
479
|
+
:return: data-frame
|
|
480
|
+
"""
|
|
481
|
+
ab = ABTest(data=self.create_groups(data=self.data[self.data['promotion_id'].isin([p[0], p[1]])],
|
|
482
|
+
metric='amount',
|
|
483
|
+
feature='promotion_id', before_after_test=False),
|
|
484
|
+
test_groups='promotion_id',
|
|
485
|
+
feature='amount',
|
|
486
|
+
groups="groups",
|
|
487
|
+
exporting_data=False,
|
|
488
|
+
export_path=self.path,
|
|
489
|
+
confidence_level=self.confidence_level,
|
|
490
|
+
boostrap_sample_ratio=self.boostrap_ratio)
|
|
491
|
+
ab.ab_test_init()
|
|
492
|
+
return self.decision_of_test_promo_comparison(decision=ab.get_results(), groups=[p[0], p[1]])
|
|
493
|
+
|
|
494
|
+
def create_promotion_comparison_test(self, date):
|
|
495
|
+
"""
|
|
496
|
+
Comparing all combination of Promotions
|
|
497
|
+
IF THERE IS NO PROMOTION CONNECTION ON ORDERS INDEX, IT IS NOT STARTED !!
|
|
498
|
+
:param date: recent date
|
|
499
|
+
:return:
|
|
500
|
+
"""
|
|
501
|
+
if self.has_promotion_connection:
|
|
502
|
+
self.get_unique_promotions()
|
|
503
|
+
self.promotion_combinations = list(filter(lambda x: x[0] != x[1],
|
|
504
|
+
list(product(self.promotions, self.promotions))))
|
|
505
|
+
|
|
506
|
+
for p in self.promotion_combinations:
|
|
507
|
+
self.promotion_comparison = pd.concat([self.promotion_comparison,
|
|
508
|
+
self.execute_promotion_comparison_test(p)])
|
|
509
|
+
self.insert_into_reports_index(self.promotion_comparison,
|
|
510
|
+
abtest_type='promotion_comparison',
|
|
511
|
+
index=self.order_index)
|
|
512
|
+
del self.promotion_comparison
|
|
513
|
+
self.promotion_comparison = None
|
|
514
|
+
|
|
515
|
+
def insert_into_reports_index(self,
|
|
516
|
+
abtest,
|
|
517
|
+
abtest_type,
|
|
518
|
+
index='orders'):
|
|
519
|
+
"""
|
|
520
|
+
via query_es.py, each report can be inserted into the reports index with the given format.
|
|
521
|
+
{"id": unique report id,
|
|
522
|
+
"report_date": start_date or current date,
|
|
523
|
+
"report_name": "abtest",
|
|
524
|
+
"index": "main",
|
|
525
|
+
"report_types": {"abtest_type": promotion_comparison || segments_change_monthly_before_after_amount, etc},
|
|
526
|
+
"data": abtest.fillna(0.0).to_dict("records") - dataframe to list of dictionary
|
|
527
|
+
}
|
|
528
|
+
!!! null values are assigned to 0.
|
|
529
|
+
|
|
530
|
+
:param abtest: data set, data frame
|
|
531
|
+
:param start_date: data start date
|
|
532
|
+
:param abtest_type: orders, downloads, customer_journeys
|
|
533
|
+
:param index: dimensionality of data index orders_location1 ; dimension = location1
|
|
534
|
+
"""
|
|
535
|
+
list_of_obj = [{"id": np.random.randint(200000000),
|
|
536
|
+
"report_date": convert_to_day(current_date_to_day()).isoformat(),
|
|
537
|
+
"report_name": "abtest",
|
|
538
|
+
"index": get_index_group(index),
|
|
539
|
+
"report_types": {"abtest_type": abtest_type},
|
|
540
|
+
"data": abtest.fillna(0.0).to_dict("records")}]
|
|
541
|
+
self.query_es.insert_data_to_index(list_of_obj, index='reports')
|
|
542
|
+
|
|
543
|
+
def build_in_tests(self, date):
|
|
544
|
+
"""
|
|
545
|
+
execute AB Test for given orders index
|
|
546
|
+
:param date: recent date for query data
|
|
547
|
+
:return:
|
|
548
|
+
"""
|
|
549
|
+
_date = str(current_date_to_day())[0:10] if date is None else date
|
|
550
|
+
self.get_orders_data(end_date=_date)
|
|
551
|
+
self.get_products(end_date=_date)
|
|
552
|
+
self.data = self.get_time_period(self.data, "session_start_date")
|
|
553
|
+
self.assign_organic_orders()
|
|
554
|
+
self.get_max_order_date()
|
|
555
|
+
self.get_customer_segments()
|
|
556
|
+
self.generate_before_after_dates(convert_to_day(_date))
|
|
557
|
+
self.generate_test_groups()
|
|
558
|
+
self.create_before_after_test(date)
|
|
559
|
+
self.create_promotion_comparison_test(date)
|
|
560
|
+
|
|
561
|
+
def fetch(self, abtest_name, start_date=None):
|
|
562
|
+
"""
|
|
563
|
+
Example of cohort_name;
|
|
564
|
+
promotion_comparison
|
|
565
|
+
segments_change_monthly_before_after_amount
|
|
566
|
+
segments_change_monthly_before_after_orders
|
|
567
|
+
segments_change_weekly_before_after_amount
|
|
568
|
+
segments_change_weekly_before_after_orders
|
|
569
|
+
segments_change_daily_before_after_amount
|
|
570
|
+
segments_change_daily_before_after_orders
|
|
571
|
+
product_usage_before_after_amount
|
|
572
|
+
product_usage_before_after_orders
|
|
573
|
+
promotion_usage_before_after_amount
|
|
574
|
+
promotion_usage_before_after_orders
|
|
575
|
+
|
|
576
|
+
Directly, these arguments are sent to elasticsearch reports index in order to fetch related reports.
|
|
577
|
+
|
|
578
|
+
Example AB Test Result;
|
|
579
|
+
|
|
580
|
+
segments accept_Ratio mean_control mean_validation is_amount_increased_per_segments_per_monthly
|
|
581
|
+
0 lost 0.633333 24.180795 23.996087 False
|
|
582
|
+
1 at risk 0.616667 23.587849 23.914558 True
|
|
583
|
+
2 new customers 0.433333 31.431178 31.835731 True
|
|
584
|
+
3 champions 0.233333 18.990791 20.332927 True
|
|
585
|
+
4 potential loyalist 0.133333 40.590746 39.738574 False
|
|
586
|
+
5 can`t lose them 0.066667 24.448692 16.543292 False
|
|
587
|
+
6 need attention 0.016667 30.248727 26.593158 False
|
|
588
|
+
7 loyal customers 0.000000 28.847650 29.915854 True
|
|
589
|
+
8 others 0.000000 33.230453 38.482046 True
|
|
590
|
+
9 promising 0.000000 42.453413 24.186591 False
|
|
591
|
+
|
|
592
|
+
:param abtest_name: e.g. promotion_comparison
|
|
593
|
+
:param start_date: directly sending start_date to report_date in reports index.
|
|
594
|
+
:return: data data-frame
|
|
595
|
+
"""
|
|
596
|
+
|
|
597
|
+
boolean_queries, date_queries = [], []
|
|
598
|
+
boolean_queries = [{"term": {"report_name": "abtest"}},
|
|
599
|
+
{"term": {"report_types.abtest_type": abtest_name}},
|
|
600
|
+
{"term": {"index": get_index_group(self.order_index)}}]
|
|
601
|
+
|
|
602
|
+
if start_date is not None:
|
|
603
|
+
date_queries = [{"range": {"report_date": {"gte": convert_to_iso_format(start_date)}}}]
|
|
604
|
+
|
|
605
|
+
self.query_es = QueryES(port=self.port,
|
|
606
|
+
host=self.host)
|
|
607
|
+
self.query_es.query_builder(fields=None, _source=True,
|
|
608
|
+
date_queries=date_queries,
|
|
609
|
+
boolean_queries=boolean_queries)
|
|
610
|
+
_res = self.query_es.get_data_from_es(index="reports")
|
|
611
|
+
_data = pd.DataFrame()
|
|
612
|
+
if len(_res) != 0:
|
|
613
|
+
_data = pd.DataFrame(_res[0]['_source']['data'])
|
|
614
|
+
return _data
|
|
615
|
+
|