此部分内容会每日更新,包括但不限于基础知识,进阶知识,数据处理,图表展示,数据分析实战,机器学习算法等~ !!!
本人统计学硕士在读,想在2024年完成sql、python、R语言、stata、matlab等软件的复盘和巩固,目前在做统计学知识和R语言的复习~
后续考虑出相关视频进行讲解说明,请大家持续点赞+收藏+关注哈,大家一起沟通交流~
1.4 函数定义与调用(约10道习题)
习题1:
题目:编写一个函数,输入两个数字,返回它们的和。
习题1解答:
sum_numbers <- function(num1, num2) {
return(num1 + num2)
}
# 调用函数
result <- sum_numbers(3, 5)
print(result) # 输出: 8
习题2:
题目:创建一个函数,该函数接受一个字符串作为参数,并返回该字符串的长度。
习题2解答:
get_string_length <- function(str) {
return(length(str))
}
# 调用函数
str_len <- get_string_length("Hello")
print(str_len) # 输出: 5
习题3:
题目:编写一个函数,用于计算一个给定列表的平均值。
习题3解答:
average_of_list <- function(lst) {
if (length(lst) > 0) {
return(mean(lst))
} else {
return(NA) # 或者可以抛出错误或返回其他默认值
}
}
# 调用函数
avg <- average_of_list(c(1, 2, 3, 4))
print(avg) # 输出: 2.5
习题4:
题目:创建一个函数,该函数接受一个整数作为参数,并返回其是否为偶数的布尔值。
习题4解答:
is_even_number <- function(num) {
return(num %% 2 == 0)
}
# 调用函数
is_even <- is_even_number(4)
print(is_even) # 输出: TRUE
习题5:
题目:编写一个函数,它接受一个字符串和一个整数作为参数,返回字符串中每n个字符后的子字符串。
习题5解答:
get_substring_every_n <- function(str, n) {
if (n <= 0) {
stop("n must be a positive integer")
}
substrings <- seq(1, nchar(str), by = n)
return(substr(str, substrings, substrings + n - 1))
}
# 调用函数
substrs <- get_substring_every_n("abcdefgh", 3)
print(substrs) # 输出: "abc" "def" "gh"
习题6:
题目:创建一个函数,该函数接受两个列表作为参数,并返回它们合并后的新列表。
习题6解答:
merge_lists_function <- function(lst1, lst2) {
return(c(lst1, lst2))
}
# 调用函数
merged_list <- merge_lists_function(c(1, 2, 3), c(4, 5, 6))
print(merged_list) # 输出: 1 2 3 4 5 6
习题7:
题目:编写一个函数,它接受一个列表和一个元素作为参数,如果元素在列表中,则返回True,否则返回False。
习题7解答:
element_in_list_func <- function(lst, elem) {
return(elem %in% lst)
}
# 调用函数
is_in_list <- element_in_list_func(c(1, 2, 3), 2)
print(is_in_list) # 输出: TRUE
习题8:
题目:创建一个函数,该函数接受一个数字列表和一个目标数字作为参数,返回列表中第一个大于目标数字的元素的索引。
习题8解答:
index_of_first_greater <- function(lst, target) {
for (i in seq_along(lst)) {
if (lst[i] > target) {
return(i)
}
}
return(NULL) # 如果没有找到大于target的元素,返回NULL
}
# 调用函数
index <- index_of_first_greater(c(1, 3, 5, 7), 4)
print(index) # 输出: 2
习题9:
题目:编写一个函数,用于计算一个给定数字列表的乘积。
习题9解答:
product_of_list_func <- function(lst) {
if (length(lst) > 0) {
return(prod(lst))
} else {
return(1) # 空列表的乘积定义为1
}
}
# 调用函数
prod_val <- product_of_list_func(c(2, 3, 4))
print(prod_val) # 输出: 24
习题10:
题目:创建一个函数,该函数接受一个数字列表作为参数,并返回列表中的最大和最小值的元组。
习题10解答:
max_min_of_list_func <- function(lst) {
if (length(lst) > 0) {
return(c(min(lst), max(lst)))
} else {
return(c(NA, NA)) # 或者可以抛出错误或返回其他默认值
}
}
# 调用函数
max_min <- max_min_of_list_func(c(7, 1, 9, 3, 5))
print(max_min) # 输出: 1 9
函数的参数传递与返回值处理
习题11:
题目:编写一个函数,它接受两个参数(a和b),将a的值增加5并返回新的a值,同时保持b的值不变。
习题11解答:
increase_a <- function(a, b) {
a <- a + 5
return(list(a = a, b = b))
}
# 调用函数
result <- increase_a(3, 4)
print(result$a) # 输出: 8
print(result$b) # 输出: 4
习题12:
题目:创建一个函数,该函数接受三个参数,并返回它们的和。确保函数能够正确处理不同数据类型(如整数和浮点数)的输入。
习题12解答:
sum_three_numbers <- function(x, y, z) {
return(x + y + z)
}
# 调用函数
sum_result <- sum_three_numbers(1, 2.5, 3)
print(sum_result) # 输出: 6.5
习题13:
题目:编写一个函数,它接受一个列表和一个整数作为参数,返回一个新的列表,其中包含原始列表中所有大于该整数的元素。
习题13解答:
filter_greater_than <- function(lst, threshold) {
return(lst[lst > threshold])
}
# 调用函数
filtered_list <- filter_greater_than(c(1, 3, 5, 2, 4), 3)
print(filtered_list) # 输出: 3 5 4
习题14:
题目:创建一个函数,该函数接受两个列表作为参数,并返回一个新的列表,其中包含两个列表中所有不重复的元素。
习题14解答:
union_unique <- function(lst1, lst2) {
return(unique(c(lst1, lst2)))
}
# 调用函数
unique_list <- union_unique(c(1, 2, 3), c(2, 3, 4, 5))
print(unique_list) # 输出: 1 2 3 4 5
习题15:
题目:编写一个函数,它接受一个列表和一个函数作为参数,对列表中的每个元素应用该函数,并返回一个新的列表,其中包含应用函数后的结果。
习题15解答:
apply_function_to_list <- function(lst, func) {
return(lapply(lst, func))
}
# 调用函数
squared_list <- apply_function_to_list(c(1, 2, 3), function(x) x^2)
print(squared_list) # 输出: 1 4 9
习题16:
题目:创建一个函数,该函数接受一个字符串和一个函数作为参数,将函数应用于字符串中的每个字符,并返回一个新的字符串,其中包含应用函数后的结果。
习题16解答:
apply_function_to_string <- function(str, func) {
return(paste(sapply(strsplit(str, ""), func), collapse = ""))
}
# 调用函数
upper_case_str <- apply_function_to_string("hello", toupper)
print(upper_case_str) # 输出: HELLO
习题17:
题目:编写一个函数,它接受一个数字和一个函数作为参数,如果数字是偶数,则调用函数并返回结果;如果数字是奇数,则返回原始数字。
习题17解答:
conditional_apply <- function(num, func) {
if (num %% 2 == 0) {
return(func(num))
} else {
return(num)
}
}
# 调用函数
double_if_even <- function(x) x * 2
result <- conditional_apply(4, double_if_even)
print(result) # 输出: 8
习题18:
题目:创建一个函数,该函数接受两个函数作为参数,并返回一个新的函数,该新函数依次调用这两个函数并返回它们的结果。
习题18解答:
compose_functions <- function(func1, func2) {
return(function(x) func2(func1(x)))
}
# 调用函数
add_then_multiply <- compose_functions(function(x) x + 1, function(x) x * 2)
result <- add_then_multiply(3)
print(result) # 输出: 8
习题19:
题目:编写一个函数,它接受一个列表和一个函数作为参数,返回一个新的列表,其中包含原始列表中满足函数条件的元素。
习题19解答:
filter_by_condition <- function(lst, condition_func) {
return(lst[sapply(lst, condition_func)])
}
# 调用函数
is_positive <- function(x) x > 0
positive_numbers <- filter_by_condition(c(-1, 2, -3, 4, -5), is_positive)
print(positive_numbers) # 输出: 2 4
习题20:
题目:创建一个函数,该函数接受一个字典和一个函数作为参数,对字典中的每个值应用该函数,并返回一个新的字典,其中包含应用函数后的结果。
习题20解答:
apply_function_to_dict <- function(dict, func) {
return(lapply(dict, function(value) func(value)))
}
# 调用函数
double_values <- function(value) value * 2
doubled_dict <- apply_function_to_dict(list(a = 1, b = 2, c = 3), double_values)
print(doubled_dict) # 输出: a 2 b 4 c 6
函数的调试与优化(约10道习题)
请注意,函数的调试与优化通常需要实际编写和运行代码,并根据出现的错误或性能问题进行调整。因此,以下题目更侧重于识别潜在问题和提出优化策略,而不是直接的代码实现。
习题21:
题目:描述在R中调试函数时可能遇到的常见错误类型,并给出至少三种调试策略。
在R中调试函数时,可能会遇到以下几种常见的错误类型:
- 语法错误:如拼写错误、缺少括号、错误的函数名等。
- 运行时错误:例如,尝试访问不存在的变量、数组越界、调用不存在的函数等。
- 逻辑错误:代码可以正常运行,但结果不符合预期,可能是因为算法逻辑不正确或条件判断有误。
- 性能问题:代码可以正确运行,但处理大型数据集时效率低下,可能是因为使用了不合适的算法或数据结构。
调试策略:
- 使用
print
语句:在代码的关键部分添加print
语句,输出变量的值,帮助定位问题所在。 - 使用调试器:RStudio提供了一个强大的调试器,可以设置断点、单步执行、查看变量值等。
- 简化问题:尝试使用更小的数据集或简化问题的复杂度,以便更容易地定位错误。
- 查阅文档和社区资源:查看相关函数的文档,了解正确的用法;在R社区(如Stack Overflow)搜索类似问题,看是否有现成的解决方案。
习题22:
题目:分析以下函数,找出可能存在的逻辑错误或性能问题,并提出优化建议:
my_function <- function(x) {
result <- 0
for (i in 1:length(x)) {
result <- result + x[i]
}
return(result)
}
函数my_function
的目的是计算向量x
中所有元素的和。从逻辑上看,这个函数是正确的,但它没有利用R的向量化操作,这可能会导致性能问题,特别是在处理大型数据集时。
优化建议:
- 使用向量化操作:可以直接使用
sum
函数来计算向量的和,而不需要循环。
my_function <- function(x) {
return(sum(x))
}
- 检查输入:在函数中增加对输入
x
的检查,确保它是数值向量。
my_function <- function(x) {
if (!is.numeric(x) || !is.vector(x)) {
stop("Input must be a numeric vector.")
}
return(sum(x))
}
习题23:
题目:考虑一个函数,它在处理大型数据集时运行缓慢。提出至少两种可能的优化方法。
处理大型数据集时,函数运行缓慢可能是因为使用了不高效的算法或数据结构。以下是两种可能的优化方法:
-
使用向量化操作:R的向量化操作通常比循环更快,因为它们是在底层用C或Fortran实现的。尽量避免使用显式的for循环,而是利用R的向量化特性。
-
数据分块处理:如果数据集太大,无法一次性加载到内存中,可以考虑将数据分块处理。每次只加载和处理一部分数据,然后保存结果,再继续处理下一部分数据。
习题24:
题目:编写一个函数,该函数包含一个明显的逻辑错误。解释错误的原因,并提出修复建议。
my_function <- function(x) {
if (x > 0) {
return("Positive")
} else {
return("Negative")
}
}
# 测试函数
result <- my_function(0)
print(result) # 输出应该是"Negative"或"Positive",但这里什么都不会输出,因为0既不大于0也不小于0
错误原因:函数没有处理x
等于0的情况,导致当x
为0时,函数不会返回任何值。
修复建议:在else
语句之前添加一个条件来处理x
等于0的情况。
my_function <- function(x) {
if (x > 0) {
return("Positive")
} else if (x < 0) {
return("Negative")
} else {
return("Zero")
}
}
习题25:
题目:分析以下函数,找出可能导致内存泄漏的代码段,并提出解决方案。
假设函数如下:
leaky_function <- function(data) {
result <- list()
for (i in 1:length(data)) {
temp <- data.frame(data[i]) # 创建新的数据框,但没有释放内存
result <- c(result, temp) # 将数据框添加到列表中,进一步消耗内存
}
return(result)
}
在这个函数中,每次循环都会创建一个新的data.frame
对象temp
,并将其添加到result
列表中。这些临时对象如果没有被及时清理,可能会导致内存泄漏。
解决方案:
-
避免在循环中创建大型对象:如果可能的话,尝试在循环外部创建对象,并在循环内部更新它。
-
使用
lapply
或sapply
代替循环:这些函数可以更高效地处理列表和向量,并且通常不需要显式地创建临时对象。
习题26:
题目:描述在R中如何使用profvis
包来分析和优化函数的性能。
在R中,profvis
包是一个非常有用的工具,用于分析和优化函数的性能。profvis
提供了一个交互式的可视化界面,识别代码中的瓶颈和优化机会。
以下是如何使用profvis
包来分析和优化函数性能的步骤:
-
首先,需要安装并加载
profvis
包。如果还没有安装,可以通过运行install.packages("profvis")
来安装。然后,通过library(profvis)
来加载包。 -
使用
profvis
函数包装想要分析的代码。例如,如果有一个名为my_function
的函数,可以这样使用profvis
:
library(profvis)
profvis({
# 调用函数
result <- my_function(your_arguments)
})
习题27:
题目:假设有一个函数,它在处理向量输入时效率很低。提出优化方法,并写代码示例对比。
假设有一个函数slow_vector_function
,它在处理向量输入时效率很低。这个函数可能是通过循环来遍历向量的每个元素并进行某种计算。一个常见的优化方法是使用向量化操作来替代循环,因为R的向量化操作通常比循环更快。
以下是一个示例:
# 假设的原始低效函数
slow_vector_function <- function(vec) {
result <- numeric(length(vec))
for (i in 1:length(vec)) {
result[i] <- vec[i]^2 + 2 * vec[i] + 1
}
return(result)
}
# 优化后的函数,使用向量化操作
fast_vector_function <- function(vec) {
return(vec^2 + 2 * vec + 1)
}
# 创建一个向量用于测试
test_vector <- 1:10000
# 对比执行时间
system.time({ slow_result <- slow_vector_function(test_vector) })
system.time({ fast_result <- fast_vector_function(test_vector) })
# 检查结果是否相同
all.equal(slow_result, fast_result)
在这个例子中,slow_vector_function
使用了一个for循环来计算每个元素的平方、乘以2然后加1。而fast_vector_function
则直接对整个向量进行了这些操作,利用了R的向量化特性。通过比较执行时间,可以看到优化后的函数fast_vector_function
运行得更快。同时,我们也使用all.equal
来确认两个函数的结果是否相同。