2016-03-09 13 views
8

Ein Haufen Daten von einer hasserfüllten lokalen Regierungsstelle heruntergeladen. Es gibt 77.000 Einträge von Elementen, die in einer einfachen Textdatei genau wie folgt aussehen. Ich brauche diesen Haufen Dung in R als ein Datenrahmen zu importieren:Importieren von schrecklich formatierten Daten in Textdatei zu R

Instrument: 201301240005447 
Recorded: 01/24/2013 
Consideration: $150,125.00 
Document Type: MORTGAGES 
Pages: 17 
Grantor: BYRES, CONNIE R/BYRES, SCOTT 
Grantee: MORTGAGE ELECTRONIC REGISTRATION SYSTEMS INC/QUICKEN LOANS INC 
Legal Description: * St:5495 MCNAMARA LN City:FLINT PrpId:1135532002 CC:11 T:8 R:7 S:35 ext:PT OF NE4 
* 
---------------------------------/--------------------------------- 
Instrument: 201301240005408 
Recorded: 01/24/2013 
Consideration: $65,124.00 
Document Type: MORTGAGES 
Pages: 17 
Grantor: SANNE, BETTY LOU/SANNE, KENNETH D 
Grantee: JPMORGAN CHASE BANK NA 
Legal Description: Sub:WOODCROFT NO 1 Lt:188 St:2213 RADCLIFFE AVE City:FLINT PrpId:4024106003 CC:54 
* 
---------------------------------/--------------------------------- 

Es gibt gemeinsame Zeichenvektoren wie „Instrument“, „Grantor“ und „PrpId“. Wie genau würde ich das in R importieren? Würde das ein Parsing oder Scraping beinhalten?

Unnötig zu sagen, ich habe versucht, diese Datei nach Excel zu importieren, hat aber nicht funktioniert. Ich denke, R würde viel besser funktionieren, muss nur herausfinden, wie. Danke

+0

@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

+0

Ich denke, die ":" s in der rechtlichen Beschreibung würde einige Schwierigkeiten hinzufügen, wenn Sie die 'yaml.load' verwenden. – cgage

+0

Versuchte "read.delim" und bekam wirklich urkomische Ergebnisse ... lol – Elan

Antwort

2

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 
+0

Danke, @bgoldst. Dies ist in der Tat sehr robust und abgerufen eine große Menge an nutzbaren Daten. – Elan

7

Anfänger mit R so bin ich sicher, dass die Leute bessere Wege hinzufügen, aber hier ist eine, die funktioniert, solange die Felder jedes Datensatzes in Anzahl und Reihenfolge festgelegt sind;

# Use gsubfn to get read.pattern 
install.packages('gsubfn')  
library(gsubfn) 

# Read all data rows into 'data'  
data = read.pattern('Test/test.txt', '([^:]*):(.*)', as.is=TRUE, fill=TRUE) 

# Reshape the data to 8 columns  
df = as.data.frame(matrix(data$V2, ncol=8, byrow=TRUE)) 

# Set the column names to reasonable values. 
colnames(df) = data$V1[1:8] 

     Instrument Recorded Consideration Document Type Pages        Grantor               Grantee                 Legal Description 
1 201301240005447 01/24/2013 $150,125.00 MORTGAGES  17  BYRES, CONNIE R/BYRES, SCOTT MORTGAGE ELECTRONIC REGISTRATION SYSTEMS INC/QUICKEN LOANS INC * St:5495 MCNAMARA LN City:FLINT PrpId:1135532002 CC:11 T:8 R:7 S:35 ext:PT OF NE4 
2 201301240005408 01/24/2013 $65,124.00 MORTGAGES  17 SANNE, BETTY LOU/SANNE, KENNETH D           JPMORGAN CHASE BANK NA Sub:WOODCROFT NO 1 Lt:188 St:2213 RADCLIFFE AVE City:FLINT PrpId:4024106003 CC:54 
+0

Ich bin neu in reguläre Ausdrücke und habe Probleme zu begreifen, was genau mit all den Symbolen passiert, z.B. ''([^:] *): (. *)''. Kannst du genau aufschlüsseln, was hier vor sich geht? – cgage

+1

