Skip to content
Varlig Guide
Varlig home

Lists and statistics

A list keeps several values together under one name, such as a week of step counts, a set of exam marks or a month of receipts. Once the values are in a list, you can total them, average them, measure how spread out they are, and pick out the ones that matter.

This page covers writing and indexing lists, summary statistics, adding up the lines above, labelling lines with tags so you can total them wherever they are, sorting and slicing, transforming a list with map and filter, and arithmetic on whole lists. Reach for it whenever you have a column of figures to summarise. Lists of rows can also act as matrices, which have their own page.

Writing a list

Put the values in square brackets, separated by commas:

calc
scores = [72, 85, 91, 64, 88] gives [72, 85, 91, 64, 88]
scores[0] gives 72
scores[4] gives 88
length(scores) gives 5

A value’s position in the list is its index, written in square brackets after the list. Indexes start at zero, so scores[0] is the first mark and scores[4] is the fifth. The last element is always at length(scores) - 1. A negative, fractional or too-large index is an error; indexes don’t wrap around from the end.

Elements can be calculations, quantities with units, or other lists:

calc
[2 * 45, 60 + 15] gives [90, 75]
[1.5 kg, 750 g] gives [1.5 kg, 750 g]
[[1, 2], [3, 4]][1][0] gives 3

With nested lists, the first index picks the inner list and the second picks a value from it: [1] selects [3, 4], then [0] selects 3.

Thousands separators work inside a list, so you can paste figures as they’re written. Put a space after each comma between values, and a comma followed by three digits stays part of the number:

calc
salaries = [£32,000, £41,500, £38,250] gives [£32,000.00, £41,500.00, £38,250.00]
average(salaries) gives £37,250.00

A few things to watch for:

  • Without spaces, every comma separates, so [8200,10400] has two values. A list such as [100,200,300] could be one number or three, so Varlig shows an error that spells out both.
  • Answers show thousands separators and a space after each comma, so [8,200, 10,400] is a list of two values.
  • A list holds at most 1,024 values.

Summary statistics

calc
scores = [72, 85, 91, 64, 88]
sum(scores) gives 400
average(scores) gives 80
median(scores) gives 85
min(scores) gives 64
max(scores) gives 91
count(scores) gives 5

mean and avg are other names for average, total for sum, stdev for stddev and count for length, so spreadsheet habits carry over. product multiplies the values together. For the most common value, a percentile or a quartile, see below.

With a named list, you can also write the statistic in words:

calc
scores = [72, 85, 91, 64, 88]
average of scores gives 80
total of scores gives 400

sum of, mean of, median of, min of, max of and count of work the same way. After of, write a name or the values themselves, separated by commas, with or without round brackets. For values in square brackets, use the function form, as in average([72, 85, 91]).

Most statistics, including sum, product, average, median, min, max, count and stddev, also accept separate values instead of a list:

calc
max(72, 85, 91) gives 91
sum(1, 2, 3, 4) gives 10
average of 70, 85, 91 gives 82
mean of (1,2,3) gives 2
average(1,200, 1,800) gives 1,500

Adding up the lines above

A line that says only sum, total, subtotal, grand total, average, max or min works out that figure for the answers above it. It covers its own section, which starts at the previous total, a --- divider or a # heading, so each part of a note gets its own figure as you type. It works like the Subtotal option in a line’s context menu:

calc
# Groceries
bread = £1.20
milk = £1.45
eggs = £2.30
total gives £4.95
# Household
bin bags = £3.50
washing up liquid = £1.80
total gives £5.30

The word can carry a label, take a name of its own, convert the answer or have a comment after it, so the line reads like the rest of your note:

calc
tickets = £64
train = £41.20
Estimated total: total gives £105.20

est = total stores the figure under a name, and total // still to book adds a note beside it. total in kg converts before it shows, which is handy when a section mixes grams and kilograms. A statistic word beside an operator is the figure its own line would show, so £5 + subtotal adds a fiver to the running total.

calc
# Parcels
12 kg gives 12 kg
4 kg gives 4 kg
850 g gives 850 g
total in kg gives 16.85 kg
max gives 12 kg

If you’ve given a value the name sum or total, the line shows your value instead. When there’s nothing above to work with, the line says Unsupported: no amounts above to add up rather than showing a blank or a zero, which usually means a heading you’d forgotten was there.

Each amount is counted once. When a later line in the section works something out from a named amount, the total counts that line rather than both, so a shopping list of unit prices and quantities adds up to what you’ll actually pay:

calc
shirt = £18.50
shorts = £9
9 * shirt gives £166.50
10 * shorts gives £90.00
fee = £40
vat = fee * 20% gives £8.00
total gives £304.50

shirt and shorts are counted inside the two lines that use them, while fee stays in the total because vat only takes a share of it.

