Signals now supports calculated properties

Your tracking is in place and events are flowing. Now your team wants your website to show a same-day delivery banner to shoppers who usually browse from London, England, before the page renders. geo_city alone can’t tell London, England from London, Ontario. Your events already carry geo_country too, but a Signals attribute, the value kept up to date for each user, needs the two as one value, like GB-London.

Calculated properties now handle that for you in Signals. You combine the fields in the attribute definition with concat, sum, product, min, max, or coalesce, and Signals computes the value.

Music only, with the explanation on screen. 43 sec

How calculated properties work

Three rules apply to any calculated property:

  1. What you can combine. Snowplow’s out-of-the-box atomic fields, such as geo_country, and properties from event and entity schemas, including your own custom ones, in any mix.
  2. When Signals computes it. In-stream as each event arrives. With a backfill, it also covers the events already in your warehouse, so a new attribute starts with history.
  3. How it feeds the aggregation. Each event gets its own value, like GB-London, and the attribute’s aggregation then runs across all of a user’s events.

Example: finding the right London

Take a shopper who browses from London, Ontario. An attribute built on geo_city alone returns London, so they see a same-day delivery offer that can’t reach them.

Diagram. On the left, geo_city on its own: the value London could mean London, England or London, Ontario. On the right, a concat of geo_country and geo_city: GB plus London gives GB-London for London, England, and CA plus London gives CA-London for London, Ontario, so the delivery banner reaches the right shoppers.

The obvious fix is a second attribute for the most frequent country, but that breaks too. A shopper from London, England who spends a week on vacation in Paris and Lyon can end up with France as their most frequent country and London as their most frequent city. Their attributes return FR and London, so the banner’s check for GB and London fails, and they miss an offer they could use.

Diagram. A London shopper back from vacation has GB and London 3 times, FR and Paris 2 times, and FR and Lyon 2 times. With two separate attributes, the most frequent country is FR with 4 events and the most frequent city is London with 3 events, giving FR plus London, which matches none of the events. With one calculated property, concat produces GB-London, FR-Paris, and FR-Lyon on each event, and most_frequent returns GB-London, a place the user browsed from.

A calculated property joins geo_country and geo_city on each event, so the attribute counts real places: CA-London for the Ontario shopper, and GB-London for the one back from vacation.

You can build this attribute in three ways.

In the Console UI

Tick geo_country and geo_city in the attribute’s property picker. A panel appears where you set their order, the operation, and a separator.

The property picker in Snowplow Console with geo_country and geo_city ticked in the Atomic tab. The calculated property panel above shows both as reorderable chips, the concat operation selected, and a hyphen as the separator.

With concat and a hyphen, each event contributes a value like GB-London, and a most_frequent aggregation returns the one that comes up most.

With an AI agent

Ask the Snowplow Assistant in Console, or any agent connected to the Snowplow MCP server:

“Create an attribute with the country and city a user browses from most often.”

In the Python SDK

from snowplow_signals import Attribute, AtomicProperty, CalculatedProperty

usual_location = Attribute(
    name="usual_location",
    description="Country and city pair a user browses from most often",
    type="string",
    events=[],
    aggregation="most_frequent",
    property=CalculatedProperty(
        operation="concat",
        separator="-",
        properties=[
            AtomicProperty(name="geo_country"),
            AtomicProperty(name="geo_city"),
        ],
    ),
)

What’s in this release

There are six operations to choose from, each shown with an example personalization use:

  1. concat joins values into a string, with an optional separator: mkt_source and mkt_medium give google-cpc, to tailor the landing message to the channel a user arrived from.
  2. sum adds numbers: subtotal, tax, and shipping give an order’s total, which a sum aggregation turns into lifetime spend for loyalty offers.
  3. product multiplies numbers: price and quantity on an add-to-cart event give the value of the items added, for a free-shipping nudge.
  4. min takes the smallest value: the lower of member_price and standard_price is the price to show a member.
  5. max takes the largest value: the higher of list_price and compare_at_price is the original price to show next to a discount.
  6. coalesce returns the first property that has a value: sale_price, then list_price, on a purchase event gives what a user actually paid, for price-sensitive offers.

Calculated properties are available in the Console UI, through the Snowplow Assistant and the MCP server, and in the Python SDK from snowplow-signals 0.4.9.

To build your own, see the calculated properties documentation.

Agnes Kiss

Data Engineer, Snowplow