อ่านไฟล์ข้อความที่มีความกว้างคงที่


93

ฉันพยายามโหลดชุดข้อมูลที่มีรูปแบบน่าเกลียดนี้ลงในเซสชัน 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

จนถึงตอนนี้ฉันสามารถอ่านบรรทัดด้วย

  x = readLines(path)

แต่ไฟล์ผสม 'พื้นที่สีขาว' กับ '-' เป็นตัวคั่นและฉันไม่ใช่ผู้เชี่ยวชาญ regex ฉันขอขอบคุณความช่วยเหลือใด ๆ ในการเปลี่ยนสิ่งนี้ให้เป็นเฟรมข้อมูล R ที่ดีและสะอาด ขอบคุณ!


5
และดูread.fwfเพื่ออ่านข้อมูลที่จัดรูปแบบความกว้างคงที่
Paul Hiemstra

1
ฉันคิดว่าเป็นความคิดที่ดีกว่าในการประมวลผลแต่ละแถว มันผสมอักขระ '-' กับ ''
Fernando

หรือคุณสามารถพูดว่าช่องว่างสีขาวหรือ - เป็นเพียงอักขระตัวเดียวดังนั้นก่อนอื่นให้แทนที่จำนวนที่เกิดขึ้นทั้งหมดของช่องว่างด้วยอักขระแท็บจากนั้นแยกรายการที่คั่นด้วยแท็บทั้งหมดบน - หรือช่องว่างสีขาว
GitaarLAB

ความกว้างคงที่ = ไม่มีตัวคั่น นั่นหมายความว่า "-" เป็นเครื่องหมายลบและช่องว่างไม่ใช่ตัวคั่นเช่นกันซึ่งเพิ่งเกิดขึ้นเมื่อตัวเลขไม่เต็มความกว้างที่มีอยู่ทั้งหมด
Eusebio Rufian-Zilbermann

คำตอบ:


184

นี่คือไฟล์ที่มีความกว้างคงที่ ใช้read.fwf()อ่าน:

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

อัปเดต

แพ็กเกจreadr(เผยแพร่เมื่อเมษายน 2015) เป็นทางเลือกที่ง่ายและรวดเร็ว

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

การเปรียบเทียบความเร็ว: readr::read_fwf()เป็น ~ 2x utils::read.fwf ()เร็วกว่า


10
@ แอนดรีคุณรู้ได้อย่างไรว่าความกว้างและการข้ามคืออะไร?
Koba

12
@Koba: ฉันคัดลอกและวางหนึ่งในบรรทัดลงในโปรแกรมแก้ไขข้อความที่มีจำนวนคอลัมน์และฉันนับความกว้างสำหรับแต่ละคอลัมน์ด้วยตนเอง (รวมถึงช่องว่างเมื่อจำเป็น) นอกจากนี้คุณสามารถบอกได้ว่าคุณต้องข้าม 4 บรรทัดทั้งหมดก่อนที่จะไปยังข้อมูลดิบ
rayryeng

5
คำตอบของ @ Pavithra ด้านล่างด้วยความกว้างของคอลัมน์เชิงลบสำหรับการข้ามช่องว่างที่ไม่ต้องการอาจจะเหมาะกว่าสำหรับคำตอบที่ยอมรับ
Marius Butuc

1
@ แอนดรีคุณได้ค่า fwf_widths มาได้อย่างไร?
BICube

3
@Ala ฉันเชื่อว่าreadr::fwf_emptyจะพยายามเดาความกว้างให้คุณ ตัวอย่างสำหรับreadr::read_fwfแสดงการใช้งานสำหรับreadr::fwf_empty.
Jake Fisher

55

อีกวิธีหนึ่งในการกำหนดความกว้าง ...

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 ในอาร์กิวเมนต์ widths บอกว่ามีคอลัมน์หนึ่งอักขระที่ควรละเว้น -5 ในอาร์กิวเมนต์ widths บอกว่ามีคอลัมน์ห้าอักขระที่ควรละเว้นเช่นเดียวกัน ...

อ้างอิง: https://www.inkling.com/read/r-cookbook-paul-teetor-1st/chapter-4/recipe-4-6


20

