仅当所有值都满足条件时才重命名列

时间:2020-12-23 16:20:12

标签: r function dataframe dplyr

我想根据这些列中的所有值是否满足条件来重命名数据中的列。例如,在数字列中,如果所有值都大于 5,则将该列重命名为 large_values,否则保持列名不变。另一个示例,如果字符列中的所有值都没有 & 字符,则将该列重命名为 no_ampersand

数据

library(tibble)

df <- 
  tribble(~age_1, ~age_2, ~string_1,
        2, 7, "abc",
        3, 8, "efg",
        1, 11, "hi&",
        10, 6, "klmn",
        50, 100, "opq")

定义两个函数用于演示

  1. 向量元素是否大于某个值
is_larger_than_val <- function(x, y) {
  all(x > y)
}
  1. 矢量元素是否包含 &
does_contain_ampersand <- function(x) {
  all(grepl("&", x))
}

问题

因此,使用这些函数,我如何重命名 df 的标题,以便何时

is_larger_than_val(df$age_2, 5)

[1] TRUE

age_2 重命名为 large_values,但在其他情况下

is_larger_than_val(df$age_1, 5)

[1] FALSE

会保持 age_1 不变吗?

同样,因为

does_contain_ampersand(df$string_1)

[1] FALSE

会保持 string_1 不变(但如果它是 TRUE,那么 string_1 会被重命名为 no_ampersand)?

期望的输出

给定当前数据,根据我在 is_larger_than_val()does_contain_ampersand() 中指定的条件重命名应该返回:

# A tibble: 5 x 3
  age_1 large_values string_1
  <dbl>        <dbl> <chr>   
1     2            7 abc     
2     3            8 efg     
3     1           11 hi&     
4    10            6 klmn    
5    50          100 opq  

我确信这可以通过嵌套一些 if else 语句来实现,但我想知道是否有更简单的方法(也许使用 tidyverse 技巧?)。

谢谢!


编辑


把问题放在上下文中

在下面@Ian 的评论之后,这是我的问题的上下文。
  1. 我有一个源自 R 文件的 JSON 数据对象。一旦我将 JSON 文件读入 R,它最终会变成以下格式:
vec <- c(1, 2, 3)
names(vec) <- c("A", "B", "C")
my_data_object_as_list <- as.list(vec)

my_data_object_as_list
## $A
## [1] 1

## $B
## [1] 2

## $C
## [1] 3
  1. 我想构建一个函数,该函数接受 my_data_object_as_list 并将其重组为表格。
require(tidyr)
require(dplyr)
require(tidyselect)

organize_in_table <- function(as_list_object) {
    as_list_object %>%
    bind_rows() %>%
    pivot_longer(cols = tidyselect::everything())
}
organize_in_table(my_data_object_as_list)

## # A tibble: 3 x 2
##   name  value
##   <chr> <dbl>
## 1 A         1
## 2 B         2
## 3 C         3
  1. 因为 organize_in_table() 非常通用,所以它返回一个表,其中的列名(namevalue)并不表示每列的含义。为了解决这个问题,我想给 purpose 添加一个 organize_in_table() 参数,下面是一个例子:
organize_in_table <-
  function(as_list_object,
           purpose = NULL) {
    table <- as_list_object %>%
      bind_rows() %>%
      pivot_longer(cols = tidyselect::everything())
    
    if (is.null(purpose)) {
      return(table)
    } else if (purpose == "match_letters_and_numbers") {
      table <- rename(table, letters = name, numbers = value)
    }
    return(table)
  }

现在,organize_in_table() 可以返回具有有意义名称的对象:

df_letters_and_numbers <- 
  organize_in_table(my_data_object_as_list, "match_letters_and_numbers")

> df_letters_and_numbers
## # A tibble: 3 x 2
##   letters numbers
##   <chr>     <dbl>
## 1 A             1
## 2 B             2
## 3 C             3
  1. 这里我遇到了一个问题:我怎么知道 df_letters_and_numbers[1] 被正确命名为 lettersdf_letters_and_numbers[2]numbers?我在什么基础上验证过——当我在 table 中重命名 organize_in_table() 时——第一列 (name) 都是字母,第二列 (value) 都是数字?我没有验证。

如果 my_data_object_as_list 看起来像这样会怎样:

vec_2 <- c("A", "B", "C")
names(vec_2) <- c(1, 2, 3)
my_data_object_as_list_2 <- as.list(vec_2)

> my_data_object_as_list_2 
## $`1`
## [1] "A"

## $`2`
## [1] "B"

## $`3`
## [1] "C"

然后,如果我运行 organize_in_table(my_data_object_as_list_2, "match_letters_and_numbers"),我将获得列名不匹配的数据框:

## # A tibble: 3 x 2
##   letters numbers
##   <chr>   <chr>  
## 1 1       A      
## 2 2       B      
## 3 3       C     

底线

所以我的结论是,我必须根据要重命名的每一列的内容,在 organize_in_table() 内调整重命名步骤。为此,我认为第一步应该是为我想要执行的每个测试定义一个单独的函数(例如,列号中的所有值都是吗?所有值都是字母吗?)。因为我想让 organize_in_table() 尽可能地可扩展,所以我希望它接受我可以为其构建测试功能的任何测试。

2 个答案:

答案 0 :(得分:2)

在数据上使用 sapply 以在列上创建逻辑索引,但要注意与号大小写中的字符列:

i <- sapply(df, is_larger_than_val, y = 5)
names(df)[i] <- "large_values"

i <- sapply(df, does_contain_ampersand)
i <- i | !sapply(df, is.character)
names(df)[!i] <- "no_ampersand"

names(df)
#[1] "age_1"        "large_values" "no_ampersand"

答案 1 :(得分:2)

sls deploy 解决方案非常简单,非常棒。这是一个带有 sapply 的替代解决方案,需要更多的努力。我认为它的原理相同,但我添加了一个步骤来识别数字和字符串列,以确保仅测试某些列

data.table
相关问题