2011-01-07 1 views
11

Immaginate di avere un file di .txt la seguente struttura:Un algoritmo per il testo di filtraggio dei file

>>> header 
>>> header 
>>> header 
K L M 
200 0.1 1 
201 0.8 1 
202 0.01 3 
... 
800 0.4 2 
>>> end of file 
50 0.1 1 
75 0.78 5 
... 

vorrei leggere tutti i dati ad eccezione di linee indicate con >>> e le linee sotto la linea >>> end of file. Finora ho risolto questo problema utilizzando read.table(comment.char = ">", skip = x, nrow = y) (x e correntemente corretti). Questo legge i dati tra l'intestazione e >>> end of file.

Tuttavia, mi piacerebbe rendere la mia funzione un po 'più plastica per quanto riguarda il numero di righe. I dati possono avere valori superiori a 800 e di conseguenza più righe.

Io potrei scan o readLines il file e vedere quale riga corrisponde allo >>> end of file e calcolare il numero di righe da leggere. Quale approccio useresti?

+0

Fornire alcuni dati fittizi. =) – aL3xa

+0

@ aL3xa: lo snippet è già mostrato insufficiente? –

risposta

11

Ecco un modo per farlo:

Lines <- readLines("foo.txt") 
markers <- grepl(">", Lines) 
want <- rle(markers)$lengths[1:2] 
want <- seq.int(want[1] + 1, sum(want), by = 1) 
read.table(textConnection(Lines[want]), sep = " ", header = TRUE) 

che dà:

> read.table(textConnection(Lines[want]), sep = " ", header = TRUE) 
    K L M 
1 200 0.10 1 
2 201 0.80 1 
3 202 0.01 3 
4 800 0.40 2 

Sui dati snippet da Lei forniti (nel file di foo.txt, e dopo aver rimosso le linee ...).

+1

+1, è bello conoscere 'rle', che non ho mai usato prima. Mi chiedo però se c'è un modo per modificare la definizione di 'read.table' (e/o' scan' e/o 'readLines'), aggiungendo un argomento opzionale' EOF' in modo che salvi quando incontra il Stringa 'EOF'. In questo modo potremmo farlo in una sola passata anziché in 2. –

+0

L'argomento EOF sarebbe una bella aggiunta. –

+0

Speravo ci fosse un modo per aggiungere un argomento EOF facoltativo alla definizione sorgente per 'scan', ma chiama' .Internal (scan ...) ', quindi l'unico modo è quello di cambiare l'interno (C?) codice per scansione ... –

11

Qui ci sono un paio di modi.

1) readLine legge nelle linee del file in L e imposta skip al numero di linee per saltare all'inizio e end.of.file al numero di riga della marcatura fine dei dati di riga. Il comando read.table utilizza quindi queste due variabili per rileggere i dati.

File <- "foo.txt" 

L <- readLines(File) 
skip <- grep("^.{0,2}[^>]", L)[1] - 1 
end.of.file <- grep("^>>> end of file", L) 

read.table(File, header = TRUE, skip = skip, nrow = end.of.file - skip - 2) 

Una variante potrebbe essere quella di utilizzare textConnection al posto del File nella linea read.table:

read.table(textConnection(L), header = TRUE, 
    skip = skip, nrow = end.of.file - skip - 2) 

2) Un'altra possibilità è quella di utilizzare sed o awk/gawk. Considera questo programma gawk a una riga. Il programma si chiude se vede la linea che segna la fine dei dati; altrimenti, salta la riga corrente se quella linea inizia con >>> e se nessuno di questi accade, stampa la linea. Siamo in grado di convogliare foo.txt attraverso il programma gawk e leggerlo usando read.table.

cat("/^>>> end of file/ { exit }; /^>>>/ { next }; 1\n", file = "foo.awk") 
read.table(pipe('gawk -f foo.awk foo.txt'), header = TRUE) 

Una variante di questo è che potremmo omettere la porzione /^>>>/ {next}; del programma gawk, che salta le linee >>> all'inizio, e utilizzare comment = ">" in the read.table` chiamare invece.

+0

+1 un bel paio di risposte Gabor, specialmente l'awk. –

+0

La soluzione awk/gawk sarebbe molto utile se non si poteva prevedere la struttura del file in anticipo. –