所以我有一个家庭作业问题,我真的很难在R中编码。
这就是问题:编写一个函数difference()
,它将一个向量X作为参数并返回一个向量
每个元素和下一个元素之间的差异:
X[2]-X[1], X[3]-X[2], X[4]-X[3]
等等。
因此difference(c(5,2,9,4,8))
会返回c(-3,7,-5,4)
到目前为止,我有这个:
difference<-function(X) {
for (i in X)
X.val<-X[i]-X[i-1]
return(X.val)
}
difference(c(5,2,9,4,8))
我似乎无法使用函数来减去X[2]-X[1]
并且它返回的数字比我运行函数时应该多一个。任何人都可以帮助我吗?
答案 0 :(得分:6)
您的代码存在一些问题。由于这是家庭作业,我不打算提供正确的代码,但我会帮助突出显示您出错的地方,以帮助您更接近。我没有提供答案的唯一原因是因为这些都是很好的学习经历。如果您对更新后的尝试发表评论,我会继续更新我的回答以指导您。
问题是您正在使用for (i in X)
,它实际上会循环遍历X
的值,而不是其索引。因此,在您的示例中,i
将等于5然后是2然后是9然后是4然后是8.如果我们从i == 5开始,则代码执行此操作:X.val <- X[5] - X[5 - 1]
。此时,您将X.val
指定为4,因为X[5]
等于8且X[4]
等于4.在下一次迭代中,i == 2.所以这会将X.val
设置为-3,因为X[2]
为2而X[1]
为5。
要解决此问题,您需要循环浏览X
的索引。您可以使用for (i in 1:length(X))
执行此操作,其中length(X)
将为您提供与X
中的元素数相等的数字。
您发现的下一个问题是您获得了一个额外的号码。考虑输出中应该包含多少数字以及i
应该从哪里开始意味着什么,这一点非常重要。提示:你真的应该从1开始吗?
最后,在每次迭代中覆盖X.val
。令我感到惊讶的是,您在结果中获得了额外的数字,因为您应该只收到NA
,因为最后一个数字是8而X
中没有8个元素。但是,您需要重写代码,以便不覆盖X.val
,而是在每次迭代时附加代码。
我希望有所帮助。
如下面的评论中所述,您的代码现在看起来像这样:
difference <- function(X) {
for (i in 2:length(X)) {
X[i] <- X[i] - X[i-1]
}
return(X)
}
difference(c(5, 2, 9, 4, 8))
我们现在非常非常接近最终解决方案。我们只需解决一个快速问题。
问题是我们现在正在覆盖我们的X值,这很糟糕。由于我们的数字c(5,2,9,4,8)
作为变量X
传递给函数,因此行X[i] <- X[i] - X[i-1]
将开始覆盖我们的值。因此,一次单步执行一次迭代,我们得到以下结果:
i
设置为2 X[2]
目前等于2 X[i] <- X[i] - X[i-1]
行,其评估方式如下:X[2] <- X[2] - X[1]
- &gt; X[2] <- 2 - 5
- &gt; X[2] <- -3
X[2]
现在设置为-3 i
设置为3 X[3]
目前等于9 X[i] <- X[i] - X[i-1]
,评估如下:X[3] <- X[3] - X[2]
- &gt; X[3] <- 9 - -3
- &gt; X[3] <- 12
X[3]
现已设为12 从前两次迭代中可以看出,我们覆盖了X
变量,这直接影响了我们运行函数时得到的差异。
要解决此问题,我们只需返回使用X.val
,就像之前一样。由于此变量没有值,因此无法覆盖任何值。我们的功能现在看起来像这样:
difference <- function(X) {
for (i in 2:length(X)) {
X.val[i] <- X[i] - X[i-1]
}
return(X.val)
}
现在,对于每次迭代,都不会覆盖任何内容,我们的X
值保持不变。但是我们有两个问题。如果我们运行此新代码,我们最终会收到一条错误消息,告诉我们x.diff
不存在。早些时候,我告诉过你,你可以索引你正在制作的变量,这是真的。我们只需告诉R,我们所做的变量首先是变量。有几种方法可以做到这一点,但第二种最好的方法是创建一个与预期输出具有相同class
的变量。由于我们知道我们希望输出为数字列表,因此我们只需将X.val
设为numeric
向量即可。我们的代码现在看起来像这样:
difference <- function(X) {
X.val <- numeric()
for (i in 2:length(X)) {
X.val[i] <- X[i] - X[i-1]
}
return(X.val)
}
请注意,X.val
的分配在我们进入for
循环之前发生。作为练习,你应该考虑为什么会这样,然后尝试在for
循环中移动它,看看会发生什么。
所以这就解决了我们的第一个问题。尝试运行代码,看看你得到了什么。您会注意到输出的第一个元素是NA
。为什么会出现这种情况,我们该如何解决?提示:它与i
的值有关。
现在我们得到了正确的答案,让我们看一下可用的一些提示和技巧,这要归功于R. R有一些固有的功能可以用在矢量上。要查看此操作,请运行以下示例:
a <- 1:10
b <- 11:20
a + b
a - b
a * b
a / b
正如您所看到的,R将自动执行所谓的&#34;元素智能&#34;向量的操作。您会注意到a - b
与我们在此尝试的内容非常相似。区别在于a
和b
是两个不同的向量,我们一次处理一个向量。那么我们如何设置我们的问题呢?简单:我们创建两个向量。
x <- c(5, 2, 9, 4, 8)
y <- x[2:length(x)]
z <- x[1:(length(x)-1)]
y - z
您应该注意到y - z
现在为我们提供了我们想要的功能答案。我们可以将其应用于difference
函数,如下所示:
difference <- function(X) {
y <- X[2:length(X)]
z <- X[1:(length(X)-1)]
return(y-z)
}
使用这个技巧,我们不再需要使用for
循环,这在R中可能非常慢,而是使用向量化操作,这在R中非常快。正如评论中所述,我们实际上可以跳过将这些值分配到y
和z
的步骤,而只是直接返回我们想要的内容:
difference <- function(X) {
return(X[2:length(X)] - X[1:(length(X)-1)])
}
我们现在刚刚成功创建了一个完成我们希望做的单行功能。让我们看看我们是否能让它更清洁。 R提供了两个非常便于查看数据的函数:head()
和tail()
。 head
允许您查看前n个元素,tail
允许您查看最后n个元素。让我们看一个例子。
a <- 1:50
head(a) # defaults to 6 elements
tail(a) # defaults to 6 elements
head(a, n=20) # we can change how many elements to return
tail(a, n=20)
head(a, n=-1) # returns all but the last element
tail(a, n=-1) # returns all but the first element
对于我们想要做的事情,最后两个是最重要的。在我们最新版本的difference
中,我们查看X[2:length(X)]
,这是另一种说法&#34; X
中除第一个元素&#34;之外的所有元素。我们也在查看X[1:(length(X)-1)]
,这是另一种说法&#34; X
中除最后一个元素&#34;之外的所有元素。让我们清理一下:
difference <- function(X) {
return(tail(X, -1) - head(X, -1))
}
正如您所看到的,这是一种更清晰的定义功能的方式。
所以这些都是技巧。我们来看一些提示。第一种是从这样的简单函数中删除return
。如果函数不是赋值,R将自动返回最后一个命令。要查看此操作,请尝试运行两个不同的功能:
difference_1 <- function(X) {
x.diff <- tail(X, -1) - head(X, -1)
}
difference_1(1:10)
difference_2 <- function(X) {
tail(X, -1) - head(X, -1)
}
difference_2(1:10)
在difference_1
中,您会注意到没有返回任何内容。这是因为该命令是一个赋值命令。您可以使用return
命令强制它返回值。
下一个小贴士是你一段时间不需要的东西,但它很重要。回到我们现有的difference
版本(您现在使用的代码,而不是我在本次更新中提及的任何内容),我们会将值分配给X.val
,导致它成长&#34;随着时间的推移。要查看这意味着什么,请运行以下代码:
x.val <- numeric()
length(x)
x.val[1] <- 1
length(x)
x.val[2] <- 2
length(x)
你会发现长度不断增长。这通常是R代码中巨大减速的重点。正确的方法是创建x.val
,其长度等于我们需要的大小。这要快得多,将来会为您省去一些痛苦。以下是它的工作原理:
difference <- function(X) {
x.val <- numeric(length=(length(X) - 1))
for (i in 2:length(X)) {
x.val[i-1] <- X[i] - X[i-1]
}
return(x.val)
}
在我们当前的代码中,这并没有真正的区别。但是,如果您将来处理非常大的数据,这可能需要数小时甚至数天的计算时间。
我希望这一切都能帮助你更好地理解R中的一些功能。祝你好运!