如何在众多 CSV 中测试某些列名的存在

Posted

技术标签:

【中文标题】如何在众多 CSV 中测试某些列名的存在【英文标题】:How to test for existence of certain column names across numerous CSVs 【发布时间】:2021-03-11 14:39:35 【问题描述】:

我有大约 60 个 csv 文件要合并。一个挑战是列的命名不一致,尽管基本上所有文件(应该)都有相同的数据。

为了解决这个问题,我想先测试一下哪些文件有某些 列名(哪些没有)。我有一个字符串向量,其中每个元素反映一个列名,以检查它是否存在于每个 csv 文件中。

我正在尝试实现一个数据框:

:每一列对应一个列名,我要测试是否存在 :每一行对应一个csv文件 :在每个单元格中,01 标记 csv 文件是否有列名

例如 3 个 CSV

library(tidyverse)

df_1 <-
  tribble(~ date, ~ name, ~ age, ~ gender,
        "2020-11-29", "sarah", 43, "female")

df_2 <-
  tribble(~ createdAt, ~ person, ~ age, ~ is_female,
        "2020-10-10", "bob", 25, 0)

df_3 <- 
  tribble(~ date, ~ name, ~ age_value, ~ gender,
        "2010-01-07", "wendy", 70, "female")
write_csv(df_1, "csv_1.csv")
write_csv(df_2, "csv_2.csv")
write_csv(df_3, "csv_3.csv")

带有名称的向量

现在假设我不知道上面创建的 3 个 CSV 中的列名。 我相信每个 CSV 的列名应该是任何一个 datenameageage_valuegender

col_names_to_test <-
  c(
    "date",
    "name",
    "age",
    "age_value",
    "gender"
  )

解决方案的基础

这只是我的方向,基于定义阅读和编辑功能的this nice solution,然后在定义的功能上使用list.filespurrr::map_df

read_plus <- 
  function(flnm) 
  read_csv(flnm, col_types = cols(.default = "c")) # %>%
  ## here some testing against the vector `col_names_to_test` ?
  

tbl_with_sources <-
   list.files(path = //folder-with-csv-files,
              pattern = "*.csv", 
              full.names = TRUE,
              recursive = TRUE) %>% 
  map_df(~ read_plus(.))

这只是一个一般性的想法...我习惯于 tidyverse 方法,但我会对任何解决方案感到满意。

所需输出

  filename  date  name   age age_value gender
  <chr>    <dbl> <dbl> <dbl>     <dbl>  <dbl>
1 csv_1        1     1     1         0      1
2 csv_2        0     0     1         0      0
3 csv_3        1     0     0         1      1

【问题讨论】:

【参考方案1】:

定义一个函数ok,给定文件名f返回一个命名的0/1向量,其长度与col_names_to_test相同,如果col_names_to_test的对应组件作为该文件中的列名存在,则该向量为1否则为 0。然后定义一个文件名向量files。给它命名而不带扩展名,并使用map_dfr 对其应用ok

这是相当紧凑的,只使用 purrr。

library(purrr)

ok <- function(f) +setNames(col_names_to_test %in% names(read.csv(f)), col_names_to_test)
files <- Sys.glob("csv_*.csv")
shortnames <- sub("\\.csv$", "", basename(files))
files %>% setNames(shortnames) %>% map_dfr(ok, .id = "file")

给予:

# A tibble: 3 x 6
  file   date  name   age age_value gender
  <chr> <int> <int> <int>     <int>  <int>
1 csv_1     1     1     1         0      1
2 csv_2     0     0     1         0      0
3 csv_3     1     1     0         1      1

更新

已完全修改。

【讨论】:

太棒了。我将在我的情况下添加,我将files 分配给files &lt;- list.files(path = //folder-with-csv-files, pattern = "*.csv", full.names = TRUE, recursive = TRUE) 另外,您介意在setNames 之前解释+ 在函数ok 中的作用吗? 将逻辑向量转换为 0/1 向量。试试+ c(TRUE, FALSE) 还在处理完整路径名的解决方案中添加了短名称行。 我还有一个问题,我想知道是否需要单独的帖子或可以在此处添加 - 调整 ok 函数(或创建 ok_2 以使其返回)是否足够简单1 也用于部分匹配?例如,如果col_names_to_test 具有age,而csv_3 具有age_value,我们将认为这是一个匹配项,因为age_value 包含age 作为子字符串。这在我们不知道 CSV 中列名的确切措辞的情况下很有用,但可以猜测“年龄”列可能至少有 age 作为子字符串。【参考方案2】:

如果你只想要匹配col_names_to_test的列的索引,你可以使用这种方法:

library('data.table')
library('dplyr')

col_names_to_test = c('date', 'name', 'age', 'age_value', 'gender')

# define columns indexes matching the pattern
DefCols = function(input_path, patterns) 
  pattern = patterns %>%
    str_flatten('|')
  cols = input_path %>%
    fread(nrows = 1) %>%
    colnames() %>%
    str_which(pattern)
  return(cols)


# define the input directory
input_dir = ''

cols = input_dir %>%
  dir(pattern = '.*.csv$', full.names = TRUE, recursive = TRUE) %>%
  lapply(DefCols, col_names_to_test)

但是,如果您还想加载仅包含匹配列的数据框,则可以对其进行扩展,如下所示:

library('data.table')
library('dplyr')

col_names_to_test = c('date', 'name', 'age', 'age_value', 'gender')

# define columns indexes matching the pattern
LoadDF = function(input_path, patterns) 
  pattern = patterns %>%
    str_flatten('|')
  cols = input_path %>%
    fread(nrows = 1) %>%
    colnames() %>%
    str_which(pattern)
  df = input_path %>%
    fread(drop = -cols) %>%
    as.data.frame()
  return(df)


# define the input directory
input_dir = ''

dfs = 'input_dir' %>%
  dir(pattern = '.*.csv$', full.names = TRUE, recursive = TRUE) %>%
  lapply(LoadDF, col_names_to_test)

注意:当我加载数据以检查列名时,我只保留第一行 (nrows = 1),因为我不关心每个单元格中的值。

【讨论】:

以上是关于如何在众多 CSV 中测试某些列名的存在的主要内容,如果未能解决你的问题,请参考以下文章

自动检测文件中CSV标题的存在

如何根据一组列名获取数据库对象列表?

如何使用 csv 文件的列名作为输入选择?

如何在 Excel 中组合具有不同列名和列顺序的多个 CSV 文件?

BigQuery - 如何在不使用列名作为值的情况下导入 CSV?

如何仅从 csv 文件(熊猫)加载列名?