R

if Statement

只能針對一個 TRUE 或是 FALSE 做執行:

  • if (condition) {true_action}
  • if (condition) {true_action} else {false_action}
  • if (condition1) {true_action1} else if (condition2) {true_action2} else {false_action}
?`if`
grade <- function(x) {
  if (x > 90) {
    "A"
  } else if (x > 80) {
    "B"
  } else if (x > 50) {
    "C"
  } else {
    "F"
  }
}
x1 <- if (TRUE) 1 else 2
x2 <- if (FALSE) 1 else 2

c(x1, x2)
## [1] 1 2
  • 請留意會出現錯誤的情況:
# if ("x") 1
# #> Error in if ("x") 1 : 引數無法解譯為邏輯值
# if (logical()) 1
# #> Error in if (logical()) 1 : 引數長度為零
# if (NA) 1
# #> Error in if (NA) 1 : 需要 TRUE/FALSE 值的地方有缺值
  • 以下情況則出現警告:
if (c(TRUE, FALSE)) 1
## Warning in if (c(TRUE, FALSE)) 1: 條件的長度 > 1,因此只能用其第一元素
## [1] 1
if (c(TRUE, TRUE)) 1
## Warning in if (c(TRUE, TRUE)) 1: 條件的長度 > 1,因此只能用其第一元素
## [1] 1
  • 注意else的位置
# x <- 10
# if (x < 3) {
#   x <- 0
# } 
# else {
#   x
# }

ifelse()

?ifelse
x <- c(6:-4)
sqrt(x)  #- gives warning
## Warning in sqrt(x): 產生了 NaNs
##  [1] 2.449490 2.236068 2.000000 1.732051 1.414214 1.000000 0.000000      NaN
##  [9]      NaN      NaN      NaN
sqrt(ifelse(x >= 0, x, NA))  # no warning
##  [1] 2.449490 2.236068 2.000000 1.732051 1.414214 1.000000 0.000000       NA
##  [9]       NA       NA       NA
## Note: the following also gives the warning !
ifelse(x >= 0, sqrt(x), NA)
## Warning in sqrt(x): 產生了 NaNs
##  [1] 2.449490 2.236068 2.000000 1.732051 1.414214 1.000000 0.000000       NA
##  [9]       NA       NA       NA
set.seed(100)
d <- sample(x = 20:60, size = 100, replace = TRUE)
d
##   [1] 29 57 44 33 42 41 25 23 25 53 26 26 37 31 54 27 37 44 21 23 23 51 40 46 58
##  [26] 35 30 21 25 48 49 49 48 42 50 52 50 48 47 50 60 60 60 43 26 20 28 57 39 33
##  [51] 33 43 22 55 46 24 23 31 35 41 37 22 24 40 47 44 34 31 56 45 58 43 31 28 30
##  [76] 54 45 60 35 56 49 46 37 27 46 27 21 38 50 42 31 45 24 33 59 36 31 49 59 50
ifelse(d >= 40, ifelse(d >= 60, "C", "B"), "A")
##   [1] "A" "B" "B" "A" "B" "B" "A" "A" "A" "B" "A" "A" "A" "A" "B" "A" "A" "B"
##  [19] "A" "A" "A" "B" "B" "B" "B" "A" "A" "A" "A" "B" "B" "B" "B" "B" "B" "B"
##  [37] "B" "B" "B" "B" "C" "C" "C" "B" "A" "A" "A" "B" "A" "A" "A" "B" "A" "B"
##  [55] "B" "A" "A" "A" "A" "B" "A" "A" "A" "B" "B" "B" "A" "A" "B" "B" "B" "B"
##  [73] "A" "A" "A" "B" "B" "C" "A" "B" "B" "B" "A" "A" "B" "A" "A" "A" "B" "B"
##  [91] "A" "B" "A" "A" "B" "A" "A" "B" "B" "B"

switch()

  • switchif else關係密切。在大多數的情況之下,可用switch改寫if else控制語句。
x_option <- function(x) {
  if (x == "a") {
    "option 1"
  } else if (x == "b") {
    "option 2" 
  } else if (x == "c") {
    "option 3"
  } else {
    stop("Invalid `x` value")
  }
}

可改寫為:

?switch

