# python
print(1. + 2.)
## 3.0
print(10.**2) # exponent
## 100.0
print(3 / 2)
## 1.5
print(3 // 2) # integer division
## 1
print(15 % 4) # modulo = remainder
## 3
# R
print(1 + 2)
## [1] 3
print(10 ^ 2) # exponent
## [1] 100
print(3 / 2)
## [1] 1.5
print(as.integer(3 / 2)) # there is no direct integer division
## [1] 1
print(15 %% 4) # modulo = remainder
## [1] 3
Most to least powerful. Use parentheses to ensure intended order of operations.
# python
print(100 + 1000/10**2)
## 110.0
print(100 + (1000/10)**2)
## 10100.0
print((100 + 1000/10)**2)
## 40000.0
# R
print(100 + 1000/10^2)
## [1] 110
print(100 + (1000/10)^2)
## [1] 10100
print((100 + 1000/10)^2)
## [1] 40000
# python
a = 2
print(a)
## 2
print(3 > a) # greater
## True
print(3 < a) # smaller
## False
print(a == 2) # equals
## True
print(a != 2) # not equals
## False
# R
a <- 2
print(a)
## [1] 2
print(3 > a) # greater
## [1] TRUE
print(3 < a) # smaller
## [1] FALSE
print(a == 2) # equals
## [1] TRUE
print(a != 2) # not equals
## [1] FALSE
# NOTE: in R these comparisons also work for vectors and are applied on an entry-by-entry basis
a <- c(1, 2, 7)
print(a)
## [1] 1 2 7
print(3 > a)
## [1] TRUE TRUE FALSE
print(3 < a)
## [1] FALSE FALSE TRUE
print(a == 2)
## [1] FALSE TRUE FALSE
print(a != 2)
## [1] TRUE FALSE TRUE
# python --> keywords and, or
a = 2
b = 3
print((a == 2) and (b > 1))
## True
print((a != 2) or (b > 1))
## True
# R - operators &&, ||
a <- 2
b <- 3
print(a == 2 && b > 1)
## [1] TRUE
print(a != 2 || b > 1)
## [1] TRUE
# however, be careful chaining vectors with logical comparisons
a <- c(1, 2, 7)
print(a)
## [1] 1 2 7
print(a == 1) # vector of TRUE/FALSE values
## [1] TRUE FALSE FALSE
print(a == 1 && b > 1) # only 1st value of vector is used in && chain!!
## [1] TRUE
print(a == 1 & b > 1) # use & and | instead for element-wise chaining
## [1] TRUE FALSE FALSE
# also possible for multi-vector chains such as in this data table
dplyr::data_frame(
a = c(T, F, F, T),
b = c(F, T, T, T),
a_and_b = a & b,
a_or_b = a | b,
a_not_b = a != b
)
## # A tibble: 4 x 5
## a b a_and_b a_or_b a_not_b
## <lgl> <lgl> <lgl> <lgl> <lgl>
## 1 TRUE FALSE FALSE TRUE TRUE
## 2 FALSE TRUE FALSE TRUE TRUE
## 3 FALSE TRUE FALSE TRUE TRUE
## 4 TRUE TRUE TRUE TRUE FALSE
# python
BACON = 1 # 0001 in binary
LETTUCE = 2 # 0010 in binary
TOMATO = 4 # 0100 in binary
SOURDOUGH = 8 # 1000 in binary
sandwich = BACON | TOMATO | SOURDOUGH # = 1101 in binary (all but lettuce)
print(sandwich)
# what bits does the sandwich have in common with each ingredient?
## 13
print(sandwich & BACON) # the BACON bit is also 1 in the sandwich
## 1
print(sandwich & LETTUCE) # the LETTUCE bit is 0 in the sandwich
## 0
print(sandwich & TOMATO) # the TOMATO bit is 1 in the sandwich
## 4
print(sandwich & SOURDOUGH) # the SOURDOUGH bit is 1 in the sandwich
## 8
# R
# CAUTION: remember that & and | are _ELEMENT-wise_ operators for vectors,
# NOT bitwise operators like in python - if bitwise is needed, there are
# specific functions defined for it
BACON <- 1 # 0001 in binary
LETTUCE <- 2 # 0010 in binary
TOMATO <- 4 # 0100 in binary
SOURDOUGH <- 8 # 1000 in binary
sandwich <- bitwOr(bitwOr(BACON, TOMATO), SOURDOUGH)
print(sandwich)
## [1] 13
print(bitwAnd(sandwich, BACON))
## [1] 1
print(bitwAnd(sandwich, LETTUCE))
## [1] 0
print(bitwAnd(sandwich, TOMATO))
## [1] 4
print(bitwAnd(sandwich, SOURDOUGH))
## [1] 8
# python
def foo1():
pass
print(foo1()) # if no return value is defined, return value is None
## None
def foo2():
return(1)
print(foo2()) # one return value
## 1
def foo3():
return(1, "two", 3.14)
print(foo3()) # multiple return values
## (1, 'two', 3.14)
# R
foo1 <- function() {
}
str(foo1()) # if no return value defined, return value is NULL
## NULL
# BUT: last statement of a function becomes automatically the return value!!
# --> good coding = be explicit and always use return() statements in a function
foo1b <- function() {
1
}
str(foo1b())
## num 1
foo2 <- function() {
return(1)
}
str(foo2()) # one return value
## num 1
foo3 <- function() {
return(list(1, "two", 3.14))
}
str(foo3()) # multiple return values
## List of 3
## $ : num 1
## $ : chr "two"
## $ : num 3.14
# python
def foo4(x, y):
return(x * y**2)
print(foo4(2, 5)) # parameters passed in order
## 50
print(foo4(y = 2, x = 5)) # passed by explicit name
# parameter defaults
## 20
def foo5(x, y = 5):
return (x * y**2)
print(foo5(2)) # use with default for y
## 50
print(foo5(2, 10)) # overwrite default for y
# variables as parameters
## 200
a = 4
print(foo5(x = a)) # x takes the value of a inside the function
# cannot pass undefined variables
## 100
try:
foo5(x = undef)
except:
print("Unexpected error:", sys.exc_info()[1])
# cannot omit parameters that don't have defaults, even if they are not used
## Unexpected error: name 'undef' is not defined
def foo6(x, y = 5):
return(y**2) # x not used!
try:
foo6()
except:
print("Unexpected error:", sys.exc_info()[1])
## Unexpected error: foo6() missing 1 required positional argument: 'x'
# R
foo4 <- function(x, y) {
return(x * y^2)
}
print(foo4(2, 5)) # parameters passed in order
## [1] 50
print(foo4(y = 2, x = 5)) # passed by explicit name
## [1] 20
# parameter defaults
foo5 <- function(x, y = 5) {
return (x * y^2)
}
print(foo5(2)) # use with default for y
## [1] 50
print(foo5(2, 10)) # overwrite default for y
## [1] 200
# variables as parameters
a <- 4
print(foo5(x = a)) # x takes the value of a inside the function
## [1] 100
# CAN omit parameters that don't have defaults IF they are not used
foo6 <- function(x, y = 5) {
return(y^2) # x not used!
}
print(foo6())
## [1] 25
# CAN also pass variables that are not defined IF they are not used
print(foo6(x = undef))
## [1] 25
# Explanation: R uses so-called lazy-evaluation, the parameters do NOT actually
# get evaluated for their value UNTIL they are needed (if they are never needed,
# it never checks whether they actually exist).
foo6b <- function(x, y = 5) {
return(rlang::enquo(x)) # return the structure of the x argument
}
print(foo6b(x = undef)) # until its value is needed, x is just an expression defined in a specific environment
## <quosure>
## expr: ^undef
## env: global
# special parameter ...
foo6c <- function(x, y = 5, ...) {
str(list(...))
}
foo6c(x = 2, y = 5) # only named parameters provided
## list()
foo6c(x = 2, z = 10, k = 9.81) # additional parameters provided in unspecific ...
## List of 2
## $ z: num 10
## $ k: num 9.81
Variables defined inside functions (local variables) do NOT overwrite variables of the same name defined in the global environment. Local variables disappear once the function finishes.
# python
a = 2
def foo8():
a = 395
print(a) # print the value defined inside the function
print(a)
## 2
foo8()
## 395
print(a) # kept its original value
## 2
# R
a <- 2
foo8 <- function() {
a <- 395
print(a) # print the value defined inside the function
}
print(a)
## [1] 2
foo8()
## [1] 395
print(a) # kept its original value
## [1] 2
Also called passing by reference (as opposed to passing by value).
# python
# unlike primite data types (integers, numerics, strings, booleans), objects like
# tuples, dicts, etc. get passed by reference and DO change inside the function
a_list = [3.14, 9.8]
def foo9(somelist):
somelist[0] = 6.67e-11
somelist.append(6.022e23)
print(somelist) # print the value at end of function
print(a_list)
## [3.14, 9.8]
foo9(a_list)
## [6.67e-11, 9.8, 6.022e+23]
print(a_list) # did NOT keep its original value
# Advantage: large objects don't get copied each time
# Disadvantage: functions can alter global variables accidentally or without the
# user realizing
## [6.67e-11, 9.8, 6.022e+23]
# R
# even objects get passed by value (i.e. no passing by reference / do not get altered)
a_list <- list(3.14, 9.8)
foo9 <- function(somelist) {
somelist[1] <- 6.67e-11
somelist <- c(somelist, list(6.022e23))
str(somelist)
}
str(a_list)
## List of 2
## $ : num 3.14
## $ : num 9.8
foo9(a_list)
## List of 3
## $ : num 6.67e-11
## $ : num 9.8
## $ : num 6.02e+23
str(a_list) # a list stayed the same
## List of 2
## $ : num 3.14
## $ : num 9.8
# Advantage: functions don't accidentally change variables outside their scope
# Disadvantage: large objects get copied (although lazy evaluation helps some)
# R - changing global variables after all (NOTE: this is not recommended
# pratice in R)
a_list <- list(3.14, 9.8)
foo10 <- function() {
a_list[1] <<- 6.67e-11 # using <<- assigns global variable
a_list <- c(a_list, list(6.022e23)) # using <- does not
str(a_list)
}
str(a_list)
## List of 2
## $ : num 3.14
## $ : num 9.8
foo10()
## List of 3
## $ : num 6.67e-11
## $ : num 9.8
## $ : num 6.02e+23
str(a_list) # the command using <<- altered the global variable
## List of 2
## $ : num 6.67e-11
## $ : num 9.8
# BETTER: use return value and EXPLICITLY overwrite your global variable
a_list <- list(3.14, 9.8)
foo11 <- function(somelist) {
somelist[1] <- 6.67e-11
somelist <- c(somelist, list(6.022e23))
return(somelist)
}
str(a_list)
## List of 2
## $ : num 3.14
## $ : num 9.8
str(foo11(a_list))
## List of 3
## $ : num 6.67e-11
## $ : num 9.8
## $ : num 6.02e+23
str(a_list) # a list did not change
## List of 2
## $ : num 3.14
## $ : num 9.8
a_list <- foo11(a_list) # overwrite with new value returned by function
str(a_list) # now a_list has the new value BUT the function did not change it, the user did with the explicit re-assignment to a_list
## List of 3
## $ : num 6.67e-11
## $ : num 9.8
## $ : num 6.02e+23