The 3 Rs – Arithmetic

You can think of R as like a giant calculator. It has a great capacity for mathematical operations. In this article I’ll look at some of the more basic ones, to give a flavour of what basic arithmetic you can do.

Operators

In the main your arithmetic is going to involve adding up, dividing, subtracting and multiplication. These things are all carried out using basic operators that are familiar to everyone: + – * / and ().

The order of the operators is important. R will evaluate your maths in a set order. multiplication (*) and division (/) are evaluated first. Then addition (+) and subtraction (-). As well as this basic order, things inside parentheses () will be evaluated before anything “outside” the parentheses (still in the */+- order). So, remember this running order when you type your maths e.g.

7 + 4 * 11
[1] 51

(7 + 4) * 11
[1] 121

As well as the basic operators there are some “extras”.

Powers (i.e. exponents) are designated using the caret (^) character. These are evaluated before any of the other operators. For example:

2^2 ; 2^3 ; 2^4
[1] 4
[1] 8
[1] 16

3 * 2^3 + 1
[1] 25

If you want a fractional power, you enclose this in parentheses e.g.

64^(1/2)
[1] 8

64^(-2)
[1] 0.0002441406

square root is simply a power of 0.5 e.g. x^0.5 but there is a special command sqrt() to deal with this.

x <- c(9, 16, 25, 36, 49, 59)
sqrt(x)
[1] 3.000000 4.000000 5.000000 6.000000 7.000000 7.681146

You can use the % symbol to compute modulo (%%) and integer division (%/%) like so:

16 %/% 3
[1] 5

16 %% 3
[1] 1

So, you have 16 ÷ 3 giving 5 and 1 remainder.

Matrix operations

There are a bunch of commands associated with matrix math.

Matrix multiplication

Multiply two matrices using the %*% operator.

## Make some matrices
(x <- cbind(1,1:3,c(2,0,1)))
     [,1] [,2] [,3]
[1,]    1    1    2
[2,]    1    2    0
[3,]    1    3    1

(y <- c(1, 3, 2))
[1] 1 3 2

(y1 <- matrix(1:3, nrow = 1))
     [,1] [,2] [,3]
[1,]    1    2    3

(z <- matrix(3:1, ncol = 1))
     [,1]
[1,]    3
[2,]    2
[3,]    1

## Various multiplications
> x %*% y
     [,1]
[1,]    8
[2,]    7
[3,]   12

x %*% y1 # Order can be important
Error in x %*% y1 : non-conformable arguments

y1 %*% x
     [,1] [,2] [,3]
[1,]    6   14    5

x %*% z
     [,1]
[1,]    7
[2,]    7
[3,]   10

z %*% x # Order is important
Error in z %*% x : non-conformable arguments

y %*% y # Matrix multiplication of two vectors
     [,1]
[1,]   14

Matrix Cross Products

You can compute the cross product using the %*% operator and the t() command. Alternatively the crossprod() and tcrossprod() commands can do the job:

(x <- cbind(1,1:3,c(2,0,1)))
     [,1] [,2] [,3]
[1,]    1    1    2
[2,]    1    2    0
[3,]    1    3    1

(y = c(1, 3, 2))
[1] 1 3 2

(y1 = matrix(1:3, nrow = 1))
     [,1] [,2] [,3]
[1,]    1    2    3

## Cross products
> crossprod(x, y) # Same as t(x) %*% y
     [,1]
[1,]    6
[2,]   13
[3,]    4

crossprod(x, y1) # Gives error as y1 wrong "shape"
Error in crossprod(x, y1) : non-conformable arguments

tcrossprod(x, y1) # Same as x %*% t(y1)
     [,1]
[1,]    9
[2,]    5
[3,]   10

Other matrix maths

There are various other commands associated with matrix math. They are not really operators (except %o%) as such but I’ll list them here:

 

