> For the complete documentation index, see [llms.txt](https://docs.mediarithmics.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mediarithmics.io/setup-and-monitor/plan-and-integrate/set-up-advanced-features/affinity-and-intention-ml-algorithm.md).

# Affinity & Intention

Add computed and predictive data - affinity totals, propensity scores, cohorts - to your user graph.

## Your goal

Enrich your user graph with data you do not collect but calculate: affinity totals, propensity and intention signals, predicted attributes. In mediarithmics these are produced by objects declared in your schema and backed by algorithms hosted on the platform.

## Expected outcome

Fields that behave like any other field in a query or a segment, but whose values are computed rather than collected - so your teams can segment on "high affinity with IT products" without writing the aggregation themselves.

## Why this exists

User data alone is not enough for personalised marketing. The datamart therefore manages **collected data and computed data homogeneously**: both are declared in the same schema, and from the outside nothing distinguishes them. From the inside, computed data is associated with algorithms loaded into the platform as [plugins](/setup-and-monitor/extend-the-platform/plugin-architecture.md).

The algorithm that produces predictive data on a user is a **Machine Learning Function** (ML Function). Like every other plugin, it is modifiable and customisable.

## The three mechanisms, and when to use which

{% tabs %}
{% tab title="Computed field" %}
A **computed field** is a dynamic field defined in the schema, linked to a script that is triggered for each new user activity or profile update, and that performs a computation on a regular basis.

Use it when the outcome cannot be achieved with existing schema directives, or to simplify a query by replacing several directives with a single field. Because the value is precomputed, queries using it respond faster.

Typical uses include a **weighted sum such as an affinity total**, the most recent event of a type, the total amount for a type of event over a period, or a comparison between channels.

Full details: [Computed fields](/setup-and-monitor/collect-and-manage-your-data/design-your-data-model-and-schema/computed-fields.md).
{% endtab %}

{% tab title="ML Function" %}
An **ML Function** adds live predictive data to a user graph. A [UserScore](/setup-and-monitor/understand-mediarithmics/how-mediarithmics-works.md#information-attached-to-a-userpoint) - a set of data calculated dynamically that provides predictive information on a user - is connected to an ML Function.

A UserScore can predict, for example, expected lifetime value, churn risk, age, or gender.

ML Functions are managed in **Settings > Datamart > ML Functions**.
{% endtab %}

{% tab title="ML Algorithm" %}
An **ML Algorithm** (Machine Learning Algorithm) manages the calculation of predictions attached to a user in an **event-based** manner. An ML Algorithm is used in conjunction with an ML Function.

ML Algorithms are managed in **Settings > Datamart > ML Algorithms**.
{% endtab %}
{% endtabs %}

## Set up an ML Function

The shape of the work is the same whichever prediction you are adding.

{% stepper %}
{% step %}

### Decide the attributes the model reads

Choose the user attributes the calculation is based on. Pick attributes that are relevant to the business, available on both logged-in and anonymous users where possible, and drawn from more than one part of the graph - events, profiles, device information.
{% endstep %}

{% step %}

### Write the query that feeds it

An ML Function requires a query to fetch the data used in its configuration. It is a GraphQL query returning exactly the fields the model needs, for example:

```graphql
{agents {user_agent_info {os_family}} profiles {age} events{city}}
```

See [GraphQL queries](/setup-and-monitor/operate-and-monitor/query-your-datamart/graphql-queries.md).
{% endstep %}

{% step %}

### Instantiate the function

Go to **Settings > Datamart > ML Functions** and create a new ML Function, choosing the datamart it applies to and the algorithm behind it. The configuration panel asks for:

| Field               | What it sets                                                            |
| ------------------- | ----------------------------------------------------------------------- |
| Name                | How you recognise the function.                                         |
| Hosting Object Type | The object the computed value is attached to, for instance `UserPoint`. |
| Field Type Name     | The type of the field added to the schema.                              |
| Field Name          | The name of the field as it will appear in queries.                     |
| Query               | The GraphQL query from the previous step.                               |
| Properties          | The algorithm's own parameters.                                         |
| {% endstep %}       |                                                                         |

{% step %}

### Update the schema and activate

Add the field to your schema so it can be queried, then activate the function. Activation is done through the API:

```json
PUT https://api.mediarithmics.com/v1/ml_functions/<ml_function_id>
{
  "batch_mode": true,
  "status": "ACTIVE"
}
```

{% endstep %}

{% step %}

### Let it load initially

An initial computation runs over your existing users. Expect it to take time before values are available on every UserPoint.
{% endstep %}
{% endstepper %}

## Set up the Cohort-based Lookalike model

A [Cohort lookalike segment](https://app.gitbook.com/s/XSPACE_AUDIENCE/build-your-audiences/segment-reference/segment-types/cohort-lookalike-segment) expands a seed audience through **cohorts**: groups of users that share the attributes you chose to characterise your user base. The cohort model is an ML Function like any other, but it has to exist on the datamart before anyone can build such a segment, and its attributes are an organisation-level decision rather than a per-segment one.

{% hint style="warning" %}
Only **one** Cohort-based Lookalike model can be set up at a time in an organisation, so agree on the attributes before you create it. Changing them later means rebuilding the model.
{% endhint %}

The five steps below have to be performed **in this order**.

{% stepper %}
{% step %}

### Define the attributes

A cohort is assigned to a user according to the attributes - features, in data-science terms - you have defined to characterise your users. You declare them as JSON.

Three attribute types are available:

| Type               | Use it for                                                           | How the values are declared                                                                                        |
| ------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `FREQUENCY_ENUM`   | A finite list of values, such as operating systems.                  | `values`: the list itself.                                                                                         |
| `FREQUENCY_NUMBER` | Number buckets, such as age.                                         | `intervals`: `from` inclusive, `to` exclusive. Anything outside the declared intervals falls into an extra bucket. |
| `FREQUENCY_TEXT`   | An infinite or very long list of values, such as keywords or cities. | `vector_size`: used as a modulo on the values, to reduce their disparity to a fixed number. Choose it carefully.   |

`field_path` is the path of the attribute from the `UserPoint` definition in your [schema](/setup-and-monitor/collect-and-manage-your-data/design-your-data-model-and-schema/schema-definition.md).

Cohorts built on `os_family` (on `UserAgentInfo`, nested in `UserAgent`), `age` (on `UserProfile`) and `city` (on `UserEvent`) are declared like this:

```json
[
  {
    "type": "FREQUENCY_ENUM",
    "field_path": "agents.user_agent_info.os_family",
    "values": [
      "OTHER",
      "WINDOWS",
      "MAC_OS",
      "LINUX",
      "ANDROID",
      "IOS"
    ]
  },
  {
    "type": "FREQUENCY_NUMBER",
    "field_path": "profiles.age",
    "intervals": [
      {
        "from": 0,
        "to": 10
      },
      {
        "from": 10,
        "to": 100
      }
    ]
  },
  {
    "type": "FREQUENCY_TEXT",
    "field_path": "events.city",
    "vector_size": 100
  }
]
```

{% hint style="info" %}
**How the number buckets above read.** First bucket: `age >= 0` and `age < 10`. Second bucket: `age >= 10` and `age < 100`. Third bucket: everything that fell into neither.
{% endhint %}

Recommendations for the selection itself:

* Pick attributes that can be used to segment users and that are relevant to the business.
* Pick attributes available on all your users, logged in and logged out.
* Pick attributes from various typologies: `UserEvent`, `UserProfile`, and so on.
* Select between **3 and 10** attributes, carrying **between 50 and 300 values** in total.
* Keep the default of **1024 cohorts**, which is a `Cohort Id Bit Size` of 10.

The function also needs the GraphQL query that fetches exactly those fields. For the attributes above:

```graphql
{agents {user_agent_info {os_family}} profiles {age} events{city}}
```

{% endstep %}

{% step %}

### Create the ML Function

Go to **Settings > Datamart > ML Functions** and click **New Ml Function**, pick the datamart the function applies to, then choose the **simhash-cohorts-calulation** algorithm. Fill the configuration panel with:

| Field                           | Value                                                                   |
| ------------------------------- | ----------------------------------------------------------------------- |
| Name                            | `Cohort ML Function`                                                    |
| Hosting Object Type             | `UserPoint`                                                             |
| Field Type Name                 | `ClusteringCohort`                                                      |
| Field Name                      | `clustering_cohort`                                                     |
| Query                           | The GraphQL query from the previous step.                               |
| Properties > Features           | The attributes JSON from the previous step, on one line.                |
| Properties > Cohort Id Bit Size | Sets the number of cohorts in your datamart, as 2^(Cohort Id Bit Size). |

Then save.
{% endstep %}

{% step %}

### Activate it

Set `batch_mode` to `true` and the status to `ACTIVE`:

```json
PUT https://api.mediarithmics.com/v1/ml_functions/<ml_function_id>
{
  "batch_mode": true,
  "status": "ACTIVE"
}
```

{% endstep %}

{% step %}

### Update the schema

Two changes in your runtime schema. Add a `clustering_cohort` field on `UserPoint`:

```graphql
type UserPoint  @TreeIndexRoot(index:"USER_INDEX") {
   ...
   clustering_cohort:ClusteringCohort
   ...
}
```

And create the `ClusteringCohort` type:

```graphql
type ClusteringCohort  {
   id:ID! @TreeIndex(index:"USER_INDEX")
   expiration_ts:Timestamp @TreeIndex(index:"USER_INDEX")
   cohort_id:String! @TreeIndex(index:"USER_INDEX")
   last_modified_ts:Timestamp! @TreeIndex(index:"USER_INDEX")
}
```

See [Schema definition](/setup-and-monitor/collect-and-manage-your-data/design-your-data-model-and-schema/schema-definition.md) for how a schema change is published.
{% endstep %}

{% step %}

### Ask for the initial loading

Cohorts on your existing UserPoints are calculated by an initial loading, which the mediarithmics team runs on your datamart. Ask your account manager for it once the four steps above are done.
{% endstep %}
{% endstepper %}

## Stories: ready-made Intention and Affinity scores

The three mechanisms above are the ones you build yourself. **Stories** is a suite of two scoring algorithms mediarithmics runs for you, producing predictive scores about a user's purchasing behaviour from the browsing and transactional data you already collect. Both are recalculated in real time, as soon as a session ends and new events are ingested, and both surface directly in the [Segment builders](https://app.gitbook.com/s/XSPACE_AUDIENCE/build-your-audiences/segment-reference/segment-builders).

{% hint style="warning" %}
Neither score is enabled by default. To activate one, contact your Customer Success Manager and tell them the product scope - a category for Intention, a category or a brand for Affinity - and the channels it should cover.
{% endhint %}

### What each one tells you

|                                                 | Stories Intention                                     | Stories Affinity                                                  |
| ----------------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------- |
| **Answers**                                     | Is this user in an active purchasing cycle right now? | How interested is this user in this product category, in general? |
| **Nature**                                      | Ephemeral - tied to one purchasing cycle              | Persistent - a long-term view of preferences                      |
| **Built from**                                  | The sequence of browsing interactions                 | Browsing history, add-to-cart events, and purchases               |
| **Can score a category the user never touched** | No                                                    | Yes, through correlation across your user base                    |
| **Typical use**                                 | Reach hot prospects earlier than cart abandoners      | Audience extension and content personalisation                    |

### Prerequisites

Both algorithms read the same three events, but they need different properties on them.

| Event        | Intention requires       | Affinity requires                    |
| ------------ | ------------------------ | ------------------------------------ |
| Product view | `category`, `product_id` | `category`                           |
| Add to cart  | `category`, `product_id` | `category`                           |
| Purchase     | `category`, `product_id` | `category`, `unit_price`, `quantity` |

The properties must be captured at the event level. If your current collection does not fully meet this, your CSM can look at adapting the setup to your situation.

### Stories Intention: stories, scores and ranks

When a user's sequence of interactions matches a pattern associated with a high probability of purchase, a **story** opens for that user on the relevant product category, and evolves as they keep interacting.

Because one user can be in several purchasing cycles at once, each story carries a **rank** reflecting its relative likelihood of conversion. Rank 1 is the story with the highest purchase probability, and up to 10 simultaneous stories can be surfaced per user, each with its own rank, score and product context.

Each story also carries an intensity score:

| Score  | Interpretation                                                                   |
| ------ | -------------------------------------------------------------------------------- |
| High   | The user is highly likely to convert in the short term                           |
| Medium | The user is actively browsing but has not yet reached a strong conversion signal |
| Low    | The user has begun showing early interest signals                                |

{% hint style="info" %}
**Why the rank matters.** A user has three active stories: `Running shoes` (rank 1), `Sports nutrition` (rank 2), and `Fitness equipment` (rank 3). Targeting rank 1 only sends that user the most conversion-ready message, and limits marketing pressure at the same time.
{% endhint %}

A story **closes** when a purchase is captured, online or in store, or after a period of inactivity. The inactivity window is determined algorithmically per product category, reflecting how long that category's purchasing cycles naturally run, and is recalculated frequently from observed purchase data. Every new relevant interaction on the same category extends the story's lifetime.

### Stories Affinity: persistent interest, including hidden interest

The Affinity score reflects a user's degree of interest in a product category, computed from everything available: browsing history, add-to-cart events, and purchases. It uses the same three intensity levels - High, Medium, Low - reading as strong, meaningful, and emerging interest respectively.

Its distinctive capability is surfacing **hidden affinities**. By analysing correlation patterns across your whole user base, it can score a user on a category they have never directly interacted with.

{% hint style="info" %}
**A hidden affinity.** A user who recently bought a TV can receive a High affinity score for `Home Cinema` without ever having visited that category, because the algorithm has detected that users who interact with TV products tend to be interested in Home Cinema products too.
{% endhint %}

A user holds one affinity score per product category within the configured scope, and simply has no score for a category they show no signal on.

### Using the scores in a segment

Both scores surface directly in the segment builders, so the audience work itself needs no query language. Which score answers which question, how the intensity levels and the Intention ranks translate into a segment, and the activation patterns they support are covered by [Target users by affinity and intention](https://app.gitbook.com/s/XSPACE_AUDIENCE/build-your-audiences/target-users-by-affinity-and-intention) in the `Audience & Activation` section.

## Verify

* The computed field or score appears in your schema and can be selected in a query.
* A query on the new field returns values, not nulls, once the initial computation has run.
* A segment built on the new field returns a plausible population size.
* For the cohort model: `clustering_cohort` appears on `UserPoint` in the published schema, and a lookalike can be created from a seed segment.

## Troubleshooting

| Symptom                                                   | Likely cause                                                                                                                                                                                        |
| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The field exists but every value is empty                 | The initial computation has not finished, or the function is not `ACTIVE`.                                                                                                                          |
| Values look stale                                         | Computed fields are recalculated on a regular basis, not on every read.                                                                                                                             |
| A computed field update is not saved                      | The computed field `State` exceeds its 1 MB maximum, in which case the update is discarded. See [Platform limits](/setup-and-monitor/plan-and-integrate/set-up-the-environment/platform-limits.md). |
| Cohorts are missing on users who existed before the model | The initial loading has not run. It is triggered by the mediarithmics team: ask your account manager.                                                                                               |

## Related pages

<table data-view="cards" data-search="false"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Computed fields</strong></td><td>The schema-side mechanism, with worked examples.</td><td><a href="/setup-and-monitor/collect-and-manage-your-data/design-your-data-model-and-schema/computed-fields.md">Computed fields</a></td></tr><tr><td><strong>Build a computed field</strong></td><td>Writing the code behind a computed field.</td><td><a href="/setup-and-monitor/extend-the-platform/build-a-computed-field.md">Build a computed field</a></td></tr><tr><td><strong>Plugin architecture</strong></td><td>How the platform hosts and runs your algorithms.</td><td><a href="/setup-and-monitor/extend-the-platform/plugin-architecture.md">Plugin architecture</a></td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.mediarithmics.io/setup-and-monitor/plan-and-integrate/set-up-advanced-features/affinity-and-intention-ml-algorithm.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
