bugfix> r > 投稿

私は約60を持っています csv マージしたいファイル。 1つの課題は、列の名前の一貫性がないことですが、本質的にすべてのファイル(必要)は同じデータを持っています。

この問題に対処するために、私は最初にどのファイルが持っているかをテストしたいと思います特定の列名(およびそうではない)。各要素が列名を反映する文字列のベクトルがあり、各csvファイルに存在するかどうかを確認します。

私は次のようなデータフレームを実現しようとしています。

  • :各列は、存在するかどうかをテストしようとしている列名に対応しています
  • :各行は1つのcsvファイルに対応します
  • :各セルで、どちらか 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の列名は次のとおりです。どちらか datenameageage_valuegender

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

ソリューションの基礎

これは私の方向性であり、この素晴らしい解決策 読み取りおよび編集機能を定義し、次に使用する 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(.))

これは単なる一般的な考え方です...私はきちんとしたアプローチに慣れていますが、どんな解決策にも満足します。

必要な出力
 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

回答 2 件
  • 関数を定義する ok ファイル名を付けた f と同じ長さの名前付き0/1ベクトルを返します col_names_to_test の対応するコンポーネントの場合は1 col_names_to_test そのファイルには列名として存在し、それ以外の場合は0です。次に、ファイル名のベクトルを定義します files 。拡張子なしで名前を付けて適用します ok を使用してそれに map_dfr

    これは適度にコンパクトで、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")
    files %>% setNames(sub("\\.csv$", "", .)) %>% 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
    
    
    更新

    完全に改訂しました。

  • 一致する列のインデックスのみが必要な場合 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 )、各セルの値は気にしないためです。

あなたの答え