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.
Files changed (254) hide show
  1. customeranalytics/__init__.py +319 -0
  2. customeranalytics/configs.py +480 -0
  3. customeranalytics/data_storage_configurations/__init__.py +245 -0
  4. customeranalytics/data_storage_configurations/data_access.py +165 -0
  5. customeranalytics/data_storage_configurations/data_works_pipeline.py +207 -0
  6. customeranalytics/data_storage_configurations/es_create_index.py +544 -0
  7. customeranalytics/data_storage_configurations/logger.py +36 -0
  8. customeranalytics/data_storage_configurations/query_es.py +157 -0
  9. customeranalytics/data_storage_configurations/reports.py +684 -0
  10. customeranalytics/data_storage_configurations/schedule_data_integration.py +355 -0
  11. customeranalytics/docs/queries.yaml +245 -0
  12. customeranalytics/docs/web_configs.yaml +2 -0
  13. customeranalytics/exploratory_analysis/__init__.py +109 -0
  14. customeranalytics/exploratory_analysis/churn.py +232 -0
  15. customeranalytics/exploratory_analysis/cohorts.py +535 -0
  16. customeranalytics/exploratory_analysis/descriptive_statistics.py +470 -0
  17. customeranalytics/exploratory_analysis/funnels.py +407 -0
  18. customeranalytics/exploratory_analysis/product_analytics.py +340 -0
  19. customeranalytics/exploratory_analysis/promotion_analytics.py +299 -0
  20. customeranalytics/exploratory_analysis/rfm.py +202 -0
  21. customeranalytics/exploratory_analysis/sample_data/sample_data_avg_order_count_per_promo_per_cust.csv +99 -0
  22. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_1_search.csv +2 -0
  23. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_2_search.csv +46 -0
  24. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_3_search.csv +2 -0
  25. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_4_search.csv +2 -0
  26. customeranalytics/exploratory_analysis/sample_data/sample_data_churn.csv +2 -0
  27. customeranalytics/exploratory_analysis/sample_data/sample_data_churn_weekly.csv +10 -0
  28. customeranalytics/exploratory_analysis/sample_data/sample_data_client_feature_predicted.csv +1590 -0
  29. customeranalytics/exploratory_analysis/sample_data/sample_data_client_kpis.csv +7060 -0
  30. customeranalytics/exploratory_analysis/sample_data/sample_data_clvrfm_anomaly.csv +561 -0
  31. customeranalytics/exploratory_analysis/sample_data/sample_data_clvsegments_amount.csv +8 -0
  32. customeranalytics/exploratory_analysis/sample_data/sample_data_customer_journey.csv +18 -0
  33. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_clv.csv +103 -0
  34. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_downloads.csv +68 -0
  35. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_1_to_2.csv +66 -0
  36. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_2_to_3.csv +64 -0
  37. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_3_to_4.csv +55 -0
  38. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_dimension_values.csv +199 -0
  39. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_funnel.csv +36 -0
  40. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_funnel_downloads.csv +68 -0
  41. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_inorganic_ratio.csv +6070 -0
  42. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_orders.csv +57 -0
  43. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_organic_orders.csv +64 -0
  44. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_products_of_sales.csv +12574 -0
  45. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_promotion_discount.csv +64 -0
  46. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_promotion_revenue.csv +6070 -0
  47. customeranalytics/exploratory_analysis/sample_data/sample_data_dcohort_anomaly.csv +6 -0
  48. customeranalytics/exploratory_analysis/sample_data/sample_data_dcohort_anomaly_2.csv +118 -0
  49. customeranalytics/exploratory_analysis/sample_data/sample_data_deliver.csv +47 -0
  50. customeranalytics/exploratory_analysis/sample_data/sample_data_deliver_kpis.csv +2 -0
  51. customeranalytics/exploratory_analysis/sample_data/sample_data_deliver_weekday_hour.csv +25 -0
  52. customeranalytics/exploratory_analysis/sample_data/sample_data_dfunnel_anomaly.csv +118 -0
  53. customeranalytics/exploratory_analysis/sample_data/sample_data_dimension_kpis.csv +199 -0
  54. customeranalytics/exploratory_analysis/sample_data/sample_data_dorders_anomaly.csv +85 -0
  55. customeranalytics/exploratory_analysis/sample_data/sample_data_frequency_clusters.csv +6 -0
  56. customeranalytics/exploratory_analysis/sample_data/sample_data_frequency_recency.csv +3740 -0
  57. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_funnel.csv +25 -0
  58. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_funnel_downloads.csv +25 -0
  59. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_inorganic_ratio.csv +2353 -0
  60. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_orders.csv +25 -0
  61. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_organic_orders.csv +2353 -0
  62. customeranalytics/exploratory_analysis/sample_data/sample_data_inorganic_orders_per_promotion_per_day.csv +6070 -0
  63. customeranalytics/exploratory_analysis/sample_data/sample_data_kpis.csv +2 -0
  64. customeranalytics/exploratory_analysis/sample_data/sample_data_monetary_clusters.csv +6 -0
  65. customeranalytics/exploratory_analysis/sample_data/sample_data_monetary_frequency.csv +3740 -0
  66. customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_funnel.csv +5 -0
  67. customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_funnel_downloads.csv +5 -0
  68. customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_orders.csv +4 -0
  69. customeranalytics/exploratory_analysis/sample_data/sample_data_most_combined_products.csv +11 -0
  70. customeranalytics/exploratory_analysis/sample_data/sample_data_most_ordered_categories.csv +11 -0
  71. customeranalytics/exploratory_analysis/sample_data/sample_data_most_ordered_products.csv +11 -0
  72. customeranalytics/exploratory_analysis/sample_data/sample_data_order_and_payment_amount_differences.csv +91 -0
  73. customeranalytics/exploratory_analysis/sample_data/sample_data_prepare.csv +101 -0
  74. customeranalytics/exploratory_analysis/sample_data/sample_data_prepare_weekday_hour.csv +25 -0
  75. customeranalytics/exploratory_analysis/sample_data/sample_data_product_kpis.csv +201 -0
  76. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_amount_accept.csv +11 -0
  77. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_amount_reject.csv +7 -0
  78. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_orders_accept.csv +11 -0
  79. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_orders_reject.csv +11 -0
  80. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_comparison.csv +91 -0
  81. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_kpis.csv +99 -0
  82. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_number_of_customer.csv +99 -0
  83. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_amount_accept.csv +81 -0
  84. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_amount_reject.csv +11 -0
  85. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_orders_accept.csv +55 -0
  86. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_orders_reject.csv +37 -0
  87. customeranalytics/exploratory_analysis/sample_data/sample_data_purchase_amount_distribution.csv +22 -0
  88. customeranalytics/exploratory_analysis/sample_data/sample_data_recency_clusters.csv +6 -0
  89. customeranalytics/exploratory_analysis/sample_data/sample_data_recency_monetary.csv +3740 -0
  90. customeranalytics/exploratory_analysis/sample_data/sample_data_rfm.csv +2001 -0
  91. customeranalytics/exploratory_analysis/sample_data/sample_data_ride.csv +101 -0
  92. customeranalytics/exploratory_analysis/sample_data/sample_data_ride_weekday_hour.csv +25 -0
  93. customeranalytics/exploratory_analysis/sample_data/sample_data_segmentation.csv +11 -0
  94. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_daily_before_after_amount.csv +10 -0
  95. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_daily_before_after_orders.csv +9 -0
  96. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_monthly_before_after_amount.csv +10 -0
  97. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_monthly_before_after_orders.csv +11 -0
  98. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_weekly_before_after_amount.csv +10 -0
  99. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_weekly_before_after_orders.csv +10 -0
  100. customeranalytics/exploratory_analysis/sample_data/sample_data_user_counts_per_order_seq.csv +47 -0
  101. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_order_per_user.csv +13 -0
  102. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_payment_amount.csv +13 -0
  103. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_session_per_user.csv +13 -0
  104. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_downloads.csv +13 -0
  105. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_1_to_2.csv +13 -0
  106. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_2_to_3.csv +13 -0
  107. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_3_to_4.csv +12 -0
  108. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_funnel.csv +25 -0
  109. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_funnel_downloads.csv +25 -0
  110. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_orders.csv +10 -0
  111. customeranalytics/ml_process/__init__.py +64 -0
  112. customeranalytics/ml_process/ab_test.py +615 -0
  113. customeranalytics/ml_process/anomaly_detection.py +456 -0
  114. customeranalytics/ml_process/clv_prediction.py +301 -0
  115. customeranalytics/ml_process/customer_segmentation.py +354 -0
  116. customeranalytics/ml_process/delivery_analytics.py +754 -0
  117. customeranalytics/utils.py +186 -0
  118. customeranalytics/web/app/__init__.py +37 -0
  119. customeranalytics/web/app/base/__init__.py +9 -0
  120. customeranalytics/web/app/base/forms.py +16 -0
  121. customeranalytics/web/app/base/models.py +43 -0
  122. customeranalytics/web/app/base/routes.py +126 -0
  123. customeranalytics/web/app/base/static/assets/css/app.css +20 -0
  124. customeranalytics/web/app/base/static/assets/fonts/Inter-Black.woff +0 -0
  125. customeranalytics/web/app/base/static/assets/fonts/Inter-Black.woff2 +0 -0
  126. customeranalytics/web/app/base/static/assets/fonts/Inter-BlackItalic.woff +0 -0
  127. customeranalytics/web/app/base/static/assets/fonts/Inter-BlackItalic.woff2 +0 -0
  128. customeranalytics/web/app/base/static/assets/fonts/Inter-Bold.woff +0 -0
  129. customeranalytics/web/app/base/static/assets/fonts/Inter-Bold.woff2 +0 -0
  130. customeranalytics/web/app/base/static/assets/fonts/Inter-BoldItalic.woff +0 -0
  131. customeranalytics/web/app/base/static/assets/fonts/Inter-BoldItalic.woff2 +0 -0
  132. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBold.woff +0 -0
  133. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBold.woff2 +0 -0
  134. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBoldItalic.woff +0 -0
  135. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBoldItalic.woff2 +0 -0
  136. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLight-BETA.woff +0 -0
  137. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLight-BETA.woff2 +0 -0
  138. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLightItalic-BETA.woff +0 -0
  139. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLightItalic-BETA.woff2 +0 -0
  140. customeranalytics/web/app/base/static/assets/fonts/Inter-Italic.woff +0 -0
  141. customeranalytics/web/app/base/static/assets/fonts/Inter-Italic.woff2 +0 -0
  142. customeranalytics/web/app/base/static/assets/fonts/Inter-Light-BETA.woff +0 -0
  143. customeranalytics/web/app/base/static/assets/fonts/Inter-Light-BETA.woff2 +0 -0
  144. customeranalytics/web/app/base/static/assets/fonts/Inter-LightItalic-BETA.woff +0 -0
  145. customeranalytics/web/app/base/static/assets/fonts/Inter-LightItalic-BETA.woff2 +0 -0
  146. customeranalytics/web/app/base/static/assets/fonts/Inter-Medium.woff +0 -0
  147. customeranalytics/web/app/base/static/assets/fonts/Inter-Medium.woff2 +0 -0
  148. customeranalytics/web/app/base/static/assets/fonts/Inter-MediumItalic.woff +0 -0
  149. customeranalytics/web/app/base/static/assets/fonts/Inter-MediumItalic.woff2 +0 -0
  150. customeranalytics/web/app/base/static/assets/fonts/Inter-Regular.woff +0 -0
  151. customeranalytics/web/app/base/static/assets/fonts/Inter-Regular.woff2 +0 -0
  152. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBold.woff +0 -0
  153. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBold.woff2 +0 -0
  154. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBoldItalic.woff +0 -0
  155. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBoldItalic.woff2 +0 -0
  156. customeranalytics/web/app/base/static/assets/fonts/Inter-Thin-BETA.woff +0 -0
  157. customeranalytics/web/app/base/static/assets/fonts/Inter-Thin-BETA.woff2 +0 -0
  158. customeranalytics/web/app/base/static/assets/fonts/Inter-ThinItalic-BETA.woff +0 -0
  159. customeranalytics/web/app/base/static/assets/fonts/Inter-ThinItalic-BETA.woff2 +0 -0
  160. customeranalytics/web/app/base/static/assets/fonts/Inter-italic.var.woff2 +0 -0
  161. customeranalytics/web/app/base/static/assets/fonts/Inter-upright.var.woff2 +0 -0
  162. customeranalytics/web/app/base/static/assets/fonts/Inter.var.woff2 +0 -0
  163. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.eot +0 -0
  164. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.svg +3570 -0
  165. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.ttf +0 -0
  166. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.woff +0 -0
  167. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.woff2 +0 -0
  168. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.eot +0 -0
  169. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.svg +801 -0
  170. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.ttf +0 -0
  171. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.woff +0 -0
  172. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.woff2 +0 -0
  173. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.eot +0 -0
  174. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.svg +5028 -0
  175. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.ttf +0 -0
  176. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.woff +0 -0
  177. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.woff2 +0 -0
  178. customeranalytics/web/app/base/static/assets/img/avatars/avatar-1.png +0 -0
  179. customeranalytics/web/app/base/static/assets/img/avatars/avatar-2.png +0 -0
  180. customeranalytics/web/app/base/static/assets/img/avatars/avatar-3.png +0 -0
  181. customeranalytics/web/app/base/static/assets/img/avatars/avatar-4.png +0 -0
  182. customeranalytics/web/app/base/static/assets/img/avatars/avatar-5.png +0 -0
  183. customeranalytics/web/app/base/static/assets/img/avatars/info.jpeg +0 -0
  184. customeranalytics/web/app/base/static/assets/img/icons/customanality.png +0 -0
  185. customeranalytics/web/app/base/static/assets/img/icons/icon-48x48.png +0 -0
  186. customeranalytics/web/app/base/static/assets/js/app.js +2 -0
  187. customeranalytics/web/app/base/static/assets/js/app.js.LICENSE +566 -0
  188. customeranalytics/web/app/base/static/assets/js/app.js.LICENSE.txt +556 -0
  189. customeranalytics/web/app/base/static/assets/js/modules/bootstrap.js +15 -0
  190. customeranalytics/web/app/base/static/assets/js/modules/chartjs.js +4 -0
  191. customeranalytics/web/app/base/static/assets/js/modules/feather.js +8 -0
  192. customeranalytics/web/app/base/static/assets/js/modules/flatpickr.js +4 -0
  193. customeranalytics/web/app/base/static/assets/js/modules/moment.js +4 -0
  194. customeranalytics/web/app/base/static/assets/js/modules/sidebar.js +23 -0
  195. customeranalytics/web/app/base/static/assets/js/modules/theme.js +16 -0
  196. customeranalytics/web/app/base/templates/accounts/login.html +81 -0
  197. customeranalytics/web/app/base/templates/accounts/register.html +80 -0
  198. customeranalytics/web/app/base/templates/includes/footer.html +28 -0
  199. customeranalytics/web/app/base/templates/includes/navigation.html +51 -0
  200. customeranalytics/web/app/base/templates/includes/scripts.html +3 -0
  201. customeranalytics/web/app/base/templates/includes/sidebar.html +146 -0
  202. customeranalytics/web/app/base/templates/layouts/base-fullscreen.html +36 -0
  203. customeranalytics/web/app/base/templates/layouts/base.html +84 -0
  204. customeranalytics/web/app/base/util.py +30 -0
  205. customeranalytics/web/app/home/__init__.py +9 -0
  206. customeranalytics/web/app/home/forms.py +1039 -0
  207. customeranalytics/web/app/home/models.py +438 -0
  208. customeranalytics/web/app/home/profiles.py +310 -0
  209. customeranalytics/web/app/home/routes.py +386 -0
  210. customeranalytics/web/app/home/search.py +288 -0
  211. customeranalytics/web/app/home/static/assets/img/avatars/avatar-1.png +0 -0
  212. customeranalytics/web/app/home/static/assets/img/avatars/avatar-2.png +0 -0
  213. customeranalytics/web/app/home/static/assets/img/avatars/avatar-3.png +0 -0
  214. customeranalytics/web/app/home/static/assets/img/avatars/avatar-4.png +0 -0
  215. customeranalytics/web/app/home/static/assets/img/avatars/avatar-5.png +0 -0
  216. customeranalytics/web/app/home/static/assets/img/icons/customanality.png +0 -0
  217. customeranalytics/web/app/home/static/assets/img/icons/icon-48x48.png +0 -0
  218. customeranalytics/web/app/home/templates/abtest-product.html +280 -0
  219. customeranalytics/web/app/home/templates/abtest-promotion.html +362 -0
  220. customeranalytics/web/app/home/templates/abtest-segments.html +298 -0
  221. customeranalytics/web/app/home/templates/add-data-delivery.html +184 -0
  222. customeranalytics/web/app/home/templates/add-data-product.html +177 -0
  223. customeranalytics/web/app/home/templates/add-data-purchase.html +392 -0
  224. customeranalytics/web/app/home/templates/anomaly.html +261 -0
  225. customeranalytics/web/app/home/templates/clv.html +211 -0
  226. customeranalytics/web/app/home/templates/cohorts.html +308 -0
  227. customeranalytics/web/app/home/templates/customer-segmentation.html +308 -0
  228. customeranalytics/web/app/home/templates/data-es.html +152 -0
  229. customeranalytics/web/app/home/templates/data-execute.html +397 -0
  230. customeranalytics/web/app/home/templates/delivery.html +404 -0
  231. customeranalytics/web/app/home/templates/funnel-customer.html +240 -0
  232. customeranalytics/web/app/home/templates/funnel-session.html +236 -0
  233. customeranalytics/web/app/home/templates/index.html +524 -0
  234. customeranalytics/web/app/home/templates/index2.html +515 -0
  235. customeranalytics/web/app/home/templates/page-403.html +46 -0
  236. customeranalytics/web/app/home/templates/page-404.html +45 -0
  237. customeranalytics/web/app/home/templates/page-500.html +45 -0
  238. customeranalytics/web/app/home/templates/pages-blank.html +33 -0
  239. customeranalytics/web/app/home/templates/pages-sign-in.html +67 -0
  240. customeranalytics/web/app/home/templates/pages-sign-up.html +61 -0
  241. customeranalytics/web/app/home/templates/product.html +203 -0
  242. customeranalytics/web/app/home/templates/profile.html +1009 -0
  243. customeranalytics/web/app/home/templates/rfm.html +254 -0
  244. customeranalytics/web/app/home/templates/search.html +167 -0
  245. customeranalytics/web/app/home/templates/settings.html +205 -0
  246. customeranalytics/web/app/home/templates/stats-desc.html +230 -0
  247. customeranalytics/web/app/home/templates/stats-purchase.html +221 -0
  248. customeranalytics/web/config.py +30 -0
  249. customeranalytics/web/db.sqlite3 +0 -0
  250. customeranalytics/web/run.py +30 -0
  251. customeranalytics-0.1.dist-info/METADATA +1102 -0
  252. customeranalytics-0.1.dist-info/RECORD +254 -0
  253. customeranalytics-0.1.dist-info/WHEEL +4 -0
  254. customeranalytics-0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,301 @@
