【问题标题】:Creating a simple for loop in R在 R 中创建一个简单的 for 循环
【发布时间】:2021-05-03 12:34:04
【问题描述】:

我有一个名为“Volume”的小标题,我在其中存储了一些数据(10 列 - 前 2 列是字符,30 行)。 现在我想计算与我的小标题第 3 列相对应的每一列的相对体积。 我目前的解决方案如下所示:

rel.Volume_unmod = tibble(
            "Volume_OD" = Volume[[3]] / Volume[[3]],
            "Volume_Imp" = Volume[[4]] / Volume[[3]],
            "Volume_OD_1" = Volume[[5]] / Volume[[3]],
            "Volume_WS_1" = Volume[[6]] / Volume[[3]],
            "Volume_OD_2"  = Volume[[7]] / Volume[[3]],
            "Volume_WS_2" = Volume[[8]] / Volume[[3]], 
            "Volume_OD_3" = Volume[[9]] / Volume[[3]],
            "Volume_WS_3" = Volume[[10]] / Volume[[3]])
rel.Volume_unmod 

我想保留 tibble 结构和标签。我确信对此有更好的解决方案,但我对 R 相对较新,所以这对我来说并不明显。我尝试的是这样的,但我实际上无法运行:

rel.Volume = NULL
for(i in Volume[,3:10]){

rel.Volume[i] = tibble(Volume = Volume[[i]] / Volume[[3]])
}

【问题讨论】:

  • 欢迎堆栈溢出。请让您的问题可重现:使用dput(Volume) 将您的数据副本粘贴到问题中,请参阅minimal reproducible example

标签: r for-loop


【解决方案1】:

样机数据

由于您没有提供一些数据,我按照您提供的描述创建了一些模型数据。这里:

set.seed(1)
Volume <- data.frame(ID = sample(letters, 30, TRUE),
                     GR = sample(LETTERS, 30, TRUE))
Volume[3:10] <- rnorm(30*8)

Dplyr 解决方案

library(dplyr)

# rename columns [brute force]
cols <- c("Volume_OD","Volume_Imp","Volume_OD_1","Volume_WS_1","Volume_OD_2","Volume_WS_2","Volume_OD_3","Volume_WS_3")
colnames(Volume)[3:10] <- cols

# divide by Volumn_OD
rel.Volume_unmod <- Volume %>% 
  mutate(across(all_of(cols), ~ . / Volume_OD))

# result
rel.Volume_unmod

说明

  • 我不知道您的列的名称。这些名称可能与您打算在rel.Volume_unmod 中创建的列的名称相对应。无论如何,为了避免任何问题,我重命名了这些列(有点残忍)。如果您愿意,可以使用dplyr::rename 进行操作。
  • 有很多方法可以选择您想要mutate 的列。 mutate 是来自 dplyr 的动词,允许您创建新列或对列执行操作或功能。
  • across 是来自dplyr 的副词。让我们通过说它是一个允许您在多个列上执行函数的函数来简化。在这种情况下,我想通过Volum_OD 进行除法。
  • ~tidyverse 创建匿名函数的方法。 ~ . / Volum_OD 等价于 function(x) x / Volumn_OD
  • all_of 是必要的,因为在这种特定情况下,我为 across 提供了一个字符向量。没有它,它仍然可以工作,但您会收到警告,因为它不明确,并且在相同情况下可能无法正常工作。

更多信息

查看this book 以了解有关使用tidyversedplyr 是其中的一部分)进行数据操作的更多信息。


Base-R 解决方案

rel.Volume_unmod <- Volume

# rename columns
cols <- c("Volume_OD","Volume_Imp","Volume_OD_1","Volume_WS_1","Volume_OD_2","Volume_WS_2","Volume_OD_3","Volume_WS_3")
colnames(rel.Volume_unmod)[3:10] <- cols

# divide by columns 3
rel.Volume_unmod[3:10] <- lapply(rel.Volume_unmod[3:10], `/`, rel.Volume_unmod[3])
rel.Volume_unmod