x_option <- function(x) {
  switch(x,
    a = "option 1",
    b = "option 2",
    c = "option 3",
    stop("Invalid `x` value")
  )
}
x_option("a")
## [1] "option 1"
x_option("b")
## [1] "option 2"
# x_option("v")
# Error in x_option("v") : Invalid `x` value
centre <- function(x, type) {
  switch(type,
         mean = mean(x),
         median = median(x),
         trimmed = mean(x, trim = .1))
}
set.seed(seed = 100)
x <- rcauchy(10)
centre(x, "mean")
## [1] 2.178666
centre(x, "median")
## [1] 0.8209592
centre(x, "trimmed")
## [1] 1.122283
centre(x, 1)
## [1] 2.178666
centre(x, 2)
## [1] 0.8209592
centre(x, 3)
## [1] 1.122283
centre(x, "trimed")
centre(x, 4)
ans <- centre(x, 4)
ans
## NULL
(switch("c", a = 1, b = 2))
## NULL
  • switch()還有一個方便的功能-當多個參數共用一個回傳值時:
legs <- function(x) {
  switch(x,
    cow = ,
    horse = ,
    dog = 4,
    human = ,
    chicken = 2,
    plant = 0,
    stop("Unknown input")
  )
}
legs(x = "cow")
## [1] 4
legs(x = "human")
## [1] 2

Loops

  • for (item in vector) { perform_action }
for (i in 1:3) {
  print(i ^ 2)
}
## [1] 1
## [1] 4
## [1] 9
# N.B.: for assigns the item to the current environment, overwriting any existing variable with the same name:

i <- 100
for (i in 1:3) {}
i
## [1] 3
  • 有兩個方法可提前終止for loop:

    • next: 離開當下的loop,回到loop的頂部,繼續下一次的

    • break: 離開整個loop

for (i in 1:10) {
  if (i < 3) 
    next
  print(i)
  if (i >= 5)
    break
}
## [1] 3
## [1] 4
## [1] 5
  • 運用for loop時,可預先配置物件大小,如atomic vector或是list:
means <- c(1, 50, 20)
out <- vector("list", length(means))
for (i in 1:length(means)) {
  out[[i]] <- rnorm(10, means[[i]])
}
  • 上述例子中,length(x)為0(為空向量)時,需小心使用1:length(x)
means <- c()
out <- vector("list", length(means))

# for (i in 1:length(means)) {
#   out[[i]] <- rnorm(10, means[[i]])
# }

# Error in rnorm(10, means[[i]]) : invalid arguments
  • 上述範例出現錯的原因在於:
1:length(means)
## [1] 1 0
  • 建議使用seq_along()函數:
seq_along(means)
## integer(0)
out <- vector("list", length(means))
for (i in seq_along(means)) {
  out[[i]] <- rnorm(10, means[[i]])
}

out
## list()

當你事先無法確定需要運算的次數,進而無法預先配置物件大小時,有以下兩種工具可使用:

  • while(): while( condition) action: performs action while condition is TRUE.
  • repeat():repeat action forever (i.e. until it encounters break).

Python

If Statement

  • if 述句的一般形式如下:

if test1:
    statements1
elif test2:
    statements2
else:
    statements3

簡單範例如下:

if 1:
    print('true!!!')
## true!!!
if not 1:
    print('true!!!')
else:
    print('false!!!')
## false!!!
x = 'killer rabbit' 
if x == 'roger':
    print("shave and a haircut") 
elif x == 'bugs':
    print("what's up doc?") 
else:
    print('Run away! Run away!')
## Run away! Run away!
  • Python沒有switch述句可以使用
  • 你可以使用多重if-else述句來替代
  • 亦或用dict做替代方案,會更具彈性-類似switch的功能
choice = 'eggs'

print({'spam': 1.25,
        'ham': 1.99,
        'eggs': 0.99,
        'bacon': 1.10}[choice])
## 0.99

也可利用dict的方法-get():

branch = {'spam': 1.25, 'ham': 1.99, 'eggs': 0.99}
print(branch.get('spam', 'Bad choice'))
## 1.25
print(branch.get('bacon', 'Bad choice'))
## Bad choice

當然,也可以用if-else來改寫:

choice = 'bacon'
if choice in branch:
    print(branch[choice]) 
else:
    print('Bad choice')
    
## Bad choice

更建議用try的語法來實現:

try: 
    print(branch[choice])
except KeyError: 
    print('Bad choice')
## Bad choice
def default(x):
    pass

branch = {'spam': lambda x: x ** 2,
          'ham': lambda x: 10 * x,
          'eggs': lambda x: x / 2
          }
          
choice = 'eggs'
branch.get(choice, default)(10)
## 5.0
branch.get('NCCU', default)(10)

if X:
    A = Y
else:
    A = Z

