首页 > 解决方案 > 在 R 中创建带有两个 FOR cicle 的表不会正确填充数据框“Maux”

问题描述

当我尝试从包含大量数据(totalSurvey)的表中创建数据框时,使用这两个 for 循环(x用于 Gender 和yfor Age)进行过滤,当循环完成时,它只有 Men 和 Age 16 的数据。什么我做错了吗?

for (x in 1:2) {
  for (y in 1:4){
    vGen <-ifelse(x==1, "Hombre" , ifelse(x==2, "Mujer"))
    vEdad <-ifelse(y==1, "13" , ifelse( y==2, "14", ifelse( y==3, "15", "16")))
    
    Maux <- filter(totalSurv, Genero == vGen, Edad == vEdad)  
  }
}

标签: rdataframefor-loop

解决方案


我建议一种不同的方法。我猜缺少样本数据:

eg <- expand.grid(Genero = c("Hombre", "Mujer"), Edad = as.character(13:16))
eg$Maux <- seq_len(nrow(eg))
eg
#   Genero Edad Maux
# 1 Hombre   13    1
# 2  Mujer   13    2
# 3 Hombre   14    3
# 4  Mujer   14    4
# 5 Hombre   15    5
# 6  Mujer   15    6
# 7 Hombre   16    7
# 8  Mujer   16    8

从这里,合并/加入它:

left_join(eg, totalSurv, by = c("Genero", "Edad"))

你应该有一个框架(tbl_df),里面只有你需要的数据。这里的Maux字段是一个整数,表示该行eg的数据属于起始帧中的哪一行。


一种效率低得多但对您来说可能更具可读性的替代方案:

lst_of_frames <- bind_rows(lapply(1:2, function(x) {
  bind_rows(lapply(1:4, function(y) {
    vGen <-ifelse(x==1, "Hombre" , ifelse(x==2, "Mujer"))
    vEdad <-ifelse(y==1, "13" , ifelse( y==2, "14", ifelse( y==3, "15", "16")))
    filter(totalSurv, Genero == vGen, Edad == vEdad)
  }))
}))

我所做的只是用调用替换你的for循环,将它们中的每一个都包装在 a 中,并将你的和代码转储到内部循环中。lapplydplyr::bind_rowsifelsefilter

另一种方法是从 0 行帧开始并将rbind每个结果附加(在 R 中)到空帧。我会提前说,这种方法是个坏主意

Maux <- totalSurv[0,] # intentionally empty, but with all columns/classes
for (x in 1:2) {
  for (y in 1:4){
    vGen <-ifelse(x==1, "Hombre" , ifelse(x==2, "Mujer"))
    vEdad <-ifelse(y==1, "13" , ifelse( y==2, "14", ifelse( y==3, "15", "16")))
    
    Maux <- bind_rows(Maux, filter(totalSurv, Genero == vGen, Edad == vEdad))
  }
}

值得重复,这是一个坏主意。为什么?在 R 中迭代地添加到一个框架是低效的,因为每次你只添加一行时,它都会将框架的所有内容复制到一个新对象中。这意味着如果您Maux有 99999 行并且您只想再添加 1 行,那么您在内存中拥有这 99999 行两次。内存占用是一回事,每次这样做都需要更多时间来复制所有这些东西。

但它仍然有效,如果你只用几行这样做几次,那么一切都很好,天也不会塌陷。但是不要把我在这里提供它作为建议它是一般的正确方法

(顺便说一句:我仍然没有修复不正确的ifelse(x==2, "Mujer"):应该有第三个对象。或者只是将整行替换为

ifelse(x==1, "Hombre" , "Mujer")

最后一个想法:你不需要迭代数字,如果你对这些数字做的唯一事情是ifelseing 它们。你可以改为

Maux <- totalSurv[0,]
for (vGen in c("Hombre", "Mujer")) {
  for (vEdad in as.character(13:16)) {
    Maux <- bind_rows(Maux, filter(totalSurv, Genero == vGen, Edad == vEdad))
  }
}
# or
Maux <- bind_rows(lapply(c("Hombre", "Mujer"), function(vGen) {
  bind_rows(lapply(as.character(13:16), function(vEdad) {
    filter(totalSurv, Genero == vGen, Edad == vEdad)
  }))
}))

以获得更紧凑和直接代码的体验。

(但我仍然认为我的第left_join一种方法是最直接的,并且允许您定义您需要的非常具体的组合。我为您更新的列名更新了它,顺便说一句。)


推荐阅读