Compare commits
3 Commits
8dd41dfc96
...
e7166a7957
| Author | SHA1 | Date | |
|---|---|---|---|
| e7166a7957 | |||
| 2817ed240a | |||
| e44251c3d6 |
@@ -18,27 +18,44 @@ from speaking_styles import SPEAKING_STYLES
|
|||||||
# CLI argument parsing for batch automation
|
# CLI argument parsing for batch automation
|
||||||
# When run as script: python 03_quant_report.script.py --age '["18 to 21 years"]' --consumer '["Starter"]'
|
# When run as script: python 03_quant_report.script.py --age '["18 to 21 years"]' --consumer '["Starter"]'
|
||||||
# When run in Jupyter: args will use defaults (all filters = None = all options selected)
|
# When run in Jupyter: args will use defaults (all filters = None = all options selected)
|
||||||
|
|
||||||
|
# Central filter configuration - add new filters here only
|
||||||
|
# Format: 'cli_arg_name': 'QualtricsSurvey.options_* attribute name'
|
||||||
|
FILTER_CONFIG = {
|
||||||
|
'age': 'options_age',
|
||||||
|
'gender': 'options_gender',
|
||||||
|
'ethnicity': 'options_ethnicity',
|
||||||
|
'income': 'options_income',
|
||||||
|
'consumer': 'options_consumer',
|
||||||
|
'business_owner': 'options_business_owner',
|
||||||
|
'employment_status': 'options_employment_status',
|
||||||
|
'personal_products': 'options_personal_products',
|
||||||
|
'ai_user': 'options_ai_user',
|
||||||
|
'investable_assets': 'options_investable_assets',
|
||||||
|
'industry': 'options_industry',
|
||||||
|
}
|
||||||
|
|
||||||
def parse_cli_args():
|
def parse_cli_args():
|
||||||
parser = argparse.ArgumentParser(description='Generate quant report with optional filters')
|
parser = argparse.ArgumentParser(description='Generate quant report with optional filters')
|
||||||
parser.add_argument('--age', type=str, default=None, help='JSON list of age groups')
|
|
||||||
parser.add_argument('--gender', type=str, default=None, help='JSON list of genders')
|
# Dynamically add filter arguments from config
|
||||||
parser.add_argument('--ethnicity', type=str, default=None, help='JSON list of ethnicities')
|
for filter_name in FILTER_CONFIG:
|
||||||
parser.add_argument('--income', type=str, default=None, help='JSON list of income groups')
|
parser.add_argument(f'--{filter_name}', type=str, default=None, help=f'JSON list of {filter_name} values')
|
||||||
parser.add_argument('--consumer', type=str, default=None, help='JSON list of consumer segments')
|
|
||||||
|
parser.add_argument('--filter-name', type=str, default=None, help='Name for this filter combination (used for .txt description file)')
|
||||||
|
|
||||||
# Only parse if running as script (not in Jupyter/interactive)
|
# Only parse if running as script (not in Jupyter/interactive)
|
||||||
try:
|
try:
|
||||||
# Check if running in Jupyter by looking for ipykernel
|
# Check if running in Jupyter by looking for ipykernel
|
||||||
get_ipython() # noqa: F821
|
get_ipython() # noqa: F821
|
||||||
return argparse.Namespace(age=None, gender=None, ethnicity=None, income=None, consumer=None)
|
# Return namespace with all filters set to None
|
||||||
|
return argparse.Namespace(**{f: None for f in FILTER_CONFIG}, filter_name=None)
|
||||||
except NameError:
|
except NameError:
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
# Parse JSON strings to lists
|
# Parse JSON strings to lists
|
||||||
args.age = json.loads(args.age) if args.age else None
|
for filter_name in FILTER_CONFIG:
|
||||||
args.gender = json.loads(args.gender) if args.gender else None
|
val = getattr(args, filter_name)
|
||||||
args.ethnicity = json.loads(args.ethnicity) if args.ethnicity else None
|
setattr(args, filter_name, json.loads(val) if val else None)
|
||||||
args.income = json.loads(args.income) if args.income else None
|
|
||||||
args.consumer = json.loads(args.consumer) if args.consumer else None
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
cli_args = parse_cli_args()
|
cli_args = parse_cli_args()
|
||||||
@@ -54,7 +71,7 @@ cli_args = parse_cli_args()
|
|||||||
# mo.stop(file_browser.path(index=0) is None, mo.md("**⚠️ Please select a `_Labels.csv` file above to proceed**"))
|
# mo.stop(file_browser.path(index=0) is None, mo.md("**⚠️ Please select a `_Labels.csv` file above to proceed**"))
|
||||||
# RESULTS_FILE = Path(file_browser.path(index=0))
|
# RESULTS_FILE = Path(file_browser.path(index=0))
|
||||||
|
|
||||||
RESULTS_FILE = 'data/exports/2-2-26/JPMC_Chase Brand Personality_Quant Round 1_February 2, 2026_Labels.csv'
|
RESULTS_FILE = 'data/exports/2-3-26_Copy-2-2-26/JPMC_Chase Brand Personality_Quant Round 1_February 2, 2026_Labels.csv'
|
||||||
QSF_FILE = 'data/exports/OneDrive_2026-01-21/Soft Launch Data/JPMC_Chase_Brand_Personality_Quant_Round_1.qsf'
|
QSF_FILE = 'data/exports/OneDrive_2026-01-21/Soft Launch Data/JPMC_Chase_Brand_Personality_Quant_Round_1.qsf'
|
||||||
|
|
||||||
# %%
|
# %%
|
||||||
@@ -100,13 +117,60 @@ BEST_CHOSEN_CHARACTER = "the_coach"
|
|||||||
# %%
|
# %%
|
||||||
# mo.stop(filter_form.value is None, mo.md("**Please submit filter above to proceed**"))
|
# mo.stop(filter_form.value is None, mo.md("**Please submit filter above to proceed**"))
|
||||||
# CLI args: None means "all options selected" (use S.options_* defaults)
|
# CLI args: None means "all options selected" (use S.options_* defaults)
|
||||||
_filter_age = cli_args.age if cli_args.age is not None else S.options_age
|
# Build filter values dict dynamically from FILTER_CONFIG
|
||||||
_filter_gender = cli_args.gender if cli_args.gender is not None else S.options_gender
|
_active_filters = {}
|
||||||
_filter_ethnicity = cli_args.ethnicity if cli_args.ethnicity is not None else S.options_ethnicity
|
for filter_name, options_attr in FILTER_CONFIG.items():
|
||||||
_filter_income = cli_args.income if cli_args.income is not None else S.options_income
|
cli_value = getattr(cli_args, filter_name)
|
||||||
_filter_consumer = cli_args.consumer if cli_args.consumer is not None else S.options_consumer
|
all_options = getattr(S, options_attr)
|
||||||
|
_active_filters[filter_name] = cli_value if cli_value is not None else all_options
|
||||||
|
|
||||||
_d = S.filter_data(data_all, age=_filter_age, gender=_filter_gender, income=_filter_income, ethnicity=_filter_ethnicity, consumer=_filter_consumer)
|
_d = S.filter_data(data_all, **_active_filters)
|
||||||
|
|
||||||
|
# Write filter description file if filter-name is provided
|
||||||
|
if cli_args.filter_name and S.fig_save_dir:
|
||||||
|
# Get the filter slug (e.g., "All_Respondents", "Cons-Starter", etc.)
|
||||||
|
_filter_slug = S._get_filter_slug()
|
||||||
|
_filter_slug_dir = S.fig_save_dir / _filter_slug
|
||||||
|
_filter_slug_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Build filter description
|
||||||
|
_filter_desc_lines = [
|
||||||
|
f"Filter: {cli_args.filter_name}",
|
||||||
|
"",
|
||||||
|
"Applied Filters:",
|
||||||
|
]
|
||||||
|
_short_desc_parts = []
|
||||||
|
for filter_name, options_attr in FILTER_CONFIG.items():
|
||||||
|
all_options = getattr(S, options_attr)
|
||||||
|
values = _active_filters[filter_name]
|
||||||
|
display_name = filter_name.replace('_', ' ').title()
|
||||||
|
if values != all_options:
|
||||||
|
_short_desc_parts.append(f"{display_name}: {', '.join(values)}")
|
||||||
|
_filter_desc_lines.append(f" {display_name}: {', '.join(values)}")
|
||||||
|
else:
|
||||||
|
_filter_desc_lines.append(f" {display_name}: All")
|
||||||
|
|
||||||
|
# Write detailed description INSIDE the filter-slug directory
|
||||||
|
_filter_file = _filter_slug_dir / f"{cli_args.filter_name}.txt"
|
||||||
|
_filter_file.write_text('\n'.join(_filter_desc_lines))
|
||||||
|
|
||||||
|
# Append to summary index file at figures/<export_date>/filter_index.txt
|
||||||
|
_summary_file = S.fig_save_dir / "filter_index.txt"
|
||||||
|
_short_desc = "; ".join(_short_desc_parts) if _short_desc_parts else "All Respondents"
|
||||||
|
_summary_line = f"{_filter_slug} | {cli_args.filter_name} | {_short_desc}\n"
|
||||||
|
|
||||||
|
# Append or create the summary file
|
||||||
|
if _summary_file.exists():
|
||||||
|
_existing = _summary_file.read_text()
|
||||||
|
# Avoid duplicate entries for same slug
|
||||||
|
if _filter_slug not in _existing:
|
||||||
|
with _summary_file.open('a') as f:
|
||||||
|
f.write(_summary_line)
|
||||||
|
else:
|
||||||
|
_header = "Filter Index\n" + "=" * 80 + "\n\n"
|
||||||
|
_header += "Directory | Filter Name | Description\n"
|
||||||
|
_header += "-" * 80 + "\n"
|
||||||
|
_summary_file.write_text(_header + _summary_line)
|
||||||
|
|
||||||
# Stop execution and prevent other cells from running if no data is selected
|
# Stop execution and prevent other cells from running if no data is selected
|
||||||
# mo.stop(len(_d.collect()) == 0, mo.md("**No Data available for current filter combination**"))
|
# mo.stop(len(_d.collect()) == 0, mo.md("**No Data available for current filter combination**"))
|
||||||
|
|||||||
89
README.md
89
README.md
@@ -94,7 +94,7 @@ combinations.append({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Filter keys** must match CLI argument names:
|
4. **Filter keys** must match CLI argument names (defined in `FILTER_CONFIG` in `03_quant_report.script.py`):
|
||||||
- `age` — values from `survey.options_age`
|
- `age` — values from `survey.options_age`
|
||||||
- `gender` — values from `survey.options_gender`
|
- `gender` — values from `survey.options_gender`
|
||||||
- `ethnicity` — values from `survey.options_ethnicity`
|
- `ethnicity` — values from `survey.options_ethnicity`
|
||||||
@@ -144,4 +144,89 @@ combinations.append({
|
|||||||
|
|
||||||
- **Empty filters dict** = all respondents (no filtering)
|
- **Empty filters dict** = all respondents (no filtering)
|
||||||
- **Omitted filter keys** = all options for that dimension selected
|
- **Omitted filter keys** = all options for that dimension selected
|
||||||
- **Output folder names** are auto-generated from active filters by `QualtricsSurvey.filter_data()`
|
- **Output folder names** are auto-generated from active filters by `QualtricsSurvey.filter_data()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding a New Filter Dimension
|
||||||
|
|
||||||
|
To add an entirely new filter dimension (e.g., a new demographic question), you need to update several files:
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
1. **Update `utils.py` — `load_data()`** to populate the `options_*` attribute:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# In load_data(), add after existing options:
|
||||||
|
self.options_region = sorted(df['QID99'].drop_nulls().unique().to_list()) if 'QID99' in df.columns else []
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Update `utils.py` — `filter_data()`** to accept and apply the filter:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Add parameter to function signature:
|
||||||
|
def filter_data(self, q: pl.LazyFrame, ..., region:list=None) -> pl.LazyFrame:
|
||||||
|
|
||||||
|
# Add filter logic in function body:
|
||||||
|
self.filter_region = region
|
||||||
|
if region is not None:
|
||||||
|
q = q.filter(pl.col('QID99').is_in(region))
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Update `plots.py` — `_get_filter_slug()`** to include the filter in directory slugs:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Add to the filters list:
|
||||||
|
('region', 'Reg', getattr(self, 'filter_region', None), 'options_region'),
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Update `plots.py` — `_get_filter_description()`** for human-readable descriptions:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Add to the filters list:
|
||||||
|
('Region', getattr(self, 'filter_region', None), 'options_region'),
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Update `03_quant_report.script.py` — `FILTER_CONFIG`**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
FILTER_CONFIG = {
|
||||||
|
'age': 'options_age',
|
||||||
|
'gender': 'options_gender',
|
||||||
|
# ... existing filters ...
|
||||||
|
'region': 'options_region', # ← New filter
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This **automatically**:
|
||||||
|
- Adds `--region` CLI argument
|
||||||
|
- Includes it in Jupyter mode (defaults to all options)
|
||||||
|
- Passes it to `S.filter_data()`
|
||||||
|
- Writes it to the `.txt` filter description file
|
||||||
|
|
||||||
|
6. **Update `run_filter_combinations.py`** to generate combinations (optional):
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Add after existing filter loops:
|
||||||
|
for region in survey.options_region:
|
||||||
|
combinations.append({
|
||||||
|
'name': f'Region-{region}',
|
||||||
|
'filters': {'region': [region]}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Currently Available Filters
|
||||||
|
|
||||||
|
| CLI Argument | Options Attribute | QID Column | Description |
|
||||||
|
|--------------|-------------------|------------|-------------|
|
||||||
|
| `--age` | `options_age` | QID1 | Age groups |
|
||||||
|
| `--gender` | `options_gender` | QID2 | Gender |
|
||||||
|
| `--ethnicity` | `options_ethnicity` | QID3 | Ethnicity |
|
||||||
|
| `--income` | `options_income` | QID15 | Income brackets |
|
||||||
|
| `--consumer` | `options_consumer` | Consumer | Consumer segments |
|
||||||
|
| `--business_owner` | `options_business_owner` | QID4 | Business owner status |
|
||||||
|
| `--employment_status` | `options_employment_status` | QID13 | Employment status |
|
||||||
|
| `--personal_products` | `options_personal_products` | QID14 | Personal products |
|
||||||
|
| `--ai_user` | `options_ai_user` | QID22 | AI user status |
|
||||||
|
| `--investable_assets` | `options_investable_assets` | QID16 | Investable assets |
|
||||||
|
| `--industry` | `options_industry` | QID17 | Industry |
|
||||||
12
plots.py
12
plots.py
@@ -45,6 +45,12 @@ class QualtricsPlotsMixin:
|
|||||||
('consumer', 'Cons', getattr(self, 'filter_consumer', None), 'options_consumer'),
|
('consumer', 'Cons', getattr(self, 'filter_consumer', None), 'options_consumer'),
|
||||||
('ethnicity', 'Eth', getattr(self, 'filter_ethnicity', None), 'options_ethnicity'),
|
('ethnicity', 'Eth', getattr(self, 'filter_ethnicity', None), 'options_ethnicity'),
|
||||||
('income', 'Inc', getattr(self, 'filter_income', None), 'options_income'),
|
('income', 'Inc', getattr(self, 'filter_income', None), 'options_income'),
|
||||||
|
('business_owner', 'BizOwn', getattr(self, 'filter_business_owner', None), 'options_business_owner'),
|
||||||
|
('employment_status', 'Emp', getattr(self, 'filter_employment_status', None), 'options_employment_status'),
|
||||||
|
('personal_products', 'Prod', getattr(self, 'filter_personal_products', None), 'options_personal_products'),
|
||||||
|
('ai_user', 'AI', getattr(self, 'filter_ai_user', None), 'options_ai_user'),
|
||||||
|
('investable_assets', 'InvAsts', getattr(self, 'filter_investable_assets', None), 'options_investable_assets'),
|
||||||
|
('industry', 'Ind', getattr(self, 'filter_industry', None), 'options_industry'),
|
||||||
]
|
]
|
||||||
|
|
||||||
for _, short_code, value, options_attr in filters:
|
for _, short_code, value, options_attr in filters:
|
||||||
@@ -101,6 +107,12 @@ class QualtricsPlotsMixin:
|
|||||||
('Consumer', getattr(self, 'filter_consumer', None), 'options_consumer'),
|
('Consumer', getattr(self, 'filter_consumer', None), 'options_consumer'),
|
||||||
('Ethnicity', getattr(self, 'filter_ethnicity', None), 'options_ethnicity'),
|
('Ethnicity', getattr(self, 'filter_ethnicity', None), 'options_ethnicity'),
|
||||||
('Income', getattr(self, 'filter_income', None), 'options_income'),
|
('Income', getattr(self, 'filter_income', None), 'options_income'),
|
||||||
|
('Business Owner', getattr(self, 'filter_business_owner', None), 'options_business_owner'),
|
||||||
|
('Employment Status', getattr(self, 'filter_employment_status', None), 'options_employment_status'),
|
||||||
|
('Personal Products', getattr(self, 'filter_personal_products', None), 'options_personal_products'),
|
||||||
|
('AI User', getattr(self, 'filter_ai_user', None), 'options_ai_user'),
|
||||||
|
('Investable Assets', getattr(self, 'filter_investable_assets', None), 'options_investable_assets'),
|
||||||
|
('Industry', getattr(self, 'filter_industry', None), 'options_industry'),
|
||||||
]
|
]
|
||||||
|
|
||||||
for display_name, value, options_attr in filters:
|
for display_name, value, options_attr in filters:
|
||||||
|
|||||||
@@ -60,11 +60,24 @@ def get_filter_combinations(survey: QualtricsSurvey) -> list[dict]:
|
|||||||
'filters': {'gender': [gender]}
|
'filters': {'gender': [gender]}
|
||||||
})
|
})
|
||||||
|
|
||||||
# Ethnicity - one at a time
|
# Ethnicity - grouped by individual values
|
||||||
for ethnicity in survey.options_ethnicity:
|
# Ethnicity options are comma-separated (e.g., "White or Caucasian, Hispanic or Latino")
|
||||||
|
# Create filters that include ALL options containing each individual ethnicity value
|
||||||
|
ethnicity_values = set()
|
||||||
|
for ethnicity_option in survey.options_ethnicity:
|
||||||
|
# Split by comma and strip whitespace
|
||||||
|
values = [v.strip() for v in ethnicity_option.split(',')]
|
||||||
|
ethnicity_values.update(values)
|
||||||
|
|
||||||
|
for ethnicity_value in sorted(ethnicity_values):
|
||||||
|
# Find all options that contain this value
|
||||||
|
matching_options = [
|
||||||
|
opt for opt in survey.options_ethnicity
|
||||||
|
if ethnicity_value in [v.strip() for v in opt.split(',')]
|
||||||
|
]
|
||||||
combinations.append({
|
combinations.append({
|
||||||
'name': f'Ethnicity-{ethnicity}',
|
'name': f'Ethnicity-{ethnicity_value}',
|
||||||
'filters': {'ethnicity': [ethnicity]}
|
'filters': {'ethnicity': matching_options}
|
||||||
})
|
})
|
||||||
|
|
||||||
# Income - one at a time
|
# Income - one at a time
|
||||||
@@ -74,22 +87,78 @@ def get_filter_combinations(survey: QualtricsSurvey) -> list[dict]:
|
|||||||
'filters': {'income': [income]}
|
'filters': {'income': [income]}
|
||||||
})
|
})
|
||||||
|
|
||||||
# Consumer segments - one at a time
|
# Consumer segments - combine _A and _B options
|
||||||
|
# Group options by base name (removing _A/_B suffix)
|
||||||
|
consumer_groups = {}
|
||||||
for consumer in survey.options_consumer:
|
for consumer in survey.options_consumer:
|
||||||
|
# Check if ends with _A or _B
|
||||||
|
if consumer.endswith('_A') or consumer.endswith('_B'):
|
||||||
|
base_name = consumer[:-2] # Remove last 2 chars (_A or _B)
|
||||||
|
if base_name not in consumer_groups:
|
||||||
|
consumer_groups[base_name] = []
|
||||||
|
consumer_groups[base_name].append(consumer)
|
||||||
|
else:
|
||||||
|
# Not an _A/_B option, keep as-is
|
||||||
|
consumer_groups[consumer] = [consumer]
|
||||||
|
|
||||||
|
for base_name, options in consumer_groups.items():
|
||||||
combinations.append({
|
combinations.append({
|
||||||
'name': f'Consumer-{consumer}',
|
'name': f'Consumer-{base_name}',
|
||||||
'filters': {'consumer': [consumer]}
|
'filters': {'consumer': options}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Business Owner - one at a time
|
||||||
|
for business_owner in survey.options_business_owner:
|
||||||
|
combinations.append({
|
||||||
|
'name': f'BusinessOwner-{business_owner}',
|
||||||
|
'filters': {'business_owner': [business_owner]}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Employment Status - one at a time
|
||||||
|
for employment_status in survey.options_employment_status:
|
||||||
|
combinations.append({
|
||||||
|
'name': f'Employment-{employment_status}',
|
||||||
|
'filters': {'employment_status': [employment_status]}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Personal Products - one at a time
|
||||||
|
for personal_products in survey.options_personal_products:
|
||||||
|
combinations.append({
|
||||||
|
'name': f'Products-{personal_products}',
|
||||||
|
'filters': {'personal_products': [personal_products]}
|
||||||
|
})
|
||||||
|
|
||||||
|
# AI User - one at a time
|
||||||
|
for ai_user in survey.options_ai_user:
|
||||||
|
combinations.append({
|
||||||
|
'name': f'AIUser-{ai_user}',
|
||||||
|
'filters': {'ai_user': [ai_user]}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Investable Assets - one at a time
|
||||||
|
for investable_assets in survey.options_investable_assets:
|
||||||
|
combinations.append({
|
||||||
|
'name': f'Assets-{investable_assets}',
|
||||||
|
'filters': {'investable_assets': [investable_assets]}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Industry - one at a time
|
||||||
|
for industry in survey.options_industry:
|
||||||
|
combinations.append({
|
||||||
|
'name': f'Industry-{industry}',
|
||||||
|
'filters': {'industry': [industry]}
|
||||||
})
|
})
|
||||||
|
|
||||||
return combinations
|
return combinations
|
||||||
|
|
||||||
|
|
||||||
def run_report(filters: dict, dry_run: bool = False) -> bool:
|
def run_report(filters: dict, name: str = None, dry_run: bool = False) -> bool:
|
||||||
"""
|
"""
|
||||||
Run the report script with given filters.
|
Run the report script with given filters.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filters: Dict of filter_name -> list of values
|
filters: Dict of filter_name -> list of values
|
||||||
|
name: Name for this filter combination (used for .txt description file)
|
||||||
dry_run: If True, just print command without running
|
dry_run: If True, just print command without running
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -97,6 +166,10 @@ def run_report(filters: dict, dry_run: bool = False) -> bool:
|
|||||||
"""
|
"""
|
||||||
cmd = [sys.executable, str(REPORT_SCRIPT)]
|
cmd = [sys.executable, str(REPORT_SCRIPT)]
|
||||||
|
|
||||||
|
# Add filter-name for description file
|
||||||
|
if name:
|
||||||
|
cmd.extend(['--filter-name', name])
|
||||||
|
|
||||||
for filter_name, values in filters.items():
|
for filter_name, values in filters.items():
|
||||||
if values:
|
if values:
|
||||||
cmd.extend([f'--{filter_name}', json.dumps(values)])
|
cmd.extend([f'--{filter_name}', json.dumps(values)])
|
||||||
@@ -140,7 +213,7 @@ def main():
|
|||||||
print("\nDRY RUN - Commands that would be executed:")
|
print("\nDRY RUN - Commands that would be executed:")
|
||||||
for combo in combinations:
|
for combo in combinations:
|
||||||
print(f"\n{combo['name']}:")
|
print(f"\n{combo['name']}:")
|
||||||
run_report(combo['filters'], dry_run=True)
|
run_report(combo['filters'], name=combo['name'], dry_run=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Run each combination with progress bar
|
# Run each combination with progress bar
|
||||||
@@ -149,7 +222,7 @@ def main():
|
|||||||
|
|
||||||
for combo in tqdm(combinations, desc="Running reports", unit="filter"):
|
for combo in tqdm(combinations, desc="Running reports", unit="filter"):
|
||||||
tqdm.write(f"Running: {combo['name']}")
|
tqdm.write(f"Running: {combo['name']}")
|
||||||
if run_report(combo['filters']):
|
if run_report(combo['filters'], name=combo['name']):
|
||||||
successful += 1
|
successful += 1
|
||||||
else:
|
else:
|
||||||
failed.append(combo['name'])
|
failed.append(combo['name'])
|
||||||
|
|||||||
54
utils.py
54
utils.py
@@ -750,6 +750,12 @@ class QualtricsSurvey(QualtricsPlotsMixin):
|
|||||||
self.filter_consumer:list = None
|
self.filter_consumer:list = None
|
||||||
self.filter_ethnicity:list = None
|
self.filter_ethnicity:list = None
|
||||||
self.filter_income:list = None
|
self.filter_income:list = None
|
||||||
|
self.filter_business_owner:list = None # QID4
|
||||||
|
self.filter_employment_status:list = None # QID13
|
||||||
|
self.filter_personal_products:list = None # QID14
|
||||||
|
self.filter_ai_user:list = None # QID22
|
||||||
|
self.filter_investable_assets:list = None # QID16
|
||||||
|
self.filter_industry:list = None # QID17
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -837,6 +843,12 @@ class QualtricsSurvey(QualtricsPlotsMixin):
|
|||||||
self.options_consumer = sorted(df['Consumer'].drop_nulls().unique().to_list()) if 'Consumer' in df.columns else []
|
self.options_consumer = sorted(df['Consumer'].drop_nulls().unique().to_list()) if 'Consumer' in df.columns else []
|
||||||
self.options_ethnicity = sorted(df['QID3'].drop_nulls().unique().to_list()) if 'QID3' in df.columns else []
|
self.options_ethnicity = sorted(df['QID3'].drop_nulls().unique().to_list()) if 'QID3' in df.columns else []
|
||||||
self.options_income = sorted(df['QID15'].drop_nulls().unique().to_list()) if 'QID15' in df.columns else []
|
self.options_income = sorted(df['QID15'].drop_nulls().unique().to_list()) if 'QID15' in df.columns else []
|
||||||
|
self.options_business_owner = sorted(df['QID4'].drop_nulls().unique().to_list()) if 'QID4' in df.columns else []
|
||||||
|
self.options_employment_status = sorted(df['QID13'].drop_nulls().unique().to_list()) if 'QID13' in df.columns else []
|
||||||
|
self.options_personal_products = sorted(df['QID14'].drop_nulls().unique().to_list()) if 'QID14' in df.columns else []
|
||||||
|
self.options_ai_user = sorted(df['QID22'].drop_nulls().unique().to_list()) if 'QID22' in df.columns else []
|
||||||
|
self.options_investable_assets = sorted(df['QID16'].drop_nulls().unique().to_list()) if 'QID16' in df.columns else []
|
||||||
|
self.options_industry = sorted(df['QID17'].drop_nulls().unique().to_list()) if 'QID17' in df.columns else []
|
||||||
|
|
||||||
return df.lazy()
|
return df.lazy()
|
||||||
|
|
||||||
@@ -853,15 +865,21 @@ class QualtricsSurvey(QualtricsPlotsMixin):
|
|||||||
|
|
||||||
return q.select(QIDs).rename(rename_dict)
|
return q.select(QIDs).rename(rename_dict)
|
||||||
|
|
||||||
def filter_data(self, q: pl.LazyFrame, age:list=None, gender:list=None, consumer:list=None, ethnicity:list=None, income:list=None) -> pl.LazyFrame:
|
def filter_data(self, q: pl.LazyFrame, age:list=None, gender:list=None, consumer:list=None, ethnicity:list=None, income:list=None, business_owner:list=None, employment_status:list=None, personal_products:list=None, ai_user:list=None, investable_assets:list=None, industry:list=None) -> pl.LazyFrame:
|
||||||
"""Filter data based on provided parameters
|
"""Filter data based on provided parameters
|
||||||
|
|
||||||
Possible parameters:
|
Possible parameters:
|
||||||
- age: list of age groups to include
|
- age: list of age groups to include (QID1)
|
||||||
- gender: list
|
- gender: list (QID2)
|
||||||
- consumer: list
|
- consumer: list (Consumer)
|
||||||
- ethnicity: list
|
- ethnicity: list (QID3)
|
||||||
- income: list
|
- income: list (QID15)
|
||||||
|
- business_owner: list (QID4)
|
||||||
|
- employment_status: list (QID13)
|
||||||
|
- personal_products: list (QID14)
|
||||||
|
- ai_user: list (QID22)
|
||||||
|
- investable_assets: list (QID16)
|
||||||
|
- industry: list (QID17)
|
||||||
|
|
||||||
Also saves the result to self.data_filtered.
|
Also saves the result to self.data_filtered.
|
||||||
"""
|
"""
|
||||||
@@ -887,6 +905,30 @@ class QualtricsSurvey(QualtricsPlotsMixin):
|
|||||||
if income is not None:
|
if income is not None:
|
||||||
q = q.filter(pl.col('QID15').is_in(income))
|
q = q.filter(pl.col('QID15').is_in(income))
|
||||||
|
|
||||||
|
self.filter_business_owner = business_owner
|
||||||
|
if business_owner is not None:
|
||||||
|
q = q.filter(pl.col('QID4').is_in(business_owner))
|
||||||
|
|
||||||
|
self.filter_employment_status = employment_status
|
||||||
|
if employment_status is not None:
|
||||||
|
q = q.filter(pl.col('QID13').is_in(employment_status))
|
||||||
|
|
||||||
|
self.filter_personal_products = personal_products
|
||||||
|
if personal_products is not None:
|
||||||
|
q = q.filter(pl.col('QID14').is_in(personal_products))
|
||||||
|
|
||||||
|
self.filter_ai_user = ai_user
|
||||||
|
if ai_user is not None:
|
||||||
|
q = q.filter(pl.col('QID22').is_in(ai_user))
|
||||||
|
|
||||||
|
self.filter_investable_assets = investable_assets
|
||||||
|
if investable_assets is not None:
|
||||||
|
q = q.filter(pl.col('QID16').is_in(investable_assets))
|
||||||
|
|
||||||
|
self.filter_industry = industry
|
||||||
|
if industry is not None:
|
||||||
|
q = q.filter(pl.col('QID17').is_in(industry))
|
||||||
|
|
||||||
self.data_filtered = q
|
self.data_filtered = q
|
||||||
return self.data_filtered
|
return self.data_filtered
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user