That follows a chain of names as far as it goes. Where a note splits an amount into shares and then adds the shares back up, the line that adds them counts, and so does the amount they were shares of:

calc
pot = £900
1st = 50% of pot gives £450.00
2nd = 30% of pot gives £270.00
3rd = 20% of pot gives £180.00
1st + 2nd + 3rd gives £900.00
total gives £900.00

Only like amounts are added. Percentages, rates and plain counts are left out when there’s money or a quantity above, and so is a line with an error. Plain numbers pasted in a column are added when they’re written to the penny, the way the amounts are; one carrying more decimals, such as an exchange rate or a factor, is left out. The total’s hint names everything it left out, such as “Leaves out line 3 (people = 2)” or “Leaves out line 2 (an error)”, so you can see at a glance what’s in the figure.

A list of two or more amounts written without names, straight above the total and below a line worked out from names, is added on its own. That way a budget worked out at the top of a note isn’t counted again in the list below it:

calc
budget = £18,000
remaining = budget - £6,388.25 gives £11,611.75
- skip hire £250 gives £250.00
- deep clean £120 gives £120.00
subtotal gives £370.00

To total lines that aren’t next to each other, label them with a tag instead.

Most common values

mode gives the value that turns up more often than any other, and modes gives every value tied for the most common, as a list:

calc
marks = [3, 4, 4, 5, 5, 5, 6]
mode(marks) gives 5
modes([1, 1, 2, 2, 3]) gives [1, 2]

mode answers only when one value is strictly the most common. If nothing repeats, or several values tie, it says so rather than picking one:

calc
mode([1, 2, 3]) gives Unsupported: No value repeats, so this list has no mode; use median or average instead

Percentiles and quartiles

percentile(list, rank) takes a rank from 0 to 100 and gives the value at that point, interpolating between the two nearest observations. quartile(list, k) is the same thing at quarters, with k from 0 to 4, so quartile(list, 1) is percentile(list, 25):

calc
scores = [72, 85, 91, 64, 88]
percentile(scores, 50) gives 85
percentile(scores, 25) gives 72
quartile(scores, 3) gives 88
quartile(scores, 4) gives 91
percentile([1, 2, 3, 4], 25) gives 1.75
quartile([1, 2, 3, 4], 3) gives 3.25

The 50th percentile is the median, and the 0th and 100th — quartile(list, 0) and quartile(list, 4) — are the smallest and largest values, the same as min and max. A rank outside its range is an error rather than the nearest value that would fit:

calc
percentile([1, 2, 3, 4], 101) gives Unsupported: A percentile is between 0 and 100
quartile([1, 2, 3, 4], 5) gives Unsupported: A quartile is 0, 1, 2, 3 or 4

Spread: variance and standard deviation

calc
scores = [72, 85, 91, 64, 88]
variance(scores) gives 132.5
stddev(scores) to 1 dp gives 11.5

Both are sample statistics: they divide by one less than the number of values (n - 1), which is the usual choice when your figures are a sample from something larger. They need at least two values. Add to 1 dp after any statistic to round it.

When your list is the whole population rather than a sample, use the population forms, which divide by n and need only one value:

calc
# Train delays in minutes
delays = [2, 4, 4, 4, 5, 5, 7, 9]
sample_variance(delays) gives 4.5714285714
population_variance(delays) gives 4
sample_stddev(delays) gives 2.1380899353
population_stddev(delays) gives 2
Call Divides the squared differences by Needs
variance, sample_variance n - 1 Two values
population_variance n One value
stddev, stdev, sample_stddev n - 1 Two values
population_stddev n One value

sample_variance and sample_stddev are longer names for variance and stddev, so you can spell out which one you mean. The short names have always been the sample forms and still are.

Lists with units

sum, average, median, mode, modes, percentile, quartile, min, max, stddev and population_stddev keep units, and convert between compatible ones. The values are read in the first one’s unit, and the answer comes back in it:

calc
commutes = [24 min, 31 min, 27 min, 22 min]
sum(commutes) gives 1 hour 44 min
average(commutes) gives 26 min
median(commutes) gives 25.5 min
percentile(commutes, 25) gives 23.5 min
population_stddev(commutes) gives 3.3911649916 min
sum([1 kg, 500 g]) gives 1.5 kg
modes([1 kg, 500 g, 1 kg, 500 g]) gives [0.5 kg, 1 kg]

The variances are the exception: they square their observations, and no unit survives that, so they take plain numbers only.

calc
sample_variance([1 kg, 2 kg]) gives Unsupported: A variance squares its observations, so it takes plain numbers; use stddev for amounts with a unit

sort also only works on plain numbers. A statistic on values with incompatible units, such as sum([1 kg, 3 m]), gives an error rather than quietly skipping values.

