Understanding Functions in spaciv

Every card in a spaciv visualisation shows a number, a bar, a percentage, something calculated from your portfolio data. The function is what defines that calculation. Once you understand how functions work, you can read any card in the platform and know exactly where its numbers come from, and start building your own.

What a function actually does

A function tells spaciv two things: which objects to look at, and what to calculate from them. Every function is built from one or more building blocks called sumof terms, combined with basic math.

A single sumof term looks like this:

sumof("Object Type" is "Space", "Area")

This says: look at all objects where the Object Type is Space, then sum up their Area. The result is the total floor area of every space in the current context.

Selection and operation

Every sumof term has two parts, separated by a comma:

The selection decides which objects are included. The operation is the calculation run across them. It's usually a single property, but it can also be a small formula, such as multiplying two properties together:

sumof("Object Type" is "Space", "Area" * "Count")

This multiplies each space's Area by its Count before summing the results, useful when a single row represents more than one identical object.

How filters affect functions

A function is rarely the only thing filtering your data. By the time a number reaches a card, several layers of context have already been applied on top of the function itself:

  1. Scenario — different scenarios can represent different states of your portfolio.
  2. Project-level variables — applied across the whole project.
  3. Dashboard filters — set for the specific dashboard you're viewing.
  4. Card-level context — which node you're focused on, or how you've categorised the data.

The function is evaluated last, within all of that context. This is why the same function can show different numbers on different dashboards, or different cards, even though the underlying calculation never changed. If a number ever looks unexpected, it's worth checking these layers before assuming the function itself is wrong.

Take sumof("Object Type" is "Space", "Area" * "Count"). This sums the total area of all spaces if and only if the filters of this visualisation, or the segmentation, don't constrain it further. If you segment by Country in your card, then the data you're aggregating is partial, which means you are filtering for Country is "UK" AND Object Type is "Space", for the UK value. The function itself hasn't changed, but what it's summing has been narrowed by the the segmenting which is applied in the card.

Combining multiple sumof terms

A function isn't limited to a single sumof. It's really just a math expression, and sumof terms are one kind of value you can use inside it, alongside plain numbers and variables. That makes the pattern quite flexible:

A function can be pure math with no sumof at all, a constant added to a single sumof, or several sumof terms combined together, each with its own selection and operation, joined by normal operators like +, -, *, and /.

The most common reason to combine two sumof terms is to calculate an average:

sumof("Object Type" is "Space", "Area") / sumof("Object Type" is "Space", "Count")

Here, the total area is divided by the total count, giving the average area per space. Each sumof runs its own filter and calculation independently, so the two terms don't need to share the same selection, though in this example they do.

Using variables

Functions can reference variables by name, using dollar signs around the variable name, for example $Occupancy Target$. This is especially useful for two situations:

A quick worked example

Imagine a dataset of eight objects: five spaces and three consumers (people or equipment assigned to a space). Each space has an Area property.

Change the selection to "Object Type" is "Consumer" and the same math now describes consumers instead, without touching the underlying data at all. This is the core idea behind functions: the same building blocks can answer very different questions, just by changing what you select.

Using segments in functions

When you segment a visualisation, for example by Attendance (what percentage of people is in/out of office), you sometimes want to visualise each segment in the context, like calculating the segment's proportional size. In the function, the segment keyword can be used to control which calculations are run on the segment (e.g. users in office) from those that are run on the entire dataset (all users).

Example

You're segmenting a card by Attendance, so each segment represents one attendance status (in office, out of office).

sumof("Object Type" is "Consumer", "Count" * segment) / sumof("Object Type" is "Consumer", "Count")

The numerator uses segment, so it only counts people within the current segment, for example just the "In Office" group. The denominator leaves segment out, so it counts everyone, across all attendance statuses. Dividing the two gives the percentage of people in the specific segment out of the total, the small percentage over the big one. The function runs twice, as we have two segments. Both results are presented in one visualization.

Best practice: set up properties in functions before you need them

It's worth including certain properties in your functions early, even if they have no effect yet, because they tend to become relevant later. Count and Growth are good examples: they might not matter for your first few visualisations, but they come up often enough that it's easier to build them into functions from the start than to retrofit them afterward. The same logic applies to Enterprise accounts working with costs. As soon as a project starts dealing with currency, it's worth having a dedicated property such as Exchange Rate set up from the beginning, so it's already there to reference the moment you start calculating, rather than adding it in after the fact.

Key takeaways