如何测试多个 CSV 中是否存在某些列名
How to test for existence of certain column names across numerous CSVs
我有大约 60 个 csv
个文件要合并。一个挑战是列的命名不一致,尽管基本上所有文件(应该)具有相同的数据。
为了处理这个问题,我想先测试哪些文件有某些列名(哪些没有)。我有一个字符串向量,其中每个元素反映一个列名,要检查它是否存在于每个 csv 文件中。
我正在尝试实现一个数据框:
- columns: 每列对应一个列名我要测试是否存在
- 行:每一行对应一个csv文件
- values:在每个单元格中,
0
或1
来标记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 的列名都应该是 或者 date
, name
, age
, age_value
, gender
.
col_names_to_test <-
c(
"date",
"name",
"age",
"age_value",
"gender"
)
解决方案的基础
这只是我的方向,基于定义阅读和编辑功能的,然后在定义的功能上使用list.files
和purrr::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
如果你只想要匹配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
),因为我不关心每个单元格中的值。
定义一个函数 ok
给定文件名 f
returns 一个命名的 0/1 向量,其长度与 col_names_to_test
相同,如果对应的分量为 1 col_names_to_test
作为列名存在于该文件中,否则为 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
更新
已完全改版。
我有大约 60 个 csv
个文件要合并。一个挑战是列的命名不一致,尽管基本上所有文件(应该)具有相同的数据。
为了处理这个问题,我想先测试哪些文件有某些列名(哪些没有)。我有一个字符串向量,其中每个元素反映一个列名,要检查它是否存在于每个 csv 文件中。
我正在尝试实现一个数据框:
- columns: 每列对应一个列名我要测试是否存在
- 行:每一行对应一个csv文件
- values:在每个单元格中,
0
或1
来标记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 的列名都应该是 或者 date
, name
, age
, age_value
, gender
.
col_names_to_test <-
c(
"date",
"name",
"age",
"age_value",
"gender"
)
解决方案的基础
这只是我的方向,基于定义阅读和编辑功能的list.files
和purrr::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
如果你只想要匹配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
),因为我不关心每个单元格中的值。
定义一个函数 ok
给定文件名 f
returns 一个命名的 0/1 向量,其长度与 col_names_to_test
相同,如果对应的分量为 1 col_names_to_test
作为列名存在于该文件中,否则为 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
更新
已完全改版。