span-data 0.4.0__cp311-abi3-win_amd64.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 (71) hide show
  1. span_data/__init__.py +5 -0
  2. span_data/c360_compiler/__init__.pyd +0 -0
  3. span_data/c360_compiler/benchmark_flags.pyd +0 -0
  4. span_data/c360_compiler/c360_compiler.pyd +0 -0
  5. span_data/c360_compiler/c360_result.pyd +0 -0
  6. span_data/c360_compiler/c360_table_result.pyd +0 -0
  7. span_data/c360_compiler/io/__init__.pyd +0 -0
  8. span_data/c360_compiler/io/config_loader.pyd +0 -0
  9. span_data/c360_compiler/io/duckdb_catalogue.pyd +0 -0
  10. span_data/c360_compiler/io/duckdb_filters.pyd +0 -0
  11. span_data/c360_compiler/io/output_adapter.pyd +0 -0
  12. span_data/c360_compiler/sml/__init__.pyd +0 -0
  13. span_data/c360_compiler/sml/generator.pyd +0 -0
  14. span_data/c360_compiler/sml/schema.pyd +0 -0
  15. span_data/c360_compiler/table_names.pyd +0 -0
  16. span_data/c360_compiler/transform/__init__.pyd +0 -0
  17. span_data/c360_compiler/transform/consent.pyd +0 -0
  18. span_data/c360_compiler/transform/demographics.pyd +0 -0
  19. span_data/c360_compiler/transform/enrichment.pyd +0 -0
  20. span_data/c360_compiler/transform/metrics.pyd +0 -0
  21. span_data/c360_compiler/transform/segments.pyd +0 -0
  22. span_data/c360_compiler/transform/utils.pyd +0 -0
  23. span_data/c360_compiler/types.pyd +0 -0
  24. span_data/c360_compiler/validation/__init__.pyd +0 -0
  25. span_data/c360_compiler/validation/config_checks.pyd +0 -0
  26. span_data/c360_compiler/validation/output_checks.pyd +0 -0
  27. span_data/cli/__init__.py +106 -0
  28. span_data/cli/__main__.py +6 -0
  29. span_data/id_grapher/__init__.pyd +0 -0
  30. span_data/id_grapher/blocking_diagnostics.pyd +0 -0
  31. span_data/id_grapher/clean_data.pyd +0 -0
  32. span_data/id_grapher/constants.pyd +0 -0
  33. span_data/id_grapher/field_definition_map.pyd +0 -0
  34. span_data/id_grapher/graph_result.pyd +0 -0
  35. span_data/id_grapher/id_grapher.pyd +0 -0
  36. span_data/id_grapher/overrides.pyd +0 -0
  37. span_data/id_grapher/profile_assignment_sql.pyd +0 -0
  38. span_data/id_grapher/span_config.pyd +0 -0
  39. span_data/io/__init__.pyd +0 -0
  40. span_data/io/config/__init__.pyd +0 -0
  41. span_data/io/config/adapters.pyd +0 -0
  42. span_data/io/config/errors.pyd +0 -0
  43. span_data/io/config/examples/__init__.pyd +0 -0
  44. span_data/io/config/examples/data/ANALYTICS_EVENTS_EMAIL_EVENTS.csv +3 -0
  45. span_data/io/config/examples/data/ANALYTICS_EVENTS_ORDER_EVENTS.csv +4 -0
  46. span_data/io/config/examples/data/ANALYTICS_EVENTS_SESSION_EVENTS.csv +3 -0
  47. span_data/io/config/examples/data/ANALYTICS_SOURCES_CRM_CONTACTS.csv +2 -0
  48. span_data/io/config/examples/data/ANALYTICS_SOURCES_PORTAL_USERS.csv +2 -0
  49. span_data/io/config/examples/data/ANALYTICS_SOURCES_RETAIL_CUSTOMERS.csv +3 -0
  50. span_data/io/config/examples/data/__init__.pyd +0 -0
  51. span_data/io/config/examples/example_span_config.yaml +279 -0
  52. span_data/io/config/loaders.pyd +0 -0
  53. span_data/io/config/models.pyd +0 -0
  54. span_data/io/config/pipeline.pyd +0 -0
  55. span_data/io/config/validate.pyd +0 -0
  56. span_data/io/data_source.pyd +0 -0
  57. span_data/io/duckdb_connection.pyd +0 -0
  58. span_data/io/duckdb_table_io.pyd +0 -0
  59. span_data/io/metric_window.pyd +0 -0
  60. span_data/io/table_result.pyd +0 -0
  61. span_data/packaging_info/CHANGELOG.md +154 -0
  62. span_data/packaging_info/LICENSE +29 -0
  63. span_data/packaging_info/THIRD_PARTY_NOTICES.md +44 -0
  64. span_data/packaging_info/USAGE.md +434 -0
  65. span_data/packaging_info/__init__.py +0 -0
  66. span_data-0.4.0.dist-info/METADATA +109 -0
  67. span_data-0.4.0.dist-info/RECORD +71 -0
  68. span_data-0.4.0.dist-info/WHEEL +5 -0
  69. span_data-0.4.0.dist-info/entry_points.txt +2 -0
  70. span_data-0.4.0.dist-info/licenses/span_data/packaging_info/LICENSE +29 -0
  71. span_data-0.4.0.dist-info/top_level.txt +1 -0
