Malloy's render provides a variety of ways to render numeric values. Tagging a number will change how the number is displayed in the result set but the underlying value doesn't change.
Number Format Tags
| Tag | Description | Examples |
|---|---|---|
# number |
Custom number format | # number="$#,##0", # number=1m, # number=auto, # number=id |
# percent |
Format as percentage | # percent |
# currency |
Currency format | # currency, # currency=usd2m, # currency=eur |
# duration |
Duration format | # duration=seconds, # duration=minutes, # duration.terse |
# data_volume |
Storage size format | # data_volume=bytes, # data_volume=mb |
The examples on this page use the following semantic model.
source: airports is duckdb.table('../data/airports.parquet') extend { dimension: name is concat(code, ' - ', full_name) measure: airport_count is count() } source: flights is duckdb.table('../data/flights.parquet') extend { join_one: orig is airports on origin = orig.code join_one: dest is airports on destination = dest.code measure: flight_count is count() }
# percent
Carriers as percentage of flights
run: flights -> { group_by: carrier # percent aggregate: percent_of_flights is flight_count / all(flight_count) }
[ { "carrier": "WN", "percent_of_flights": 0.2573783375431738 }, { "carrier": "US", "percent_of_flights": 0.10928088577750582 }, { "carrier": "AA", "percent_of_flights": 0.10027347046489979 }, { "carrier": "NW", "percent_of_flights": 0.09738216554968143 }, { "carrier": "UA", "percent_of_flights": 0.09499546149228454 }, { "carrier": "DL", "percent_of_flights": 0.09317715840116929 }, { "carrier": "RU", "percent_of_flights": 0.046614679244954715 }, { "carrier": "MQ", "percent_of_flights": 0.046020178234302996 }, { "carrier": "EV", "percent_of_flights": 0.04573017774130216 }, { "carrier": "HP", "percent_of_flights": 0.028275048067581715 }, { "carrier": "AS", "percent_of_flights": 0.024513741673360845 }, { "carrier": "CO", "percent_of_flights": 0.020703135195329833 }, { "carrier": "B6", "percent_of_flights": 0.01404182387110058 }, { "carrier": "OH", "percent_of_flights": 0.012818021790637044 }, { "carrier": "TZ", "percent_of_flights": 0.00879571495271542 } ]
WITH __stage0 AS ( SELECT group_set, CASE WHEN group_set=1 THEN base."carrier" END as "carrier__1", (CASE WHEN group_set=1 THEN COUNT(1) END)*1.0/MAX((CASE WHEN group_set=0 THEN COUNT(1) END)) OVER () as "percent_of_flights__1" FROM '../data/flights.parquet' as base CROSS JOIN (SELECT UNNEST(GENERATE_SERIES(0,1,1)) as group_set ) as group_set GROUP BY 1,2 ) SELECT "carrier__1" as "carrier", MAX(CASE WHEN group_set=1 THEN "percent_of_flights__1" END) as "percent_of_flights" FROM __stage0 WHERE group_set NOT IN (0) GROUP BY 1 ORDER BY 2 desc NULLS LAST
# number
Malloy uses LookML-style format strings (similar to Excel custom number formats) for formatting numbers.
# number="0" # Integer (123) # number="*00#" # Integer zero-padded to 3 places (001) # number='0 "String"' # Integer followed by a string (123 String) # Note "String" can be replaced with any other word # number="0.##" # Number up to 2 decimals (1. or 1.2 or 1.23) # number="0.00" # Number with exactly 2 decimals (1.23) # number="*00#.00" # Number zero-padded to 3 places and exactly 2 decimals (001.23) # number="#,##0" # Number with comma between thousands (1,234) # number="#,##0.00" # Number with comma between thousands and 2 decimals (1,234.00) # number='0.000,,"M"' # Number in millions with 3 decimals (1.234 M) # Note division by 1 million happens automatically # number='0.000,"K"' # Number in thousands with 3 decimals (1.234 K) # Note division by 1 thousand happens automatically # number="$0" # Dollars with 0 decimals ($123) # number="$0.00" # Dollars with 2 decimals ($123.00) # number='"€"0' # Euros with 0 decimals (€123) # number="$#,##0.00" # Dollars with comma btwn thousands and 2 decimals ($1,234.00) # number="$#.00;($#.00)" # Dollars with 2 decimals, positive values displayed # normally, negative values wrapped in parenthesis # number='0\%' # Display as percent with 0 decimals (1 becomes 1%) # number='0.00\%' # Display as percent with 2 decimals (1 becomes 1.00%) # number="0%" # Convert to percent with 0 decimals (.01 becomes 1%) # number="0.00%" # Convert to percent with 2 decimals (.01 becomes 1.00%)
run: flights -> { // tag a single element aggregate: # number="0" `integer` is flight_count // tag multiple elements at once. # number="$#,##0;($#,##0)" aggregate: dollars is flight_count neg_dollars is 0 - flight_count }
[ { "integer": 344827, "dollars": 344827, "neg_dollars": -344827 } ]
SELECT (COUNT(1)) as "integer", (COUNT(1)) as "dollars", 0-(COUNT(1)) as "neg_dollars" FROM '../data/flights.parquet' as base
# number=big
The # number=big renderer formats large numbers with abbreviated notation and a single decimal place for easier readability. Numbers are automatically scaled and labeled with the appropriate magnitude:
K for thousands (1,000)
M for millions (1,000,000)
B for billions (1,000,000,000)
T for trillions (1,000,000,000,000)
Q for quadrillions (1,000,000,000,000,000)
# number shorthand
Pattern: # number={decimals}{scale} where scale is k, m, b, t, q, or auto.
| Shorthand | Description | Example output |
|---|---|---|
# number=1k |
1 decimal, scale to thousands | 42.5k |
# number=1m |
1 decimal, scale to millions | 42.5m |
# number=auto |
Auto-select scale based on magnitude | 42.5m or 1.2k |
# number=id |
Raw number with no comma separators | 123456 |
# number=big |
Abbreviated with 1 decimal (legacy) | 42.5M |
# transpose run: flights -> { aggregate: # number=1k thousands is flight_count # number=1m millions is flight_count * 1000 # number=auto auto_scaled is flight_count * 1000 # number=id as_id is flight_count }
[ { "thousands": 344827, "millions": 344827000, "auto_scaled": 344827000, "as_id": 344827 } ]
SELECT (COUNT(1)) as "thousands", (COUNT(1))*1000 as "millions", (COUNT(1))*1000 as "auto_scaled", (COUNT(1)) as "as_id" FROM '../data/flights.parquet' as base
# number verbose syntax
For finer control, use the verbose object syntax:
# number { scale=m decimals=1 suffix=word }| Property | Values | Description |
|---|---|---|
scale |
k, m, b, t, q, auto |
Divide by this magnitude |
decimals |
0, 1, 2, ... |
Number of decimal places |
suffix |
word, scientific, letter |
Suffix style after the number |
Suffix styles:
word: full word (e.g. "42.5 million")scientific: scientific notation styleletter: single uppercase letter (e.g. "42.5M")
# transpose run: flights -> { aggregate: # number { scale=m decimals=1 suffix=word } word_style is flight_count * 1000 # number { scale=m decimals=2 suffix=scientific } scientific_style is flight_count * 1000 # number { scale=m decimals=1 suffix=letter } letter_style is flight_count * 1000 }
[ { "word_style": 344827000, "scientific_style": 344827000, "letter_style": 344827000 } ]
SELECT (COUNT(1))*1000 as "word_style", (COUNT(1))*1000 as "scientific_style", (COUNT(1))*1000 as "letter_style" FROM '../data/flights.parquet' as base
# currency
# currency shorthand
Pattern: # currency={code}{decimals}{scale} — e.g. # currency=usd2m for "$42.5m".
Codes: usd ($), eur (€), gbp (£). Scale and suffix options match # number.
# currency verbose syntax
For finer control, use the verbose object syntax:
# currency { scale=m decimals=2 suffix=finance }| Property | Values | Description |
|---|---|---|
scale |
k, m, b, t, q, auto |
Divide by this magnitude |
decimals |
0, 1, 2, ... |
Number of decimal places |
suffix |
finance, word, none, auto |
Suffix style |
Suffix styles:
finance: financial notation (e.g. "$42.54MM")word: full word (e.g. "$42.5 million")none: no suffix, just the scaled numberauto: automatically choose a suffix
# transpose run: flights -> { aggregate: # currency=usd1m usd_shorthand is flight_count * 1000 # currency { scale=m decimals=2 suffix=finance } finance_style is flight_count * 1000 # currency { scale=m decimals=1 suffix=word } word_style is flight_count * 1000 # currency { scale=k suffix=none } no_suffix is flight_count }
[ { "usd_shorthand": 344827000, "finance_style": 344827000, "word_style": 344827000, "no_suffix": 344827 } ]
SELECT (COUNT(1))*1000 as "usd_shorthand", (COUNT(1))*1000 as "finance_style", (COUNT(1))*1000 as "word_style", (COUNT(1)) as "no_suffix" FROM '../data/flights.parquet' as base
Durations
The # duration renderer interprets a value as a number of seconds and renders it as a human-adjusted duration. Other units can be specified like # duration="minutes", with possible units of "nanoseconds", "microseconds", "milliseconds", "seconds", "minutes", "hours", and "days".
# duration properties
| Property | Description |
|---|---|
=unit |
Input unit: nanoseconds, milliseconds, seconds (default), minutes, hours, days |
.terse |
Abbreviated units (ns, µs, ms, s, m, h, d) |
.number |
SSF format for numeric parts: # duration { number="0.0" } |
run: flights -> { group_by: dep_date is dep_time.day # duration="minutes" aggregate: longest_flight_time is max(flight_time) total_flight_time is flight_time.sum() aggregate: flight_count limit: 20 }
[ { "dep_date": "2005-12-31T00:00:00.000Z", "longest_flight_time": "362", "total_flight_time": "17874", "flight_count": 175 }, { "dep_date": "2005-12-30T00:00:00.000Z", "longest_flight_time": "464", "total_flight_time": "21054", "flight_count": 188 }, { "dep_date": "2005-12-29T00:00:00.000Z", "longest_flight_time": "485", "total_flight_time": "19923", "flight_count": 186 }, { "dep_date": "2005-12-28T00:00:00.000Z", "longest_flight_time": "440", "total_flight_time": "20367", "flight_count": 197 }, { "dep_date": "2005-12-27T00:00:00.000Z", "longest_flight_time": "480", "total_flight_time": "20381", "flight_count": 197 }, { "dep_date": "2005-12-26T00:00:00.000Z", "longest_flight_time": "480", "total_flight_time": "19683", "flight_count": 194 }, { "dep_date": "2005-12-25T00:00:00.000Z", "longest_flight_time": "1920", "total_flight_time": "19037", "flight_count": 167 }, { "dep_date": "2005-12-24T00:00:00.000Z", "longest_flight_time": "489", "total_flight_time": "17130", "flight_count": 159 }, { "dep_date": "2005-12-23T00:00:00.000Z", "longest_flight_time": "467", "total_flight_time": "20241", "flight_count": 201 }, { "dep_date": "2005-12-22T00:00:00.000Z", "longest_flight_time": "466", "total_flight_time": "19360", "flight_count": 200 }, { "dep_date": "2005-12-21T00:00:00.000Z", "longest_flight_time": "321", "total_flight_time": "20181", "flight_count": 192 }, { "dep_date": "2005-12-20T00:00:00.000Z", "longest_flight_time": "449", "total_flight_time": "20855", "flight_count": 203 }, { "dep_date": "2005-12-19T00:00:00.000Z", "longest_flight_time": "346", "total_flight_time": "18514", "flight_count": 207 }, { "dep_date": "2005-12-18T00:00:00.000Z", "longest_flight_time": "464", "total_flight_time": "20605", "flight_count": 179 }, { "dep_date": "2005-12-17T00:00:00.000Z", "longest_flight_time": "319", "total_flight_time": "15966", "flight_count": 139 }, { "dep_date": "2005-12-16T00:00:00.000Z", "longest_flight_time": "455", "total_flight_time": "22007", "flight_count": 195 }, { "dep_date": "2005-12-15T00:00:00.000Z", "longest_flight_time": "412", "total_flight_time": "20679", "flight_count": 204 }, { "dep_date": "2005-12-14T00:00:00.000Z", "longest_flight_time": "455", "total_flight_time": "21639", "flight_count": 193 }, { "dep_date": "2005-12-13T00:00:00.000Z", "longest_flight_time": "326", "total_flight_time": "19801", "flight_count": 190 }, { "dep_date": "2005-12-12T00:00:00.000Z", "longest_flight_time": "449", "total_flight_time": "19841", "flight_count": 176 } ]
SELECT DATE_TRUNC('day', base."dep_time") as "dep_date", max(base."flight_time") as "longest_flight_time", COALESCE(SUM(base."flight_time"),0) as "total_flight_time", COUNT(1) as "flight_count" FROM '../data/flights.parquet' as base GROUP BY 1 ORDER BY 1 desc NULLS LAST LIMIT 20
Data Volume
The # data_volume tag formats numbers as human-readable storage sizes. Specify the unit of the input value:
| Tag | Input unit | Example output |
|---|---|---|
# data_volume = bytes |
Bytes | 3.7 KB |
# data_volume = kb |
Kilobytes | 3.7 MB |
# data_volume = mb |
Megabytes | 3.6 GB |
# data_volume = gb |
Gigabytes | 3.5 TB |
# data_volume = tb |
Terabytes | 3.4 PB |
Values are automatically scaled to the most readable unit.
run: flights -> { aggregate: # data_volume = bytes total_bytes is flight_count * 1024 # data_volume = mb total_mb is flight_count * 256 }
[ { "total_bytes": 353102848, "total_mb": 88275712 } ]
SELECT (COUNT(1))*1024 as "total_bytes", (COUNT(1))*256 as "total_mb" FROM '../data/flights.parquet' as base