Baca file teks lebar tetap

90

Saya mencoba memuat kumpulan data berformat jelek ini ke sesi R saya: 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

Sejauh ini, saya bisa membaca baris dengan

  x = readLines(path)

Tetapi file tersebut mencampur 'ruang putih' dengan '-' sebagai pemisah, dan saya bukan ahli regex. Saya menghargai bantuan apa pun untuk mengubahnya menjadi bingkai data R yang bagus dan bersih. Terima kasih!

Fernando
sumber
5
Dan lihat read.fwfuntuk membaca data berformat lebar tetap.
Paul Hiemstra
1
Saya pikir itu ide yang lebih baik untuk memproses setiap baris. Ini mencampur karakter '-' dengan ''.
Fernando
Atau, Anda dapat mengatakan spasi putih atau - hanya satu karakter, jadi pertama-tama ganti semua kemunculan beberapa spasi dengan karakter tab, lalu pisahkan semua entri yang dipisahkan tab di - atau spasi.
GitaarLAB
Lebar tetap = tanpa pemisah. Itu berarti "-" adalah tanda minus dan spasi juga bukan pemisah, mereka hanya muncul jika angka tidak memenuhi seluruh lebar yang tersedia
Eusebio Rufian-Zilbermann

Jawaban:

183

Ini adalah file dengan lebar tetap. Gunakan read.fwf()untuk membacanya:

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

Memperbarui

Paket readr(dirilis April 2015) memberikan alternatif yang sederhana dan cepat.

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

Perbandingan kecepatan: readr::read_fwf()~ 2x lebih cepat dari utils::read.fwf ().

Andrie
sumber
8
@Andrie, bagaimana Anda tahu apa itu lebar dan lompatan?
Koba
12
@Koba: Saya menyalin dan menempelkan salah satu baris ke editor teks yang memiliki jumlah kolom dan saya menghitung lebar setiap kolom secara manual (termasuk spasi jika diperlukan). Anda juga dapat mengetahui bahwa Anda harus melewati 4 baris penuh sebelum Anda mendapatkan data mentah.
rayryeng
5
Jawaban @ Pavithra di bawah dengan lebar kolom negatif untuk melewatkan spasi yang tidak diinginkan mungkin lebih cocok untuk jawaban yang diterima.
Marius Butuc
1
@Andrie Bagaimana Anda mendapatkan nilai fwf_widths?
BICube
3
@Ala Saya yakin readr::fwf_emptyakan mencoba menebak lebarnya untuk Anda. Contoh untuk readr::read_fwfmenunjukkan penggunaan untuk readr::fwf_empty.
Jake Fisher
55

Cara lain untuk menentukan lebar ...

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
)

-1 dalam argumen widths mengatakan ada kolom satu karakter yang harus diabaikan, argumen -5 di widths mengatakan ada kolom lima karakter yang harus diabaikan, demikian juga ...

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

Pavithra Gunasekara
sumber
20

Pertama, pertanyaan itu langsung dari kursus Coursera "Dapatkan Data dan Bersihkan" oleh Leeks. Meskipun ada bagian lain dari pertanyaan itu, bagian tersulitnya adalah membaca file.

Konon, kursus tersebut sebagian besar ditujukan untuk pembelajaran.

Saya benci prosedur lebar tetap R. Ini lambat dan untuk sejumlah besar variabel, sangat cepat menjadi merepotkan untuk meniadakan kolom tertentu, dll.

Saya pikir lebih mudah digunakan readLines()dan kemudian dari penggunaan itu substr()untuk membuat variabel Anda

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
sumber
2
Pendekatan ini berhasil untuk saya. Dua tip tambahan: 1) Anda dapat mendefinisikan mydata menjadi hanya data yang Anda butuhkan. Jadi bisa sesederhana mydata <- data.frame(var4 = substr(x,29,32))jika Anda hanya membutuhkan data kolom keempat. Selain itu, untuk pengguna Windows, Notepad ++ dengan plugin TextFX akan memberi Anda penggaris karakter yang dihitung dan sederhana sehingga Anda dapat mengetahui apa yang harus dimasukkan ke dalam nilai awal dan penghentian substr. Perhatikan, bagaimanapun, bahwa nilai stop lebih dari satu posisi dari karakter terakhir yang ingin Anda pertahankan.
globalSchmidt
5

Saya mendokumentasikan di sini daftar alternatif untuk membaca file dengan lebar tetap di R, serta menyediakan beberapa tolok ukur yang tercepat.

Pendekatan pilihan saya adalah menggabungkan freaddengan stringi; itu kompetitif sebagai pendekatan tercepat, dan memiliki manfaat tambahan (IMO) menyimpan data Anda sebagai 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

Perhatikan bahwa freadsecara otomatis menghapus spasi kosong di depan dan di belakang - terkadang, ini tidak diinginkan, dalam hal ini ditetapkan strip.white = FALSE.


Kita juga bisa memulai dengan vektor lebar kolom wwdengan melakukan:

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)

Dan kita bisa memilih kolom mana yang akan dikecualikan lebih kuat dengan menggunakan indeks negatif seperti:

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

Kemudian ganti col_ends$beg[ii]dengan abs(col_ends$beg[ii])dan di baris berikutnya:

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

Terakhir, jika Anda ingin nama kolom juga dibaca secara terprogram, Anda dapat membersihkannya dengan 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 != ""]

(perhatikan bahwa menggabungkan langkah ini dengan freadakan memerlukan pembuatan salinan tabel untuk menghapus baris header, dan karenanya tidak efisien untuk kumpulan data yang besar)

MichaelChirico
sumber
4

Saya tidak tahu apa-apa tentang R, tetapi saya dapat memberi Anda regex yang akan cocok dengan baris seperti itu:

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