backsolveforwardsolve These commands solve a system of linear equations where the coefficient matrix is upper (“right”, “R”) or lower (“left”, “L”) triangular.
chol This command computes the Choleski factorization of a real matrix. The matrix must be symmetric, positive definite. The result returned is the upper triangular factor of the Choleski decomposition, that is, the matrix R such that R’R = x.
detdeterminant The determinant command calculates the modulus of the determinant (optionally as a logarithm) and the sign of the determinant. The det command calculates the determinant of a matrix (it is a wrapper for the determinant command).
diag Matrix diagonals. This command has several uses; it can extract or replace the diagonal of a matrix. Alternatively, the command can construct a diagonal matrix.
eigen Computes eigenvalues and eigenvectors for matrix objects; that is, carries out spectral decomposition. The result is a list containing $values and $vectors.
outer%o% The outer command calculates the outer product of arrays and matrix objects. The %o% symbol is a convenience wrapper for outer(X, Y, FUN = “*”).
qr This command computes the QR decomposition of a matrix. It provides an interface to the techniques used in the LINPACK routine DQRDC or the LAPACK routines DGEQP3 and (for complex matrices) ZGEQP3. The result holds a class attribute “qr”.
solve Solves a system of equations. This command solves the equation a %*% x = b for x, where b can be either a vector or a matrix.
svd The svd command computes the singular value decomposition of a rectangular matrix. The result is a list containing $d, the singular values of x. If nu > 0 and nv > 0, the result also contains $u and $v, the left singular and right singular vectors of x.

Logical Operators

Some operators are used to provide a logical result (i.e. TRUE or FALSE).

Logical AND

The & operator is used for the logical AND in an elementwise manner. If you double up the operator && you perform the operation on only the first element

Logical OR

The vertical bar | is used for the logical OR in an elementwise manner. If you double up the operator || you perform the operation on only the first element.

Logical NOT

The ! operator is used for the logical NOT.

Logical Exclusive OR

The command xor(x, y) acts as an exclusive OR operator.

The logical operators work in conjunction with other operators (comparisons) to produce results. So, look at the examples in the next section.

Comparisons

Comparison operators allow you to compare elements. These are often used in conjunction with the logical operators.

 

== Equal to. Note that the single = denotes assignment so == is used as a comparison operator.
!= Not equal to.
< Less than.
<= Less than or equal to.
>= Greater than or equal to.
> Greater than.

 

Here are some simple examples:

## Make some vectors
yy <- c(2, 3, 6, 5, 3)
zz <- c(4, 2, 4, 7, 4)

yy ; zz
[1] 2 3 6 5 3
[1] 4 2 4 7 4

yy > 4 # Elements greater than 4
[1] FALSE FALSE  TRUE  TRUE FALSE

zz != 4 # Elements not equal to 4
[1] FALSE  TRUE FALSE  TRUE FALSE

zz == 4 # Elements that are equal to 4
[1]  TRUE FALSE  TRUE FALSE  TRUE

yy > 3 & zz > 4 # Two conditions AND
[1] FALSE FALSE FALSE  TRUE FALSE

yy > 3 && zz > 4 # Test first element only
[1] FALSE

yy == 3 | zz == 4 # Two conditions OR
[1]  TRUE  TRUE  TRUE FALSE  TRUE

xor(yy > 3, zz > 4) # Exclusive OR
[1] FALSE FALSE  TRUE FALSE FALSE

Selection and Matching

The comparison and logical operators are used to obtain TRUE/FALSE results. However, they are not always the best choice for selection. There are times when you are looking for a single logical result but using regular operators either fails, or produces more than one. In these cases, the selection commands are helpful. They aren’t strictly mathematical operators but it is helpful to be aware of them. Additionally the isTRUE() command can “force” a single logical as a result.

Match any item in an object

Use the any() command to return a single TRUE result if any element meets the specified criteria.

Match all items in an object

Use the all() command to return a single TRUE result if all elements meet the specified criteria.

Compare objects

The identical() command compares two items and returns a TRUE if they are exactly equal. The all.equal() command is similar but with a bit more tolerance.

(yy <- c(2, 3, 6, 5, 3)) # Make a vector
[1] 2 3 6 5 3

any(yy > 5) # Are any elements > 5?
[1] TRUE

all(yy < 5) # Are all elements < 5?
[1] FALSE

all(yy >= 2) # Are all elements >= 2?
[1] TRUE

## Some simple values
x1 <- 0.5 – 0.3
x2 <- 0.3 – 0.1

## Are results the same?
x1 == x2
[1] FALSE

## Set tolerance to 0 to show actual difference
all.equal(x1, x2, tolerance = 0)
[1] "Mean relative difference: 1.387779e-16"

## Use default tolerance
all.equal(x1, x2)
[1] TRUE

# Wrap command in isTRUE() for logical
isTRUE(all.equal(x1, x2))
[1] TRUE

isTRUE(all.equal(x1, x2, tolerance = 0))
[1] FALSE

## Character vectors
pp <- c("a", "f", "h", "q", "r")
qq <- c("d", "e", "x", "c", "s")
rr <- pp

# Test for equality
all.equal(pp, qq)
[1] "5 string mismatches"

