本文已参加「新人创作礼」活动,一起开启掘金创作之路。
这次带来的是 RStudio 的编程基础。
各个知识点后面都有对应的小练习哦,大家可以利用刚刚学到的知识来试着写写看!
到目前为止, 我们所用到的R代码基本只能算是一条条命令的集合, 只能用于解决一些相对简单的问题, 当面对更复杂的情况时, 常常会显得力不从心.
从今天开始, 我们将会涉及R编程的核心. 通过对控制流与函数的学习, 我们将有能力利用计算机去解决一些比较复杂的问题, 这种实践能力需要足够时长的训练才能掌握, 这使它成为了高薪工作的门槛之一.
市面上的大部分R语言书籍都以统计/数据分析/机器学习为主, 并没有涉及或很少涉及R作为一门编程语言的内容, 如果你想更加深入的学习这部分, 可以参考这本书: The Art of R Programming (R语言编程艺术)
Conditionals 条件判断结构
本节开始,我们将学习对象之间的关系运算, 和 条件判断结构.
- 关系运算
- 相等
- 关于浮点数之间的比较
- 逻辑运算
- 条件语句
- 条件语句(2)
- 练习:3n+1
# Relational Operators 关系运算
# Greater and less than 大于小于
1 < 2
pi > 3
exp(1) > pi
-1 <= 0
-1 >= -2
# - Equality 相等
3 == (2+1)
"intermediate" != "r"
TRUE != FALSE
"Rchitect" != "rchitect"
# 关于浮点数之间的比较
1.1 * 2 == 2.2
2 * 1.1 == 2.2
2/7 * 1.1 == 2.2/7
2/7 * 1.1 - 2.2/7
1.1 * 2/7 - 2.2/7
all.equal(2/7 *1.1 , 2.2/7)
abs(2/7 * 1.1 - 2.2/7)
abs(1.1 * 2/7 - 2.2/7)
abs(2/7 * 1.1 - 2.2/7) < 1e-10
# Logical Operators
TRUE > FALSE
as.numeric(TRUE)
as.numeric(FALSE)
TRUE & TRUE
TRUE & FALSE
FALSE & FALSE
TRUE | TRUE
TRUE | FALSE
FALSE | FALSE
!TRUE
!FALSE
# Conditional Statements 条件语句
# if...else...
if(condition){
do sth ...
}
if(condition){
do sth ...
}else{
do sth else...
}
if(condition 1){
do sth ...
}else if(condition 2){
do sth else...
}else{
do sth else...
}
x = 4
if(x > 0){
print('x is positive')
}
if(x > 0){
print('x is positive')
}else{
print('x is NOT positive')
}
if(x > 0){
print('x is positive')
}else if(x == 0){
print('x is zero')
}else{
print('x is negative')
}
# ifelse
ifelse(condition, yes, no)
ifelse(1:5 > 2, 'large', 'small')
# coding exercise
# 3n+1 problem
# 其正式名称是 Collatz Conjecture, 其内容是:
# 对于一个正整数n, 如果为偶数, 就变为n/2; 如果是奇数, 就变为3n+1
# 对于任意的正整数n, 都会变成1
# 写一段code, 对于我们任意指定的n, 完成一次'3n+1变换'
延伸阅读: Collatz conjecture
# Collatz conjecture
# From Wikipedia, the free encyclopedia
# (https://en.wikipedia.org/wiki/Collatz_conjecture)
#
# The Collatz conjecture is a conjecture in mathematics that concerns a sequence defined as follows: start with any positive integer n. Then each term is obtained from the previous term as follows: if the previous term is even, the next term is one half of the previous term. If the previous term is odd, the next term is 3 times the previous term plus 1. The conjecture is that no matter what value of n, the sequence will always reach 1.
#
# The conjecture is named after Lothar Collatz, who introduced the idea in 1937, two years after receiving his doctorate.
Loops 循环
循环结构可以让我们高效的处理大量重复性的工作. 本节我们将学习三种类型的循环结构.
- For loop
- 基于vector/list/matrix/array的For loop
- While loop
- break: 跳出循环
- next: 跳过本次迭代中的剩余部分
- Repeat loop
- 练习
# for loop
for(var in seq) {
expr
}
for(i in 1:5){
print(i)
}
for(i in 1:5){
cat(i)
}
L1 = list(1:3,1:4,5:9)
for(x in L1){
print(x)
}
for(i in 1:5){
for(j in 1:3){
cat("( row:", i, " column:", j, ")")
}
cat("\n")
}
mat = matrix(1:18, nrow=3); mat
for(i in 1:3){
for(j in 1:6){
print(mat[i,j])
}
}
# while loop
while(cond) {
expr
}
# repeat loop
repeat{
expr
}
# break
# 用于跳出循环
# next
# 用于跳过循环的本次迭代中的剩余部分, 直接进入循环的下一次迭代
# repeat loop 本身没有终止功能, 必须配合 break 使用.
# for loop 和 while loop 本身具有终止条件, 但也可以配合 break 使用.
# example
# 打印出向量x中每一个元素的平方
x = 11:15
for(i in x){
print(i^2)
}
i = 1
while(i <= length(x)){
print(x[i]^2)
i = i + 1
}
i = 1
repeat{
print(x[i]^2)
i = i + 1
if(i > length(x)) break
}
# example
# 创建一个5*5矩阵, 其i行j列的元素是i+j+ij
mat = matrix(0, nrow=5, ncol=5)
for(i in 1:5){
for(j in 1:5){
mat[i,j] = i + j + i*j
}
}
mat
mat = matrix(0, nrow=5, ncol=5)
i = 1
j = 1
while(i <= 5){
while(j <= 5){
mat[i,j] = i + j + i*j
j = j + 1
}
j = 1
i = i + 1
}
mat
mat = matrix(0, nrow=5, ncol=5)
i = 1
j = 1
repeat{
repeat{
mat[i,j] = i + j + i*j
j = j + 1
if(j > 5) break
}
j = 1
i = i + 1
if(i > 5) break
}
mat
# coding exercise (用三种loop分别完成)
# 对于一个给定的正整数n, 其Collatz Sequence指的是:
# 其经过反复3n+1变换, 直到变为1, 的序列
# 比如5的Collatz Sequence 是: 5,16,8,4,2,1
# Q1:
# 写一段code, 对给定的正整数n, 打印出这个Collatz Sequence
# Q2:
# 写一段code, 对给定的正整数n, 不打印, 而是将其Collatz Sequence保存到一个向量中
# Q3:
# 阶乘是一种常用操作, R提供了函数factorial来计算阶乘.
# 写一段code, 完成对指定非负整数计算阶乘 (不允许调用factorial)
习题
# Q1:
# 对于不超过10000的所有正整数, 不打印, 记录每个数的Collatz Sequence.
# hint: 思考用何种数据结构来记录
# Q2:
# 接上题, 不超过10000的所有正整数中, 哪个数具有最长的Collatz Sequence, 长度是多少? 并打印出这个序列
Introduction to Functions 函数
循环和条件判断可以帮助我们处理很多重复性的操作, 降低我们的工作量, 提高效率. 函数的引入将进一步提升这种工作效率. 同时, 通过将一些功能包装成函数, 代码的可读性和正确性也能得到极大的提高. 因此,在几乎所有编程语言中, 函数都是极端重要的. 在本节中, 我们将学习如何调用函数, 如何编写自定义函数.
- 函数的介绍
- 函数的文档
- 调用函数
- 函数内部的函数
- 函数的参数(Arguments)
- 编写自定义函数
- 关于返回值和return
- 复杂的返回值
- 练习
# Introduction to Functions 函数的介绍
# 函数是一段经过包装的代码块, 通过输入允许的参数, 函数会执行内部代码并返回代码运行后的结果.
# Function documentation 函数的文档
read.table
help(read.table)
?read.table
# Arguments of function 函数的参数
args(read.table)
# Use a function 调用函数
read.table("./data/R0_09_student01.csv", header=T, sep=",")
sum(1:100)
sum(c(1:100,NA))
sum(c(1:100,NA), na.rm=T)
# Functions inside functions 函数内部的函数
# 在一个函数内部, 可以调用其它的函数, 这使得代码结构变得更清晰更易读
# 利用函数, 我们才能编写具有复杂功能的代码
# Writing Functions 编写自定义函数
function(arglist){
expr
return(value)
}
# example
# 编写函数: 返回一个整数向量中奇数的个数
f1 = function(x){
n = 0
for(k in x){
if(k %% 2 == 1){
n = n + 1
}
}
return(n)
}
f1(1:10)
# 关于函数名和参数名: 尽量使用有意义的名字
count_odd_elements = function(x){
n = 0
for(k in x){
if(k %% 2 == 1){
n = n + 1
}
}
return(n)
}
# 参数的默认值
# 有些参数具有预先设定的默认值, 因此这些参数在调用函数时可以不指定值, 属于可选参数
# 有些参数没有预先设定的默认值, 因此这些参数在调用函数时必须输入参数值.
# 关于返回值和return
# R函数支持 "不显式"调用return(), 此时R函数中最后一句"有输出"的代码的值将作为返回值
count_odd_elements = function(x){
n = 0
for(k in x){
if(k %% 2 == 1){
n = n + 1
}
}
n
}
# 同时, 避免显式调用return()会使得代码执行时间略微缩短
# 但我个人一般更倾向于显式的调用return, 为了代码更易读
# 当你需要多个返回值时?
# 返回复杂对象
# 函数的返回值只能有一个, 准确的说, 只能有一个R对象,
# 因此如果你需要多个返回值, 那么只要将它们包装成一个对象就可以了
basic_statistics = function(x){
stat = list(min = min(x, na.rm=T),
max = max(x, na.rm=T),
mean = mean(x, na.rm=T),
median = median(x, na.rm=T)
)
return(stat)
}
basic_statistics(c(1,2,7,10,13))
# coding exercise
# Q1:
# 创建一个函数, 对给定的正整数n, 返回这个数的Collatz Sequence
# Q2:
# 创建一个函数, 对给定的正整数n, 返回这个数的Collatz Sequence 和 Sequence的长度
# Q3:
# 阶乘是一种常用操作, R提供了函数factorial来计算阶乘.
# 创建一个函数, 对给定的非负整数n, 返回其阶乘 (不允许调用factorial)
习题
# Q1:
# prime number (素数/质数) 指的是除了1和自身, 没有其它因数的正整数.
# 创建一个函数, 给定n, 判断n是否是素数, 返回TRUE or FALSE. 并检验这个函数的正确性
# Q2:
# 创建一个函数, 给定n, 返回不超过n的所有素数. 并检验这个函数的正确性