在 R 中创建一个简单的 for 循环

时间:2021-05-03 12:34:04

标签: r for-loop

我有一个名为“Volume”的小标题,我在其中存储了一些数据(10 列 - 前 2 列是字符,30 行)。 现在我想计算与我的 tibble 的第 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]])
}

2 个答案:

答案 0 :(得分:2)

样机数据

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

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,但实际上 / 是一个原始函数。你可以用这种方式写同样的东西:
 `/`(A, B) # same as A / B
    可以为
  • lapply 提供附加参数,这些参数直接传递给应用于列表的函数(在本例中为 /)。因此,我们将 rel.Volume_unmod[3] 作为附加参数编写。
  • lapply 总是返回一个列表。但是,由于我们将 lapply 的结果分配给“数据帧的一部分”,我们将只编辑数据帧的列,因此,我们将拥有一个数据帧而不是列表。让我用更技术的方式重新表述。当您分配 rel.Volume_unmod[3:10] <- lapply(...) 时,您不仅仅是将列表分配给 rel.Volume_unmod[3:10]。您在技术上正在使用此分配函数:[<-。这是一个允许编辑列表/矢量/数据框中的项目的功能。具体来说,[<- 允许您在不修改列表/向量/数据框的属性的情况下分配新项目。正如我之前所说,数据框只是具有特定属性的列表。然后,当您使用 [<- 时,您修改了列,但保持属性(在本例中为类 data.frame)保持不变。这就是魔法起作用的原因。

答案 1 :(得分:0)

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

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 是列表的特殊情况。)

最后但并非最不重要的一点是,在尝试重新分配给该变量时,使用 rel.Volume 初始化变量 NULL 将导致错误,因为您还没有告诉 Rrel.Volume {1}} 应该是。

试试这个,如果你喜欢(感谢@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 软件包。根据我的经验,从长远来看,您最好先学习 base-R 并在您更熟悉核心语言时添加“糖”。以后你仍然可以学习使用 Tidyverse 函数(但是,为什么会有人呢?;-))。