【问题标题】:Converting some dataframe values to NA: values to convert are column-dependent, and given in a separate list将一些数据帧值转换为 NA:要转换的值取决于列,并在单独的列表中给出
【发布时间】:2021-03-26 03:01:36
【问题描述】:

在数据框中,我想将一些值转换为NA。哪些值应变为NA 取决于列。此基于列的值规范在单独的列表对象中给出。我想写一个可以接受的函数:

  1. 要清理的数据框
  2. 一个向量,指定要清理的列
  3. 为每一列指定每个值的列表都可以

并且将返回一个干净的数据框,其中不需要的值变成了NA。虽然可以使用for 循环来完成这样的任务,但我正在尝试找出是否有更简单的迭代方式来完成它。我通常喜欢tidyverse 解决方案,但对任何想法都很满意。

示例数据

在下面的数据集中,每一列都有自己的一组有效值应该保留,其余的应该变成NA

library(tibble)

set.seed(2020)

## generate random strings: https://stackoverflow.com/a/42734863/6105259
sample_strings <- function(n = 5000) {
  a <- do.call(paste0, replicate(5, sample(letters, n, TRUE), FALSE))
  paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(letters, n, TRUE))
}

df <-
  tibble(id = 1:40,
         color = sample(c(1:5), size = 40, replace = TRUE),
         political = sample(c(sample(c("republican", "democrat", "green_party", "libertarian"), size = 20, replace = TRUE),
                              sample_strings(20))),
         religion_status = sample(c(sample(c("secular", "traditional", "religious", "atheist", "agnostic"), size = 20, replace = TRUE), 
                                    sample_strings(20)))
         )

## # A tibble: 40 x 4
##       id color political  religion_status
##    <int> <int> <chr>      <chr>          
##  1     1     4 republican fzwue3975k     
##  2     2     4 republican mgxoe2220e     
##  3     3     1 democrat   secular        
##  4     4     1 republican secular        
##  5     5     4 aibcg6459y oqnfv1461t     
##  6     6     2 aedqi0739y ufhua9648n     
##  7     7     1 zgvox0771x agnostic       
##  8     8     5 democrat   traditional    
##  9     9     2 republican atheist        
## 10    10     2 oxgge5657l nktsl2136o     
## # ... with 30 more rows

了解哪些值应保存在以下列表中的哪一列中:

var_mapping_list <- list(preferences = list(age = list(originType = "NumberQuestionPage", 
    originIndex = 6L, title = "what is your age?", valueDescriptions = NULL), 
    political = list(originType = "QuestionPage", originIndex = 7L, 
        title = "what is your political affiliation?", valueDescriptions = list(
            republican = "I have voted most of my life to the republican party", 
            democrat = "I have voted most of my life to the democratic party", 
            other = "other")), religion_status = list(originType = "QuestionPage", 
        originIndex = 9L, title = "how do you define your religiousness level? ", 
        valueDescriptions = list(secular = "I don't practice any religion although I do belong to one", 
            traditional = "I'm observant and keep some of the practices", 
            religious = "I practice a religion", other = "other")), 
    color = list(originType = "QuestionPage", title = "which color do you like the best", 
        valueDescriptions = list(`1` = "red", `2` = "blue", `3` = "yellow", 
            `4` = "pink", `5` = "orange")), pet = list(originType = "QuestionPage", 
        originIndex = 0L, title = "do you have a pet? ", valueDescriptions = list(
            yes = "yes", no = "no"))))

以一个变量为例

说我要清理df$political。要知道要保留哪些值,我将首先访问:

var_mapping_list$preferences$political$valueDescriptions

## $republican
## [1] "I have voted most of my life to the republican party"

## $democrat
## [1] "I have voted most of my life to the democratic party"

## $other
## [1] "other"

我的规则是除了other的所有选项都是df中对应列的有效值。

所以这意味着在df$political 中,应该只保留republicandemocrat,其余的应该变成NA

因此,df$political 的示例工作流将是:

