通过 readr 包中的 tidy-selection 跨多个列指定列类型

问题描述 投票:0回答:1

我尝试使用

read_csv
中的
{readr}
CSV
文件读入 R。为了演示我的真正问题,我首先将参数
guess_max
重置为 5(默认为 1000)

library(readr)
formals(read_csv)$guess_max <- 5

并以较小的文字数据为例:

csv <- I(
"ID, Col1, Col2, VarA, VarB, VarC
1, NA, NA, NA, NA, NA
2, NA, NA, NA, NA, NA
3, NA, NA, NA, NA, NA
4, NA, NA, NA, NA, NA
5, 0, 1, x, y, z
6, NA, NA, NA, NA, NA")

read_csv(csv)

# # A tibble: 6 × 6
#      ID  Col1    Col2    VarA   VarB   VarC 
#   <dbl>  <lgl>   <lgl>   <lgl>  <lgl>  <lgl>
# 1     1  NA      NA      NA     NA     NA   
# 2     2  NA      NA      NA     NA     NA   
# 3     3  NA      NA      NA     NA     NA   
# 4     4  NA      NA      NA     NA     NA   
# 5     5  FALSE*  TRUE*   NA*    NA*    NA*
# 6     6  NA      NA      NA     NA     NA

*
:出现解析问题


guess_max
影响,仅前 5 行(列名称和
ID
1 到 4)用于猜测列类型。因为
ID
1 到 4 中的值全部缺失,所以所有列都被猜测为
logical
并且被错误解析:

  • 0
    1
    (整数)→
    FALSE
    TRUE
    (逻辑)
  • 'x'
    'y'
    'z'
    (字符)→
    NA
    (逻辑)

在这种情况下我必须手动设置

col_types

read_csv(csv, col_types = cols(Col1 = col_integer(), Col2 = col_integer(),
                               VarA = col_character(), VarB = col_character(), VarC = col_character()))

# # A tibble: 6 × 6                                                                                                   
#      ID  Col1  Col2 VarA  VarB  VarC 
#   <dbl> <int> <int> <chr> <chr> <chr>
# 1     1    NA    NA NA    NA    NA   
# 2     2    NA    NA NA    NA    NA   
# 3     3    NA    NA NA    NA    NA   
# 4     4    NA    NA NA    NA    NA   
# 5     5     0     1 x     y     z    
# 6     6    NA    NA NA    NA    NA

当列数量较多时,一一提供列类型很烦人。如果我想要指定的那些列的名称具有某些模式,我希望使用类似 的语法来指定跨多个列的类型,例如

across()
中的
{dplyr}
。伪代码如下:

read_csv(csv, col_types = cols(across(starts_with("Col"), col_integer()),
                               across(starts_with("Var"), col_character())))

是否可以通过

readr
本身或其他附加包实现?

提前致谢!


编辑

我需要使用

col_xxx()
而不是它们的缩写(
'i'
'c'
等)来创建更通用的列规范,例如

cols(across(contains("Date"), col_date(format = "%m-%d-%Y")),
     across(Fct1:Fct9, col_factor(levels = custom_levels)))
r file-io readr across tidyselect
1个回答
8
投票

read_delim()
系列使用 tidy-selection 来选择带有参数
col_select
的列。您可以利用此参数将 tidy-selection 合并到列类型的规范中。下面是一个简单的实现。关键是将
n_max = 0L
设置为仅读取列名称行。

版本1

col_across <- function(.cols, .fns, file) {
  col_selected <- read_csv(file, n_max = 0L, col_select = {{.cols}}, show_col_types = FALSE)
  lapply(col_selected, function(x) .fns)
}
使用方法
df <- read_csv(csv,
  col_types = c(col_across(starts_with("Col"), col_integer(), csv),
                col_across(VarA:VarC, col_factor(c('x', 'y', 'z')), csv))
)

上面的方法很简单,但是还过得去,但有一些缺点:

  1. 需要将相同的文件源(即对象
    csv
    )传递给每个
    col_across()
  2. read_delim
    家族包括多种变体,例如
    read_csv
    read_csv2
    read_tsv
    。调用
    df <- read_xxx(...)
    时,必须确认
    col_across()
    使用了一致的
    read_xxx
    来读取列名。


版本2

开发了

col_across
的改进版本,自动检测使用的是哪个
read_xxx
,并从外部调用中检索文件源。

col_across <- function(.cols, .fns) {
  sc <- sys.call(1L)
  sc <- match.call(match.fun(sc[[1L]]), sc)
  read_call <- sc[c(1L, match("file", names(sc), 0L))]
  read_call$n_max <- 0L
  read_call$col_select <- substitute(.cols)
  read_call$show_col_types <- FALSE
  lapply(eval(read_call, parent.frame()), function(x) .fns)
}
使用方法
df <- read_csv(csv,
  col_types = c(col_across(starts_with("Col"), col_integer()),
                col_across(VarA:VarC, col_factor(c('x', 'y', 'z'))))
)

请注意,此版本的

col_across
只能在
read_delim
系列内使用,因为
across
mutate
中的
{dplyr}
对应。

检查色谱柱规格
spec(df)

# cols(
#   ID = col_double(),
#   Col1 = col_integer(),
#   Col2 = col_integer(),
#   VarA = col_factor(levels = c("x", "y", "z"), ordered = FALSE, include_na = FALSE),
#   VarB = col_factor(levels = c("x", "y", "z"), ordered = FALSE, include_na = FALSE),
#   VarC = col_factor(levels = c("x", "y", "z"), ordered = FALSE, include_na = FALSE)
# )
© www.soinside.com 2019 - 2024. All rights reserved.