Free Interactive Course · Spark + Scala

Numeric & Math Functions

Rounding money, scaling values, comparing columns, and the math library you'll occasionally need. Most of these run live below — round, ceil/floor, abs, powers, roots, trig, greatest/least, and format_number for clean money output.

The dataset

This module uses the two DataFrames from Foundations:

peopleid, name, country, age, signup_date
ordersid, user_id, product, category, amount, qty, status, ordered_at
4.1

round, bround, ceil & floor

The rounding family. round rounds half away from zero to a given number of decimals; bround is banker's rounding (half to even); ceil always rounds up and floor always rounds down to a whole number.

Signature
def round(e: Column, scale: Int = 0): Column     // half away from zero
def bround(e: Column, scale: Int = 0): Column    // half to even (banker's)
def ceil(e: Column): Column        def floor(e: Column): Column

1. round to N decimals

Round a line total to 2 decimal places — the everyday money case:

Scala
import org.apache.spark.sql.functions.round

orders.select(
  $"product",
  round($"amount" * $"qty", 2).as("total")
).show(4, false)
Run the equivalent Spark SQL — edit it and press Run
SQL
⌘/Ctrl + Enter

2. ceil / floor

Scala
orders.select(
  $"amount",
  ceil($"amount").as("rounded_up"),
  floor($"amount").as("rounded_down")
).show(4, false)
Run the equivalent Spark SQL — edit it and press Run
SQL
⌘/Ctrl + Enter

3. bround — half to even

Banker's rounding reduces cumulative bias when you round millions of values — 2.5 rounds to 2, 3.5 rounds to 4 (always toward the even neighbour):

Scala
Seq(2.5, 3.5, 4.5).toDF("x").select(
  round($"x").as("round"),
  bround($"x").as("bround")
).show(false)
round = half away from zero · bround = half to even
xroundbround
2.532
3.544
4.554
baked output — Spark-only type, not runnable in the browser engine
round(x) with no scale rounds to a whole number but keeps the column's type (a double stays a double, e.g. 50.0). To get an integer type, add .cast("int"). Negative scale rounds to the left of the decimal point — round($"n", -2) rounds to the nearest hundred.
4.2

abs & signum

abs returns the magnitude (drops the sign); signum returns just the sign as -1.0, 0.0, or 1.0 — handy for turning a number into a direction/flag.

Signature
def abs(e: Column): Column
def signum(e: Column): Column   // -1.0, 0.0, or 1.0
Scala
Seq(-30, 0, 45).toDF("delta").select(
  $"delta",
  abs($"delta").as("magnitude"),
  signum($"delta").as("direction")
).show(false)
Run the equivalent Spark SQL — edit it and press Run
SQL
⌘/Ctrl + Enter
signum (Spark) and sign (SQL) are the same idea — Spark returns a double (-1.0), the SQL equivalent an integer (-1). Both give you a clean three-way flag for "below / at / above".
4.3

pow, sqrt, cbrt, exp & logarithms

Powers and their inverses. pow(base, exp) raises to a power, sqrt/cbrt are the square/cube roots, exp is e^x, and the log family gives you logarithms in any base.

Signature
def pow(base: Column, exp: Column): Column    // base ^ exp
def sqrt(e: Column): Column      def cbrt(e: Column): Column
def exp(e: Column): Column       // e^x
def log(e: Column): Column       // natural log (ln)
def log10(e: Column): Column     def log2(e: Column): Column
def log(base: Double, e: Column): Column   // log in a given base
Scala
import org.apache.spark.sql.functions.{pow, sqrt, exp}

Seq(16.0, 100.0).toDF("x").select(
  $"x",
  pow($"x", 2).as("squared"),
  sqrt($"x").as("root")
).show(false)
Run the equivalent Spark SQL — edit it and press Run
SQL
⌘/Ctrl + Enter

log10 runs live; the natural log (log), log2, and base-N logs behave the same way — here's the shape of the output:

Scala
Seq(8.0, 1000.0).toDF("x").select(
  log($"x").as("ln"),
  log2($"x").as("log2"),
  log10($"x").as("log10")
).show(false)
log = natural log · log2 · log10
xlnlog2log10
8.02.0793.00.903
1000.06.9089.9663.0
baked output — Spark-only type, not runnable in the browser engine
In Scala string terms, pow takes columns or numbers — pow($"x", 0.5) is another way to write sqrt, and pow($"x", lit(1.0)/3) a cube root. Watch integer division: pow($"x", 1/3) is pow($"x", 0) = 1 because 1/3 is integer 0 in Scala.
4.4

Trigonometry: sin, cos, tan, radians & degrees

The trig functions work in radians, so convert from degrees with radians first (and back with degrees). sin, cos, tan and their inverses (asin/acos/atan/atan2) are all available.

Signature
def sin(e: Column): Column   def cos(e: Column): Column   def tan(e: Column): Column
def radians(e: Column): Column   def degrees(e: Column): Column
def atan2(y: Column, x: Column): Column   // angle of a vector
Scala
Seq(0, 30, 90).toDF("deg").select(
  $"deg",
  round(sin(radians($"deg")), 3).as("sin"),
  round(cos(radians($"deg")), 3).as("cos")
).show(false)
Run the equivalent Spark SQL — edit it and press Run
SQL
⌘/Ctrl + Enter
The classic bug: feeding degrees straight into sin/cos. Always wrap with radians(...) first. Geo work (distances from lat/long via the haversine formula) is the most common place these show up in data engineering.
4.5

greatest, least, hypot, pmod, factorial & rand

greatest and least compare several columns per row (unlike max/min, which aggregate down a column). The rest are occasional specialists: hypot (√(x²+y²)), pmod (always-positive modulo), factorial, and the random generators.

Signature
def greatest(exprs: Column*): Column    def least(exprs: Column*): Column
def hypot(a: Column, b: Column): Column    def pmod(dividend: Column, divisor: Column): Column
def factorial(e: Column): Column
def rand(): Column    def randn(): Column    def monotonically_increasing_id(): Column

greatest/least pick the biggest/smallest value across columns on each row — here comparing amount against qty:

Scala
import org.apache.spark.sql.functions.{greatest, least}

orders.select(
  $"amount", $"qty",
  greatest($"amount", $"qty").as("bigger"),
  least($"amount", $"qty").as("smaller")
).show(4, false)
Run the equivalent Spark SQL — edit it and press Run
SQL
⌘/Ctrl + Enter
Don't confuse them with aggregates: greatest($"a", $"b") is row-wise (a new column), while max($"a") in a groupBy collapses many rows into one (Aggregations module). pmod differs from % on negatives: pmod(-7, 3) is 2, while -7 % 3 is -1 — use pmod for hash-bucketing. rand/randn are non-deterministic (seed them for reproducibility); monotonically_increasing_id gives unique but non-contiguous ids.
4.6

format_number — human-readable numbers

format_number turns a number into a display string with thousands separators and a fixed number of decimals — exactly what you want for reports and dashboards (but not for further math, since the result is text).

Signature
def format_number(x: Column, d: Int): Column   // returns a STRING
Scala
Seq(1234567.891, 42.5).toDF("n").select(
  format_number($"n", 2).as("pretty")
).show(false)
Run the equivalent Spark SQL — edit it and press Run
SQL
⌘/Ctrl + Enter
The output is a String ("1,234,567.89"), so do all your arithmetic first and format_number only at the very end for display. For currency with a symbol, concat_ws("", lit("$"), format_number($"amount", 2)).
P
PriyaFounder · Kettle & Co.now
Finance wants clean numbers 💰 — for every order give me the product and its line total (amount × qty) rounded to the nearest whole dollar. Call the column total.
🎯 Your mission From orders, return product and total = amount × qty rounded to the nearest whole number.
SQL
⌘/Ctrl + Enter

Ready for the full data stack?

These Spark functions are one piece. When you want pipelines, Kafka, lakehouse table formats, and a full end-to-end project, the Data Engineering course is free too.

Explore Data Engineering →

Comments

0

Join the conversation. Sign in to leave a comment — questions and feedback welcome.