Циклический просмотр файлов .dat в R и извлечение только определенных данных в виде столбцов

У меня на локальном диске более 900 папок, и каждая папка имеет один файл с расширением .dat. Я хочу перебрать каждую папку, чтобы получить доступ к файлу в ней, чтобы получить только определенные данные и записать эти данные в новый файл. Каждый файл .dat выглядит примерно так:

Authors:
#    Pallavi Subhraveti
#    Quang Ong
#    Tim Holland
#    Anamika Kothari
#    Ingrid Keseler 
#    Ron Caspi
#    Peter D Karp

# Please see the license agreement regarding the use of and distribution of 
this file.
# The format of this file is defined at http://bioinformatics.ai.sri.com
# Version: 21.5
# File Name: compounds.dat
# Date and time generated: October 24, 2017, 14:52:45

# Attributes:
#    UNIQUE-ID
#    TYPES
#    COMMON-NAME
#    ABBREV-NAME
#    ACCESSION-1
#    ANTICODON
#    ATOM-CHARGES
#    ATOM-ISOTOPES
#    CATALYZES
#    CFG-ICON-COLOR
#    CHEMICAL-FORMULA
#    CITATIONS
#    CODONS
#    COFACTORS-OF
#    MOLECULAR-WEIGHT
#    MONOISOTOPIC-MW

[Data Chunk 1]
UNIQUE-ID - CPD0-1108
TYPES - D-Ribofuranose
COMMON-NAME - β-D-ribofuranose
ATOM-CHARGES - (9 -1)
ATOM-CHARGES - (6 1)
CHEMICAL-FORMULA - (C 5)
CHEMICAL-FORMULA - (H 14)
CHEMICAL-FORMULA - (N 1)
CHEMICAL-FORMULA - (O 6)
CHEMICAL-FORMULA - (P 1)
CREDITS - SRI
CREDITS - kaipa
DBLINKS - (CHEBI "10647" NIL |kothari| 3594051403 NIL NIL)
DBLINKS - (BIGG "37147" NIL |kothari| 3584718837 NIL NIL)
DBLINKS - (PUBCHEM "25200464" NIL |taltman| 3466375284 NIL NIL)
DBLINKS - (LIGAND-CPD "C01233" NIL |keseler| 3342798255 NIL NIL)
INCHI - InChI=1S/C5H14NO6P/c6-1-2-11-13(9,10)12-4-5(8)3-7/h5,7-8H,1-4,6H2,(H,9,10)
MOLECULAR-WEIGHT - 215.142    
MONOISOTOPIC-MW - 216.0636987293    
NON-STANDARD-INCHI - InChI=1S/C5H14NO6P/c6-1-2-11-13(9,10)12-4-5(8)3-7/h5,7-8H,1-4,6H2,(H,9,10)
SMILES - C(OP([O-])(OCC(CO)O)=O)C[N+]
SYNONYMS - sn-Glycero-3-phosphoethanolamine
SYNONYMS - 1-glycerophosphorylethanolamine\
[Data Chunk 2]
//
UNIQUE-ID - URIDINE
TYPES - Pyrimidine
....
....

В каждом файле примерно 18000 строк (смотрим данные в Notepad++). Теперь я хочу создать новый файл и скопировать из данных только определенные столбцы. Я хочу, чтобы в мой вновь созданный файл копировались только эти столбцы, и файл должен выглядеть так:

UNIQUE-ID       TYPES        COMMON-NAME           CHEMICAL-FORMULA  BIGG ID    CHEMSPIDER ID    CAS ID    CHEBI ID    PUBCHEM ID    MOLECULAR-WEIGHT MONOISOTOPIC-MW

CPD0-1108    D-Ribofuranose  β-D-ribofuranose   C5H14N1O6P1      37147       NA                NA      10647       25200464        215.142       216.0636987293

URIDINE      Pyrimidine       ...

Каждый фрагмент данных в каждом файле не обязательно содержит информацию для всех нужных мне столбцов, поэтому я упомянул NA для этих столбцов в выходной таблице, которую я хочу. Хотя это совершенно нормально, если я получаю пустые значения в этих столбцах, так как позже я могу работать с этими пробелами отдельно.

Это каталог, в котором есть данные -

File 1] -> C:\Users\robbie\Desktop\Organism_Data\aact1035194-hmpcyc\compounds.dat
File 2] -> C:\Users\robbie\Desktop\Organism_Data\aaph679198-hmpcyc\compounds.dat
File 3] -> C:\Users\robbie\Desktop\Organism_Data\yreg1002368-hmpcyc\compounds.dat
File 4] -> C:\Users\robbie\Desktop\Organism_Data\tden699187-hmpcyc\compounds.dat
...
...

Я действительно склонялся к использованию функции dir в R, ссылаясь на это сообщение, но я запутался, что вставить в параметр шаблона функции при написании кода, поскольку имена организмов (имена папок) довольно странные и непоследовательные.

