# Custom Scoring Models

Cubit's default scores are calibrated for general-purpose analysis. But your organization may have specific views on what matters most-perhaps physical work is more valuable in your context, or you want to weight digitization more heavily. Custom scoring lets you apply your own dimension weights.

**Tier Required:** Enterprise

## How Custom Scoring Works

Instead of using Cubit's default formula, you provide weights for each dimension:

```
Custom Score = Sum (dimension_score x weight) / normalization_factor
```

Weights can be positive (increases exposure) or negative (protective factor).

## Basic Usage

```python
from cubit import CubitClient

client = CubitClient("cubit_your_enterprise_key")

# Define your custom weights
# Positive = increases exposure score
# Negative = decreases exposure score (protective)
result = client.calculate_custom_score(
    weights={
        "procedural": 0.5,      # Procedural work increases exposure
        "digitization": 0.3,    # Digital accessibility increases exposure
        "physicality": -0.8,    # Physical work is protective
        "socio_emotional": -0.5 # Relational work is protective
    },
    jobs=["15-1252.00", "29-1141.00", "43-3031.00"],
    aggregation="weighted_mean"
)

print("Custom Scoring Results:\n")
print(f"Weights: {result['methodology']['weights_applied']}")
print(f"Aggregation: {result['methodology']['aggregation']}\n")

print(f"{'Rank':<6} {'Job':<40} {'Custom':>8} {'Standard':>8} {'Delta':>8}")
print("-" * 75)

for job in result["results"]:
    print(f"{job['rank']:<6} {job['title'][:40]:<40} "
          f"{job['custom_score']:>8.1f} {job['standard_score']:>8.1f} "
          f"{job['delta_from_standard']:>+8.1f}")
```

**Output:**

```
Custom Scoring Results:

Weights: {'procedural': 0.5, 'digitization': 0.3, 'physicality': -0.8, 'socio_emotional': -0.5}
Aggregation: weighted_mean

Rank   Job                                       Custom  Standard    Delta
---------------------------------------------------------------------------
1      Bookkeeping, Accounting, Auditing Clerks    62.3      49.3    +13.0
2      Software Developers                         58.1      47.5    +10.6
3      Registered Nurses                           38.4      47.3     -8.9
```

## Weight Design Strategies

### Healthcare-Focused Model

Emphasize physical presence and patient relationships:

```python
healthcare_weights = {
    "procedural": 0.2,       # De-emphasize procedural (still automatable)
    "digitization": 0.3,     # Digital exposure matters
    "physicality": -1.0,     # Physical care is highly protective
    "socio_emotional": -0.8  # Patient relationships are highly protective
}
```

### Manufacturing/Industrial Model

Physical work as the primary protective factor:

```python
manufacturing_weights = {
    "procedural": 0.4,
    "digitization": 0.4,
    "physicality": -1.0,     # Hands-on work is very protective
    "socio_emotional": -0.2  # Relational skills less important
}
```

### Knowledge Work Model

Focus on cognitive automation:

```python
knowledge_weights = {
    "procedural": 0.8,       # Rule-based thinking is very exposed
    "digitization": 0.6,     # Digital accessibility is key
    "physicality": 0.0,      # Physical requirements irrelevant
    "socio_emotional": -0.4  # Client relationships provide some protection
}
```

## Aggregation Methods

### Weighted Mean (Default)

Aggregates task scores using O\*NET importance weights:

```python
result = client.calculate_custom_score(
    weights=my_weights,
    jobs=my_jobs,
    aggregation="weighted_mean"  # Default
)
```

### Maximum

Uses the highest task score (most exposed task):

```python
result = client.calculate_custom_score(
    weights=my_weights,
    jobs=my_jobs,
    aggregation="max"
)
```

Useful for identifying roles with any highly-exposed tasks.

## Comparing to Standard Scores

The `delta_from_standard` field shows how your custom model differs from Cubit's default:

```python
# Find jobs where your model disagrees most with the standard
result = client.calculate_custom_score(
    weights=my_weights,
    jobs=all_jobs,  # Large job list
    aggregation="weighted_mean"
)

# Jobs your model rates as MORE exposed
more_exposed = [
    j for j in result["results"]
    if j["delta_from_standard"] > 10
]

# Jobs your model rates as LESS exposed
less_exposed = [
    j for j in result["results"]
    if j["delta_from_standard"] < -10
]

print("Your model sees MORE risk than standard:")
for job in more_exposed[:5]:
    print(f"  {job['title']}: {job['delta_from_standard']:+.1f}")

print("\nYour model sees LESS risk than standard:")
for job in less_exposed[:5]:
    print(f"  {job['title']}: {job['delta_from_standard']:+.1f}")
```

