【问题标题】:change column type based on condition of variables根据变量条件更改列类型
【发布时间】:2017-11-10 12:40:31
【问题描述】:

我有数据,这是一个小样本:

df <- structure(list(`d955` = c("1", "4", NA, NA), 
                `65c2` = c("6a08", NA, "6a08", "6a09")), 
                 class = c("tbl_df", "tbl", "data.frame"), 
                 row.names = c(NA, -4L), .Names = c("d955", "65c2"))
# A tibble: 4 x 2
#    d955 `65c2`
#   <chr>  <chr>
# 1     1   6a08
# 2     4   <NA>
# 3  <NA>   6a08
# 4  <NA>   6a09

两列都是字符类型。我想将仅包含从 1 到 5 的数字的所有列的列类型更改为整数。我知道我可以亲自挑选列来执行此操作,但由于列会不断变化,这不是一个令人满意的选择。

那么如何自动执行此操作?我一直在从dplyr 包中查看mutate_if,但我不知道如何选择正确的列开始。

我一直在研究str_detect,它可能会起作用,但像str_detect(df, "[1234]") 这样的东西也会匹配65c2 行中数字在1-4 之间的字符串。我一直在寻找str_count 的解决方案,因为整数的计数始终为 1,但我没有找到基于字符串计数条件选择列的好解决方案...

所需的自动化结果:

# A tibble: 4 x 2
#    d955 `65c2`
#   <int>  <chr>
# 1     1   6a08
# 2     4   <NA>
# 3  <NA>   6a08
# 4  <NA>   6a09

【问题讨论】:

  • 一直在尝试将charcount &lt;- function(x) (str_count(x) == 1)users.matrix %&gt;% mutate_if(charcount, as.integer) 结合使用的tidyverse 方法。那行不通,但我想这也是一条可能的路线……

标签: r dplyr tidyverse tibble


【解决方案1】:

基于 R 的想法,

i1 <- colSums(sapply(df, function(i) i %in% c(NA, 1:5))) == nrow(df)
df[i1] <- lapply(df[i1], as.integer)

给出,

str(df)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   4 obs. of  2 variables:
 $ d955: int  1 4 NA NA
 $ 65c2: chr  "6a08" NA "6a08" "6a09"

你也可以把它变成一个函数,

my_conversion <- function(df){
  i1 <- colSums(sapply(df, function(i) i %in% c(NA, 1:5))) == nrow(df)
  df[i1] <- lapply(df[i1], as.integer)
  return(df)
}

【讨论】:

  • 啊,太好了!这行得通。堆栈让我这样做后会接受你的答案:)
【解决方案2】:

使用dplyr 包中的mutate_if 的解决方案。我们需要为此任务定义一个谓词函数 (is_one_five_only)。

library(dplyr)

# Design a function to determine if elements from one vector are all 1 to 5
# Notice that if the entire column is NA, it will report FALSE
is_one_five_only <- function(x){
  if (all(is.na(x))){
    return(FALSE)
  } else {
    x2 <- x[!is.na(x)]
    return(all(x2 %in% 1:5))
  }
}

# Apply is_one_five_only as the predicate function in mutate_if
df2 <- df %>% mutate_if(is_one_five_only, as.integer)
df2

# # A tibble: 4 x 2
#   d955 `65c2`
#   <int>  <chr>
# 1     1   6a08
# 2     4   <NA>
# 3    NA   6a08
# 4    NA   6a09

【讨论】:

    【解决方案3】:

    使用data.table

    library(data.table)
    setDT(df)
    
    # get indices of all the character columns
    # (i.e. we can skip numeric/other columns)
    char_cols = sapply(df, is.character)
    
    # := is the assignment operator in data.table --
    #  since data.table is built for efficiency,
    #  this differs from base R or dplyr assignment
    # since assignment with := is _by reference_,
    #  meaning no copies are created. there are other
    #  advantages of :=, like simple assignment
    #  by group -- see the intro vignettes
    #.SD is a reflexive reference -- if .SDcols
    #  is unspecified, it simply refers to your
    #  data.table itself -- df[ , .SD] is the same as df.
    #  .SDcols is used to restrict which columns are
    #  included in this Subset of the Data -- here,
    #  we only include character columns.
    #Finally, by lapply-ing .SD, we essentially loop
    #  over the specified columns to apply our
    #  custom-tailored function
    df[ (char_cols) := lapply(.SD, function(x) {
      if (any(grepl('[^1-5]', x))) x
      else as.integer(x)
    }, .SDcols = char_cols]
    

    希望转换逻辑清晰;可以根据需要详细说明。

    请参阅 Getting Started wiki 以获取入门知识和大量其他资源,以使自己适应 data.table 的基本要素。

    【讨论】:

    • 我不完全按照你的做法。为什么要获取 char col 索引?加快大型矩阵的速度?而且,我从未见过在 R 中使用 := (虽然,我从 Mathematica 知道),学习这个选项很有趣。 .SD 和 .SDcols 的作用是什么?
    • @raoul :=data.table 包中用于通过引用创建新变量或替换/更新现有变量。请参阅他们的 github 页面上的 data.table 介绍或查看 SO 上的其他示例。另外,请查看添加到此答案的详细 cmets。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-02
    • 2018-05-14
    • 2012-11-12
    • 2019-04-01
    • 2017-01-31
    • 2021-01-13
    • 1970-01-01
    相关资源
    最近更新 更多