> ## Documentation Index
> Fetch the complete documentation index at: https://docs.canary.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Analyze supplied quotes

> Compare supplied policy quotes, check their terms, and summarize weighted results.

Supply policy contexts and quote records to `PremiumAnalysis`. Each method returns typed rows; `to_pandas()` produces a detached table for charts.

## Supply the records

These quotes are synthetic annual premiums. Policy B represents two policies, and policy C has a population row only:

```python theme={null}
from decimal import Decimal as D
from relativities.analysis import (
    PolicyExposure, PolicyKey, PolicyQuote, PremiumAnalysis, PremiumTerms, RatingSource,
)

population = [
    PolicyExposure(PolicyKey("A"), D(1), {"size": "small"}),
    PolicyExposure(PolicyKey("B"), D(2), {"size": "large"}),
    PolicyExposure(PolicyKey("C"), D(1), {"size": None}),
]
terms = PremiumTerms("USD", "annual_policy_premium", {"deductible": D(500)})
source = RatingSource("user_supplied", "supplied-quotes:version-1")
quotes = [
    PolicyQuote(PolicyKey("A"), "current", "succeeded", D(100), terms, source),
    PolicyQuote(PolicyKey("A"), "alternative", "succeeded", D(120), terms, source),
    PolicyQuote(PolicyKey("B"), "current", "succeeded", D(1000), terms, source),
    PolicyQuote(PolicyKey("B"), "alternative", "succeeded", D(900), terms, source),
]
analysis = PremiumAnalysis(population, quotes, baseline="current", candidate="alternative")
```

Choose every term field your comparison needs. Matching fields declare equality; source and wording review establish suitability. Use `RatingSource("rater_bundle", bundle_id)` for compiled rater outputs.

## Reuse a book or native rating results

`PremiumAnalysis.from_book(book, quotes, ...)` pairs supplied quotes with every submission of a `Book` or `Study`. `PremiumAnalysis.from_market(results, book, ...)` converts policy-level `Market.rate` rows.

Both map submission columns through `segments`, `weight`, `profile`, `exposure`, `losses` and `claim_count`, and keep unquoted submissions in the population. The `from_market` callback `terms` returns each successful row’s `PremiumTerms`.

