首页 > 解决方案 > 在R中的管道中间放置一个for循环

问题描述

我需要一些帮助来使我的代码更精简。

这是我原始数据集的一小部分:

library(tidyverse)
dataset <- data.frame(UPA = c(130033353, 130033353, 130033353, 130033353,
                              130033353, 230036930, 230036930, 230036930, 230036930, 230036930,
                              230124582, 230124582, 230124582, 230124582, 230124582, 240039107,
                              240039107, 240039107, 240039107, 240039107, 320022393, 320022393,
                              320022393, 320022393, 320022393, 330093898, 330093898, 330093898,
                              330093898, 330093898),
                      UF = c(13, 13, 13, 13, 13, 23, 23, 23,23, 23, 23, 23, 23, 23, 23, 24, 24,
                             24, 24, 24, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33),
                      V1008 = c(1, 1, 1, 1, 1, 5, 5, 5, 5,5, 11, 11, 11, 11, 11,
                                8, 8, 8, 8, 8, 3, 3, 3, 3, 3, 9, 9, 9,9, 9),
                      V1014 = c(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
                      V2008 = c(22, 23, 12, 28, 29, 13, 9, 9, 2, 22, 18, 5, 8,
                                16, 16, 12, 24, 2, 25, 28, 7, 7, 7, 15, 15, 6,
                                6, 18, 14, 14),
                      V20081 = c(1, 9, 6, 3, 5, 9, 7, 6, 9, 5, 2, 6, 1, 5, 9,
                                 10, 5, 4, 5, 1, 7, 7, 7, 5, 5, 8, 8, 4, 8, 8),
                      V20082 = c(1952, 1964, 1995, 1999, 2009, 1993, 1998, 2000,
                                 2003, 2011, 1967, 1990, 1993, 1996, 2001, 1947, 1996,
                                 1998, 1997, 2012, 2010, 2010, 2010, 2011, 2011, 1981, 1981, 1984,
                                 2006, 2006),
                      V2003 = c(1, 2, 6, 8, 10, 2, 9, 10, 12, 15, 2, 3,
                                5, 7, 11, 2, 6, 7, 7, 9, 3, 3, 3, 4, 4, 1, 1, 2, 4, 4),
                      V2007 = c(1, 2, 2, 1, 2, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 2, 1, 2,
                                2, 1, 1, 1, 1, 1, 2, 2, 1, 2, 2),
                      n_p = c(1, NA, NA, NA, NA, NA, NA, NA,
                              NA, NA, NA, NA, NA, NA, NA, NA, 
                              NA, NA, NA, NA, NA, NA, NA, NA,
                              NA, 1, 2, 1, NA, NA),
                      id_dom = c(2499L, 2499L, 2499L, 2499L,
                                 2499L, 10962L, 10962L, 10962L, 10962L, 
                                 10962L, 12618L, 12618L, 12618L, 12618L, 12618L, 13673L, 
                                 13673L, 13673L, 13673L, 13673L, 25945L, 25945L, 25945L, 
                                 25945L, 25945L, 28145L, 28145L, 28145L, 28145L, 28145L))

我想以迭代的方式p201根据 's 的值更新变量的值。可以从 1 到 5 (尽管不在 中,但我很抱歉无法采集包含所有可能情况的样本)。在第一波中,我指定等于if和else。之后,我从 2 到 5 做同样的事情,只考虑仍然缺失的行。n_pn_pdatasetp201100*(n_p-1) + V2003n_p == 1NAp201

这是代码:

final_df <- dataset %>%
  group_by(UF, UPA, V1008, V1014, V2007, 
           V2008, V20081, V20082, V2003) %>%
  mutate(p201 = ifelse(n_p == 1 & V2008 != 99 &
                         V20081 != 99 & V20082 != 9999,
                       100*(n_p-1) + V2003, NA)) %>%
    fill(p201, .direction = 'down') %>%
    mutate(p201 = ifelse(n_p == 1,
                         p201,
                         ifelse(n_p == 2 & is.na(p201) & V2008 != 99 &
                                  V20081 != 99 & V20082 != 9999, 
                                100*(n_p-1) + V2003,
                                NA))) %>%
    fill(p201, .direction = 'down') %>%
    mutate(p201 = ifelse(n_p %in% 1:2,
                         p201,
                         ifelse(n_p == 3 & is.na(p201) & V2008 != 99 &
                                  V20081 != 99 & V20082 != 9999,
                                100*(n_p-1) + V2003,
                                NA))) %>%
    fill(p201, .direction = 'down') %>%
    mutate(p201 = ifelse(n_p %in% 1:3,
                         p201,
                         ifelse(n_p == 4 & is.na(p201) & V2008 != 99 &
                                  V20081 != 99 & V20082 != 9999,
                                100*(n_p-1) + V2003,
                                NA))) %>%
    fill(p201, .direction = 'down') %>%
    mutate(p201 = ifelse(n_p %in% 1:4,
                         p201,
                         ifelse(n_p == 5 & is.na(p201) & V2008 != 99 &
                                  V20081 != 99 & V20082 != 9999,
                                100*(n_p-1) + V2003,
                                NA))) %>%
    ungroup() %>%
    mutate_at(c('UF', 'UPA', 'V1008', 'p201'), as.character) %>%
    mutate(idind = ifelse(is.na(p201),
                          NA,
                          paste0(V1014, UF, UPA, V1008, p201)))

我显然可以使用类似for循环的东西(或者甚至更好的东西,使用map?)来使代码更精简,但我不知道如何使用 tidyverse 的语法在管道流中插入循环。

有人可以帮忙吗?我想要的输出正是结果final_df,但代码更清晰。

PS:请不要介意结果会产生很多 NA p201- 整个数据集更复杂,这可能不是真的。

编辑 我通过调整 Limey 的答案找到了一个解决方案——我不知道递归函数。

它是这样的:

loop <- function(data,
                 interview = 2,
                 int_final = 5){
  data <- data %>%
    group_by(UF, UPA, V1008, V1014, V2007,
             V2008, V20081, V20082, V2003) %>%
    fill(p201, .direction = 'down') %>%
    mutate(p201 = ifelse(
      n_p %in% 1:(interview-1),
      p201,
      ifelse(
          n_p == interview  & is.na(p201) &
            V2008 != 99 &
            V20081 != 99 & V20082 != 9999,
          100 * (n_p - 1) + V2003,
          NA
        )
      ))

    if(interview == int_final){
      return(data)
      } else{
        return(loop(data, interview + 1, int_final))
        }
}

final_dataset <- dataset %>%
  group_by(UF, UPA, V1008, V1014, V2007,
           V2008, V20081, V20082, V2003) %>%
  mutate(p201 = ifelse(n_p == 1  & V2008 != 99 &
                         V20081 != 99 & V20082 != 9999,
                       100*(n_p-1) + V2003, NA)) %>%
  loop() %>%
  ungroup() %>%
  mutate_at(c('UF', 'UPA', 'V1008', 'p201'), as.character) %>%
  mutate(idind = ifelse(is.na(p201),
                        NA,
                        paste0(V1014, UF, UPA, V1008, p201)))

标签: rfor-loopdplyr

解决方案


嗯。听起来像递归编程可能会奏效。我不知道您为什么要尝试这样做并且您没有提供理想的结果,所以我无法检查我的结果,但这样的事情可能会奏效。

[未经测试的代码]

doIt <- function(data, currentDepth=1, maxDepth=5) {
  data <- data %>%
    group_by(UF, UPA, V1008, V1014, V2007, 
             V2008, V20081, V20082, V2003) %>%
    mutate(p201 = ifelse(n_p %in% 1:currentDepth & V2008 != 99 & V20081 != 99 & V20082 != 9999,
                         100*(n_p-1) + V2003, 
                         NA)) %>%
    fill(p201, .direction = 'down')
    if (currentDepth == maxDepth) return(data)
    else return (doIt(data, currentDepth+1, maxDepth))
}

final_dataset <- doIt(dataset)

我试图概括你的ifelse. 该函数要么再次调用自身(如果当前深度 < maxDepth),要么返回它自己的结果。


推荐阅读