identical(x1, x2)
[1] FALSE

identical(pp, qq)
[1] FALSE

identical(pp, rr)
[1] TRUE

Note that you can use identical(TRUE, x) in lieu of isTRUE(x), where x is the condition to test.

Selection with non-logical result

You can use the which() command to obtain an “index” instead of a logical result. The command works in conjunction with the comparison and logical operators but returns a result that indicates which elements match your criteria.

yy <- c(2, 3, 6, 5, 3)
yy
[1] 2 3 6 5 3

which(yy > 3)
[1] 3 4

which(yy == 3)
[1] 2 5

Complex numbers

Complex numbers are those with “imaginary” parts. You can make complex numbers using the complex() and as.complex() commands, whilst the is.complex() command provides a quick logical test to see if an object has the class “complex”. R has several commands that can deal with complex numbers.

 

Arg Returns the argument of an imaginary number.
Conj Displays the complex conjugate for a complex number.
Im Shows the imaginary part of a complex number.
Mod Shows the modulus of a complex number.
Re Shows the real part of complex numbers.

 

Here are some simple examples:

## Make some complex numbers
z0 <- complex(real = 1:8, imaginary = 8:1)
z1 <- complex(real = 4, imaginary = 3)
z2 <- complex(real = 4, imaginary = 3, argument = 2)
z3 <- complex(real = 4, imaginary = 3, modulus = 4, argument = 2)

z0 ; z1 ; z2 ; z3
[1] 1+8i 2+7i 3+6i 4+5i 5+4i 6+3i 7+2i 8+1i
[1] 4+3i
[1] -0.4161468+0.9092974i
[1] -1.664587+3.63719i

## Get the real and imaginary parts of a complex object
Re(z0)
[1] 1 2 3 4 5 6 7 8

Im(z0)
[1] 8 7 6 5 4 3 2 1

## Get the Argument and Modulus
Arg(z1)
[1] 0.6435011

Mod(z1)
[1] 5

## Display the complex conjugate
Conj(z2)
[1] -0.4161468-0.9092974i

## Get the modulus and argument
Mod(z3)
[1] 4

Arg(z3)
[1] 2

Besides these special commands, the regular math operators work on complex numbers:

z0 + z1
[1]  5+11i  6+10i  7+ 9i  8+ 8i  9+ 7i 10+ 6i 11+ 5i 12+ 4i

z0 * z1
[1] -20+35i -13+34i  -6+33i   1+32i   8+31i  15+30i  22+29i  29+28i

z2 / z3
[1] 0.25+0i

Rounding

There are various commands that deal generally with precision and rounding.

 

abs This command returns the absolute magnitude of a numeric value (that is, ignores the sign). If it is used on a logical object the command produces 1 or 0 for TRUE or FALSE, respectively.
sign This command returns the sign of elements in a vector. If negative an item is assigned a value of –1; if positive, +1; and if zero, 0.
floor This command rounds values down to the nearest integer value.
ceiling This command rounds up a value to the nearest integer.
trunc Creates integer values by truncating items at the decimal point.
round Rounds numeric values to a specified number of decimal places.
signif This command returns a value rounded to the specified number of significant figures.

 

These are all fairly obvious; here are some examples:

## Make some values
yy <- log(2^(2:6))
yy
[1] 1.386294 2.079442 2.772589 3.465736 4.158883

floor(yy)
[1] 1 2 2 3 4

ceiling(yy)
[1] 2 3 3 4 5

trunc(yy)
[1] 1 2 2 3 4

round(yy, digits = 3)
[1] 1.386 2.079 2.773 3.466 4.159

signif(yy, digits = 3)
[1] 1.39 2.08 2.77 3.47 4.16

## Include negative numbers
xx <- -3:3
xx
[1] -3 -2 -1  0  1  2  3

sign(xx)
[1] -1 -1 -1  0  1  1  1

abs(xx)
[1] 3 2 1 0 1 2 3

These commands work on most numeric objects (e.g. data.frame, vector, matrix, table). If you have logical objects, you’ll return 1 for a TRUE and 0 for a FALSE.

Scientific Format

You can enter numbers using an exponent to make it easier to deal with very large or very small values. The exponent is indicated by the letter e or E. You can use the – sign to indicate a negative exponent. The + sign can be omitted. You must not leave a space between the value and the exponent. You can only add an exponent to a numeric value and not to a named object.

1e3
[1] 1000

1E4
[1] 10000

# No spaces "allowed"
1 e-2
Error: unexpected symbol in "1 e"