ก่อนอื่นคำถามนั้นมาจากหลักสูตร "Get Data and Clean It" ของ Coursera โดยตรงโดย Leeks ในขณะที่มีอีกส่วนหนึ่งของคำถามส่วนที่ยากคือการอ่านไฟล์

กล่าวได้ว่าหลักสูตรส่วนใหญ่มีไว้เพื่อการเรียนรู้

ฉันเกลียดขั้นตอนความกว้างคงที่ของ R มันช้าและสำหรับตัวแปรจำนวนมากมันจะกลายเป็นความเจ็บปวดอย่างรวดเร็วในการลบล้างคอลัมน์บางคอลัมน์ ฯลฯ

ฉันคิดว่ามันง่ายกว่าที่จะใช้readLines()และจากนั้นใช้substr()เพื่อสร้างตัวแปรของคุณ

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
วิธีนี้ใช้ได้ผลสำหรับฉัน เคล็ดลับเพิ่มเติมสองประการ: 1) คุณสามารถกำหนด mydata ให้เป็นเฉพาะข้อมูลที่คุณต้องการ ดังนั้นจึงอาจทำได้ง่ายราวกับmydata <- data.frame(var4 = substr(x,29,32))ว่าคุณต้องการข้อมูลคอลัมน์ที่สี่เท่านั้น นอกจากนี้สำหรับผู้ใช้ Windows, Notepad ++ ด้วยปลั๊กอิน TextFX substrจะได้รับคุณธรรมดาและเรียบง่ายผู้ปกครองตัวอักษรนับเพื่อให้คุณสามารถคิดออกว่าจะใส่ในการเริ่มต้นและหยุดค่าใน อย่างไรก็ตามโปรดทราบว่าค่าหยุดมากกว่าตำแหน่งของอักขระสุดท้ายที่คุณต้องการรักษาไว้
globalSchmidt

13

ตอนนี้คุณสามารถใช้read_fwf()ฟังก์ชันนี้ในreadrแพ็คเกจของ Hadley Wickham ได้แล้ว

read.fwf()การปรับปรุงประสิทธิภาพการทำงานขนาดใหญ่เป็นที่คาดหวังเมื่อเทียบกับฐาน


5

ฉันเอกสารที่นี่รายการของทางเลือกสำหรับการอ่านไฟล์ความกว้างคงที่ในการวิจัยตลอดจนให้เป็นมาตรฐานบางอย่างสำหรับการซึ่งเป็นที่เร็วที่สุด

แนวทางที่ฉันชอบคือการรวมfreadกับstringi; มีการแข่งขันเป็นแนวทางที่เร็วที่สุดและมีประโยชน์เพิ่มเติม (IMO) ในการจัดเก็บข้อมูลของคุณเป็น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

โปรดทราบว่าfreadโดยอัตโนมัติแถบชั้นนำและต่อท้ายช่องว่าง - strip.white = FALSEบางครั้งนี้เป็นที่ไม่พึงประสงค์ซึ่งในกรณีที่ชุด


เราสามารถเริ่มต้นด้วยเวกเตอร์ของความกว้างของคอลัมน์ได้wwโดยทำ:

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)

และเราสามารถเลือกคอลัมน์ที่จะยกเว้นได้อย่างมีประสิทธิภาพมากขึ้นโดยใช้ดัชนีเชิงลบเช่น:

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

จากนั้นแทนที่col_ends$beg[ii]ด้วยabs(col_ends$beg[ii])และในบรรทัดถัดไป:

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

สุดท้ายนี้หากคุณต้องการให้อ่านชื่อคอลัมน์แบบเป็นโปรแกรมด้วยเช่นกันคุณสามารถล้างข้อมูลด้วย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 != ""]

(โปรดทราบว่าการรวมขั้นตอนนี้freadจะต้องมีการสร้างสำเนาของตารางเพื่อลบแถวส่วนหัวและจะไม่มีประสิทธิภาพสำหรับชุดข้อมูลขนาดใหญ่)


4

ฉันไม่รู้อะไรเกี่ยวกับ R แต่ฉันสามารถให้ regex ที่ตรงกับบรรทัดดังกล่าวได้:

\s[0-9]{2}[A-Z]{3}[0-9]{4}(\s{5}[0-9]+\.[0-9]+[ -][0-9]+\.[0-9]+){4}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.