Legge file di testo a larghezza fissa


93

Sto cercando di caricare questo set di dati in formato brutto nella mia sessione R: 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

Finora posso leggere le righe con

  x = readLines(path)

Ma il file mescola "spazio bianco" con "-" come separatori e non sono un esperto di regex. Apprezzo qualsiasi aiuto per trasformarlo in un frame di dati R bello e pulito. Grazie!


5
E dai un'occhiata a read.fwfleggere i dati formattati a larghezza fissa.
Paul Hiemstra

1
Penso che sia un'idea migliore elaborare ogni riga. Mescola i caratteri "-" con ".
Fernando

In alternativa, potresti dire spazio bianco o - è solo un carattere, quindi prima sostituisci tutte le occorrenze multiple di uno spazio con un carattere di tabulazione, quindi dividi tutte le voci separate da tabulazione su - o spazio bianco.
GitaarLAB

Larghezza fissa = nessun separatore. Ciò significa che il "-" è un segno meno e nemmeno gli spazi sono separatori, si verificano solo quando il numero non riempie l'intera larghezza disponibile
Eusebio Rufian-Zilbermann

Risposte:


184

Questo è un file a larghezza fissa. Usa read.fwf()per leggerlo:

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

Aggiornare

Il pacchetto readr(rilasciato ad aprile 2015) fornisce un'alternativa semplice e veloce.

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)))

Confronto di velocità: readr::read_fwf()era ~ 2x più veloce di utils::read.fwf ().


10
@Andrie come facevi a sapere quali erano le larghezze e i salti?
Koba

12
@ Koba: ho copiato e incollato una delle righe in un editor di testo che aveva un conteggio di colonne e ho contato manualmente le larghezze per ogni colonna (inclusi gli spazi quando richiesto). Inoltre puoi dire che devi saltare 4 righe intere prima di arrivare ai dati grezzi.
rayryeng

5
La risposta di @ Pavithra di seguito con larghezze di colonna negative per saltare gli spazi vuoti indesiderati potrebbe essere più adatta per la risposta accettata.
Marius Butuc

1
@Andrie Come hai ottenuto i valori fwf_widths?
BICube

3
@Ala credo readr::fwf_emptyche tenterà di indovinare le larghezze per te. Gli esempi per readr::read_fwfmostrano l'utilizzo di readr::fwf_empty.
Jake Fisher

55

Un altro modo per determinare le larghezze ...

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
)

Il -1 nell'argomento widths dice che c'è una colonna di un carattere che dovrebbe essere ignorata, il -5 nell'argomento widths dice che c'è una colonna di cinque caratteri che dovrebbe essere ignorata, allo stesso modo ...

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


20

Prima di tutto, questa domanda proviene direttamente da un corso Coursera "Ottieni dati e puliscilo" di Leeks. Mentre c'è un'altra parte della domanda, la parte difficile è leggere il file.

Detto questo, il corso è principalmente destinato all'apprendimento.

Odio la procedura a larghezza fissa di R. È lento e per un gran numero di variabili, diventa molto rapidamente un problema negare determinate colonne, ecc.

Penso che sia più facile da usare readLines()e quindi da quell'uso substr()per creare le tue variabili

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
                     )

2
Questo approccio ha funzionato per me. Due suggerimenti aggiuntivi: 1) puoi definire i miei dati in modo che siano solo i dati di cui hai bisogno. Quindi potrebbe essere semplice come mydata <- data.frame(var4 = substr(x,29,32))se ti servisse solo la quarta colonna di dati. Inoltre, per gli utenti Windows, Notepad ++ con il plug-in TextFX ti fornirà un righello di caratteri semplice e contato in modo da poter capire cosa inserire nei valori di inizio e fine substr. Si noti, tuttavia, che il valore di arresto è uno in più rispetto alla posizione dell'ultimo carattere che si desidera conservare.
globalSchmidt


5

Documento qui l'elenco delle alternative per leggere file a larghezza fissa in R, oltre a fornire alcuni benchmark per i quali è il più veloce.

Il mio approccio preferito è combinare freadcon stringi; è competitivo come l'approccio più veloce e ha il vantaggio aggiuntivo (IMO) di archiviare i dati come 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

Nota che freadrimuove automaticamente gli spazi bianchi iniziali e finali - a volte, questo non è desiderabile, nel qual caso viene impostato strip.white = FALSE.


Avremmo anche potuto iniziare con un vettore di larghezze di colonna wwfacendo:

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)

E avremmo potuto scegliere quali colonne escludere in modo più efficace utilizzando indici negativi come:

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))

Quindi sostituisci col_ends$beg[ii]con abs(col_ends$beg[ii])e nella riga successiva:

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

Infine, se vuoi che i nomi delle colonne vengano letti anche in modo programmatico, puoi ripulire con 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 != ""]

(si noti che la combinazione di questo passaggio con freadrichiederebbe la creazione di una copia della tabella per rimuovere la riga di intestazione e sarebbe quindi inefficiente per set di dati di grandi dimensioni)


4

Non so nulla di R, ma posso fornirti una regex che corrisponderà a tali linee:

\s[0-9]{2}[A-Z]{3}[0-9]{4}(\s{5}[0-9]+\.[0-9]+[ -][0-9]+\.[0-9]+){4}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.