Frage Lesen Sie die Textdatei mit fester Breite


Ich versuche, diesen hässlich formatierten Datensatz in meine R-Sitzung zu laden: http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for

Weekly SST data starts week centered on 3Jan1990

Nino1+2      Nino3        Nino34        Nino4
Week          SST SSTA     SST SSTA     SST SSTA     SST SSTA 
03JAN1990     23.4-0.4     25.1-0.3     26.6 0.0     28.6 0.3 
10JAN1990     23.4-0.8     25.2-0.3     26.6 0.1     28.6 0.3 
17JAN1990     24.2-0.3     25.3-0.3     26.5-0.1     28.6 0.3

Bis jetzt kann ich die Zeilen mit lesen

  x = readLines(path)

Aber die Datei mischt 'Leerraum' mit '-' als Trennzeichen, und ich bin kein Regex-Experte. Ich schätze jede Hilfe bei der Verwandlung in einen schönen und sauberen R-Datenrahmen. Vielen Dank!


75
2018-01-17 16:33


Ursprung


Antworten:


Dies ist eine Datei mit fester Breite. Benutzen read.fwf() um es zu lesen:

x <- read.fwf(
  file=url("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for"),
  skip=4,
  widths=c(12, 7, 4, 9, 4, 9, 4, 9, 4))

head(x)

            V1   V2   V3   V4   V5   V6   V7   V8  V9
1  03JAN1990   23.4 -0.4 25.1 -0.3 26.6  0.0 28.6 0.3
2  10JAN1990   23.4 -0.8 25.2 -0.3 26.6  0.1 28.6 0.3
3  17JAN1990   24.2 -0.3 25.3 -0.3 26.5 -0.1 28.6 0.3
4  24JAN1990   24.4 -0.5 25.5 -0.4 26.5 -0.1 28.4 0.2
5  31JAN1990   25.1 -0.2 25.8 -0.2 26.7  0.1 28.4 0.2
6  07FEB1990   25.8  0.2 26.1 -0.1 26.8  0.1 28.4 0.3

Aktualisieren

Das Paket readr (veröffentlicht im April 2015) bietet eine einfache und schnelle Alternative.

library(readr)

x <- read_fwf(
  file="http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for",   
  skip=4,
  fwf_widths(c(12, 7, 4, 9, 4, 9, 4, 9, 4)))

Geschwindigkeitsvergleich: readr::read_fwf() war ~ 2x schneller als utils::read.fwf ().


163
2018-01-17 16:45



Eine andere Möglichkeit, die Breite zu bestimmen ...

df <- read.fwf(
  file=url("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for"),
  widths=c(-1, 9, -5, 4, 4, -5, 4, 4, -5, 4, 4, -5, 4, 4),
  skip=4
)

Das Argument -1 im Argument wides gibt an, dass es eine Ein-Zeichen-Spalte gibt, die ignoriert werden sollte, das Argument -5 im Argument wides gibt an, dass es eine fünfstellige Spalte gibt, die ebenfalls ignoriert werden sollte.

Ref: https://www.inkling.com/read/r-cookbook-paul-teetor-1st/chapter-4/recipe-4-6


50
2018-06-12 21:42



Zuallererst kommt diese Frage direkt von einem Coursera "Get Data and Clean It" -Kurs von Leeks. Während es einen anderen Teil der Frage gibt, ist der schwierige Teil das Lesen der Datei.

Das heißt, der Kurs ist hauptsächlich zum Lernen gedacht.

Ich hasse R's feste Breite Prozedur. Es ist langsam und für eine große Anzahl von Variablen wird es sehr schnell zu einem Schmerz, bestimmte Spalten usw. zu negieren.

Ich denke, es ist einfacher zu benutzen readLines() und dann von diesem Gebrauch substr() um deine Variablen zu machen

x <- readLines(con=url("http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for"))

# Skip 4 lines
x <- x[-(1:4)]

mydata <- data.frame(var1 = substr(x, 1, 10),
                     var2 = substr(x, 16, 19),
                     var3 = substr(x, 20, 23),
                     var4 = substr(x, 29, 32)  # and so on and so on
                     )

14
2017-10-17 18:35



Sie können jetzt die read_fwf() Funktion in Hadley Wickham's readr Paket.

Eine enorme Leistungsverbesserung ist im Vergleich zur Basis zu erwarten read.fwf().


10
2018-04-10 10:20



Ich dokumentiere Hier die Liste der Alternativen zum Lesen von Dateien mit fester Breite in R sowie einige Benchmarks für die schnellste.