1
+ import sys, os, inspect
2
+ import pandas as pd
3
+ import numpy as np
4
+ import glob
5
+ from clv.executor import CLV
6
+ from sqlalchemy import create_engine, MetaData
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, max_elasticsearch_bulk_insert_bytes, default_es_bulk_insert_chunk_size
13
+ from customeranalytics.utils import *
14
+ from customeranalytics.data_storage_configurations.query_es import QueryES
15
+ from customeranalytics.data_storage_configurations.reports import Reports
16
+
17
+
18
+ engine = create_engine('sqlite://///' + join(abspath_for_sample_data(), "web", 'db.sqlite3'), connect_args={'check_same_thread': False})
19
+ metadata = MetaData()
20
+ con = engine.connect()
21
+
22
+
23
+ class CLVPrediction:
24
+ """
25
+ Customer Lifetime Value Prediction;
26
+ For more information pls check; https://github.com/caglanakpinar/clv_prediction
27
+
28
+
29
+ """
30
+ def __init__(self,
31
+ temporary_export_path,
32
+ host=None,
33
+ port=None,
34
+ time_period='weekly',
35
+ download_index='downloads',
36
+ order_index='orders'):
37
+ """
38
+ ******* ******** *****
39
+ Dimensional CLV Prediction:
40
+ Descriptive Statistics must be created individually for dimensions.
41
+ For instance, the Data set contains locations dimension.
42
+ In this case, each location of 'orders' and 'downloads' indexes must be created individually.
43
+ by using 'download_index' and 'order_index' dimension can be assigned in order to create the CLV Prediction.
44
+
45
+ download_index; downloads_location1 this will be the location dimension of
46
+ parameters in order to query downloads indexes; 'location1'.
47
+ download_index; orders_location1 this will be the location dimension of
48
+ parameters in order to query orders indexes; 'location1'.
49
+ ******* ******** *****
50
+ !!!
51
+
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.time_period = time_period
62
+ self.clv = None
63
+ self.query_es = QueryES(port=port, host=host)
64
+ self.path = temporary_export_path
65
+ self.temp_csv_file = join(temporary_export_path, "temp_data.csv")
66
+ self.temp_folder = join(temporary_export_path, "temp_purchase_amount_results", "*.csv")
67
+ self.results = join(temporary_export_path, "results_*.csv")
68
+ self.clv_fields = ["client", "session_start_date", "payment_amount", "dimension"]
69
+ self.result_query = "session_start_date == session_start_date and data_type != 'actual'"
70
+ self.result_columns = ['session_start_date', 'client', 'payment_amount']
71
+ self.clv_predictions = pd.DataFrame()
72
+ self.client_dimensions = pd.DataFrame()
73
+ self.collect_reports = Reports()
74
+ self.has_prev_clv = False
75
+
76
+ def get_customers_dimesions(self, _data):
77
+ _data["dimension"] = _data["dimension"].fillna("main")
78
+ if list(_data['dimension'].unique()) != 1:
79
+ _data_pv = _data.groupby(["client", "dimension"]).agg({"session_start_date": "count"}).reset_index().rename(
80
+ columns={"session_start_date": "order_count"})
81
+ _data_pv['order_seq_num'] = _data_pv.sort_values(
82
+ by=["client", "dimension", "order_count"], ascending=False).groupby(["client"]).cumcount() + 1
83
+ _data_pv = _data_pv.query("order_seq_num == 1")
84
+ _data_pv = _data_pv[["client", "dimension"]].groupby("client").agg({"dimension": "first"}).reset_index()
85
+ self.client_dimensions = pd.merge(_data.drop('dimension', axis=1), _data_pv, on='client', how='left')
86
+
87
+ def get_orders_data(self, end_date):
88
+ """
89
+ Purchased orders are collected from Orders Index.
90
+ Session_start_date, client, payment_amount are needed for initializing CLv Prediciton.
91
+ :param end_date: final date to start clv prediction
92
+ :return:
93
+ """
94
+ self.query_es = QueryES(port=self.port, host=self.host)
95
+ self.query_es.date_queries_builder({"session_start_date": {"lt": end_date}})
96
+ self.query_es.query_builder(fields=self.clv_fields,
97
+ boolean_queries=[{"term": {"actions.purchased": True}}])
98
+ _data = pd.DataFrame(self.query_es.get_data_from_es())
99
+ _data['session_start_date'] = _data['session_start_date'].apply(lambda x: str(convert_to_date(x)))
100
+ self.get_customers_dimesions(_data)
101
+ _data.query("session_start_date == session_start_date").to_csv(self.temp_csv_file, index=False)
102
+
103
+ def check_for_previous_clv_prediction(self):
104
+ """
105
+ check if the last clv prediction is applied in 7 days.
106
+ """
107
+ try:
108
+
109
+ _reports = self.collect_reports.collect_reports(self.port, self.host, 'main',
110
+ query={"report_name": "clv_prediction"})
111
+ if len(_reports) != 0:
112
+ try:
113
+ _directory = pd.read_sql("SELECT * FROM es_connection", con).to_dict('records')[-1]['directory']
114
+ print(_directory)
115
+ clv_report = pd.read_csv(join(_directory, "build_in_reports", "main", "daily_clv.csv"))
116
+ if len(clv_report) != 0:
117
+ clv_report = clv_report.query("data_type == 'prediction'")
118
+ clv_report['date'] = clv_report['date'].apply(lambda x: convert_to_day(x))
119
+ last_transaction_date = min(list(clv_report['date']))
120
+ print(last_transaction_date)
121
+ except Exception as e:
122
+ print(e)
123
+ _reports['report_date'] = _reports['report_date'].apply(lambda x: convert_to_date(x))
124
+ last_transaction_date = max(list(_reports['report_date']))
125
+
126
+ if abs(last_transaction_date - current_date_to_day()).total_seconds() / 60 / 60 / 24 < 7:
127
+ print("clv predictions will be updated!!!!!")
128
+ self.has_prev_clv = True
129
+ except: pass
130
+
131
+ def insert_into_reports_index(self, clv_predictions, time_period, date=None, index='orders'):
132
+ """
133
+ via query_es.py, each report can be inserted into the reports index with the given format.
134
+ {"id": unique report id,
135
+ "report_date": date or current date,
136
+ "report_name": "clv_prediction",
137
+ "index": "main",
138
+ "report_types": {"time_period": weekly, monthly, daily },
139
+ "data": segments.fillna(0.0).to_dict("records") - dataframe to list of dictionary
140
+ }
141
+ !!! null values are assigned to 0.
142
+
143
+ :param segments: data set, data frame
144
+ :param index: dimensionality of data index orders_location1 ; dimension = location1
145
+ """
146
+
147
+ iteration = int(len(clv_predictions) / default_es_bulk_insert_chunk_size) + 1
148
+ print("number of bulk iteration :", iteration)
149
+ print("bulk size :", default_es_bulk_insert_chunk_size)
150
+ for i in range(iteration):
151
+ _start = i * default_es_bulk_insert_chunk_size
152
+ _end = ((i+1) * default_es_bulk_insert_chunk_size) if i != (iteration - 1) else len(clv_predictions) + 1
153
+ _clv_predictions = clv_predictions.iloc[_start:_end]
154
+ _list_of_obj = [{"id": np.random.randint(200000000),
155
+ "report_date": current_date_to_day().isoformat() if date is None else convert_to_day(
156
+ date).isoformat(),
157
+ "report_name": "clv_prediction",
158
+ "index": get_index_group(index),
159
+ "report_types": {"time_period": time_period, "partition": i},
160
+ "data": _clv_predictions.fillna(0.0).to_dict("records")}]
161
+ self.query_es.insert_data_to_index(_list_of_obj, index='reports')
162
+
163
+ def convert_time_period(self, period):
164
+ """
165
+ clv_prediction library takes time-period as week, day, month.
166
+ This converts the general time-period into the clv_prediction form.
167
+ :param period: weekly or daily or month.
168
+ :return: week or day or month
169
+ """
170
+ if period == 'weekly':
171
+ return 'week'
172
+ if period == 'daily':
173
+ return 'day'
174
+ if period == 'monthly':
175
+ return 'month'
176
+ if period == '6 months':
177
+ return '6*month'
178
+
179
+ def remove_temp_files(self):
180
+ """
181
+ Removing temp_data.csv, .csv files in temp_purchase_amount_results and results*.csv files
182
+ """
183
+ # remove temp_data.csv
184
+ try:
185
+ os.unlink(self.temp_csv_file)
186
+ except Exception as e:
187
+ print("no file is observed!!!")
188
+
189
+ # remove results*.csv files
190
+ for f in glob.glob(self.results):
191
+ print(f)
192
+ try:
193
+ os.unlink(f)
194
+ except Exception as e:
195
+ print(e)
196
+ print("no file is observed!!!")
197
+
198
+ # remove .csv files in temp_purchase_amount_results
199
+ for f in glob.glob(self.temp_folder):
200
+ try:
201
+ os.unlink(f)
202
+ except Exception as e:
203
+ print(e)
204
+ print("no file is observed!!!")
205
+
206
+ def execute_clv(self, start_date, job='train_prediction', time_period='6 months'):
207
+ """
208
+ 1. train clv prediction models
209
+ For more details about models pls check; https://github.com/caglanakpinar/clv_prediction
210
+ 2. predict users of feature payment amount via using built models.
211
+ 3. use 'train_prediction' for the job argument which allows us to train and predict the results.
212
+ If time period is weekly and if there is a stored .json and .h5 trained models in last 7 days,
213
+ skips the train process and uses the latest trained model.
214
+ 4. Each model and stored in the directory at elasticsearch folder. After it is used, it is removed.
215
+ 5. If last stored clv prediction is applied before 7 days, it is not triggered for prediction again.
216
+
217
+ :param start_date: date of start for clv prediction
218
+ :param job: train or prediction
219
+ :param time_period: weekly, daily, monthly
220
+ """
221
+ start_date = str(current_date_to_day())[0:10] if start_date is None else start_date
222
+ self.check_for_previous_clv_prediction()
223
+ if not self.has_prev_clv:
224
+ self.get_orders_data(end_date=start_date)
225
+ self.clv = CLV(customer_indicator="client",
226
+ amount_indicator="payment_amount",
227
+ job=job,
228
+ date=start_date,
229
+ data_source='csv',
230
+ data_query_path=self.temp_csv_file,
231
+ time_period=self.convert_time_period(time_period),
232
+ time_indicator="session_start_date",
233
+ export_path=self.path)
234
+ self.clv.clv_prediction()
235
+ self.clv_predictions = self.clv.get_result_data().query(self.result_query)[self.result_columns]
236
+ self.clv_predictions = self.clv_predictions.rename(columns={"session_start_date": "date"})
237
+ self.clv_predictions['date'] = self.clv_predictions['date'].apply(lambda x: convert_to_iso_format(x))
238
+ self.add_dimensions()
239
+ self.clv_predictions = self.clv_predictions[['dimension', 'date', 'payment_amount', 'client']]
240
+ self.insert_into_reports_index(self.clv_predictions,
241
+ time_period=time_period,
242
+ date=start_date,
243
+ index=self.order_index)
244
+ del self.clv
245
+ del self.clv_predictions
246
+ self.clv = None
247
+ self.clv_predictions = pd.DataFrame()
248
+ self.remove_temp_files()
249
+
250
+ def add_dimensions(self):
251
+ _prediction_start_date = min(self.clv_predictions['date'])
252
+ if len(self.client_dimensions) != 0:
253
+ _clv_predictions_newcomers = self.clv_predictions.query("client == 'newcomers'")
254
+ print(_clv_predictions_newcomers.head())
255
+ self.clv_predictions = pd.merge(self.clv_predictions.query("client != 'newcomers'"),
256
+ self.client_dimensions[['client', 'dimension']], on="client", how="left")
257
+ # add newcomers of dimensions
258
+ _dimension_user_ratios = self.clv_predictions.groupby('dimension').agg(
259
+ {'client': lambda x: len(np.unique(x))}).reset_index().rename(columns={'client': 'ratio'})
260
+ _dimension_user_ratios['ratio'] = _dimension_user_ratios['ratio'] / len(
261
+ self.clv_predictions['client'].unique())
262
+ _clv_predictions_newcomers = _clv_predictions_newcomers.groupby("date").agg(
263
+ {"payment_amount": "sum"}).reset_index().rename(columns={'payment_amount': 'pa'})
264
+ _clv_predictions_newcomers['client'] = 'newcomers'
265
+ dfs = []
266
+ for _ratio in list(_dimension_user_ratios['ratio']):
267
+ _clv_predictions_newcomers['payment_amount'] = _clv_predictions_newcomers['pa'] * _ratio
268
+ dfs.append(_clv_predictions_newcomers)
269
+ self.clv_predictions = pd.concat([self.clv_predictions.query("client != 'newcomers'")] + dfs)
270
+ else: self.clv_predictions['dimension'] = 'main'
271
+ self.clv_predictions['dimension'] = self.clv_predictions['dimension'].apply(lambda x: str(x))
272
+ self.clv_predictions = self.clv_predictions.query("date > @_prediction_start_date")
273
+
274
+ def fetch(self, end_date=None, time_period='weekly'):
275
+ """
276
+ Collect CLV Prediction results with the given date.
277
+ :param end_date: final date to start clv prediction
278
+ :param time_period: weekly or daily or month.
279
+ :return:
280
+ """
281
+
282
+ boolean_queries, date_queries = [], []
283
+ boolean_queries = [{"term": {"report_name": "clv_prediction"}},
284
+ {"term": {"report_types.time_period": time_period}},
285
+ {"term": {"index": get_index_group(self.order_index)}}]
286
+
287
+ if end_date is not None:
288
+ date_queries = [{"range": {"report_date": {"lt": convert_to_iso_format(end_date)}}}]
289
+
290
+ self.query_es = QueryES(port=self.port,
291
+ host=self.host)
292
+ self.query_es.query_builder(fields=None, _source=True,
293
+ date_queries=date_queries,
294
+ boolean_queries=boolean_queries)
295
+ _res = self.query_es.get_data_from_es(index="reports")
296
+ _data = pd.DataFrame()
297
+ if len(_res) != 0:
298
+ _data = pd.DataFrame(_res[0]['_source']['data'])
299
+ if end_date is not None:
300
+ _data[time_period] = _data["session_start_date"].apply(lambda x: convert_to_date(x))
301
+ return _data
@@ -0,0 +1,354 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import sys, os, inspect
4
+ import h2o
5
+ from h2o.estimators.kmeans import H2OKMeansEstimator
6
+ from h2o.grid.grid_search import H2OGridSearch
7
+
8
+
9
+ currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
10
+ parentdir = os.path.dirname(currentdir)
11
+ sys.path.insert(0, parentdir)
12
+
13
+ from customeranalytics.configs import default_es_port, default_es_host
14
+ from customeranalytics.utils import *
15
+ from customeranalytics.data_storage_configurations.query_es import QueryES
16
+ from customeranalytics.exploratory_analysis import query_exploratory_analysis, ea_configs
17
+
18
+
19
+ class CustomerSegmentation:
20
+ """
21
+ Customer Segmentation is one of the crucial problems for Businesses that are mostly engaging with their buyers.
22
+ This relationship for some reason might differ from one customer to another one.
23
+ By using RFM (Recency - Frequency Monetary) each customer can be segmented by using Clustering Algorithms.
24
+
25
+ - Data Manipulation:
26
+ - It has been already applied on rfm.py. Collect data from reports index.
27
+ - Segment Each Metric individually by using KMeans with k = 5.
28
+ Why is k decided as 5?
29
+ - It is related to the Segmentation rule after assigning the segmentation process.
30
+ - There, Each cluster number named to a human-readable name rather than 1, 2, ..5.
31
+
32
+ - In order to optimize parameters of KMeans (except k). GRidSearch is applied.
33
+ - After Clustering Process has been done for R, F, and M, each cluster of average value sorted and
34
+ cluster labels (1, 2, 3, 4, 5) are reassigned in order. label 5 will be the highest, 1 will be the lowest.
35
+ - At the end, the user is the label in human-readable format;
36
+ "champions": {'r': [5], 'f': [5], 'm': [5]},
37
+ "loyal_customers": {'r': [3, 4, 5], 'f': [3, 4, 5], 'm': [5, 4, 3, 3]},
38
+ "potential loyalist": {'r': [5, 4, 3], 'f': [5, 4, 3], 'm': [4, 3, 2]},
39
+ "new customers": {'r': [5], 'f': [2, 1], 'm': [1, 2, 3, 4, 5]},
40
+ "promising": {'r': [3, 4], 'f': [1, 2], 'm': [1, 2]},
41
+ "need attention": {'r': [3, 2], 'f': [3, 2], 'm': [3, 2]},
42
+ "about to sleep": {'r': [3, 4], 'f': [1, 2], 'm': [1, 2]},
43
+ "at risk": {'r': [2], 'f': [5, 4, 3], 'm': [5, 4, 3]},
44
+ "can`t lose them": {'r': [1, 2], 'f': [5], 'm': [5]},
45
+ "hibernating": {'r': [2, 3], 'f': [2], 'm': [2]},
46
+ "lost": {'r': [1], 'f': [1, 2, 3, 4, 5], 'm': [1, 2, 3, 4, 5]}}
47
+
48
+ """
49
+ def __init__(self,
50
+ host=None,
51
+ port=None,
52
+ download_index='downloads',
53
+ order_index='orders'):
54
+ """
55
+ ******* ******** *****
56
+ Dimensional Customer Segmentation:
57
+ Descriptive Statistics must be created individually for dimensions.
58
+ For instance, the Data set contains locations dimension.
59
+ In this case, each location of 'orders' and 'downloads' indexes must be created individually.
60
+ by using 'download_index' and 'order_index' dimension can be assigned in order to create the Segmentations
61
+
62
+ download_index; downloads_location1 this will be the location dimension of
63
+ parameters in order to query downloads indexes; 'location1'.
64
+ download_index; orders_location1 this will be the location dimension of
65
+ parameters in order to query orders indexes; 'location1'.
66
+ ******* ******** *****
67
+ !!!
68
+
69
+ :param host: elasticsearch host
70
+ :param port: elasticsearch port
71
+ :param download_index: elasticsearch port
72
+ :param order_index: elasticsearch port
73
+ """
74
+ self.port = default_es_port if port is None else port
75
+ self.host = default_es_host if host is None else host
76
+ self.download_index = download_index
77
+ self.order_index = order_index
78
+ self.query_es = QueryES(port=port, host=host)
79
+ self.rfm = pd.DataFrame()
80
+ self.METRICS = ['recency', 'frequency', 'monetary']
81
+ self.METRIC_VALUES = {'recency': ['recency', 'rec', 'r'],
82
+ 'frequency': ['frequency', 'freq', 'f'],
83
+ 'monetary': ['monetary', 'mon', 'm']
84
+ }
85
+ self.Z_VALUES = [-1.96, 1.96] # 95% OF CONFIDENCE
86
+ self.clustering_parameters = {'split_ratio': 0.8, 'seed': 1234, 'k': 5,
87
+ 'hyper_params': {'standardize': [True, False],
88
+ 'init': ['Random', 'Furthest', 'PlusPlus']},
89
+ 'search_criteria': {'strategy': "Cartesian"}
90
+ }
91
+ self.accepted_minimum_prob = 0.05
92
+ self.frequency_segments = {}
93
+ self.recency_segments = {}
94
+ self.monetary_segments = {}
95
+ self.customer_segments = {"champions": {'r': [5], 'f': [5], 'm': [5]},
96
+ "loyal customers": {'r': [4, 5], 'f': [4, 5], 'm': [5, 4, 3, 2, 1]},
97
+ "potential loyalist": {'r': [5, 4, 3], 'f': [5, 4, 3], 'm': [4, 3, 2]},
98
+ "new customers": {'r': [5], 'f': [2, 1], 'm': [1, 2, 3, 4, 5]},
99
+ "promising": {'r': [3, 4], 'f': [1, 2], 'm': [1, 2]},
100
+ "need attention": {'r': [3, 2], 'f': [3, 2], 'm': [3, 2]},
101
+ "about to sleep": {'r': [3, 4], 'f': [1, 2], 'm': [1, 2]},
102
+ "at risk": {'r': [2], 'f': [5, 4, 3], 'm': [5, 4, 3]},
103
+ "can`t lose them": {'r': [1, 2], 'f': [5], 'm': [5]},
104
+ "hibernating": {'r': [2, 3], 'f': [2], 'm': [2]},
105
+ "lost": {'r': [1], 'f': [1, 2, 3, 4, 5], 'm': [1, 2, 3, 4, 5]}}
106
+ self.segments_numerics = {}
107
+ self.detected_segments = []
108
+ self.insert_columns = ["client", "recency_segment", "frequency_segment",
109
+ "monetary_segment", "segments", "segments_numeric"]
110
+
111
+ def get_rfm(self, date):
112
+ """
113
+ RFM values for segmentation can be fetched from the reports index with related dimensions.
114
+ """
115
+ ea_configs['rfm']['order_index'] = self.order_index
116
+ _total_cols, _counter = 0, 0
117
+ while len(set(self.rfm.columns) & set(self.METRICS)) != 3:
118
+ self.rfm = query_exploratory_analysis(ea_configs, {"start_date": date}, "rfm")
119
+ if _counter >= 10:
120
+ _total_cols = 3
121
+ else:
122
+ _total_cols = len(set(self.rfm.columns) & set(self.METRICS))
123
+ _counter += 1
124
+
125
+ def segmentation(self, data, metric):
126
+ """
127
+ Each RFM segmentation will be k = 1,.. , 5
128
+ Step of Clustering Process;
129
+ 1. Convert rfm data-frame into the H2O data-frame
130
+ 2. Split data for train and validation.
131
+ 3. Create KMeans process.
132
+ 4. Create GridSearch for KMeans parameters. These are;
133
+ a. standardize; standardize the numeric columns to have a mean of zero and unit variance. Standardization is highly recommended;
134
+ b. split ratio; train validation split ratio, the lower ratio can decrease Underfitting Problem
135
+ c. init; 'Random', 'Furthest', 'PlusPlus'
136
+ d. search criteria - strategy;
137
+ These segments are later on CustomerSegmentation will be sorted according to theirs.
138
+ :param data: rfm data-frame with columns recency or monetary or frequency
139
+ :param metric: recency or monetary or frequency
140
+ :return:
141
+ """
142
+ h2o.init() # initialize the H2O environment
143
+ rfm_data = h2o.H2OFrame(data) # convert pandas data-frame to H2O data-frame
144
+ # split train and test data
145
+ train, valid = rfm_data.split_frame(ratios=[self.clustering_parameters['split_ratio']],
146
+ seed=self.clustering_parameters['seed'])
147
+ # build KMeans algorithm
148
+ rfm_kmeans = H2OKMeansEstimator(k=self.clustering_parameters['k'],
149
+ seed=self.clustering_parameters['seed'],
150
+ max_iterations=int(len(data) / 2))
151
+ rfm_kmeans.train(x=metric, training_frame=train, validation_frame=valid)
152
+ # grid search for KMeans parameters
153
+ grid = H2OGridSearch(model=rfm_kmeans, hyper_params=self.clustering_parameters['hyper_params'],
154
+ search_criteria=self.clustering_parameters['search_criteria'])
155
+ # train using the grid
156
+ grid.train(x=metric, training_frame=train, validation_frame=valid)
157
+ # sort the grid models by total within cluster sum-of-square error.
158
+ sorted_grid = grid.get_grid(sort_by='tot_withinss', decreasing=False)
159
+ prediction = sorted_grid[0].predict(rfm_data)
160
+ data = rfm_data.concat(prediction, axis=1)[[metric, 'predict']].as_data_frame(use_pandas=True)
161
+ data = data.rename(columns={'predict': metric + '_segment'})
162
+ # assign segments for each metric (Recency - Monetary - Frequency)
163
+ data[metric + '_segment'] = data[metric + '_segment'].apply(lambda x: x + 1)
164
+ return data
165
+
166
+ def current_day_r_f_m_clustering(self):
167
+ """
168
+ combine segmented values to the recent rfm data frame. After concatenation;
169
+
170
+ client frequency recency monetary recency_segment frequency_segment monetary_segment
171
+ 6115 u_174194 0.0 1663 32.8500 2 2 3
172
+ 10060 u_220840 1.0 342 22.4700 4 2 4
173
+ """
174
+ for metric in self.METRICS:
175
+ self.rfm = pd.concat([self.rfm, self.segmentation(self.rfm[[metric]], metric).drop(metric, axis=1)], axis=1)
176
+
177
+ def get_frequency_segments(self):
178
+ """
179
+ Each segmented label is assigned as unsorted format.
180
+ Here finding the cluster centroid for frequency clusters (it is calculated via average at this process).
181
+ e.g. centroids - labels pairs 7.2 - 5, 4.2 - 2, 30.2 - 4, 1.3 - 3, 44 - 1,
182
+ sorted_labels;
183
+ previous_labels sorted_labels centroids
184
+ 3 1 1.3
185
+ 2 2 4.2
186
+ 5 3 7.2
187
+ 4 4 30.2
188
+ 1 5 44
189
+ """
190
+ for i in self.rfm.groupby("frequency_segment").agg(
191
+ {"frequency": "mean"}).reset_index().sort_values(
192
+ by='frequency', ascending=True).reset_index(drop=True).reset_index().to_dict('records'):
193
+ self.frequency_segments[i['frequency_segment']] = 5 - i['index']
194
+
195
+ def get_recency_segments(self):
196
+ """
197
+ Each segmented label is assigned as unsorted format.
198
+ Here finding the cluster centroid for recency clusters (it is calculated via average at this process).
199
+ e.g. centroids - labels pairs 7.2 - 5, 4.2 - 2, 30.2 - 4, 1.3 - 3, 44 - 1,
200
+ sorted_labels;
201
+ previous_labels sorted_labels centroids
202
+ 3 1 1.3
203
+ 2 2 4.2
204
+ 5 3 7.2
205
+ 4 4 30.2
206
+ 1 5 44
207
+
208
+ """
209
+ for i in self.rfm.groupby("recency_segment").agg(
210
+ {"recency": "mean"}).reset_index().sort_values(
211
+ by='recency', ascending=True).reset_index(drop=True).reset_index().to_dict('records'):
212
+ self.recency_segments[i['recency_segment']] = 5 - i['index']
213
+
214
+ def get_segments_numeric(self):
215
+ """
216
+ format; {"champions": 0, ..... , "can`t lose them": 8}
217
+ """
218
+ self.segments_numerics = {s[0]: s[1] for s in zip(list(self.customer_segments.keys()) + ['others'],
219
+ list(range(len(list(self.customer_segments.keys()))+1)))}
220
+
221
+ def get_monetary_segments(self):
222
+ """
223
+ Each segmented label is assigned as unsorted format.
224
+ Here finding the cluster centroid for monetary clusters (it is calculated via average at this process).
225
+ e.g. centroids - labels pairs 7.2 - 5, 4.2 - 2, 30.2 - 4, 1.3 - 3, 44 - 1,
226
+ sorted_labels;
227
+ previous_labels sorted_labels centroids
228
+ 3 1 1.3
229
+ 2 2 4.2
230
+ 5 3 7.2
231
+ 4 4 30.2
232
+ 1 5 44
233
+
234
+ """
235
+ for i in self.rfm.groupby("monetary_segment").agg(
236
+ {"monetary": "mean"}).reset_index().sort_values(
237
+ by='monetary', ascending=True).reset_index(drop=True).reset_index().to_dict('records'):
238
+ self.monetary_segments[i['monetary_segment']] = 5 - i['index']
239
+
240
+ def combine_segments(self):
241
+ """
242
+ apply sorted segments
243
+ """
244
+ self.rfm['frequency_segment'] = self.rfm['frequency_segment'].apply(lambda x: self.frequency_segments[x])
245
+ self.rfm['recency_segment'] = self.rfm['recency_segment'].apply(lambda x: self.recency_segments[x])
246
+ self.rfm['monetary_segment'] = self.rfm['monetary_segment'].apply(lambda x: self.monetary_segments[x])
247
+
248
+ def detect_customer_segments(self):
249
+ """
250
+ Human Readable Form Of Segments;
251
+ After the segmentation process, There will be 5 * 5 * 5 = 125 individual segments will be created.
252
+ e.g. 1 - 3 - 5 (r - f - m)
253
+ Now, another topic is how segmentation outputs can be converted to clearly understandable segments.
254
+ By searching on articles about RFM segmentation, one the usual segments are;
255
+ - champions
256
+ - loyal_customers
257
+ - potential loyalist
258
+ - new customers
259
+ - promising
260
+ - need attention
261
+ - about to sleep
262
+ - at risk
263
+ - can`t lose them
264
+ - hibernating
265
+ - lost
266
+ Each segment of label ranges are assigned to the variable 'self.customer_segments'.
267
+ """
268
+ total_clients = list(self.rfm['client'].unique())
269
+ for s in self.customer_segments:
270
+ _segment_clients = list(self.rfm[(self.rfm['recency_segment'].isin(self.customer_segments[s]['r'])) &
271
+ (self.rfm['frequency_segment'].isin(self.customer_segments[s]['f'])) &
272
+ (self.rfm['monetary_segment'].isin(self.customer_segments[s]['m'])) & (
273
+ self.rfm['client'].isin(total_clients))]['client'])
274
+ self.detected_segments += list(zip(_segment_clients, (len(_segment_clients) * [s])))
275
+ total_clients = list(set(total_clients) - set(_segment_clients))
276
+
277
+ def insert_into_reports_index(self, segments, date=None, index='orders'):
278
+ """
279
+ via query_es.py, each report can be inserted into the reports index with the given format.
280
+ {"id": unique report id,
281
+ "report_date": date or current date,
282
+ "report_name": "segmentation",
283
+ "index": "main",
284
+ "report_types": {},
285
+ "data": segments.fillna(0.0).to_dict("records") - dataframe to list of dictionary
286
+ }
287
+ !!! null values are assigned to 0.
288
+
289
+ :param segments: data set, data frame
290
+ :param index: dimensionality of data index orders_location1 ; dimension = location1
291
+ """
292
+ list_of_obj = [{"id": np.random.randint(200000000),
293
+ "report_date": current_date_to_day().isoformat() if date is None else date,
294
+ "report_name": "segmentation",
295
+ "index": get_index_group(index),
296
+ "report_types": {},
297
+ "data": segments.fillna(0.0).to_dict("records")}]
298
+ self.query_es.insert_data_to_index(list_of_obj, index='reports')
299
+
300
+ def execute_customer_segment(self, start_date=None):
301
+ """
302
+ Process of the Customer Segmentation
303
+ 1. fetch rfm calculated values from reports index.
304
+ 2. create KMeans Segmentation.
305
+ 3. sort labels related to centroids for R, F, and M individually.
306
+ 4. combine sorted labels.
307
+ 6. assign human-readable segments instead of 1, 2, ..5 labels.
308
+ 7. insert human-readable segments and number labels of each segment into the reports index.
309
+ reports index must be from right dimensional, related orders_index.
310
+
311
+ :param start_date: date of reporting date
312
+ :return:
313
+ """
314
+ self.get_rfm(date=start_date)
315
+ self.current_day_r_f_m_clustering()
316
+ self.get_frequency_segments()
317
+ self.get_recency_segments()
318
+ self.get_monetary_segments()
319
+ self.combine_segments()
320
+ self.detect_customer_segments()
321
+ self.get_segments_numeric()
322
+
323
+ self.rfm = pd.merge(self.rfm,
324
+ pd.DataFrame(self.detected_segments).rename(columns={0: "client", 1: "segments"}),
325
+ on='client', how='left')
326
+ self.rfm['segments'] = self.rfm['segments'].fillna('others')
327
+ self.rfm['segments_numeric'] = self.rfm['segments'].apply(lambda x: self.segments_numerics[x])
328
+
329
+ self.insert_into_reports_index(self.rfm[self.insert_columns], date=start_date, index=self.order_index)
330
+ h2o.shutdown(prompt=False)
331
+
332
+ def fetch(self, start_date=None):
333
+ """
334
+ Collect Customer segmentation results with the given date.
335
+ :param start_date: customer_segmentation first date
336
+ :return:
337
+ """
338
+ boolean_queries, date_queries = [], []
339
+ boolean_queries = [{"term": {"report_name": "segmentation"}},
340
+ {"term": {"index": get_index_group(self.order_index)}}]
341
+
342
+ if start_date is not None:
343
+ date_queries = [{"range": {"report_date": {"gte": convert_to_iso_format(start_date)}}}]
344
+
345
+ self.query_es = QueryES(port=self.port,
346
+ host=self.host)
347
+ self.query_es.query_builder(fields=None, _source=True,
348
+ date_queries=date_queries,
349
+ boolean_queries=boolean_queries)
350
+ _res = self.query_es.get_data_from_es(index="reports")
351
+ _data = pd.DataFrame()
352
+ if len(_res) != 0:
353
+ _data = pd.DataFrame(_res[-1]['_source']['data'])
354
+ return _data