1e-2
[1] 0.01

Values are generally “printed” by R in regular format but sometimes they will appear in scientific format. This makes no difference to your calculations but sometimes you want the result to be displayed in scientific format and at other times not. There are two ways to achieve the result you want.

The simplest way to present your results objects in an appropriate format is to use the format() command. You simply set scientific = TRUE to prepare an object in that format (set FALSE to use regular format). The downside to this is that the object is prepared as a text result, which might be inconvenient.

The other way is to alter the options() and to set the scipen option. The default is 0. Negative values tend to produce scientific notation and positive values are less likely to do so.

## Make some values
yy <- c(1, 10, 100, 1000, 10000, 100000)
zz <- c(1, 12, 123, 1234, 12345, 123456)

yy ; zz
[1] 1e+00 1e+01 1e+02 1e+03 1e+04 1e+05
[1]      1     12    123   1234  12345 123456

## Use format() to force scientific or foxed format
format(yy, scientific = FALSE)
[1] "     1" "    10" "   100" "  1000" " 10000" "100000"

format(zz, scientific = TRUE, digits = 3)
[1] "1.00e+00" "1.20e+01" "1.23e+02" "1.23e+03" "1.23e+04" "1.23e+05"

## Check the scipen option
options("scipen")
$scipen
[1] 0

## Set scipen
options(scipen = 1)
> yy
[1]      1     10    100   1000  10000 100000

options(scipen = -6)
zz
[1] 1.00000e+00 1.20000e+01 1.23000e+02 1.23400e+03 1.23450e+04 1.23456e+05

print(zz, digits = 3)
[1] 1.00e+00 1.20e+01 1.23e+02 1.23e+03 1.23e+04 1.23e+05

options(scipen = 0) # Reset scipen

You may need to tweak the values of scipen but in general the number of digits in the result is your guideline.

Extremes

The largest and smallest items can be extracted using max() and min() commands respectively. These commands produce a single value as the result.

The range() command produces two values, the smallest and largest, in that order.

## Set random number generator
set.seed(99)

## Make some values
xx <- runif(n = 10, max = 100, min = -100)

max(xx)
[1] 98.50176

min(xx)
[1] -77.24366

range(xx)
[1] -77.24366  98.50176

range(xx)[1] # Just the min value
[1] -77.24366

range(xx)[2:1] # Display max then min
[1]  98.50176 -77.24366

If you want the 2nd largest, or the 3rd smallest (for example) then you need to use the order() command to get an “index”. Set the sort order to decreasing = FALSE (the default) to get the smallest values, set decreasing = TRUE to get the largest values.

xx
[1]  16.942370 -77.243665  36.852949  98.501755   6.998717  93.322813  34.285512
[8] -41.084458 -28.327403 -64.937049

order(xx) # Index in ascending order
[1]  2 10  8  9  5  1  7  3  6  4

xx[order(xx)[1]] # 1st smallest (min)
[1] -77.24366

xx[order(xx)[2]] # 2nd smallest
[1] -64.93705

xx[order(xx, decreasing = TRUE)[1]] # 1st largest (max)
[1] 98.50176

xx[order(xx, decreasing = TRUE)[2]] # 2nd largest
[1] 93.32281

Logarithms

Logarithms and their reverse (anti-logs?) are dealt with using the log() and exp() commands respectively. The default base is the natural log (e) but you can specify the base explicitly. There are also several convenience commands:

 

log(x, base = exp(1)) The basic log command. The default base is natural. Use base parameter to specify alternative (as long as it works out as a numeric).
log10 A convenience function computes log base 10.
log2 Computes log base 2.
log1p Computes log(x + 1). See also expm1().
exp The antilog for base e.
expm1 The antilog for base e -1 i.e. exp(x) -1. See also log1p().

 

Using the commands is fairly simple.

log(1:4) # Natural log
[1] 0.0000000 0.6931472 1.0986123 1.3862944

log(1:4, base = 3) # Log base 3
[1] 0.0000000 0.6309298 1.0000000 1.2618595

log10(1:4) # Log base 10
[1] 0.0000000 0.3010300 0.4771213 0.6020600

log2(1:4) # Log base 2
[1] 0.000000 1.000000 1.584963 2.000000

log(0:3) # Regular log gives infinity for 0
[1]      -Inf 0.0000000 0.6931472 1.0986123

log1p(0:3) # Add 1 to values then log
[1] 0.0000000 0.6931472 1.0986123 1.3862944

10^0.6 # The antilog (base 10) of 0.6
[1] 3.981072