The exactness rules behind all of these are in What the engine guarantees.

Empty lists

Call on an empty list Result
sum([]) 0
product([]) 1
count([]), length([]) 0
average([]), median([]), min([]), max([]) Error
variance([]), stddev([]) Error

An empty list most often turns up as the result of a filter that matched nothing, so an average of a filtered list can fail even when the original list has values.

Tags

A tag is a # with a word joined straight on to it, such as #food. Write one anywhere on a line to label that line. The tag takes no part in the sum, so the line still shows its own answer, and it groups that line with every other line carrying the same tag. total of #food then adds those lines up:

calc
£12.50 lunch #food gives £12.50
£8 coffee #food gives £8.00
flights = £420 #trip gives £420.00
total of #food gives £20.50

That’s what a tag gives you that a subtotal can’t: the lines don’t have to sit next to each other. A week of spending, a trip and a shopping list can share one block, each with its own figure at the bottom. Hold the pointer over a tag to see what it adds up to so far.

Every statistic works on a tag. of is optional, the tag can come before or after the word, and the answer takes a label or a name of its own, as any other line does:

calc
#food £12.50
#food £8
£420 #trip
total of #food gives £20.50
average of #food gives £10.25
count of #food gives 2
min of #food gives £8.00
max of #food gives £12.50
#food total gives £20.50
Food this week: sum of #food gives £20.50
eaten = avg #food gives £10.25

sum, total, average, avg, mean, count, min and max all read this way.

A tag statistic stands where an amount does, so a calculation can carry on from it. That’s how you take a deposit off a running total, scale one up, or set two tags against each other:

calc
£20 shop #food gives £20.00
£30 meal out #food gives £30.00
total of #food - £10 gives £40.00
2 * total of #food gives £100.00
Cooking kit: total of #food gives £50.00

A line can carry as many tags as you like, and each one counts it. Name two tags in one phrase and you get the lines that carry both:

calc
£50 dinner #trip #food gives £50.00
£30 taxi #trip gives £30.00
£9 sandwich #food gives £9.00
total of #trip gives £80.00
total of #food gives £59.00
total of #trip #food gives £50.00

What a tag total covers

A tag phrase looks over exactly the same lines a plain total would, and follows the same rules:

  • It covers its own section, back to the previous total, a --- divider or a # heading.
  • It adds only like amounts. Percentages, rates, plain counts and lines with errors are left out, and the hint names them, such as “Leaves out line 2 (people = 2)”.
  • Each amount is counted once, so a tagged price that a later tagged line multiplies is counted in that line rather than in both.

So a heading is all it takes to keep one week’s #food apart from the next:

calc
# Week 1
£20 lunch #food gives £20.00
total of #food gives £20.00
# Week 2
£30 lunch #food gives £30.00
total of #food gives £30.00

When there’s nothing to add up, the line says so rather than showing a blank or a zero, which usually means a typo in the tag or a heading you’d forgotten was there:

calc
£12.50 lunch #food gives £12.50
total of #drinks gives Unsupported: no lines tagged #drinks above
ring the plumber #jobs
total of #jobs gives Unsupported: no amounts tagged #jobs above

Writing tag names

A tag name can hold letters, digits, _, - and /, so #q1-2026 and #trip/lisbon are each one name. Capitals make no difference:

calc
£20 flight #trip/lisbon gives £20.00
£35 hotel #Trip/Lisbon gives £35.00
total of #trip/lisbon gives £55.00

A # that isn’t joined to a word keeps its old meaning, so nothing you already write changes:

You write What it is
# Weekly shop A comment, because of the space after the hash
5 + 5 # comment The same: everything after the hash is ignored
C#4 in Hz A musical sharp, because the hash is joined to the note
#1, Invoice #123 A number, not a tag: a tag name can’t start with a digit
#ff0000, #fff A colour code
varlig.app/docs#tags A web address with a fragment

A tag name has to start with a letter, and three, four, six or eight hex digits after the hash are read as a colour, so #fee is a colour while #fees is a tag.

Tags in a note and tags in a block

The tags that organise your notes in the sidebar are described in Tags, links and backlinks. Those stop at the edge of a calc block: a #food you write inside a block labels that line for its total and never appears in the sidebar, and a #food in the note around the block files the note without touching your sums. Write the tag in both places when you want both, and note that the two differ in one detail: a tag in a note can hold spaces when you close it with a second #, while a tag in a calc block always ends at the first space.

List reference

