1.3 Scalars

1.3.1 Defining scalars

We have learned that scalars are objects of length 1 (aka. atomic objects) and how to define objects by assigning names to them. So let’s define some scalar objects and then use some generic functions to check their length and type:

a <- 1     # assign/set a to 1
a          # print a
#> [1] 1
a + 2      # evaluate a + 2
#> [1] 3
sum(a, 2)  # evaluate the function sum() with arguments a and 2
#> [1] 3

b <- 2
b
#> [1] 2
b * b
#> [1] 4
prod(b, b)
#> [1] 4
b ^ 2
#> [1] 4
b^3
#> [1] 8

c <- a + b  # assign c to the sum of a + b 
c
#> [1] 3
length(c)       # 1 could indicate a scalar OR the number of digits...
#> [1] 1
length(1000)    # Check: also 1 (i.e., NOT number of digits)
#> [1] 1
typeof(c)       # numbers are of type "integer" or "double" 
#> [1] "double"
typeof(3.14159) # decimal numbers are of type "double"
#> [1] "double"

d <- "word" # note the quotes ("")
d
#> [1] "word"
length(d)  # also a scalar
#> [1] 1
nchar(d)   # 5 characters long
#> [1] 4
typeof(d)  # but of type "character"
#> [1] "character"

e <- b > a  # assign (b > a) to an object e
e
#> [1] TRUE
length(e)  # also a scalar
#> [1] 1
typeof(e)  # of type "logical"
#> [1] "logical"

Practice

  • Further examine the scalars we have just defined (e.g., by combining them, assigning new objects, and checking their typeof, length, or nchar):
b  # is a number
a  # ...
b > a          # neither number nor chacacter:
typeof(a > b)  # type "logical"
nchar(a > b)   # returns 4: nchar of the character "TRUE"

f <- !e
f
typeof(f)
length(f)
nchar(f)

Thus, our exploration shows that objects a, b and c are numeric objects (which can be of type integer or of type double), whereas d is a text object (of type character), and e is the result of a test that is either TRUE or FALSE (of type logical).

1.3.2 Changing objects

To change an existing object, we need to re-assign it. Thus, changing an object works just like creating it:

# Check values (defined above):
a
#> [1] 1
b
#> [1] 2
a/b
#> [1] 0.5

a <- 100  # changes a
a         # a has changed
#> [1] 100
a/b       # a/b changes when a has been changed
#> [1] 50

b <- 200  # changes b
b         # b has changed
#> [1] 200
a/b       # a/b changes when b has been changed
#> [1] 0.5

d
#> [1] "word"
d <- "weird"  # changes d
d 
#> [1] "weird"

This implies that the order of evaluations matters: The same object (e.g., a or a/b) has different contents at different locations and at different times. (Note that the line numbers to the left of your editor window mark locations and that R scripts are typically evaluated in a top-down fashion.)

1.3.3 Applying functions to scalars

We have applied some simple functions to data arguments above, but not all functions can be applied to all data. Importantly, most functions require specific types of arguments to work (i.e., the types of the arguments must match the required argument types of the function). When viewing this requirement from the perspective of existing objects, the type of an object determines which functions can be applied to it:

# Numeric objects:
a
#> [1] 100
typeof(a)  # a generic function (working with all object types)
#> [1] "double"
length(a)  # a scalar
#> [1] 1
a + b      
#> [1] 300
sum(a, b)  # an arithmetic function (requiring numeric object types)
#> [1] 300

# Character objects:
d
#> [1] "weird"
typeof(d)
#> [1] "character"
length(d) # a scalar 
#> [1] 1
nchar(d)  # the "length" of a character object
#> [1] 5

# Logical objects:
e
#> [1] TRUE
typeof(e)
#> [1] "logical"
!e          # negation (reverses logical value)
#> [1] FALSE
!!e
#> [1] TRUE
isTRUE(e)   # tests a logcial expression
#> [1] TRUE
isTRUE(!e)
#> [1] FALSE
e == !!e    # tests equality
#> [1] TRUE

In case of a mismatch between function and object types, an error may occur. For instance, arithmetic functions typically require numeric data and yield errors when applied to text (i.e., character data).

Practice

  • Given our current definitions of scalars, evaluate the following expressions (and explain any errors or unexpected results):
