在purrr :: pmap()中应用for循环

时间:2018-08-22 05:29:33

标签: r for-loop purrr

我尝试从两列soldiersuperior创建一个在每个soldier上方具有较高排名的列表列,以后我可以嵌套以形成长数据,这些列将位于每个上方军衔。因此,对于“ Srg”,值将是“ Lt,Maj,Col,Gen”,对于“ Maj”,值将是“ Col,Gen”。

当前,我发现使用purrr::pmap()将此for循环应用于整个数据帧的唯一方法是要求我将变量和数据帧名称硬编码到for循环中。

是否可以通过将数据和变量名作为参数的更通用的函数来提取这些等级?

library(dplyr)
library(tidyr)
library(purrr)

# Create test data
data <-
  dplyr::tibble(
    soldier = c("Srg", "Lt", "Maj", "Col", "Gen"),
    superior  = c("Lt", "Maj", "Col", "Gen", NA)
  )

# Define custom function
get_ranks_above <- function(id, max_steps = 5){

  ranks_above <- vector("list", length = max_steps)

  for (i in 1:max_steps) {
    ranks_above[[i]] <- 
      data.frame(
        superior_list = data$superior[data$soldier == id]
      )

    id <- ranks_above[[i]]$superior_list
  }

  do.call(rbind, ranks_above)
}

# Apply custom function
data_ranked <- 
  data %>%
  mutate(
    ranks_above = pmap(
      list(id = soldier), 
      get_ranks_above
    )
  )

# Unnest list column and add numeric ranks
data_ranked %>% 
  unnest(ranks_above) %>% 
  drop_na() %>% 
  group_by(soldier) %>% 
  mutate(rank_from_top = seq(n(),1)) %>% 
  ungroup()

当我尝试使用数据和变量名的参数编写自定义函数get_ranks_above()时,出现错误消息: mutate_impl(.data,点)中的错误:   评估错误:元素1的长度为2,而不是1或5。

get_ranks_above <- function(data, id = soldier, lower_rank = data$soldier, upper_rank = data$superior, max_steps = 5){

  ranks_above <- vector("list", length = max_steps)

  for (i in 1:max_steps) {
    ranks_above[[i]] <- 
      data.frame(
        superior_list = upper_rank[lower_rank == id]
      )

    id <- ranks_above[[i]]$superior_list
  }

  do.call(rbind, ranks_above)
}

data_ranked <- 
  data %>%
  mutate(
    ranks_above = pmap(
      list(
        data = data, 
        id = soldier, 
        lower_rank = data$soldier, 
        upper_rank = data$superior, 
        max_steps = 5
      ), 
      get_ranks_above
    )
  )

2 个答案:

答案 0 :(得分:0)

我认为做您想做的事的最简单方法是使用有序因子来相互比较等级。在这里,我可以使用parse_factor来创建有序因子,使用ranks作为级别并指定应该按照给定的级别进行排序(请注意ranks已经按顺序排列)。这使得superior易于确定,我们只需遍历ranks并检查哪个>比我们当前的soldier以及子集ranks相应。然后我们可以根据需要unnest并获得我们的长格式数据。

library(tidyverse)
ranks <- c("Srg", "Lt", "Maj", "Col", "Gen")
set.seed(12345)
some_soldiers <- tibble(
  soldier = sample(ranks, 5)
)
some_soldiers
#> # A tibble: 5 x 1
#>   soldier
#>   <chr>  
#> 1 Col    
#> 2 Gen    
#> 3 Maj    
#> 4 Lt     
#> 5 Srg

some_soldiers %>%
  mutate(
    soldier = parse_factor(soldier, levels = ranks, ordered = TRUE),
    superior = map(soldier, ~ ranks[which(ranks > .x)])
  ) %>%
  unnest()
#> # A tibble: 10 x 2
#>    soldier superior
#>    <ord>   <chr>   
#>  1 Col     Gen     
#>  2 Maj     Col     
#>  3 Maj     Gen     
#>  4 Lt      Maj     
#>  5 Lt      Col     
#>  6 Lt      Gen     
#>  7 Srg     Lt      
#>  8 Srg     Maj     
#>  9 Srg     Col     
#> 10 Srg     Gen

reprex package(v0.2.0)于2018-08-21创建。

答案 1 :(得分:0)

我会用有序因素解决这个问题。一旦有了包含所有信息的表,就可以轻松地将其合并到任何数据框。总体思路:

library(dplyr)
library(purrr)
sld_levels <- c("Srg", "Lt", "Maj", "Col", "Gen")
tibble(sld_rank = factor(sld_levels, 
                         levels = sld_levels, 
                         ordered = TRUE)) %>% 
  mutate(rank_above = map(.x = sld_rank, ~sld_rank[.x < sld_rank]))