library(tidyr)
library(rlang)
library(dplyr)

vec_political_values_to_keep <-
  var_mapping_list$preferences$political$valueDescriptions %>%
  bind_rows %>%
  pivot_longer(cols = tidyselect::everything(), 
               names_to = "option_key", 
               values_to = "description") %>%
  filter(option_key != "other") %>%
  pull(option_key)
 

df %>% 
  mutate(political = recode(political, !!!rlang::set_names(vec_political_values_to_keep), .default = NA_character_)) ## https://stackoverflow.com/a/63916563/6105259


## # A tibble: 40 x 4
##       id color political  religion_status
##    <int> <int> <chr>      <chr>          
##  1     1     4 republican fzwue3975k     
##  2     2     4 republican mgxoe2220e     
##  3     3     1 democrat   secular        
##  4     4     1 republican secular        
##  5     5     4 NA         oqnfv1461t     
##  6     6     2 NA         ufhua9648n     
##  7     7     1 NA         agnostic       
##  8     8     5 democrat   traditional    
##  9     9     2 republican atheist        
## 10    10     2 NA         nktsl2136o  

我想将以上内容扩展到df 中的任何感兴趣的变量。

期望的输出

指定向量

colnames_to_clean <- c("color", "political", "religion_status")

[1] "color"           "political"       "religion_status"

应该返回以下数据框:

##       id color political  religion_status
##    <int> <int> <chr>      <chr>          
##  1     1     4 republican NA             
##  2     2     4 republican NA             
##  3     3     1 democrat   secular        
##  4     4     1 republican secular        
##  5     5     4 NA         NA             
##  6     6     2 NA         NA             
##  7     7     1 NA         NA             
##  8     8     5 democrat   traditional    
##  9     9     2 republican NA             
## 10    10     2 NA         NA  

我将不胜感激!

【问题讨论】:

  • 说实话,您的问题太长了,而且包含无关信息。您真正的问题是如何以半自动化的方式解析调查数据以重新编码答案。但目前还不清楚您真正想从var_mapping_list 中提取什么,因为您的示例甚至没有green_party,但您在代码的其他地方却有。尝试更多地关注您的问题。
  • 感谢您的评论。我已尝试多次修改问题以使其更清晰,但可能还不够。 green_party确实出现在数据中,但因为不在var_mapping_list中,我们知道green_party不属于数据,因此应该变成@ 987654346@。 var_mapping_list 的全部目的是让我们知道哪些值应该保留在 df 中。
  • 我又编辑了一遍,希望更清楚。如果需要进一步澄清,我将不胜感激。

标签: r list dataframe dplyr tidyr


【解决方案1】:

这是一种可能性。 首先将有效值放入tibble

new_list <- tibble(
  name  = names(var_mapping_list$preferences),
  x = var_mapping_list$preferences
) %>%
  mutate(all_vals = map2(x, name, ~ names(.x$valueDescriptions))) %>%
  select(-x)

这样做的好处是您现在可以轻松地使用 tidyverse 中的有效值。 其次,加入有效值并检查当前值是否为有效值:

df %>%
  gather(name, val, -id) %>%
  left_join(new_list, by = "name") %>% 
  group_by(name) %>%
  mutate(val = map2_chr(val, all_vals, ~if_else(.x %in% setdiff(.y, "other"), .x, NA_character_))) %>%
  select(-all_vals) %>%
  spread(name, val)

# A tibble: 40 x 4
      id color political  religion_status
   <int> <chr> <chr>      <chr>          
 1     1 4     republican NA             
 2     2 4     republican NA             
 3     3 1     democrat   secular        
 4     4 1     republican secular        
 5     5 4     NA         NA             
 6     6 2     NA         NA             
 7     7 1     NA         NA             
 8     8 5     democrat   traditional    
 9     9 2     republican NA             
10    10 2     NA         NA             
# ... with 30 more rows

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-19
    • 1970-01-01
    相关资源
    最近更新 更多