努力创建差异函数

Struggling creating a difference function

所以我有一个家庭作业问题,我真的很难用 R 编写代码。

这就是问题所在:编写一个函数 difference() 以向量 X 作为参数,returns 是向量 X 每个元素和下一个元素之间的区别: X[2]-X[1], X[3]-X[2], X[4]-X[3],等等

因此difference(c(5,2,9,4,8))会returnc(-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] 的函数,而且它 return 比我 运行 该函数时应该多一个数。谁能帮帮我?

您的代码有几个问题。由于这是家庭作业,我不会提供正确的代码,但我会帮助突出显示您出错的地方,以帮助您更接近。我没有提供答案的唯一原因是因为这些都是很好的学习经历。如果您对更新的尝试发表评论,我会继续更新我的答案来指导您。

问题是您使用的是 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,而是在每次迭代时附加到它。

希望对您有所帮助。

更新 #1

如以下评论所述,您的代码现在如下所示:

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] 将开始覆盖我们的值。因此,一次进行一次迭代,我们得到以下结果:

步骤1:
  • i 设置为 2
  • X[2] 目前等于 2
  • 然后我们 运行 行 X[i] <- X[i] - X[i-1],它的计算如下: X[2] <- X[2] - X[1] --> X[2] <- 2 - 5 --> X[2] <- -3
  • X[2] 现在设置为 -3
第2步:
  • i 设置为 3
  • X[3] 目前等于 9
  • 然后我们 运行 X[i] <- X[i] - X[i-1],它的计算方式如下: X[3] <- X[3] - X[2] --> X[3] <- 9 - -3 --> 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 循环中,看看会发生什么。

这样,就解决了我们的第一个问题。尝试 运行ning 代码,看看你得到了什么。您会注意到输出的第一个元素是 NA。为什么会出现这种情况,我们该如何解决?提示:与i.

的值有关

更新#2

现在我们有了正确的答案,让我们看一下 R 提供的一些提示和技巧。R 有一些可以在向量上使用的固有特性。要查看此操作,运行 以下示例:

a <- 1:10
b <- 11:20
a + b
a - b
a * b
a / b

如您所见,R 会自动对向量执行所谓的 "element wise" 运算。您会注意到 a - b 与我们在这里尝试做的非常相似。区别在于 ab 是两个不同的向量,我们一次处理一个向量。那么我们如何设置我们的问题来像这样工作呢?简单:我们创建两个向量。

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 中非常快。如注释,我们实际上可以跳过将这些值分配给 yz 的步骤,而是直接 return 我们想要的:

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)],这是 "all elements in X except the first element" 的另一种说法。我们还查看了 X[1:(length(X)-1)],这是 "all elements in X except the last element" 的另一种说法。让我们清理一下:

difference <- function(X) {
  return(tail(X, -1) - head(X, -1))
}

如您所见,这是定义我们函数的更简洁的方法。

这就是技巧。让我们看一些提示。第一个是从像这样的简单函数中删除 return 。如果函数不是赋值,R 将自动 return 最后一个命令。要查看实际效果,请尝试 运行 使用两个不同的函数:

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 没有任何内容。这是因为该命令是赋值命令。您可以使用 return 命令将其强制为 return 一个值。

下一个提示是您暂时不需要的,但它很重要。回到我们拥有的 difference 的当前版本(您现在使用的代码,而不是我在本次更新中提到的任何内容),我们将值分配给 X.val,这导致它 "grow"随着时间的推移。要查看这意味着什么,运行 下面的代码:

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 中的一些功能。祝一切顺利!