3^0.63 # The antilog (base 3) of 0.63
[1] 1.997958

exp(1.098) # The natural antilog of 1.098
[1] 2.998164

expm1(1.098) # The natural antilog of 1.098 then minus 1
[1] 1.998164

Trigonometry

R has a suite of commands that carry out trigonometric functions.

 

Regular Hyperbolic
Sine sin() asin() sinh() asinh()
Cosine cos() acos() cosh() acosh()
Tangent tan() atan() tanh() atanh()

 

The commands work out the basic functions and also hyperbolic equivalents. Angles are in radians (a right angle is pi/2 radians).

Here are some simple examples using the cosine:

cos(45) # Angle in radians
[1] 0.525322

cos(45 * pi/180) # Convert 45 radians to degrees
[1] 0.7071068

acos(1/2) * 180/pi # To get result in degrees
[1] 60

acos(sqrt(3)/2) * 180/pi # To get result in degrees
[1] 30

cosh(0.5) # Hyperbolic function
[1] 1.127626

Summation

There are various commands associated with lists of values (that is list in a general sense, not an R list object).

Adding things

The sum() command returns the sum of all the numbers specified. This works for most R objects, including data.frame, matrix and vectors. Logical values are treated as 1 (TRUE) or 0 (FALSE). NA items are treated as 0. You can “ignore” NA items using na.rm = TRUE as a parameter in the command.

Multiplying things

The prod() command returns the product of all the numbers specified, that is each value multiplied by the “next”. This works for most R objects, including data.frame, matrix and vectors (items are taken columnwise for data.frame objects). Logical values are treated as 1 (TRUE) or 0 (FALSE). NA items are treated as 0. You can “ignore” NA items using na.rm = TRUE as a parameter in the command.

v <- 1:9
m <- matrix(1:9, ncol = 3)
d <- data.frame(a = 1:3, b = 4:6, c = 7:9)
v ; m ; d
[1] 1 2 3 4 5 6 7 8 9

     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

  a b c
1 1 4 7
2 2 5 8
3 3 6 9

## sum for vector, matrix and data.frame
sum(v)
[1] 45

sum(m)
[1] 45

sum(d) # Reads data columnwise
[1] 45

## product for vector, matrix and data.frame
prod(v)
[1] 362880

prod(m)
[1] 362880

prod(d) # Reads data columnwise
[1] 362880

## Make a locical vector
z <- c(TRUE, TRUE, FALSE, TRUE)
z
[1]  TRUE  TRUE FALSE  TRUE

## Results for logicals
sum(z)
[1] 3

prod(z)
[1] 0

The factorial() command is similar to prod(). With prod() you specify prod(x:y) as a sequence, whilst in factorial() you specify factorial(y). If you provide more than one value, you end up with multiple results:

factorial(3) # i.e. 3 * 2 * 1
[1] 6

factorial(5) # i.e. 5 * 4 * 3 * 2 * 1
[1] 120

factorial(v)
[1]      1      2      6     24    120    720   5040  40320 362880

factorial(m)
     [,1] [,2]   [,3]
[1,]    1   24   5040
[2,]    2  120  40320
[3,]    6  720 362880

You can also use the gamma() command, which equates to factorial(x-1). Essentially prod(x:y) = gamma(y+1) = factorial(y).

Cumulative functions

There are several cumulative functions built-in to R. These determine sum, product, maximum and minimum.

 

cumsum Determines the cumulative sum
cumprod Cumulative product
cummax Cumulative maximum
cummin Cumulative minimum

 

The commands operate on numeric objects, usually vector or matrix objects. The commands work on data.frame objects but compute results per column. The commands do not work directly on lists (but you can use the lapply() command).

v <- 1:9
m <- matrix(1:9, ncol = 3)
d <- data.frame(a = 1:3, b = 4:6, c = 7:9)

v ; m ; d
[1] 1 2 3 4 5 6 7 8 9

     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

  a b c
1 1 4 7
2 2 5 8
3 3 6 9

cumsum(v)
[1]  1  3  6 10 15 21 28 36 45

cumprod(m)
[1]      1      2      6     24    120    720   5040  40320 362880

# For data.frame calculations are carried out column by column
cummax(d)
  a b c
1 1 4 7
2 2 5 8
3 3 6 9

It is possible to make custom functions that calculate other cumulative results, but that is another story.

There are many other mathematical functions in R. This has been a brief overview of some of the “simpler” arithmetic (although the foray into logic may not count as math).

Comments are closed.