@cgage Die Regex hat 2 "Capture-Gruppen" (umgeben von Klammern), die sich in zwei Spalten, V1 und V2, verwandeln. Die erste Aufnahme '([^:] *)' stimmt mit so vielen Zeichen wie möglich überein, die kein Doppelpunkt sind. '[^:]' entspricht einem beliebigen Zeichen, das kein Doppelpunkt ist, und '*' bedeutet null oder mehr davon. Das wird zum Feldnamen, da alles bis zum ersten Doppelpunkt der Name ist. Der Doppelpunkt zwischen den Gruppen entspricht dem Doppelpunkt, den wir entfernen möchten. Die zweite Erfassungsgruppe '(. *)' Entspricht allem anderen bis zum Ende der Zeile ('.' ist ein beliebiges Zeichen und' * 'bedeutet null oder mehr) –

+1

@cgage [Regex101] (https://regex101.com/ r/dX7jS4/1) gibt auch anständige Erklärungen mit mehr Details. –

2
rl <- readLines(textConnection('Instrument: 201301240005447 
Recorded: 01/24/2013 
Consideration: $150,125.00 
Document Type: MORTGAGES 
Pages: 17 
Grantor: BYRES, CONNIE R/BYRES, SCOTT 
Grantee: MORTGAGE ELECTRONIC REGISTRATION SYSTEMS INC/QUICKEN LOANS INC 
Legal Description: * St:5495 MCNAMARA LN City:FLINT PrpId:1135532002 CC:11 T:8 R:7 S:35 ext:PT OF NE4 
* 
    ---------------------------------/--------------------------------- 
Instrument: 201301240005408 
Recorded: 01/24/2013 
Consideration: $65,124.00 
Document Type: MORTGAGES 
Pages: 17 
Grantor: SANNE, BETTY LOU/SANNE, KENNETH D 
Grantee: JPMORGAN CHASE BANK NA 
Legal Description: Sub:WOODCROFT NO 1 Lt:188 St:2213 RADCLIFFE AVE City:FLINT PrpId:4024106003 CC:54 
* 
    ---------------------------------/---------------------------------')) 

Sie können diese nutzen, um Dinge auswählen, die Sie wollen, definieren eine Hilfsfunktion jedes Feld (ähnlich einer Frage, die ich earlier today beantwortet) zu extrahieren

n <- c('Instrument', 'Recorded', 'Consideration', 'Document Type', 
     'Pages', 'Grantor', 'Grantee', 'Legal Description') 
f <- function(what, string = rl) { 
    gsub(sprintf('%s\\:\\s*([^~]*)|.', what), '\\1', string, perl = TRUE) 
} 

## read in the lines and do some minimal processing 
rl <- gsub('^\\* ', '\n', rl[grepl('^[A-Z*]', rl)]) 
rl <- paste0(rl, collapse = '~') 
rl <- strsplit(rl, '\\n')[[1]] 

data.frame(setNames(lapply(n, f), n)) 

#  Instrument Recorded Consideration Document.Type Pages 
# 1 201301240005447 01/24/2013 $150,125.00 MORTGAGES  17 
# 2 201301240005408 01/24/2013 $65,124.00 MORTGAGES  17 
#        Grantor 
# 1  BYRES, CONNIE R/BYRES, SCOTT 
# 2 SANNE, BETTY LOU/SANNE, KENNETH D 
#               Grantee 
# 1 MORTGAGE ELECTRONIC REGISTRATION SYSTEMS INC/QUICKEN LOANS INC 
# 2           JPMORGAN CHASE BANK NA 
#                 Legal.Description 
# 1 * St:5495 MCNAMARA LN City:FLINT PrpId:1135532002 CC:11 T:8 R:7 S:35 ext:PT OF NE4 
# 2 Sub:WOODCROFT NO 1 Lt:188 St:2213 RADCLIFFE AVE City:FLINT PrpId:4024106003 CC:54 

oder

n <- c('Instrument', 'Recorded', 'Consideration') 
data.frame(setNames(lapply(n, f), n)) 

#  Instrument Recorded Consideration 
# 1 201301240005447 01/24/2013 $150,125.00 
# 2 201301240005408 01/24/2013 $65,124.00 
1

Lösung nur mit {Base} ohne regex.Dies ist nicht sehr elegant:

# read file and parse out values from field names 
q <- readLines('ugly.txt') 
q <- lapply(X = q, FUN = strsplit, split = ': ') 
q <- unlist(q) 
q <- matrix(data = q, ncol = 2, byrow = T) 
COLUMNS <- unique(q[,1]) 
q <- q[,2] 

# move values to rows of a DF and set names for DF 
q <- matrix(data = q, ncol = 9, byrow = T) 
q <- data.frame(q) 
names(q) <- COLUMNS 

# clean up data types 
q$Recorded <- as.Date(q$Recorded) 
q$Consideration <- as.numeric(q$Consideration) 
q$Pages <- as.numeric(q$Pages) 

View(q) 
Verwandte Themen