在readr::read_delim(...)中使用整齐选择指定跨多个列的列类型

thtygnil  于 2023-03-15  发布在  其他
关注(0)|答案(1)|浏览(83)

bounty将在2天后过期。回答此问题可获得+50的声誉奖励。user18894435正在寻找来自声誉良好来源的答案

我尝试使用{readr}中的read_csv将一个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,解析错误:

  • 01(整数)→ FALSETRUE(逻辑)
  • '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() in {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)))
lokaqttq

lokaqttq1#

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()

  1. read_delim家族包括多个变体,例如read_csvread_csv2read_tsv。在调用df <- read_xxx(...)时,必须确认col_across()已使用一致的read_xxx来读取列名。
版本2

开发了col_across的改进版本,它自动检测使用了哪个read_xxx,并从外部调用中检索文件源。
一个二个一个一个
请注意,此版本的col_across只能在read_delim()系列中使用,因为acrossdplyr中的mutate相同。

检查色谱柱规格
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)
# )

相关问题