Любая помощь для получения требуемого результата приветствуется. Я думал о том, как сделать это в R, но я также готов попробовать это и в python, если я получу хорошие предложения и способы справиться с этим в python. Заранее спасибо за любую помощь!

РЕДАКТИРОВАТЬ: ссылка на данные — данные


person spideypack    schedule 11.07.2018    source источник
comment
Вам не нужно определять шаблон, просто установите параметр recursive TRUE, например. dir("C:/Users/robbie/Desktop/Organism_Data/", recursive = TRUE, full.names = TRUE)   -  person Tamas Nagy    schedule 11.07.2018


Ответы (2)


другой подход, в этом случае он читает только предоставленный вами файл, но может читать несколько файлов.

Я добавляю некоторые промежуточные результаты, чтобы показать, что на самом деле делает код...

library(tidyverse)
library(data.table)
library(zoo)

# create a data.frame with the desired files
filenames <- list.files( path = getwd(), pattern = "*.dat$", recursive = TRUE, full.names = TRUE ) 

# > filenames
#[1] "C:/Users/********/Documents/Git/udls2/test.dat"

#read in the files, using data.table's fread.. here I grep lines starting with UNIQUE-ID or TYPES. create your desired regex-pattern
pattern <- "^UNIQUE-ID|^TYPES"
content.list <- lapply( filenames, function(x) fread( x, sep = "\n", header = FALSE )[grepl( pattern, V1 )] )

# > content.list
# [[1]]
#                        V1
# 1:  UNIQUE-ID - CPD0-1108
# 2: TYPES - D-Ribofuranose
# 3:    UNIQUE-ID - URIDINE
# 4:     TYPES - Pyrimidine

#add all content to a single data.table
dt <- rbindlist( content.list )

# > dt
#                        V1
# 1:  UNIQUE-ID - CPD0-1108
# 2: TYPES - D-Ribofuranose
# 3:    UNIQUE-ID - URIDINE
# 4:     TYPES - Pyrimidine

#split the text in a variable-name and it's content
dt <- dt %>% separate( V1, into = c("var", "content"), sep = " - ")

# > dt
#          var        content
# 1: UNIQUE-ID      CPD0-1108
# 2:     TYPES D-Ribofuranose
# 3: UNIQUE-ID        URIDINE
# 4:     TYPES     Pyrimidine

#add an increasing id for every UNIQUE-ID
dt[var == "UNIQUE-ID", id := seq.int( 1: nrow( dt[var=="UNIQUE-ID", ]))]

# > dt
#          var        content id
# 1: UNIQUE-ID      CPD0-1108  1
# 2:     TYPES D-Ribofuranose NA
# 3: UNIQUE-ID        URIDINE  2
# 4:     TYPES     Pyrimidine NA

#fill down id vor all variables found
dt[, id := na.locf( dt$id )]

# > dt
#          var        content id
# 1: UNIQUE-ID      CPD0-1108  1
# 2:     TYPES D-Ribofuranose  1
# 3: UNIQUE-ID        URIDINE  2
# 4:     TYPES     Pyrimidine  2

#cast
dcast(dt, id ~ var, value.var = "content")

#    id          TYPES UNIQUE-ID
# 1:  1 D-Ribofuranose CPD0-1108
# 2:  2     Pyrimidine   URIDINE
person Wimpel    schedule 11.07.2018
comment
Использование require: yihui.name/en/2014/07/library-vs -require и r-pkgs.had.co.nz /namespace.html#путь-поиска - person r2evans; 11.07.2018
comment
@r2evans каждый день узнаю больше. Я изменю свой ответ - person Wimpel; 11.07.2018

Один файл

Разбейте его на несколько логических действий:

text2chunks <- function(txt) {
  chunks <- split(txt, cumsum(grepl("^\\[Data Chunk.*\\]$", txt)))
  Filter(function(a) grepl("^\\[Data Chunk.*\\]$", a[1]), chunks)
}
chunk2dataframe <- function(vec, hdrs = NULL, sep = " - ") {
  s <- stringi::stri_split(vec, fixed=sep, n=2L)
  s <- Filter(function(a) length(a) == 2L, s)
  df <- as.data.frame(setNames(lapply(s, `[[`, 2), sapply(s, `[[`, 1)),
                      stringsAsFactors=FALSE)
  if (! is.null(hdrs)) df <- df[ names(df) %in% make.names(hdrs) ]
  df
}

hdrs — необязательный вектор имен столбцов, которые вы хотите сохранить; если не указано (или NULL), то все пары ключ/значение возвращаются в виде столбцов.

hdrs <- c("UNIQUE-ID", "TYPES", "COMMON-NAME")

Используя данные (ниже), у меня есть lines, который является вектором character из одного файла:

