How to Use Modulo in R

R Published Updated 3 mins read By Leon Wei
How to Use Modulo in R cover image

Quick summary

Summarize this blog with AI

R modulo uses floor division

x %% y returns the remainder associated with R's integer division operator, x %/% y. For ordinary integer inputs with a nonzero divisor, the relationship is x = y * (x %/% y) + (x %% y). A nonzero remainder follows the divisor's sign. Run the following blocks sequentially in base R 4.5.1.

Check positive and negative operands

cases <- data.frame(x = c(5, -5, 5, -5), y = c(3, 3, -3, -3))
cases$quotient <- cases$x %/% cases$y
cases$remainder <- cases$x %% cases$y
print(cases)
# x  y  quotient remainder
# 5  3      1        2
#-5  3     -2        1
# 5 -3     -2       -1
#-5 -3      1       -2
stopifnot(identical(cases$remainder, c(2, 1, -1, -2)))
stopifnot(all(cases$x == cases$y * cases$quotient + cases$remainder))

In particular, 5 %% -3 is -1 and -5 %% 3 is 1. Reason from the quotient rounded downward rather than importing a remainder rule from another language.

Filter even values without losing missingness

values <- c(-3, -2, -1, 0, 1, 2, NA)
is_even <- values %% 2 == 0
print(is_even)  # FALSE TRUE FALSE TRUE FALSE TRUE NA
even_values <- values[!is.na(values) & is_even]
print(even_values)  # -2 0 2
stopifnot(identical(even_values, c(-2, 0, 2)))

This definition is for integer-valued inputs. The remainder is vectorized, so no per-value loop is needed. A missing input remains unknown; the explicit filter excludes it here. If dropping unknown values would affect your analysis, count and report them instead.

Wrap a position using one-based indexing

labels <- c("A", "B", "C")
positions <- 1:8
index <- ((positions - 1L) %% length(labels)) + 1L
print(labels[index])  # A B C A B C A B
stopifnot(identical(index, c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L)))

current <- 1L
previous <- ((current - 2L) %% length(labels)) + 1L
stopifnot(previous == 3L)

Subtract one before modulo and add one afterward because R indices start at one. The divisor must be positive here; an empty label vector has no valid position to wrap to. Cycling labels produces a repeating allocation, not a random split. For a train/test split, row order may contain trends that make such an allocation inappropriate.

Keep a circular buffer state explicit

buffer <- rep(NA_real_, 3)
next_index <- 1L
for (value in c(10, 20, 30, 40, 50)) {
  buffer[next_index] <- value
  next_index <- (next_index %% length(buffer)) + 1L
}
print(buffer)  # 40 50 30: storage order, not time order
time_order <- ((next_index - 1L + 0:2) %% length(buffer)) + 1L
print(buffer[time_order])  # 30 40 50
stopifnot(identical(buffer[time_order], c(30, 40, 50)))

After five insertions, the buffer is full and next_index points to the oldest stored value. The chronological reconstruction above assumes the buffer has filled; before that, track the count of inserted values and exclude unused slots. There is no Python-style global statement in R.

Treat zero divisors and decimals as separate cases

stopifnot(is.nan(5 %% 0), is.na(NA_real_ %% 3))
divisor <- 3
stopifnot(length(divisor) == 1, is.finite(divisor), divisor != 0)
decimal_remainder <- 1 %% 0.2
print(decimal_remainder)
# Binary floating-point need not represent decimal fractions exactly.

Do not use exact decimal remainder equality as a money or time validation rule. Use appropriate integer units when your application defines them, and validate their range. Floating-point precision and large quotients can affect remainders; the integer examples above deliberately avoid that ambiguity.

References

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles
How to Use 'in' Operator in R cover image
r May 6, 2024

How to Use 'in' Operator in R

This guide covers everything you need to know about using the 'in' operator in R, including detailed examples and tips for beginners.