可改寫成:

A = Y if X else: Z

A = 't' if 'spam' else 'f'
A
## 't'
A = 't' if '' else 'f'
A
## 'f'
  • 也可以寫成類似底下方式:
['t', 'f'][bool('spam')]
## 'f'
['t', 'f'][bool('')]
## 't'

While Statement

while test: # Loop test
     statements # Loop body
else: # Optional else
     statements # Run if didn’t exit loop with break
x = 'NCCU'
while x:
    print(x, end = '\n')
    x = x[1:]
    
## NCCU
## CCU
## CU
## U
a = 0; b = 10
while a < b: # One way to code counter loops
    print(a, end=' ')
    a += 1
## 0 1 2 3 4 5 6 7 8 9

break, continue, pass, Loop else


while test:
    statements
    if test: break
    if test: continue
else:
    statements # Run if we didn’t hit a ‘break’

  • break:立即終止跳離出整個loops
# data = get_data()
# while data != "":
#     if "Good" in data:
#         print("找到單字Good")
#         break
#     data = get_data()
# else:
#     print("找不到單字Good")

範例:找出第一次出現數字1的位置:

x = [0, 0, 0, 1, 1, 0]
for i in range(0, len(x)):
    if x[i] == 1: break
i
## 3
  • continue:會跳過continue後面的程式碼,回到loop的頂部繼續執行下一次loops
# Python code:
x = 10
while x:
    x = x - 1
    if x % 2 != 0: 
        continue
    print(x, end = '\n')
## 8
## 6
## 4
## 2
## 0
# R code:
x <- 10
while(x != 0) {
  x <- x - 1
  if (x %% 2 != 0) {
    next
  }
  cat(x, sep = "\n")
}
## 8
## 6
## 4
## 2
## 0
  • pass:(no-operation placeholder)不做任何事,常為了語法要求,用於佔位。
# Python code:
def func1():
    pass # Add real code here later
type(func1)
## <class 'function'>
# R code:
func1 <- function() {
  
}
class(func1)
## [1] "function"
# Python code:
x = 10
if x < 5:
    pass
else:
    x = 5
x
## 5
# R code:
x <- 10
if (x < 5) {
    
} else {
  x <- 5
}
x
## [1] 5
  • Loop else:
y = 4

x = y // 2
while x > 1:
    if y % x == 0:
        print(y, 'has factor', x) 
        break
    x -= 1 
else:
    print(y, 'is prime')
## 4 has factor 2

for loops


for target in object:
    statements
else:
    statements

for target in object:
    statements
    if test: break
    if test: continue
else:
    statements # If we didn’t hit a ‘break’

for x in ["spam", "eggs", "ham"]:
    print(x, end = ' ')
## spam eggs ham
sum = 0
for x in [1, 2, 3, 4]:
    sum = sum + x
sum
## 10
S = "lumberjack"
T = ("and", "I'm", "okay")
for x in S: 
    print(x, end=' ')
    
## l u m b e r j a c k
for x in T: print(x, end = ' ')
## and I'm okay
T = [(1, 2), (3, 4), (5, 6)]
for a, b in T:              # Tuple assignment at work
    print(a, b)
## 1 2
## 3 4
## 5 6
D = {'a':1, 'b':2, 'c':3}
for key in D:
    print(key, '=>', D[key])    # Use dict keys iterator and index
## a => 1
## b => 2
## c => 3
D = {'a':1, 'b':2, 'c':3}
list(D.items())     # 用iterms()傳回所有的鍵與值,相對應的鍵值會存放在tuple內,並轉換為list
## [('a', 1), ('b', 2), ('c', 3)]
for key, value in D.items():
    print(key, '=>', value)
## a => 1
## b => 2
## c => 3
T = [(1, 2), (3, 4), (5, 6)]
for both in T:
    a, b = both
    print(a, b)
## 1 2
## 3 4
## 5 6
for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]: print(a, b, c)
## 1 2 3
## 4 5 6
for (a, *b, c) in [(1, 2, 3, 4), (5, 6, 7, 8)]: print(a, b, c)
## 1 [2, 3] 4
## 5 [6, 7] 8
  • Nested for loops:
items = ["aaa", 111, (4, 5), 2.01]       # A set of objects
tests = [(4, 5), 3.14]                   # Keys to search for

for key in tests:                        # For all keys
    for item in items:                   # For all items
        if item == key:                  # Check for match
            print(key, "was found")
            break
    else:
        print(key, "not found!")
## (4, 5) was found
## 3.14 not found!

