Читати текстовий файл із фіксованою шириною


90

Я намагаюсь завантажити цей потворно відформатований набір даних у свою сесію 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)

Але файл змішує "пробіли" з "-" як роздільники, і я не експерт регулярних виразів. Я ціную будь-яку допомогу щодо перетворення цього на приємний і чистий R-кадр даних. Дякую!


5
І подивіться, read.fwfщоб прочитати прочитані відформатовані дані з фіксованою шириною.
Paul Hiemstra,

1
Я думаю, що краще обробити кожен рядок. Він змішує символи "-" з "".
Фернандо

Крім того, ви можете сказати пробіл або - це лише один символ, тому спочатку замініть усі множинні випадки пробілу символом табуляції, а потім розділіть усі записи, розділені табуляцією, на - або пробіл.
GitaarLAB

Фіксована ширина = відсутність розділювачів. Це означає, що "-" - знак мінуса, і пробіли теж не є роздільниками, вони просто виникають, коли число не заповнює всю доступну ширину
Еусебіо Руфіан-Зільберманн

Відповіді:


183

Це файл із фіксованою шириною. Використовуйте 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()було ~ вдвічі швидше, ніж utils::read.fwf ().


8
@Andrie, як ти знав, які були ширини та пропуски?
Коба

12
@Koba: Я скопіював і вставив один з рядків у текстовий редактор, який мав кількість стовпців, і я вручну підрахував ширину кожного стовпця (включаючи пробіли, коли потрібно). Також ви можете сказати, що вам потрібно пропустити цілі 4 рядки, перш ніж перейти до вихідних даних.
rayryeng

5
Відповідь @ Павітри нижче з від’ємною шириною стовпця для пропуску небажаних пробілів може бути краще підходить для прийнятої відповіді.
Marius Butuc

1
@Andrie Як ви отримали значення 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

По-перше, це запитання безпосередньо з курсу курсу Coursera "Отримати дані та очистити". Хоча є інша частина питання, важка частина - читати файл.

Тим не менше, курс в основному призначений для навчання.

Я ненавиджу процедуру фіксованої ширини 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 <- data.frame(var4 = substr(x,29,32))наче вам потрібен лише четвертий стовпець даних. Крім того, для користувачів Windows Notepad ++ з плагіном TextFX отримає просту і просту лічильну лінійку символів, щоб ви могли зрозуміти, що вводити в значення початку і зупинки substr. Однак зауважте, що значення зупинки більше, ніж позиція останнього символу, який ви хочете зберегти.
globalSchmidt

13

Тепер ви можете використовувати read_fwf()функцію в readrпакеті Хедлі Вікхема .

Очікується величезне покращення продуктивності порівняно з базовою read.fwf().


5

Я документую тут список альтернатив для читання файлів фіксованої ширини в R, а також надаю деякі тести, для яких це найшвидше.

Я віддаю перевагу поєднанню 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, але я можу надати вам регулярний вираз, який буде відповідати таким рядкам:

\s[0-9]{2}[A-Z]{3}[0-9]{4}(\s{5}[0-9]+\.[0-9]+[ -][0-9]+\.[0-9]+){4}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.