Đọc tệp văn bản có chiều rộng cố định


90

Tôi đang cố gắng tải tập dữ liệu có định dạng xấu xí này vào phiên R của mình: 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

Cho đến nay, tôi có thể đọc các dòng với

  x = readLines(path)

Nhưng tệp trộn 'khoảng trắng' với '-' làm dấu phân cách và tôi không phải là chuyên gia regex. Tôi đánh giá cao bất kỳ sự giúp đỡ nào trong việc biến điều này thành một khung dữ liệu R đẹp và sạch sẽ. cảm ơn!


5
Và hãy xem read.fwfđể đọc dữ liệu được định dạng theo chiều rộng cố định.
Paul Hiemstra

1
Tôi nghĩ tốt hơn là nên xử lý từng hàng. Nó trộn các ký tự '-' với ''.
Fernando

Ngoài ra, bạn có thể nói khoảng trắng hoặc - chỉ là một ký tự, vì vậy trước tiên hãy thay thế tất cả nhiều lần xuất hiện của một khoảng trắng bằng một ký tự tab, sau đó chia tất cả mục nhập được phân tách bằng tab trên - hoặc khoảng trắng.
GitaarLAB

Chiều rộng cố định = không có dải phân cách. Điều đó có nghĩa là "-" là một dấu trừ và các khoảng trắng cũng không phải là dấu phân cách, chúng chỉ xảy ra khi số không lấp đầy toàn bộ chiều rộng có sẵn
Eusebio Rufian-Zilbermann 15/02/15

Câu trả lời:


183

Đây là tệp có chiều rộng cố định. Sử dụng read.fwf()để đọc nó:

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

Cập nhật

Gói readr(phát hành tháng 4 năm 2015) cung cấp một giải pháp thay thế đơn giản và nhanh chóng.

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

So sánh tốc độ: readr::read_fwf()nhanh hơn ~ 2 lần utils::read.fwf ().


8
@Andrie làm thế nào bạn biết chiều rộng và bỏ qua là gì?
Koba

12
@Koba: Tôi đã sao chép và dán một trong các dòng vào trình soạn thảo văn bản có số cột và tôi đã đếm thủ công độ rộng cho mỗi cột (bao gồm cả khoảng trắng khi được yêu cầu). Ngoài ra, bạn có thể nói rằng bạn cần bỏ qua 4 dòng toàn bộ trước khi đến dữ liệu thô.
rayryeng

5
Câu trả lời của @ Pavithra bên dưới với độ rộng cột phủ định để bỏ qua khoảng trắng không mong muốn có thể phù hợp hơn cho câu trả lời được chấp nhận.
Marius Butuc,

1
@Andrie Làm cách nào để bạn nhận được các giá trị fwf_widths?
BICube

3
@Ala Tôi tin rằng readr::fwf_emptysẽ cố gắng đoán chiều rộng cho bạn. Các ví dụ cho readr::read_fwfhiển thị cách sử dụng cho readr::fwf_empty.
Jake Fisher

55

Một cách khác để xác định chiều rộng ...

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 trong đối số width cho biết có cột một ký tự cần được bỏ qua, -5 trong đối số width cho biết có cột năm ký tự cần được bỏ qua, tương tự ...

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


20

Trước hết, câu hỏi đó là trực tiếp từ khóa học Coursera "Lấy dữ liệu và làm sạch nó" của Leeks. Trong khi có một phần khác của câu hỏi, phần khó khăn là đọc tệp.

Điều đó nói rằng, khóa học chủ yếu là nhằm mục đích học tập.

Tôi ghét thủ tục chiều rộng cố định của R. Nó chậm và đối với một số lượng lớn các biến, việc phủ định một số cột nhất định trở nên khó khăn, v.v.

Tôi nghĩ nó dễ sử dụng hơn readLines()và từ đó sử dụng substr()để tạo các biến của bạn

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
Cách tiếp cận này đã làm việc cho tôi. Hai mẹo bổ sung: 1) bạn có thể xác định mydata chỉ là dữ liệu bạn cần. Vì vậy, nó có thể đơn giản như mydata <- data.frame(var4 = substr(x,29,32))thể bạn chỉ cần cột dữ liệu thứ tư. Ngoài ra, đối với người dùng Windows, Notepad ++ với plugin TextFX sẽ cung cấp cho bạn thước đo ký tự được đếm đơn giản và đơn giản để bạn có thể tìm ra những gì cần đặt vào giá trị bắt đầu và dừng substr. Tuy nhiên, lưu ý rằng giá trị dừng nhiều hơn một so với vị trí của ký tự cuối cùng mà bạn muốn giữ nguyên.
globalSchmidt


5

Tôi ghi lại ở đây danh sách các lựa chọn thay thế để đọc các tệp có chiều rộng cố định trong R, cũng như cung cấp một số điểm chuẩn cho tốc độ nhanh nhất.

Cách tiếp cận ưa thích của tôi là kết hợp freadvới stringi; nó có tính cạnh tranh như là cách tiếp cận nhanh nhất và có thêm lợi ích (IMO) khi lưu trữ dữ liệu của bạn như một 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

Lưu ý rằng freadtự động loại bỏ khoảng trắng ở đầu và cuối - đôi khi, điều này là không mong muốn, trong trường hợp đó được đặt strip.white = FALSE.


Chúng ta cũng có thể bắt đầu với một vectơ chiều rộng cột wwbằng cách thực hiện:

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)

Và chúng tôi có thể đã chọn những cột nào để loại trừ mạnh mẽ hơn bằng cách sử dụng các chỉ số phủ định như:

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

Sau đó thay thế col_ends$beg[ii]bằng abs(col_ends$beg[ii])và trong dòng tiếp theo:

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

Cuối cùng, nếu bạn muốn tên cột cũng được đọc theo chương trình, bạn có thể xóa bằng 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 != ""]

(lưu ý rằng việc kết hợp bước này với freadsẽ yêu cầu tạo bản sao của bảng để xóa hàng tiêu đề và do đó sẽ không hiệu quả đối với các tập dữ liệu lớn)


4

Tôi không biết gì về R, nhưng tôi có thể cung cấp cho bạn một regex phù hợp với các dòng như vậy:

\s[0-9]{2}[A-Z]{3}[0-9]{4}(\s{5}[0-9]+\.[0-9]+[ -][0-9]+\.[0-9]+){4}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.