Call What it does
range(start,end) Whole numbers from start to end, including both ends
range(start,end,step) Count by step, which can be negative or fractional but not zero
length(list), count(list) Number of values
sort(list) Smallest to largest; plain numbers only
reverse(list) The same values in reverse order; any values
unique(list) Keep the first occurrence of each value
slice(list,start,end) Values from index start up to, but not including, index end
map(expression,variable,list) Work out the expression once for each value
filter(condition,variable,list) Keep the values for which the condition is true
calc
range(1, 5) gives [1, 2, 3, 4, 5]
range(0, 100, 25) gives [0, 25, 50, 75, 100]
range(10, 0, -2) gives [10, 8, 6, 4, 2, 0]
scores = [72, 85, 91, 64, 88]
sort(scores) gives [64, 72, 85, 88, 91]
reverse(sort(scores)) gives [91, 88, 85, 72, 64]
slice(sort(scores), 0, 3) gives [64, 72, 85]
unique([4, 2, 4, 6, 2]) gives [4, 2, 6]

reverse(sort(…)) gives largest first, and slice(sort(…), 0, 3) picks out the three lowest.

Slice bounds must satisfy 0 <= start <= end <= length. Equal start and end give an empty list. There is no scores[0:2] shorthand; write slice(scores, 0, 2).

unique compares values exactly as they are stored, so 1 kg and 1000 g count as two different values, and two results that differ in the tenth decimal place aren’t merged.

Map and filter

map works out an expression for each value in a list. filter keeps the values that pass a test. In both, the expression comes first, then a name to stand for each value, then the list:

calc
scores = [72, 85, 91, 64, 88]
map(s + 5, s, scores) gives [77, 90, 96, 69, 93]
filter(s >= 80, s, scores) gives [85, 91, 88]
count(filter(s >= 80, s, scores)) gives 3
average(filter(s >= 80, s, scores)) gives 88

Read filter(s >= 80, s, scores) as “the values s in scores where s >= 80”. Both work with units and money:

calc
map(p * 1.2, p, [£10, £25, £40]) gives [£12.00, £30.00, £48.00]
spend = [£42.50, £18.20, £65.00, £23.30]
filter(x > £30, x, spend) gives [£42.50, £65.00]
sum(filter(x > £30, x, spend)) gives £107.50

The name you choose only exists inside the call. It doesn’t change a value of the same name elsewhere in the note:

calc
x = 10
map(x * 2, x, [1, 2, 3]) gives [2, 4, 6]
x gives 10

The condition in filter must be a comparison or another true/false value. A plain number isn’t treated as true, so filter(x, x, [0, 1, 2]) is an error. Mapping or filtering an empty list gives an empty list.

List arithmetic

You can multiply or divide every value by a single number, and add or subtract two lists of the same length value by value:

calc
[30, 36, 22] / 40 * 100 gives [75, 90, 55]
2 * [3, 1, 4] gives [6, 2, 8]
-[5, -3] gives [-5, 3]
mocks = [62, 70, 60]
finals = [72, 78, 69]
finals - mocks gives [10, 8, 9]
average(finals - mocks) gives 9
[1 kg, 2 kg] + [500 g, 500 g] gives [1.5 kg, 2.5 kg]
[72, 85] == [72, 85] gives true

The first line turns marks out of 40 into percentages. The finals - mocks line pairs each student’s mock mark with their final mark.

Some operations aren’t supported on lists:

  • Adding a single number to a list, as in scores + 5. Use map(s + 5, s, scores) instead.
  • Adding lists of different lengths. Values are never repeated to make lengths match.
  • Multiplying two flat lists with *. For the sum of pairwise products, use dot: dot([2, 3, 1], [4, 5, 10]) is 33, for example two coffees at 4, three teas at 5 and one cake at 10. Between lists of rows, * is matrix multiplication, covered in Vectors and matrices.

Lists of rows

A list of rows works like a small table. average and sum work down the columns, and map can work across the rows:

calc
# Each row is one student: [mock, final]
results = [[62, 72], [70, 78], [60, 69]]
average(results) gives [64, 73]
sum(results) gives [192, 219]
map(average(r), r, results) gives [67, 74, 64.5]

median, min, max and stddev don’t accept lists of rows. Use map over the rows, as in the last line, or pull out a column into its own list.

The exact rules for all of these operations are in the advanced mathematics reference.

Putting it together

Here is a week of step counts from a watch, with a daily goal:

calc
# Steps, Monday to Sunday
steps = [8200, 10400, 6300, 12000, 9100, 7400, 9600]
goal = 9000
sum(steps) gives 63,000
average(steps) gives 9,000
median(steps) gives 9,100
Days on target: count(filter(s >= goal, s, steps)) gives 4
Spread: max(steps) - min(steps) gives 5,700
# Roughly 0.75 m per step
walked = sum(steps) * 0.75 m
walked in km gives 47.25 km

The average lands exactly on the goal, but only four days reached it, and there are 5,700 steps between the best and worst days. Change goal or paste in next week’s counts and every line updates.