Ho scritto una funzione di analisi molto generica in grado di gestire qualsiasi modello di linea di separazione e separatore di valori di campo , specificato come regex parametrizzati. Rimuove facoltativamente anche gli spazi bianchi finali dai valori dei campi e passa argomenti variadici alla singola chiamata data.frame()
che crea il data.frame risultante.
sectionedFieldLinesToFrame <- function(lines,divRE,sepRE,select,rtw=T,...) {
divLineIndexes <- grep(perl=T,divRE,lines);
## remove possible leading and trailing divs, for robustness
if (length(divLineIndexes)>0L && divLineIndexes[1L]==1L) {
leadDivCount <- match(T,c(diff(divLineIndexes)!=1L,T));
lines <- lines[-seq_len(leadDivCount)];
divLineIndexes <- divLineIndexes[-seq_len(leadDivCount)]-leadDivCount;
}; ## end if
if (length(divLineIndexes)>0L && divLineIndexes[length(divLineIndexes)]==length(lines)) {
trailDivCount <- match(T,c(rev(diff(divLineIndexes)!=1L),T));
lines <- lines[-seq(to=length(lines),len=trailDivCount)];
divLineIndexes <- divLineIndexes[-seq(to=length(divLineIndexes),len=trailDivCount)];
}; ## end if
## get fields to extract
if (missing(select)) {
allFieldLineIndexes <- grep(perl=T,sepRE,lines);
fields <- unique(sub(perl=T,paste0(sepRE,'.*'),'',lines[allFieldLineIndexes]));
} else {
fields <- select;
}; ## end if
## extract each field vector and build the data.frame
do.call(data.frame,c(setNames(lapply(fields,function(field) {
fieldLineIndexes <- grep(perl=T,paste0('^\\Q',field,'\\E',sepRE),lines);
sectionIndexes <- findInterval(fieldLineIndexes,divLineIndexes); ## 0-based
values <- sub(perl=T,paste0('^.*?',sepRE),'',lines[fieldLineIndexes]);
if (rtw) values <- sub(perl=T,'\\s+$','',values);
values[match(seq(0L,length(divLineIndexes)),sectionIndexes)];
}),fields),...));
}; ## end sectionedFieldLinesToFrame()
Ecco come usarlo:
fileName <- 'data.txt';
divRE <- '^-+/-+$';
sepRE <- ':\\s*';
df <- sectionedFieldLinesToFrame(readLines(fileName),divRE,sepRE,stringsAsFactors=F);
str(df);
## 'data.frame': 2 obs. of 8 variables:
## $ Instrument : chr "201301240005447" "201301240005408"
## $ Recorded : chr "01/24/2013" "01/24/2013"
## $ Consideration : chr "$150,125.00" "$65,124.00"
## $ Document.Type : chr "MORTGAGES" "MORTGAGES"
## $ Pages : chr "17" "17"
## $ Grantor : chr "BYRES, CONNIE R/BYRES, SCOTT" "SANNE, BETTY LOU/SANNE, KENNETH D"
## $ Grantee : chr "MORTGAGE ELECTRONIC REGISTRATION SYSTEMS INC/QUICKEN LOANS INC" "JPMORGAN CHASE BANK NA"
## $ Legal.Description: chr "* St:5495 MCNAMARA LN City:FLINT PrpId:1135532002 CC:11 T:8 R:7 S:35 ext:PT OF NE4" "Sub:WOODCROFT NO 1 Lt:188 St:2213 RADCLIFFE AVE City:FLINT PrpId:4024106003 CC:54"
è anche possibile specificare l'argomento select
per selezionare esattamente quali campi si desidera estrarre:
select <- c('Instrument','Pages','Grantor');
df <- sectionedFieldLinesToFrame(readLines(fileName),divRE,sepRE,select,stringsAsFactors=F);
df;
## Instrument Pages Grantor
## 1 201301240005447 17 BYRES, CONNIE R/BYRES, SCOTT
## 2 201301240005408 17 SANNE, BETTY LOU/SANNE, KENNETH D
Ho provato molto duramente a renderlo il più robusto possibile. Tratta con cura le possibili linee di divisione ridondanti di inizio e fine e gestisce correttamente il caso di campi incoerenti tra le sezioni.
Vale la pena sottolineare questo ultimo punto. Tutte le altre soluzioni offerte offrono presupposti estremamente fragili sui dati di input, o che ci sono esattamente 8 campi per sezione sempre nello stesso ordine, o che ogni nome di campo (eventualmente codificato) si verifica in ogni sezione. Se tale ipotesi viene violata, tali soluzioni diventano inutili. La mia funzione non formula alcuna ipotesi sul numero del campo, i nomi o la coerenza. Richiama in modo dinamico tutti i nomi dei campi presenti in qualsiasi sezione e crea i vettori corretti di ciascuno, generando gli elementi NA
in cui il campo non è presente in una determinata sezione.
ecco alcuni esempi:
sectionedFieldLinesToFrame(character(),'^-$',':');
## data frame with 0 columns and 0 rows
sectionedFieldLinesToFrame(rep('-',2L),'^-$',':');
## data frame with 0 columns and 0 rows
sectionedFieldLinesToFrame(c('A:a','-'),'^-$',':');
## A
## 1 a
sectionedFieldLinesToFrame(c('A:a','-','-'),'^-$',':');
## A
## 1 a
sectionedFieldLinesToFrame(c('A:a','-','B:b','-'),'^-$',':');
## A B
## 1 a <NA>
## 2 <NA> b
sectionedFieldLinesToFrame(c('A:a','B:b','-','B:c','-'),'^-$',':');
## A B
## 1 a b
## 2 <NA> c
sectionedFieldLinesToFrame(c('A:a','B:b','-','B:c','-','A:d'),'^-$',':');
## A B
## 1 a b
## 2 <NA> c
## 3 d <NA>
sectionedFieldLinesToFrame(c('-','-','A:a','B:b','-','B:c','-','A:d','C:e','-'),'^-$',':');
## A B C
## 1 a b <NA>
## 2 <NA> c <NA>
## 3 d <NA> e
sectionedFieldLinesToFrame(c('-','A:a','B:b','-','-','B:c','-','A:d','C:e','-'),'^-$',':');
## A B C
## 1 a b <NA>
## 2 <NA> <NA> <NA>
## 3 <NA> c <NA>
## 4 d <NA> e
@rawr, non ha funzionato, continuo a ricevere questo tipo di errore: 'Problemi con yaml.load (pasta (readlines (ingresso), collasso = "\ n"), ...): Errore scanner: durante la scansione di un tasto semplice alla riga 13, la colonna 1non può trovare previsto ':' alla riga 14, colonna 1' Forse non è nel formato YAML. Non ho idea di cosa sia. – Elan
Penso che i ":" nella descrizione legale aggiungano qualche difficoltà se si usa il 'yaml.load'. – cgage
Provato 'read.delim' e ottenuto risultati davvero esilaranti ... lol – Elan