Ich habe eine sehr generische Parsing-Funktion geschrieben, die mit jedem Muster von Trennlinie und Feld-Wert-Trennzeichen umgehen kann , angegeben als parametrisierte Regexe. Es entfernt optional auch nachfolgende Leerzeichen aus den Feldwerten und übergibt variadic Argumente an den einzelnen Aufruf, der das resultierende data.frame erstellt.
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()
Hier ist, wie es zu benutzen:
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"
Sie können auch die select
Argument angeben genau auswählen, welche Felder Sie extrahieren möchten:
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
Ich habe mich sehr bemüht, es so robust wie möglich zu machen. Es behandelt sorgfältig mögliche redundante führende und nachfolgende Trennlinien und behandelt den Fall inkonsistenter Felder zwischen Abschnitten richtig.
Es ist wert, diesen letzten Punkt zu betonen. Alle anderen angebotenen Lösungen machen äußerst spröde Annahmen über die Eingabedaten, entweder dass es genau 8 Felder pro Abschnitt immer in der gleichen Reihenfolge gibt, oder dass jeder (möglicherweise fest codierte) Feldname in jedem Abschnitt vorkommt. Wenn diese Annahme verletzt wird, werden diese Lösungen nutzlos. Meine Funktion macht keine Annahmen über Feldnummer, Namen oder Konsistenz. Es ruft dynamisch alle Feldnamen ab, die in jedem Abschnitt vorhanden sind, und erstellt geeignete Vektoren von jedem, wobei NA
Elemente generiert werden, in denen das Feld in einem bestimmten Abschnitt nicht vorhanden ist.
Hier sind einige Beispiele:
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, es nicht funktioniert, halten diese Art von Störung zu erhalten: 'Fehler in yaml.load (Paste (Leseleitungen (Eingang), Kollaps = "\ n"), ...): Scanner-Fehler: beim Scannen eines einfachen Schlüssels in Zeile 13, Spalte 1 konnte nicht erwartet ':' in Zeile 14, Spalte 1 ' Vielleicht ist es nicht im YAML-Format. Ich habe keine Ahnung was das ist. – Elan
Ich denke, die ":" s in der rechtlichen Beschreibung würde einige Schwierigkeiten hinzufügen, wenn Sie die 'yaml.load' verwenden. – cgage
Versuchte "read.delim" und bekam wirklich urkomische Ergebnisse ... lol – Elan