Mein bevorzugter Ansatz ist zu kombinieren fread mit stringi; es ist wettbewerbsfähig als der schnellste Ansatz und hat den zusätzlichen Vorteil (IMO) der Speicherung Ihrer Daten als data.table:

library(data.table)
library(stringi)

col_ends <- 
  list(beg = c(1, 10, 15, 19, 23, 28, 32, 36,
               41, 45, 49, 54, 58),
       end = c(9, 14, 18, 22, 27, 31, 35,
               40, 44, 48, 53, 57, 61))

data = fread(
  "http://www.cpc.ncep.noaa.gov/data/indices/wksst8110.for", 
  header = FALSE, skip = 4L, sep = NULL
  )[, lapply(1:(length(col_ends$beg)),
             function(ii) 
               stri_sub(V1, col_ends$beg[ii], col_ends$end[ii]))
    ][ , paste0("V", c(2, 5, 8, 11)) := NULL]
#              V1   V3   V4   V6   V7   V9  V10  V12  V13
#    1: 03JAN1990 23.4 -0.4 25.1 -0.3 26.6  0.0 28.6  0.3
#    2: 10JAN1990 23.4 -0.8 25.2 -0.3 26.6  0.1 28.6  0.3
#    3: 17JAN1990 24.2 -0.3 25.3 -0.3 26.5 -0.1 28.6  0.3
#    4: 24JAN1990 24.4 -0.5 25.5 -0.4 26.5 -0.1 28.4  0.2
#    5: 31JAN1990 25.1 -0.2 25.8 -0.2 26.7  0.1 28.4  0.2
#   ---                                                  
# 1365: 24FEB2016 27.1  0.9 28.4  1.8 29.0  2.1 29.5  1.4
# 1366: 02MAR2016 27.3  1.0 28.6  1.8 28.9  1.9 29.5  1.4
# 1367: 09MAR2016 27.7  1.2 28.6  1.6 28.9  1.8 29.6  1.5
# 1368: 16MAR2016 27.5  1.0 28.8  1.7 28.9  1.7 29.6  1.4
# 1369: 23MAR2016 27.2  0.9 28.6  1.4 28.8  1.5 29.5  1.2

Beachten Sie, dass fread löscht automatisch führende und nachfolgende Leerzeichen - manchmal ist dies unerwünscht, in diesem Fall gesetzt strip.white = FALSE.


Wir hätten auch mit einem Vektor von Spaltenbreiten beginnen können ww indem du tust:

ww <- c(9, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4)
nd <- cumsum(ww)

col_ends <-
  list(beg = c(1, nd[-length(nd)]+1L),
       end = nd)

Und wir hätten auswählen können, welche Spalten robuster durch negative Indizes wie:

col_ends <- 
  list(beg = c(1, -10, 15, 19, -23, 28, 32, -36,
               41, 45, -49, 54, 58),
       end = c(9, 14, 18, 22, 27, 31, 35,
               40, 44, 48, 53, 57, 61))

Dann ersetzen col_ends$beg[ii] mit abs(col_ends$beg[ii]) und in der nächsten Zeile:

paste0("V", which(col_ends$beg < 0))

Wenn Sie möchten, dass die Spaltennamen auch programmatisch gelesen werden, können Sie mit bereinigen readLines:

cols <-
  gsub("\\s", "", 
       sapply(1:(length(col_ends$beg)),
              function(ii) 
                stri_sub(readLines(URL, n = 4L)[4L], 
                         col_ends$beg[ii]+1L,
                         col_ends$end[ii]+1L)))

cols <- cols[cols != ""]

(Beachten Sie, dass die Kombination dieses Schrittes mit fread würde erfordern, eine Kopie der Tabelle zu erstellen, um die Kopfzeile zu entfernen, und wäre somit für große Datensätze ineffizient)


5
2018-03-31 14:34



Ich weiß nichts über R, aber ich kann Ihnen eine Regex liefern, die solchen Zeilen entspricht:

\s[0-9]{2}[A-Z]{3}[0-9]{4}(\s{5}[0-9]+\.[0-9]+[ -][0-9]+\.[0-9]+){4}

4
2018-01-17 16:43



Der direkteste Weg ist die Verwendung von read.fwf, wie oben erwähnt.

Nun, wenn das endgültige Ziel ist, es in R zu bekommen, könnten Sie es immer in Excel für einen Anfang lesen, verwenden Sie die "text to cloumns" -Funktion, um die Spalten visuell auszuschneiden, und speichern Sie dann die endgültige Datei als CSV. Danach lesen Sie die CSV in R.


0
2017-12-09 05:15