说明

  • lapply 是一个基础 R 函数,可让您将函数应用于列表或“可列出”对象的每个项目。
  • 在这种情况下,rel.Volume_unmod 是一个可列出的对象:数据帧只是一个具有相同长度的向量列表。因此,lapply 一次占用一列 [= 一项] 并应用一个函数。
  • 函数是/。你通常会看到/ 是这样使用的:A / B,但实际上/ 是一个 Primitive 函数。你可以这样写同样的东西:
 `/`(A, B) # same as A / B
  • lapply 可以提供附加参数,这些参数直接传递给应用于列表的函数(在本例中为 /)。因此,我们将rel.Volume_unmod[3] 写为附加参数。
  • lapply 总是返回一个列表。但是,由于我们将 lapply 的结果分配给“数据框的一部分”,我们将只编辑数据框的列,因此,我们将有一个数据框而不是列表。让我以更技术性的方式重新表述。当您分配rel.Volume_unmod[3:10] &lt;- lapply(...) 时,您不只是将列表分配给rel.Volume_unmod[3:10]。从技术上讲,您正在使用此分配功能:[&lt;-。这是一个允许编辑列表/向量/数据框中的项目的功能。具体来说,[&lt;- 允许您在不修改列表/向量/数据框的属性的情况下分配新项目。正如我之前所说,数据框只是具有特定属性的列表。然后,当您使用 [&lt;- 时,您会修改列,但不会更改属性(在本例中为类 data.frame)。这就是魔法起作用的原因。

【讨论】:

    【解决方案2】:

    如果没有一个最小的工作示例,很难猜出变量Volume 实际指的是什么。除此之外,您的for-loop 似乎有问题:

    for(i in Volume[,3:10]){
    

    假设Volume 指的是data.frametibble,这会导致索引在3 到10 之间的实际列向量被连续分配给i。您可以通过将print(i) 放入循环中来验证这一点。但在循环内部,您似乎实际上想使用 i 作为一个变量,该变量仅包含当前列的索引作为数字(而不是列本身):

    rel.Volume[i] = tibble(Volume = Volume[[i]] / Volume[[3]])
    

    另外,两个括号通常用于列表,而不是data.framestibbles。 (但是,您可以这样做,因为 data.frames 是列表的特殊情况。)

    最后但同样重要的是,使用NULL 初始化变量rel.Volume 将导致错误,当您尝试重新分配给该变量时,因为您没有告诉Rrel.Volume 应该是什么。

    如果你愿意,试试这个(感谢@Edo 提供示例数据):

    set.seed(1)
    
    Volume <- data.frame(ID = sample(letters, 30, TRUE),
                         GR = sample(LETTERS, 30, TRUE),
                         Vol1 = rnorm(30),
                         Vol2 = rnorm(30),
                         Vol3 = rnorm(30))
    
    rel.Volume <- Volume[1:2] # Assuming you want to keep the IDs.
    # Your data.frame will need to have the correct number of rows here already.
    
    for (i in 3:ncol(Volume)){ # ncol gives the total number of columns in data.frame
      rel.Volume[i]  = Volume[i]/Volume[3]
    }
    

    更类似于R 的方法是完全避免使用for 循环,因为R 的优势在于隐式矢量化。这些表达式将在没有循环的情况下产生相同的结果:

    # OK, this one messes up variable names...
    rel.V.2 <- data.frame(sapply(X = Volume[3:5], FUN = function(x) x/Volume[3]))
    
    rel.V.3 <- data.frame(Map(`/`, Volume[3:5], Volume[3]))
    

    既然您说您是 R 的新手,坦率地说,我建议您在学习基础知识时避免使用 Tidyverse-packages。根据我的经验,从长远来看,您最好先学习基础-R,然后在您更熟悉核心语言时添加“糖”。以后你仍然可以学习使用 Tidyverse 函数(但是,为什么会有人呢?;-))。

    【讨论】:

    • 问“为什么有人会学习 Tidyverse 函数”是相当狭隘的。 tidyverse 软件包是下载次数最多的软件包之一,这是因为人们看到了它们的价值。然后,如果你不喜欢它们,公平地说:你不必使用它们。您可以使用 base-R 自己编写所有代码。但是,tidyverse 函数已经过测试,它们有很好的文档,它们提供了可以理解的错误和警告。这取决于你的范围。在大多数 R 课程中,tidyverse 是必须的。 [继续]
    • 如果你不喜欢tidyverse,我建议你看看data.table。它基于一个不同的概念,但它(对许多人来说)现在使 R 极具竞争力。 data.table 是围绕一致性、速度、简洁的概念构建的。而且它对其他包比tidyverse包更友好(因为一些tidyverse函数修改了一些base-R函数)。
    • 查看this great question 以获取有关该主题的更多信息
    • 抱歉,最后一句话的意思是开玩笑的,指的是关于 tidyverse 正在进行的“火焰战争”。我认为这很明显。我编辑了我的帖子,添加了一个闪烁的笑脸,以使其更清晰。无论如何,我认为您并没有真正理解我试图传达的内容:我只建议您在学习基础知识时避免使用 tidyverse。根据我教授 R 语言的经验,对于大多数初学者来说,理解核心语言已经够难了。从长远来看,你通常最好先学习实际的基础知识,然后随着你的进步添加语法糖。
    • 哎呀哈哈,抱歉! :D 并不意味着听起来不好。实际上,我只是想帮助@Carlo。我认为通常初学者都在努力保持他们的代码干净。 Tidyverse 可以帮助你。另一方面,使用 baseR 和 data.table 很快就会变得一团糟。
    猜你喜欢
    • 2019-05-03
    • 1970-01-01
    • 2022-07-19
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    相关资源
    最近更新 更多