[Model a commercial book](https://docs.canary.effectiveai.app/relativities/how-to/model-commercial-books.md) uses `from_market` with a payroll exposure base and a calibrated mix.

## Inspect policy comparisons

```python theme={null}
[(change.status, change.delta) for change in analysis.premium_changes()]
# [('paired', Decimal('20')), ('paired', Decimal('-100')), ('unrated', None)]
```

Policy C stays in the population as unrated. `pair_results()` returns each policy’s two quotes, and `check_comparability()` lists each term difference.

## Summarize the population

```python theme={null}
portfolio = analysis.summarize()[0]
portfolio.paired_weight, portfolio.baseline_premium, portfolio.candidate_premium
# (Decimal('3'), Decimal('2100'), Decimal('1920'))
```

The totals use positive-weight comparable policies, so B counts twice. `summarize(by=("size",))` returns one summary per segment, and each scenario and draw keeps its own summary.

## Apply another mix

Supply one Decimal count weight per policy context:

```python theme={null}
equal_mix = analysis.with_weights({exposure.key: D(1) for exposure in population})
round(equal_mix.summarize()[0].portfolio_change_percent, 2)
# Decimal('-7.27')
```

The new weights reuse each policy’s quotes. `analysis.calibrate(targets)` derives the weights from target counts by segment. Changed rating inputs require updated quotes.

## Inspect a term mismatch

Give the alternative a higher deductible:

```python theme={null}
from dataclasses import replace

higher = replace(terms, values={"deductible": D(1000)})
changed = [replace(q, terms=higher) if q.program == "alternative" else q for q in quotes]
mismatch = PremiumAnalysis(population, changed, baseline="current", candidate="alternative")
mismatch.check_comparability()[0].differences
# (TermDifference(field='values.deductible', baseline=Decimal('500'), candidate=Decimal('1000'), kind='mismatch'),)
```

Each mismatched policy keeps its quotes with the status `basis_mismatch`, and its premium change is `None`.

## Count what was rated

```python theme={null}
counts = analysis.rateability()[0]
counts.candidate.succeeded, counts.candidate.not_attempted, counts.comparison["paired"]
# (2, 1, 2)
```

Each program’s counts separate succeeded, ineligible, unratable and unattempted contexts, with reason-code counts. Report them beside every result.

## Show the distribution of changes

```python theme={null}
spread = analysis.summarize(percentiles=(0.1, 0.5, 0.9), buckets=(-10, 0, 10))[0]
spread.delta_percent_percentiles[D("0.5")], round(spread.premium_cheaper_share, 4)
# (Decimal('-10.0'), Decimal('0.9524'))
```

A percentile is the smallest observed change whose cumulative weight share reaches the level. Buckets include their lower edge. The alternative is cheaper for 95% of baseline premium, because it prices the larger policy lower.

## Compare premium with losses

`double_lift` sorts paired policies by price ratio, candidate over baseline premium, and compares each group's actual losses with both programs' premium. Supply losses as `PolicyExposure.losses`, or with `from_book(losses=...)`:

```python theme={null}
from dataclasses import replace

experience = [replace(p, losses=loss) for p, loss in zip(population, (D(60), D(900), None))]
lift = PremiumAnalysis(experience, quotes, baseline="current", candidate="alternative")
[(g.group, g.policy_count, g.baseline_loss_ratio) for g in lift.double_lift(edges=(1,))]
# [('lt_1', 1, Decimal('0.9')), ('ge_1', 1, Decimal('0.6'))]
```

The alternative is cheaper where the current loss ratio is 90%, and dearer where it is 60%. Losing the cheaper group would improve the current book. Low current loss ratios in the cheaper groups would mark profitable business at risk. Omit `edges` for equal-weight groups, and add `claim_count` for each group's credibility.

## Spread across simulated draws

When a `Study` fills unknown risk facts, each draw rates its own version of the book. `draw_ranges` summarizes every draw, then reports quantiles across draws:

```python theme={null}
draws = [PolicyExposure(PolicyKey("A", "baseline", d)) for d in (1, 2, 3)]
drawn = [
    PolicyQuote(PolicyKey("A", "baseline", d), program, "succeeded", D(amount), terms, source)
    for d, pair in zip((1, 2, 3), [(100, 110), (100, 95), (100, 120)])
    for program, amount in zip(("current", "alternative"), pair)
]
ranges = PremiumAnalysis(draws, drawn, baseline="current", candidate="alternative").draw_ranges()
ranges.groups[0].portfolio_change_percent[D("0.5")]
# Decimal('10.0')
```

Draw 0 is the reported book, with its blanks unfilled; pass `include_draw_zero=True` to include it. These quantiles describe the spread of draws, rather than confidence intervals.

Model an unknown that applies to a whole program, such as a redacted factor, as separate low, middle and high scenarios. Drawing it policy by policy averages it away.

## Rank several programs

```python theme={null}
from relativities.analysis import PremiumRanking

third = [
    PolicyQuote(PolicyKey("A"), "third", "succeeded", D(110), terms, source),
    PolicyQuote(PolicyKey("B"), "third", "succeeded", D(950), terms, source),
]
ranking = PremiumRanking(population, [*quotes, *third], programs=("current", "alternative", "third"))
[(s.program, round(s.cheapest_share, 4)) for s in ranking.summarize()]
# [('current', Decimal('0.3333')), ('alternative', Decimal('0.6667')), ('third', Decimal('0.0000'))]
```

Shares and totals use the policies every program quoted. `ranking.to_pandas()` shows each policy’s rank and its gap to the cheapest program.

The [analysis reference](https://docs.canary.effectiveai.app/relativities/reference/analysis.md) lists the typed records, methods, and dataframe functions.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.