亦可以改寫更簡單的程式碼:

for key in tests:
    if key in items:
        print(key, 'was found')
    else:
        print(key, 'not found!')
## (4, 5) was found
## 3.14 not found!
seq1 = 'spam'
seq2 = 'scam'

res = []
for x in seq1:
    if x in seq2:
        res.append(x)

res
## ['s', 'a', 'm']
  • 可用 list comprehension 蒐集結果並以 list 儲存:
[x for x in seq1 if x in seq2]
## ['s', 'a', 'm']
def echo(message):
    print(message)
    
schedule = [(echo, 'NCCU'), (echo, 'Money and Banking')]
for (f, args) in schedule:
    f(args)
## NCCU
## Money and Banking

Advanced techniques

range()

  • 計數器循環使用
S = 'spam'
for i in range(len(S)):
    S = S[1:] + S[:1] 
    print(S, end=' ')
    
## pams amsp mspa spam
L = [1, 2, 3]
for i in range(len(L)):
    X = L[i:] + L[:i] 
    print(X, end=' ')
## [1, 2, 3] [2, 3, 1] [3, 1, 2]
S = 'abcdefghijk'
list(range(0, len(S), 2))
## [0, 2, 4, 6, 8, 10]
for i in range(0, len(S), 2): 
    print(S[i], end=' ')
## a c e g i k

zip()

L1 = [1,2,3,4]
L2 = [5,6,7,8]
zip(L1, L2)
## <zip object at 0x7fe6fb72fdc0>
list(zip(L1, L2))
## [(1, 5), (2, 6), (3, 7), (4, 8)]
for (x, y) in zip(L1, L2):
    print(x, y, '--', x+y)
## 1 5 -- 6
## 2 6 -- 8
## 3 7 -- 10
## 4 8 -- 12
T1, T2, T3 = (1,2,3), (4,5,6), (7,8,9)
T3
## (7, 8, 9)
list(zip(T1, T2, T3))
## [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
S1 = 'abc'
S2 = 'xyz123'
list(zip(S1, S2))   # # Truncates at len(shortest)
## [('a', 'x'), ('b', 'y'), ('c', 'z')]
Z = zip((1, 2, 3), (10, 20, 30))    # zip is the same: a one-pass iterator
Z
## <zip object at 0x7fe6fb730740>
list(Z)
## [(1, 10), (2, 20), (3, 30)]
for pair in Z: print(pair)          # Exhausted after one pass
Z = zip((1, 2, 3), (10, 20, 30))    # Manual iteration (iter() not needed)
next(Z)
## (1, 10)
next(Z)
## (2, 20)

enumerate()

S = 'spam'
list(enumerate(S))
## [(0, 's'), (1, 'p'), (2, 'a'), (3, 'm')]
for (offset, item) in enumerate(S):
    print(item, 'appears at offset', offset)
## s appears at offset 0
## p appears at offset 1
## a appears at offset 2
## m appears at offset 3
S = 'spam'
E = enumerate(S)  # generator object
E
## <enumerate object at 0x7fe6fb737d40>
next(E)
## (0, 's')
next(E)
## (1, 'p')
next(E)
## (2, 'a')
next(E)
# next(E)
# StopIteration: 
# 
# Detailed traceback: 
#   File "<string>", line 1, in <module>
## (3, 'm')

map()

  • maprange類似,內建函數mapzip、在Python 3.X中可得iterable object以節省內存的空間,不需在內存中一次性產生一個結果list
  • maprange不同,回傳本身就是一個iterator
  • map的效能比for loop高
M = map(abs, [-1, 0, 1])    # map returns an iterable, not a list
M
## <map object at 0x7fe6fb739610>
next(M)                     # Use iterator manually: exhausts results
## 1
next(M)
## 0
next(M)
## 1
for x in M: print(x)        # no results
M = map(abs, [-1, 0, 1]) 
for x in M: print(x)
## 1
## 0
## 1
list(map(abs, (-1, 0, 1)))   # Can force a real list if needed
## [1, 0, 1]

補充:

Iterations and Comprehensions

  • Python針對常見的『循環(Loop)』另外提供更方便也更有效率的觀念與工具:
    • Iteration Protocol(迭代協定)
    • Iterable object :
      An object is considered iterable if it is either a physically stored sequence, or an object that produces one result at a time in the context of an iteration tool like a for loop.
    • List Comprehensions (列表生成、列表推導)
  • Iterable object (可迭代物件) ==> __ iter __
  • Iterator object (迭代器物件) ==> __ next __