a + d
sum(a, d)
d^2
e + !!e

1.3.4 Arithmetic functions

For numeric objects, we can compute new numeric values by applying arithmetic functions:

## (A) Arithmetic operators: ---- 
+ 2    # keeping sign
#> [1] 2
- 3    # reversing sign
#> [1] -3
1 + 2  # addition
#> [1] 3
3 - 1  # subtraction
#> [1] 2
2 * 3  # multiplication
#> [1] 6
5 / 2  # division
#> [1] 2.5

2^3    # exponentiation
#> [1] 8

5 %/% 2  # integer division
#> [1] 2
5 %% 2   # remainder of integer division (x mod y)
#> [1] 1

As soon as an arithmetic expression contains more than 1 operator, the issue of operator precedence arises. Fortunately, R uses the same precedence rules as we have learned in school — the so-called “BEDMAS” order:

  • Brackets (),
  • Exponents ^,
  • Division / and Multiplication *,
  • Addition + and Subtraction -
## (B) Operator precedence: ---- 
1 / 2 * 3   # left to right
#> [1] 1.5
1 + 2 * 3   # precedence: */ before +-
#> [1] 7
(1 + 2) * 3 # changing order by parentheses
#> [1] 9

2^1/2   == 1
#> [1] TRUE
2^(1/2) == sqrt(2)
#> [1] TRUE

Calling ?Syntax provides a longer list of operator precedence. However, using parentheses to structure longer (arithmetic or logical) expressions increases transparency and is recommended.

All arithmetic operators can not only be used with numbers (or data objects for which is.numeric is TRUE), but also with scalar objects that are assigned to numbers:

x <- 2
y <- 3

+ x     # keeping sign 
#> [1] 2
- y     # reversing sign
#> [1] -3
x + y   # addition
#> [1] 5
x - y   # subtraction
#> [1] -1
x * y   # multiplication
#> [1] 6
x / y   # division
#> [1] 0.6666667
x ^ y   # exponentiation
#> [1] 8
x %/% y # integer division
#> [1] 0
x %% y  # remainder of integer division (x mod y)
#> [1] 2

Actually, arithmetic operators also work with numeric vectors (see Exercise 3 in Section 1.8.3).

Practice

  • Call ?Arithmetic for further information on arithmetic operators and ?Syntax for a full list of precedence groups.
?Arithmetic  # shows help on arithmetic operators
?Syntax      # shows help on operator precedence

1.3.5 Logical values and operators

By comparing numbers and using logical operators, we can obtain logical values (i.e., scalars of type logical that are either TRUE or FALSE) by conducting tests on numeric values:

## Logical comparisons:
2 > 1   # larger than
#> [1] TRUE
2 >= 2  # larger than or equal to
#> [1] TRUE
2 < 1   # smaller than
#> [1] FALSE
2 <= 1  # smaller than or equal to
#> [1] FALSE

1 == 1  # == ... equality
#> [1] TRUE
1 != 1  # != ... inequality 
#> [1] FALSE

## Logical operators:
(2 > 1) & (1 > 2)   # & ... logical AND
#> [1] FALSE
(2 < 1) | (1 < 2)   # | ... logical OR
#> [1] TRUE
(1 < 1) | !(1 < 1)  # ! ... logical negation  
#> [1] TRUE

Practice

  • Evaluate ?base::Logic to see and read the help page on logical operators.11
?base::Logic  # shows help on logical operators
  • Look up De Morgan’s Laws (e.g., on Wikipedia) and express them in R.

Hint: Verify their truth by evaluating them for 2 objects A and B that are assigned to arbitrary truth values.

Solution

A <- TRUE   # set to either TRUE or FALSE.
B <- FALSE  # set to either TRUE or FALSE.

# Irrespective of the values of A and B, 
# the following should ALWAYS evaluate to TRUE:

# (1) not (A or B) = not A and not B:
!(A | B) == (!A & !B)

# (2) not (A and B) = not A or not B:
!(A & B) == (!A | !B)

  1. The Examples of ?base::Logic illustrate a somewhat problematic regularity of R: They are informative for experienced users, but are often difficult to understand for beginners. Nevertheless, copying and trying to understand examples is a good way to learn more about a particular topic.