苦苦挣扎创造差异化功能

时间:2016-04-08 21:44:22

标签: r function difference

所以我有一个家庭作业问题,我真的很难在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]并且它返回的数字比我运行函数时应该多一个。任何人都可以帮助我吗?

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,而是在每次迭代时附加代码。

我希望有所帮助。

更新#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] - &gt; X[2] <- 2 - 5 - &gt; 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] - &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的值有关。

更新#2

现在我们得到了正确的答案,让我们看一下可用的一些提示和技巧,这要归功于R. R有一些固有的功能可以用在矢量上。要查看此操作,请运行以下示例:

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

正如您所看到的,R将自动执行所谓的&#34;元素智能&#34;向量的操作。您会注意到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的步骤,而只是直接返回我们想要的内容:

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中的一些功能。祝你好运!