head(lines)
# [1] "Authors:"                                                                              
# [2] "#    Pallavi Subhraveti"                                                               
# [3] "#    Quang Ong"                                                                        
# [4] "# Please see the license agreement regarding the use of and distribution of this file."
# [5] "# The format of this file is defined at http://bioinformatics.ai.sri.com"              
# [6] "# Version: 21.5"                                                                       
str(text2chunks(lines))
# List of 2
#  $ 1: chr [1:5] "[Data Chunk 1]" "UNIQUE-ID - CPD0-1108" "TYPES - D-Ribofuranose" "COMMON-NAME - &beta;-D-ribofuranose" ...
#  $ 2: chr [1:6] "[Data Chunk 2]" "// something out of place here?" "UNIQUE-ID - URIDINE" "TYPES - Pyrimidine" ...
str(lapply(text2chunks(lines), chunk2dataframe, hdrs=hdrs))
# List of 2
#  $ 1:'data.frame':    1 obs. of  3 variables:
#   ..$ UNIQUE.ID  : chr "CPD0-1108"
#   ..$ TYPES      : chr "D-Ribofuranose"
#   ..$ COMMON.NAME: chr "&beta;-D-ribofuranose"
#  $ 2:'data.frame':    1 obs. of  3 variables:
#   ..$ UNIQUE.ID  : chr "URIDINE"
#   ..$ TYPES      : chr "Pyrimidine"
#   ..$ COMMON.NAME: chr "&beta;-D-ribofuranose or something"

И конечный продукт:

dplyr::bind_rows(lapply(text2chunks(lines), chunk2dataframe, hdrs=hdrs))
#   UNIQUE.ID          TYPES                        COMMON.NAME
# 1 CPD0-1108 D-Ribofuranose              &beta;-D-ribofuranose
# 2   URIDINE     Pyrimidine &beta;-D-ribofuranose or something

Поскольку вы хотите повторить это во многих функциях, имеет смысл создать для этого вспомогательную функцию:

text2dataframe <- function(txt) {
  dplyr::bind_rows(lapply(text2chunks(txt), chunk2dataframe, hdrs=hdrs))
}

Много файлов

Не проверено, но должно работать:

files <- list.files(path="C:/Users/robbie/Desktop/Organism_Data/",
                    pattern="compounds.dat", recursive=TRUE, full.names=TRUE)
alldata <- lapply(files, readLines)
allframes <- lapply(alldata, text2dataframe)
oneframe <- dplyr::bind_rows(allframes)

Примечания:

  • Я использую stringi::stri_split вместо strsplit просто для удобства аргумента n=; сделать то же самое в базе R не сложно с парой дополнительных строк кода.
  • Я использую dplyr::bind_rows, потому что он очень хорошо работает с отсутствующими столбцами и другим порядком; базу rbind.data.frame можно использовать с некоторыми дополнительными усилиями/осторожностью.
  • data.frame-изменение имеет тенденцию немного смещать названия столбцов, просто имейте в виду.

Данные:

# lines <- readLines("some_filename.dat")
fulltext <- 'Authors:
#    Pallavi Subhraveti
#    Quang Ong
# Please see the license agreement regarding the use of and distribution of this file.
# The format of this file is defined at http://bioinformatics.ai.sri.com
# Version: 21.5
# File Name: compounds.dat
# Date and time generated: October 24, 2017, 14:52:45
# Attributes:
#    UNIQUE-ID
#    TYPES
[Data Chunk 1]
UNIQUE-ID - CPD0-1108
TYPES - D-Ribofuranose
COMMON-NAME - &beta;-D-ribofuranose
DO-NOT-CARE - 42
[Data Chunk 2]
// something out of place here?
UNIQUE-ID - URIDINE
TYPES - Pyrimidine
COMMON-NAME - &beta;-D-ribofuranose or something
DO-NOT-CARE - 43
'
lines <- strsplit(fulltext, '[\r\n]+')[[1]]
person r2evans    schedule 11.07.2018
comment
Спасибо за Ваш ответ. Я все еще ищу желаемый результат. Извините за путаницу, но в моих данных нет [Data Chunk 1], [Data Chunk 2] и так далее. Я отредактировал эту часть, чтобы дать членам сообщества четкое представление. Шаблон в данных находится перед каждым UNIQUE-ID (одна строка перед UNIQUE-ID) // этих двух символов. Можете ли вы помочь мне изменить код для приведенного выше шаблона. Большое спасибо заранее! - person spideypack; 12.07.2018
comment
Я подозреваю, что было бы тривиально адаптироваться к тому, как на самом деле выглядят ваши данные. Возможно, я смогу помочь, если вы предоставите точные и репрезентативные данные выборки. (Иначе GIGO.) - person r2evans; 12.07.2018
comment
Я обновил свой вопрос со ссылкой на данные. Пожалуйста помоги! - person spideypack; 13.07.2018