Есть ли лучший способ в R разделить файл с несколькими разделами - PullRequest
1 голос
/ 21 февраля 2020

Я читаю в файле CSV в R, который содержит несколько разделов, разделы содержат разные заголовки, количество строк и столбцов. Пример таблицы ниже, мне нужно разделить их на разные фреймы данных, чтобы я мог обрабатывать их в зависимости от типа раздела.

enter image description here

Количество разделов может измениться, и пока я только выяснил, как жестко их кодировать и использовать grep для разделения различных разделов на разные. фреймы данных.

Все секции имеют одинаковый формат == XY == , где X - это буква, а Y - это число.

Есть ли лучший способ разделения данных? кадр в другой кадр данных для каждого раздела, независимо от того, сколько разделов есть?

z1 <- structure(list(V1 = c("==C5===", "H1", "1", "3", "8", "==E5===", 
                            "H1", "10", "2", "==G6===", "H1", "5", "==H4===", "H1", "1", 
                            "==H6===", "H1", "10"), V2 = c("", "H2", "9", "8", "1", "", "H2", 
                                                           "4", "2", "", "", "", "", "H2", "8", "", "", ""), V3 = c("", 
                                                                                                                    "H3", "2", "5", "6", "", "", "", "", "", "", "", "", "", "", 
                                                                                                                    "", "", "")), class = "data.frame", row.names = c(NA, -18L))
DF1 <- z1[grep("==C5", z1$V1):grep("==E5", z1$V1),]
DF2 <- z1[grep("==E5", z1$V1):grep("==G6", z1$V1),]
DF3 <- z1[grep("==G6", z1$V1):grep("==H4", z1$V1),]
DF4 <- z1[grep("==H4", z1$V1):grep("==H6", z1$V1),]
DF5 <- z1[grep("==H6", z1$V1):nrow(z1),]

Ответы [ 3 ]

2 голосов
/ 21 февраля 2020

Предполагая, что вы начинаете с CSV, вот способ прочитать в каждом указанном c наборе строк.

write.table(z1, "tmp.csv", na ="", row.names = FALSE, 
            col.names = FALSE, sep = ",")

tmp <- readLines("tmp.csv")

# start of each 'file'
sof <- grep("==", tmp)

# the actual start is one past that
real_start <- sof + 1

# figure out the end of each unique df
real_end <- c(sof[-1] - 1, length(tmp))

# the number of rows to read in
to_read <- real_end - real_start

# a list to store your data.frames
my_dfs <- vector("list", length = length(real_start))
for(i in 1:length(my_dfs)){
  # suppressing warnings, as there are a lot 
  #  that come up when a column header is not
  #  in a specific data.frame
  my_dfs[[i]] <- suppressWarnings(
    data.table::fread("tmp.csv",
                      skip = sof[i],
                      nrows = to_read[i],
                      fill = FALSE,
                      check.names = FALSE,
                      data.table = FALSE,
                      select = c("H1", "H2", "H3")
    )
  )
}

Вывод этого:

[[1]]
  H1 H2 H3
1  1  9  2
2  3  8  5
3  8  1  6

[[2]]
  H1 H2
1 10  4
2  2  2

[[3]]
  H1
1  5

[[4]]
  H1 H2
1  1  8

[[5]]
  H1
1 10

Если вы не знаете заголовков столбцов, вы можете вместо этого использовать read.csv, но у вас получится несколько NA столбцов.

my_dfs2 <- vector("list", length = length(real_start))
for(i in 1:length(my_dfs2)){
  # suppressing warnings, as there are a lot 
  #  that come up when a column header is not
  #  in a specific data.frame
  my_dfs2[[i]] <- 
             read.csv("tmp.csv",
                      skip = sof[i],
                      nrows = to_read[i],
                      fill = FALSE
             )
}

Вывод этого:

[[1]]
  H1 H2 H3
1  1  9  2
2  3  8  5
3  8  1  6

[[2]]
  H1 H2  X
1 10  4 NA
2  2  2 NA

[[3]]
  H1  X X.1
1  5 NA  NA

[[4]]
  H1 H2  X
1  1  8 NA

[[5]]
  H1  X X.1
1 10 NA  NA

Затем вы можете удалить столбцы NA в качестве следующего шага обработки.

1 голос
/ 21 февраля 2020

Ниже мы предполагаем, что вам действительно нужно иметь H1, H2, ... в качестве имен столбцов каждого созданного data.frame.

Вставьте столбцы вместе, давая p, удалите элементы ==, дающие p2, сформируйте группирующую переменную g и разделите p2 на g, используя read.table для чтения в каждом компоненте. Пакеты не используются.

p <- do.call(paste, z1)
p2 <- p[!grepl("^==", p)]
g <- cumsum(grepl("^\\D", p2))
L <- lapply(split(p2, g), function(x) read.table(text = x, header = TRUE))
L

, предоставив этот список фреймов данных:

$`1`
  H1 H2 H3
1  1  9  2
2  3  8  5
3  8  1  6

$`2`
  H1 H2
1 10  4
2  2  2

...etc...

Ниже мы обсудим некоторые дополнительные вещи, которые мы можем по выбору сделать с L.

Свободные фреймы данных

Хотя это не очень рекомендуется, если вы действительно хотите создавать свободные фреймы данных, висящие в вашей глобальной среде, вы можете сделать это:

names(L) <- paste0("DF", names(L))
list2env(L, .GlobalEnv)

Используя заголовок

Если заголовок в строках == имеет значение, мы могли бы использовать их вместо 1, 2, 3, ... вот так:

names(L) <- trimws(gsub("=", "", grep("^==", p, value = TRUE)))
L

, давая:

$C5
  H1 H2 H3
1  1  9  2
2  3  8  5
3  8  1  6

$E5
  H1 H2
1 10  4
2  2  2

...etc...

Создание фрейм данных из списка

Мы можем использовать rbindlist в data.table для создания фрейма данных / data.table из L следующим образом:

library(data.table)
rbindlist(L, fill = TRUE, id = "id")

, давая следующее где id обозначает номер компонента.

   id H1 H2 H3
1:  1  1  9  2
2:  1  3  8  5
3:  1  8  1  6
4:  2 10  4 NA
5:  2  2  2 NA
6:  3  5 NA NA
7:  4  1  8 NA
8:  5 10 NA NA

или это, если вы указали заголовки в строках == в качестве имен:

     id H1 H2 H3
1: C5    1  9  2
2: C5    3  8  5
3: C5    8  1  6
4: E5   10  4 NA
5: E5    2  2 NA
6: G6    5 NA NA
7: H4    1  8 NA
8: H6   10 NA NA
1 голос
/ 21 февраля 2020

Вы можете попробовать:

lapply(split(z1, cumsum(grepl("=", z1$V1))), tail, -1)

$`1`
  V1 V2 V3
2 H1 H2 H3
3  1  9  2
4  3  8  5
5  8  1  6

$`2`
  V1 V2 V3
7 H1 H2   
8 10  4   
9  2  2   

$`3`
   V1 V2 V3
11 H1      
12  5      

$`4`
   V1 V2 V3
14 H1 H2   
15  1  8   

$`5`
   V1 V2 V3
17 H1      
18 10      
...