Lire le fichier texte de largeur fixe

89

J'essaie de charger cet ensemble de données au format laid dans ma session 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

Jusqu'à présent, je peux lire les lignes avec

  x = readLines(path)

Mais le fichier mélange «espace blanc» avec «-» comme séparateurs, et je ne suis pas un expert en regex. J'apprécie toute aide pour transformer cela en une image de données R agréable et propre. Merci!

Fernando
la source
5
Et jetez un œil à read.fwfpour lire lire les données formatées à largeur fixe.
Paul Hiemstra
1
Je pense que c'est une meilleure idée de traiter chaque ligne. Il mélange des caractères '-' avec ''.
Fernando
Alternativement, vous pouvez dire un espace blanc ou - est juste un caractère, alors remplacez d'abord toutes les occurrences multiples d'un espace par un caractère de tabulation, puis divisez toutes les entrées séparées par des tabulations sur - ou espace blanc.
GitaarLAB
Largeur fixe = pas de séparateurs. Cela signifie que le "-" est un signe moins et que les espaces ne sont pas non plus des séparateurs, ils se produisent juste lorsque le nombre ne remplit pas toute la largeur disponible
Eusebio Rufian-Zilbermann

Réponses:

181

Il s'agit d'un fichier de largeur fixe. Utilisez read.fwf()pour le lire:

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

Mettre à jour

Le package readr(publié en avril 2015) offre une alternative simple et rapide.

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

Comparaison de vitesse: readr::read_fwf()était ~ 2x plus rapide que utils::read.fwf ().

Andrie
la source
8
@Andrie comment saviez-vous quelles étaient les largeurs et les sauts?
Koba
12
@Koba: J'ai copié et collé l'une des lignes dans un éditeur de texte qui avait un nombre de colonnes et j'ai compté manuellement les largeurs pour chaque colonne (y compris les espaces blancs si nécessaire). Vous pouvez également dire que vous devez ignorer 4 lignes entières avant d'accéder aux données brutes.
rayryeng
5
La réponse de @ Pavithra ci-dessous avec des largeurs de colonne négatives pour ignorer les espaces blancs indésirables pourrait être mieux adaptée à la réponse acceptée.
Marius Butuc
1
@Andrie Comment avez-vous obtenu les valeurs fwf_widths?
BICube
3
@Ala, je crois readr::fwf_empty, tentera de deviner les largeurs pour vous. Les exemples de readr::read_fwfmontrent l'utilisation de readr::fwf_empty.
Jake Fisher
55

Une autre façon de déterminer les largeurs ...

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
)

Le -1 dans l'argument widths indique qu'il y a une colonne à un caractère qui doit être ignorée, le -5 dans l'argument widths indique qu'il y a une colonne de cinq caractères qui doit être ignorée, de même ...

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

Pavithra Gunasekara
la source
20

Tout d'abord, cette question est directement issue du cours Coursera "Get Data and Clean It" de Leeks. Bien qu'il y ait une autre partie de la question, la partie la plus difficile est la lecture du fichier.

Cela dit, le cours est principalement destiné à l'apprentissage.

Je déteste la procédure de largeur fixe de R. C'est lent et pour un grand nombre de variables, il devient très vite pénible de nier certaines colonnes, etc.

Je pense que c'est plus facile à utiliser readLines(), puis à partir de cette utilisation substr()pour créer vos variables

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
                     )
James Holland
la source
2
Cette approche a fonctionné pour moi. Deux conseils supplémentaires: 1) vous pouvez définir mydata comme étant uniquement les données dont vous avez besoin. Cela pourrait donc être aussi simple que mydata <- data.frame(var4 = substr(x,29,32))si vous n'aviez besoin que de la quatrième colonne de données. En outre, pour les utilisateurs de Windows, Notepad ++ avec le plugin TextFX vous fournira une règle de caractères comptée claire et simple afin que vous puissiez déterminer ce qu'il faut mettre dans les valeurs de début et d'arrêt substr. Notez cependant que la valeur d'arrêt est un de plus que la position du dernier caractère que vous souhaitez conserver.
globalSchmidt
5

Je documente ici la liste des alternatives pour la lecture de fichiers à largeur fixe dans R, ainsi que quelques points de repère pour lesquels est le plus rapide.

Mon approche préférée est de combiner freadavec stringi; elle est compétitive en tant qu'approche la plus rapide et présente l'avantage supplémentaire (IMO) de stocker vos données en tant que 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

Notez que freadsupprime automatiquement les espaces de début et de fin - parfois, cela n'est pas souhaitable, auquel cas défini strip.white = FALSE.


Nous aurions également pu commencer avec un vecteur de largeurs de colonnes wwen faisant:

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)

Et nous aurions pu choisir les colonnes à exclure de manière plus robuste en utilisant des indices négatifs comme:

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

Remplacez ensuite col_ends$beg[ii]par abs(col_ends$beg[ii])et dans la ligne suivante:

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

Enfin, si vous souhaitez que les noms de colonnes soient également lus par programme, vous pouvez nettoyer avec 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 != ""]

(notez que combiner cette étape avec freadnécessiterait la création d'une copie du tableau afin de supprimer la ligne d'en-tête, et serait donc inefficace pour les grands ensembles de données)

MichaelChirico
la source
4

Je ne sais rien à propos de R, mais je peux vous fournir un regex qui correspondra à ces lignes:

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