## Portfolio Analysis

Analyze your organization's workforce:

```python
# Your organization's job codes and headcounts
workforce = {
    "15-1252.00": 150,  # Software Developers
    "13-2011.00": 45,   # Accountants
    "43-4051.00": 200,  # Customer Service Reps
    "11-1021.00": 25,   # General Managers
    "29-1141.00": 80,   # Registered Nurses
}

# Get custom scores
result = client.calculate_custom_score(
    weights=my_weights,
    jobs=list(workforce.keys()),
    aggregation="weighted_mean"
)

# Build scored portfolio
portfolio = []
for job in result["results"]:
    headcount = workforce[job["soc_code"]]
    portfolio.append({
        **job,
        "headcount": headcount,
        "exposure_headcount": headcount * (job["custom_score"] / 100)
    })

# Summary statistics
total_headcount = sum(p["headcount"] for p in portfolio)
weighted_exposure = sum(
    p["custom_score"] * p["headcount"] 
    for p in portfolio
) / total_headcount

print(f"\nPortfolio Summary:")
print(f"  Total Headcount: {total_headcount}")
print(f"  Weighted Average Exposure: {weighted_exposure:.1f}")

# By exposure tier
high_exposure = sum(p["headcount"] for p in portfolio if p["custom_score"] > 60)
print(f"  High-Exposure Headcount: {high_exposure} ({high_exposure/total_headcount:.0%})")
```

## Sensitivity Analysis

Test how different weights affect results:

```python
import numpy as np

# Base weights
base = {"procedural": 0.5, "digitization": 0.3, "physicality": -0.8, "socio_emotional": -0.5}

# Test sensitivity to physicality weight
physicality_values = [-1.0, -0.5, 0.0, 0.5]
test_job = "29-1141.00"  # Nurses

print(f"Sensitivity Analysis: {test_job}\n")
print(f"{'Physicality Weight':<20} {'Custom Score':>15}")
print("-" * 35)

for phys_weight in physicality_values:
    weights = {**base, "physicality": phys_weight}
    result = client.calculate_custom_score(weights, [test_job])
    score = result["results"][0]["custom_score"]
    print(f"{phys_weight:<20} {score:>15.1f}")
```

**Output:**

```
Sensitivity Analysis: 29-1141.00

Physicality Weight        Custom Score
-----------------------------------
-1.0                            35.2
-0.5                            42.8
0.0                             50.4
0.5                             58.0
```

## Building a Scenario Planner

```python
def run_scenarios(jobs: list[str], scenarios: dict, client):
    """Compare multiple scoring scenarios."""
    
    results = {}
    for name, weights in scenarios.items():
        result = client.calculate_custom_score(weights, jobs)
        results[name] = {
            job["soc_code"]: job["custom_score"] 
            for job in result["results"]
        }
    
    return results

# Define scenarios
scenarios = {
    "Base Case": {"procedural": 0.5, "digitization": 0.3, "physicality": -0.5, "socio_emotional": -0.5},
    "AI Accelerated": {"procedural": 0.8, "digitization": 0.6, "physicality": -0.3, "socio_emotional": -0.3},
    "Physical Matters": {"procedural": 0.4, "digitization": 0.4, "physicality": -1.0, "socio_emotional": -0.3}
}

jobs = ["15-1252.00", "29-1141.00", "43-3031.00", "47-2061.00"]
results = run_scenarios(jobs, scenarios, client)

# Display comparison
print(f"{'SOC':<12}", end="")
for scenario in scenarios:
    print(f"{scenario:<18}", end="")
print()

for job in jobs:
    print(f"{job:<12}", end="")
    for scenario in scenarios:
        print(f"{results[scenario][job]:<18.1f}", end="")
    print()
```

## Next Steps

* [API Reference: Custom Scoring](/api-reference/assessments.md#post-assessmentscustom-score) - Full endpoint documentation
* [Career Transitions](/guides/career-transitions.md) - Apply custom scores to transition analysis
* [Regional Analysis](/guides/regional-analysis.md) - Geographic custom scoring


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.maidenlabs.tools/guides/custom-scoring.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