L = [1, 2, 3]
I = iter(L)      # Obtain an iterator object from an iterable
I.__next__()
## 1
I.__next__()
## 2
I.__next__()
# I.__next__()
# StopIteration: 
# 
# Detailed traceback: 
#   File "<string>", line 1, in <module>
## 3
L = [1, 2, 3]
iter(L) is L

# L.__next__()
# AttributeError: 'list' object has no attribute '__next__'
## False
I = iter(L)
I.__next__()
## 1
next(I)            # Same as I.__next__()
## 2
  • Manual iteration
L = [1, 2, 3]
for X in L:                        # Automatic iteration
    print(X ** 2, end = ' ')       # Obtains iter, calls __next__, catches exceptions
## 1 4 9
L = [1, 2, 3]
I = iter(L)                        # Manual iteration: what for loops usually do
while True:
    try:                           # try statement catches exceptions
        X = next(I)
    except StopIteration:
        break
    print(X ** 2, end=' ')
## 1 4 9
D = {'a':1, 'b':2, 'c':3}
for key in D.keys():
    print(key, D[key])
## a 1
## b 2
## c 3
D = {'a':1, 'b':2, 'c':3}
I = iter(D)
I.__next__()
## 'a'
next(I)
## 'b'
next(I)
# next(I)
# StopIteration: 
# 
# Detailed traceback: 
#   File "<string>", line 1, in <module>
## 'c'

list comprehensive

  • list comprehensive (列表推導) 可將⼀個for迴圈 (將list中的元素取出修改) 組成述句,壓縮為⼀⾏簡短易讀程式碼的簡單⽅法
  • 一般來說,list comprehensive執行速度會較for loop快(兩倍)
L = [1, 2, 3, 4, 5]
for i in range(len(L)):
    L[i] += 10
    
L
## [11, 12, 13, 14, 15]
L = [1, 2, 3, 4, 5]
L = [x + 10 for x in L]
L
## [11, 12, 13, 14, 15]
  • 也可加入if條件
L = [1, 2, 3, 4, 5]
L = [x ** 2 for x in L if x % 2 == 0]
L
## [4, 16]
  • 巢狀for loop
res = []
for x in ['2020', '2021']:
    for y in ['/一月', '/二月', '/三月']:
        res.append(x + y)
res
## ['2020/一月', '2020/二月', '2020/三月', '2021/一月', '2021/二月', '2021/三月']
res = [x + y for x in ['2020', '2021'] for y in ['/一月', '/二月', '/三月']]
res
## ['2020/一月', '2020/二月', '2020/三月', '2021/一月', '2021/二月', '2021/三月']

dict comprehensive

new_dict = {expr1: expr2 for variable in list if condition}
x = [1, 2, 3, 4]
x_squared_dict = {item: item * item for item in x}
x_squared_dict
## {1: 1, 2: 4, 3: 9, 4: 16}

Multiple Versus Single Pass Iterators

  • range 支援len與索引取值(indexing):
R = range(5)
len(R)
## 5
R[0]
## 0
  • range 支援 Multiple Iterators
R = range(3)          # range allows multiple iterators

# next(R)
# TypeError: 'range' object is not an iterator

I1 = iter(R)
next(I1)
## 0
next(I1)
## 1
I2 = iter(R)
next(I2)
## 0
next(I2)
## 1
  • 在Python 3.X中,zip , map 則『不』支援Multiple Iterators,為 Single Iterator:
Z = zip((1, 2, 3), (10, 20, 30))
I1 = iter(Z)
I2 = iter(Z)

next(I1)
## (1, 10)
next(I1)
## (2, 20)
next(I2)
## (3, 30)
M = map(abs, (-1, 0, 1))
I1 = iter(M)
I2 = iter(M)
print(next(I1), next(I1), next(I1))

# next(I2)
# StopIteration: 
## 1 0 1
R = range(3)
I1, I2 = iter(R), iter(R)
[next(I1), next(I1), next(I1)]
## [0, 1, 2]
next(I2)
## 0

dictionary view iterables

D = {'a': 1, 'b': 2, 'c': 3}
D
## {'a': 1, 'b': 2, 'c': 3}
K = D.keys()
K

# next(K)         # K are not iterators themselves
# TypeError: 'dict_keys' object is not an iterator
## dict_keys(['a', 'b', 'c'])
I = iter(K)
next(I)
## 'a'
next(I)
## 'b'
for k in D.keys(): print(k, end=' ')
## a b c