span_data/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """span_data: identity resolution and Customer 360 libraries."""
2
+
3
+ from span_data.io import DataSource, TableResult
4
+
5
+ __all__ = ["DataSource", "TableResult"]
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,106 @@
1
+ """Command-line interface for span_data (SpanConfig validate and local runs)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from span_data.io.config import load_span_config
10
+ from span_data.io.config.errors import SpanConfigError
11
+ from span_data.io.config.pipeline import run_local_span_config
12
+
13
+
14
+ def _cmd_validate_config(path: Path) -> int:
15
+ load_span_config(path)
16
+ print(f"OK: {path}")
17
+ return 0
18
+
19
+
20
+ def _cmd_run(args: argparse.Namespace) -> int:
21
+ span_config = load_span_config(args.config)
22
+ run_c360: bool | None = False if args.skip_c360 else None
23
+ run_local_span_config(
24
+ span_config,
25
+ args.data_dir,
26
+ args.output_dir,
27
+ app_name=args.app_name,
28
+ run_id_graph=not args.skip_id_graph,
29
+ run_c360=run_c360,
30
+ output_format=args.format,
31
+ data_extension=args.data_extension,
32
+ )
33
+ print(f"Finished. Outputs written to {args.output_dir}")
34
+ return 0
35
+
36
+
37
+ def main(argv: list[str] | None = None) -> int:
38
+ parser = argparse.ArgumentParser(prog="span-data", description="SPAN span_data CLI")
39
+ subparsers = parser.add_subparsers(dest="command", required=True)
40
+
41
+ validate_parser = subparsers.add_parser(
42
+ "validate-config",
43
+ help="Validate a SpanConfig YAML file",
44
+ )
45
+ validate_parser.add_argument("config", type=Path, help="Path to span_config.yaml")
46
+
47
+ run_parser = subparsers.add_parser(
48
+ "run",
49
+ help="Run ID Graph and/or C360 from SpanConfig using local data files",
50
+ )
51
+ run_parser.add_argument("config", type=Path, help="Path to span_config.yaml")
52
+ run_parser.add_argument(
53
+ "--data-dir",
54
+ type=Path,
55
+ required=True,
56
+ help="Directory of input CSV/Parquet files (named by derive_source_name(dataset))",
57
+ )
58
+ run_parser.add_argument(
59
+ "--output-dir",
60
+ type=Path,
61
+ required=True,
62
+ help="Directory for pipeline outputs",
63
+ )
64
+ run_parser.add_argument(
65
+ "--app-name",
66
+ default="LOCAL",
67
+ help="Application database name for C360 ID Graph view reference (default: LOCAL)",
68
+ )
69
+ run_parser.add_argument(
70
+ "--format",
71
+ choices=("csv", "parquet"),
72
+ default="csv",
73
+ help="C360 output file format (default: csv)",
74
+ )
75
+ run_parser.add_argument(
76
+ "--data-extension",
77
+ choices=("csv", "parquet"),
78
+ default="csv",
79
+ help="Input file extension under --data-dir (default: csv)",
80
+ )
81
+ run_parser.add_argument(
82
+ "--skip-id-graph",
83
+ action="store_true",
84
+ help="Skip ID Graph; requires data-dir/id_graph.csv when C360 is enabled",
85
+ )
86
+ run_parser.add_argument(
87
+ "--skip-c360",
88
+ action="store_true",
89
+ help="Run ID Graph only (no C360 compile)",
90
+ )
91
+
92
+ args = parser.parse_args(argv)
93
+ try:
94
+ if args.command == "validate-config":
95
+ return _cmd_validate_config(args.config)
96
+ if args.command == "run":
97
+ return _cmd_run(args)
98
+ except (SpanConfigError, ValueError, FileNotFoundError) as exc:
99
+ print(f"error: {exc}", file=sys.stderr)
100
+ return 1
101
+ parser.error(f"unknown command: {args.command}")
102
+ return 1
103
+
104
+
105
+ if __name__ == "__main__":
106
+ raise SystemExit(main())
@@ -0,0 +1,6 @@
1
+ """span-data console entry point."""
2
+
3
+ from span_data.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ EVENT_ID,CRM_CONTACT_ID,SENT_AT,OPENED,CLICKED
2
+ E1,C1,2024-02-01 10:00:00+00:00,True,False
3
+ E2,C1,2024-02-10 10:00:00+00:00,True,True
@@ -0,0 +1,4 @@
1
+ ORDER_ID,RETAIL_CUSTOMER_ID,ORDER_AT,ORDER_AMOUNT,ORDER_STATUS
2
+ O1,R1,2024-02-20 00:00:00+00:00,100.0,completed
3
+ O2,R1,2024-02-25 00:00:00+00:00,50.0,completed
4
+ O3,R2,2024-03-03 00:00:00+00:00,75.0,completed
@@ -0,0 +1,3 @@
1
+ SESSION_ID,PORTAL_USER_ID,SESSION_START_AT,SESSION_DURATION_SEC
2
+ S1,U1,2024-02-18 14:00:00+00:00,300
3
+ S2,U1,2024-02-19 15:00:00+00:00,600
@@ -0,0 +1,2 @@
1
+ CONTACT_ID,EMAIL,FIRST_NAME,LAST_NAME,GENDER,BIRTH_DATE,ZIP_CODE,STATE,PHONE,UPDATED_AT,CREATED_AT
2
+ C1,alice@example.com,Alice,Anderson,F,1990-01-01,10001,NY,555-0001,2024-01-10 10:00:00+00:00,2023-01-01 00:00:00+00:00
@@ -0,0 +1,2 @@
1
+ USER_ID,EMAIL,FIRST_NAME,LAST_NAME,ZIP_CODE,UPDATED_AT,CREATED_AT
2
+ U1,alice@example.com,Alice,Anderson,10001,2024-02-20 08:00:00+00:00,2023-08-01 00:00:00+00:00
@@ -0,0 +1,3 @@
1
+ CUSTOMER_ID,EMAIL,FIRST_NAME,LAST_NAME,PHONE,ZIP_CODE,STATE,CITY,UPDATED_AT,CREATED_AT
2
+ R1,alice@example.com,Alice,Anderson,555-0001,10001,NY,New York,2024-02-15 12:00:00+00:00,2023-06-01 00:00:00+00:00
3
+ R2,bob@example.com,Bob,Brown,555-0002,10002,CA,Los Angeles,2024-03-01 09:00:00+00:00,2024-01-15 00:00:00+00:00
@@ -0,0 +1,279 @@
1
+ # SpanConfig (illustrative example)
2
+ # Symmetric top-level: id_graph (identity resolution) + c360 (customer 360).
3
+ # Source identity is the qualified dataset name — no short aliases.
4
+ # id_graph output dataset is NOT authored — injected at runtime
5
+ # (Native App: {app}.APP_PUBLIC.{view_name}; free tier: path from ID Graph run).
6
+
7
+ schema_version: "2.0"
8
+ config_name: example_span_config
9
+ # Optional for community, mandatory for Snowflake Native app
10
+ app_settings:
11
+ view_name: example_span_config
12
+
13
+ id_graph:
14
+ linkage:
15
+ blocking_rules:
16
+ - columns: [useremail]
17
+ - columns: [useremail, firstname]
18
+ - columns: [phone, firstname]
19
+
20
+ sources:
21
+ - dataset: ANALYTICS.SOURCES.CRM_CONTACTS
22
+ id_column: CONTACT_ID
23
+ fields:
24
+ useremail: { column: EMAIL, type: email }
25
+ firstname: { column: FIRST_NAME, type: text }
26
+ lastname: { column: LAST_NAME, type: text }
27
+ gender: { column: GENDER, type: text }
28
+ birthdate: { column: BIRTH_DATE, type: text }
29
+ zipcode: { column: ZIP_CODE, type: text }
30
+ state: { column: STATE, type: text }
31
+ phone: { column: PHONE, type: phonenumber }
32
+
33
+ - dataset: ANALYTICS.SOURCES.RETAIL_CUSTOMERS
34
+ id_column: CUSTOMER_ID
35
+ fields:
36
+ useremail: { column: EMAIL, type: email }
37
+ firstname: { column: FIRST_NAME, type: text }
38
+ lastname: { column: LAST_NAME, type: text }
39
+ phone: { column: PHONE, type: phonenumber }
40
+ zipcode: { column: ZIP_CODE, type: text }
41
+ state: { column: STATE, type: text }
42
+ city: { column: CITY, type: text }
43
+
44
+ - dataset: ANALYTICS.SOURCES.PORTAL_USERS
45
+ id_column: USER_ID
46
+ fields:
47
+ useremail: { column: EMAIL, type: email }
48
+ firstname: { column: FIRST_NAME, type: text }
49
+ lastname: { column: LAST_NAME, type: text }
50
+ zipcode: { column: ZIP_CODE, type: text }
51
+
52
+ # Optional ID Graph inputs (grant consumer tables before use)
53
+ # overrides:
54
+ # dataset: ANALYTICS.SOURCES.ID_GRAPH_OVERRIDES
55
+ # seed:
56
+ # dataset: ANALYTICS.SOURCES.ID_GRAPH_SEED
57
+
58
+ c360:
59
+ enabled: true
60
+
61
+ # Demographics keyed by id_graph source dataset
62
+ demographics:
63
+ ANALYTICS.SOURCES.CRM_CONTACTS:
64
+ email: EMAIL
65
+ first_name: FIRST_NAME
66
+ last_name: LAST_NAME
67
+ gender: GENDER
68
+ birthdate: BIRTH_DATE
69
+ zip_code: ZIP_CODE
70
+ state: STATE
71
+ phone: PHONE
72
+ updated_at: UPDATED_AT
73
+ effective_from: CREATED_AT
74
+ ANALYTICS.SOURCES.RETAIL_CUSTOMERS:
75
+ email: EMAIL
76
+ first_name: FIRST_NAME
77
+ last_name: LAST_NAME
78
+ phone: PHONE
79
+ zip_code: ZIP_CODE
80
+ city: CITY
81
+ state: STATE
82
+ updated_at: UPDATED_AT
83
+ effective_from: CREATED_AT
84
+ ANALYTICS.SOURCES.PORTAL_USERS:
85
+ email: EMAIL
86
+ first_name: FIRST_NAME
87
+ last_name: LAST_NAME
88
+ zip_code: ZIP_CODE
89
+ updated_at: UPDATED_AT
90
+ effective_from: CREATED_AT
91
+
92
+ sources:
93
+ - dataset: ANALYTICS.EVENTS.EMAIL_EVENTS
94
+ id_column: CRM_CONTACT_ID
95
+ date_column: SENT_AT
96
+ dim_source: ANALYTICS.SOURCES.CRM_CONTACTS
97
+ suffix: email_sends
98
+
99
+ - dataset: ANALYTICS.EVENTS.ORDER_EVENTS
100
+ id_column: RETAIL_CUSTOMER_ID
101
+ date_column: ORDER_AT
102
+ dim_source: ANALYTICS.SOURCES.RETAIL_CUSTOMERS
103
+ suffix: orders
104
+
105
+ - dataset: ANALYTICS.EVENTS.SESSION_EVENTS
106
+ id_column: PORTAL_USER_ID
107
+ date_column: SESSION_START_AT
108
+ dim_source: ANALYTICS.SOURCES.PORTAL_USERS
109
+ suffix: web_sessions
110
+
111
+ precedence:
112
+ default:
113
+ strategy: coalesce
114
+ sources:
115
+ - ANALYTICS.SOURCES.CRM_CONTACTS
116
+ - ANALYTICS.SOURCES.RETAIL_CUSTOMERS
117
+ - ANALYTICS.SOURCES.PORTAL_USERS
118
+ zip_code:
119
+ strategy: coalesce
120
+ sources:
121
+ - ANALYTICS.SOURCES.RETAIL_CUSTOMERS
122
+ - ANALYTICS.SOURCES.CRM_CONTACTS
123
+ - ANALYTICS.SOURCES.PORTAL_USERS
124
+ city:
125
+ strategy: coalesce
126
+ sources:
127
+ - ANALYTICS.SOURCES.RETAIL_CUSTOMERS
128
+ - ANALYTICS.SOURCES.CRM_CONTACTS
129
+ - ANALYTICS.SOURCES.PORTAL_USERS
130
+ state:
131
+ strategy: coalesce
132
+ sources:
133
+ - ANALYTICS.SOURCES.RETAIL_CUSTOMERS
134
+ - ANALYTICS.SOURCES.CRM_CONTACTS
135
+ - ANALYTICS.SOURCES.PORTAL_USERS
136
+ updated_at:
137
+ strategy: greatest
138
+ sources:
139
+ - ANALYTICS.SOURCES.CRM_CONTACTS
140
+ - ANALYTICS.SOURCES.RETAIL_CUSTOMERS
141
+ - ANALYTICS.SOURCES.PORTAL_USERS
142
+ effective_from:
143
+ strategy: least
144
+ sources:
145
+ - ANALYTICS.SOURCES.CRM_CONTACTS
146
+ - ANALYTICS.SOURCES.RETAIL_CUSTOMERS
147
+ - ANALYTICS.SOURCES.PORTAL_USERS
148
+
149
+ consent:
150
+ conflict_precedence: [denied, unknown, granted]
151
+ mappings:
152
+ - source: ANALYTICS.SOURCES.CRM_CONTACTS
153
+ effective_date: CREATED_AT
154
+ purpose: promotional
155
+ medium: email
156
+ status: granted
157
+ - source: ANALYTICS.SOURCES.CRM_CONTACTS
158
+ effective_date: CREATED_AT
159
+ purpose: promotional
160
+ medium: sms
161
+ status: granted
162
+ - source: ANALYTICS.EVENTS.ORDER_EVENTS
163
+ effective_date: ORDER_AT
164
+ purpose: transactional
165
+ medium: email
166
+ status: granted
167
+
168
+ output_names:
169
+ dimensions: dim_c360_demographic_example_span_config
170
+ metrics: dim_c360_metric_example_span_config
171
+ consent: fct_c360_consent_example_span_config
172
+ segments: dim_c360_segment_example_span_config
173
+ fact: fct_c360_example_span_config_
174
+
175
+ metrics:
176
+ ltv_6m:
177
+ source: ANALYTICS.EVENTS.ORDER_EVENTS
178
+ measure: "SUM(CASE WHEN ORDER_STATUS = 'refund' THEN 0 ELSE ORDER_AMOUNT END)"
179
+ window: 6m
180
+ ltv_12m:
181
+ source: ANALYTICS.EVENTS.ORDER_EVENTS
182
+ measure: "SUM(CASE WHEN ORDER_STATUS = 'refund' THEN 0 ELSE ORDER_AMOUNT END)"
183
+ window: 12m
184
+ ltv_lifetime:
185
+ source: ANALYTICS.EVENTS.ORDER_EVENTS
186
+ measure: "SUM(CASE WHEN ORDER_STATUS = 'refund' THEN 0 ELSE ORDER_AMOUNT END)"
187
+ window: lifetime
188
+ order_count:
189
+ source: ANALYTICS.EVENTS.ORDER_EVENTS
190
+ measure: "CASE WHEN COUNT(ORDER_ID) = 0 THEN NULL ELSE COUNT(DISTINCT ORDER_ID) END"
191
+ window: lifetime
192
+ last_purchase_at:
193
+ source: ANALYTICS.EVENTS.ORDER_EVENTS
194
+ measure: "MAX(ORDER_AT)"
195
+ window: lifetime
196
+ email_send_count:
197
+ source: ANALYTICS.EVENTS.EMAIL_EVENTS
198
+ measure: "CASE WHEN COUNT(EVENT_ID) = 0 THEN NULL ELSE COUNT(DISTINCT EVENT_ID) END"
199
+ window: lifetime
200
+ email_open_count:
201
+ source: ANALYTICS.EVENTS.EMAIL_EVENTS
202
+ measure: "SUM(CASE WHEN OPENED = TRUE THEN 1 ELSE 0 END)"
203
+ window: lifetime
204
+ email_click_count:
205
+ source: ANALYTICS.EVENTS.EMAIL_EVENTS
206
+ measure: "SUM(CASE WHEN CLICKED = TRUE THEN 1 ELSE 0 END)"
207
+ window: lifetime
208
+ last_email_opened_at:
209
+ source: ANALYTICS.EVENTS.EMAIL_EVENTS
210
+ measure: "MAX(CASE WHEN OPENED = TRUE THEN SENT_AT ELSE NULL END)"
211
+ window: lifetime
212
+ web_session_count:
213
+ source: ANALYTICS.EVENTS.SESSION_EVENTS
214
+ measure: "CASE WHEN COUNT(SESSION_ID) = 0 THEN NULL ELSE COUNT(DISTINCT SESSION_ID) END"
215
+ window: lifetime
216
+ web_session_engagement:
217
+ source: ANALYTICS.EVENTS.SESSION_EVENTS
218
+ measure: "SUM(SESSION_DURATION_SEC)"
219
+ window: lifetime
220
+ last_web_session_started_at:
221
+ source: ANALYTICS.EVENTS.SESSION_EVENTS
222
+ measure: "MAX(SESSION_START_AT)"
223
+ window: lifetime
224
+ email_open_rate:
225
+ source: derived
226
+ expression: "email_open_count / COALESCE(email_send_count, 1)"
227
+ email_click_rate:
228
+ source: derived
229
+ expression: "email_click_count / COALESCE(email_send_count, 1)"
230
+ avg_session_duration_seconds:
231
+ source: derived
232
+ expression: "web_session_engagement / COALESCE(web_session_count, 1)"
233
+ average_order_value:
234
+ source: derived
235
+ expression: "ltv_lifetime / COALESCE(order_count, 1)"
236
+ last_touched_at:
237
+ source: derived
238
+ expression: "GREATEST(last_purchase_at, last_web_session_started_at, last_email_opened_at)"
239
+ customer_engagement_score:
240
+ source: derived
241
+ expression: >
242
+ (COALESCE(email_open_count, 0) / MAX(COALESCE(email_open_count, 0)) OVER()) * 25 +
243
+ (COALESCE(web_session_count, 0) / MAX(COALESCE(web_session_count, 0)) OVER()) * 25 +
244
+ (COALESCE(ltv_lifetime, 0) / MAX(COALESCE(ltv_lifetime, 0)) OVER()) * 50
245
+
246
+ segments:
247
+ is_crm_contact:
248
+ type: source_based
249
+ source: ANALYTICS.SOURCES.CRM_CONTACTS
250
+ is_retail_customer:
251
+ type: source_based
252
+ source: ANALYTICS.SOURCES.RETAIL_CUSTOMERS
253
+ is_portal_user:
254
+ type: source_based
255
+ source: ANALYTICS.SOURCES.PORTAL_USERS
256
+ is_active_customer:
257
+ type: filter_based
258
+ filter: "ltv_6m > 0"
259
+ is_new_customer:
260
+ type: filter_based
261
+ filter: "last_purchase_at >= NOW() - INTERVAL '7 days'"
262
+ value_tier_click_rate:
263
+ type: categorical_cohort
264
+ source_expression: email_click_rate
265
+ boundaries:
266
+ type: fixed_thresholds
267
+ bands:
268
+ - { label: low_click_rate, below: 0.01 }
269
+ - { label: medium_click_rate, below: 0.05 }
270
+ - { label: high_click_rate }
271
+ engagement_level:
272
+ type: categorical_cohort
273
+ source_expression: customer_engagement_score
274
+ boundaries:
275
+ type: percentile_thresholds
276
+ bands:
277
+ - { label: low_engagement, below: 0.20 }
278
+ - { label: medium_engagement, below: 0.80 }
279
+ - { label: high_engagement }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file