Р: Как читать и комбинировать таблицы с разными форматами? - PullRequest
0 голосов
/ 10 июля 2020

Проблема

Я извлек несколько таблиц из URL-адресов и объединил их. Каждый URL-адрес представляет собой идентификатор. Объединенный фрейм данных структурирован неправильно, поскольку форматы таблиц различаются между идентификаторами.

Примеры структур таблиц:

https://www.sec.gov/Archives/edgar/data/1000097/0000919574-13-001835.txt # Использует "," как разделитель тысяч https://www.sec.gov/Archives/edgar/data/1001085/0001193125-13-060806.txt # Не использует разделитель тысяч

Код

Код, используемый для извлечения таблиц (кредиты Limey для исходного кода):

all <-data.frame()

for (i in 1:nrow(URL_file)) {
  skip_to_next <- FALSE
  
  tryCatch({
    # Read the text files from the web
    fileContents <- readr::read_file(URL_file$link[i])
    # Extract the tables.  The regex isn't quite right, as it includes the starting <TABLE>
    # and ending </TABLE> tags, but more complicated regexes failed.
    tables <- stringr::str_extract_all(
      fileContents,
      stringr::regex(
        "(?s)<TABLE>(.*?)</TABLE>",
        multiline = TRUE,
        dotall = TRUE
      )
    )
    
    # Function to process a single tibble
    toTibble <- function(y) {
      lines <- str_split_fixed(y, "\n", n = Inf)
      colStarts <- c()
      colEnds <- c()
      # Scroll through to final table header
      for (i in 1:(length(lines) - 1)) {
        # Final line is '</TABLE>' because of initial regex
        # Could probably to this with regexes, but my head is hurting
        
        if (any(!is.na(stringr::str_locate(lines[i], "<\\w>")))) {
          # Define column widths based on locations of type markers.  THIS IS AN ASSUMPTION
          colStarts <-
            stringr::str_locate_all(lines[i], "<\\w>")[[1]][, "start"]
          
          for (i in 1:(length(colStarts) - 1))
            colEnds[i] <- colStarts[i + 1] - 1
          colEnds[length(colStarts)] <- stringr::str_length(lines[i])
          lines <- lines[(i + 1):(length(lines) - 1)]
          data <- dplyr::bind_rows(lapply(
            lines,
            # For each data line
            function(line)
              tibble::enframe(# Split in to columns and convert to a tibble of name/value pairs
                stringr::str_trim(
                  stringr::str_sub(line,
                                   colStarts,
                                   colEnds)
                )) %>%                  # Convert from name/value pairs to columns
              tidyr::pivot_wider(
                values_from = "value",
                names_from = "name",
                names_prefix = "Column"
              )
          ))
          # Finished
          return(data)
        }
      }
    }
    
    alldata <- bind_rows(lapply(tables[[1]], function(t)
      toTibble(t)))
    
    if (nrow(alldata) > 0) {
      alldata$ID <- URL_file$ID[i]
    }
    
    #alldata$ID <- URL_file$ID[i]
    
    all <- bind_rows(all, alldata)
    
    print(i) #print every 10th value to monitor progress of the loop
    
  }, error = function(e) {
    skip_to_next <<- TRUE
  })
  
  if (skip_to_next) {
    next
  }
}

Примеры

Это пример входных таблиц (одна строка строки из каждого из вышеуказанных URL-адресов) - и это также идеальная структура

+-----------+-------------------------+----------------+-----------+--------------+-----------------+--------+----------+-----------------------+-------------+---------+--------+-------+
|   (ID)    |     NAME OF ISSUER      | TITLE OF CLASS |   CUSIP   | VALUE(x1000) | SHRS OR PRN AMT | SH/PRN | PUT/CALL | INVESTMENT DISCRETION | OTHER MNGRS |  SOLE   | SHARED | NONE  |
+-----------+-------------------------+----------------+-----------+--------------+-----------------+--------+----------+-----------------------+-------------+---------+--------+-------+
| (1000097) | ABERCROMBIE & FITCH     | CL A           | 002896207 |        4,797 |         100,000 | SHS    |          | Shared-Defined        | 1/2/3       | 100,000 |        |       |
| (1001085) | AMERICAN TOWER CORP NEW | COM            | 03027X100 |        96566 |         1249724 | SH     |          | Defined               | 1           | 1170124 |      0 | 79600 |
+-----------+-------------------------+----------------+-----------+--------------+-----------------+--------+----------+-----------------------+-------------+---------+--------+-------+

Текущий формат данных - это файл csv, разделенный запятыми:

ABERCROMBIE & FITCH,CL A,002896207,4,797,100,000,SHS,,Shared-Defined,1/2/3,100,000,,,1000097,NA,NA,NA,
AMERICAN TOWER CORP NEW,COM,03027X100,96566,1249724,SH,,Defined,1,1170124,0,,1001085,NA,NA,NA

Одна из основных проблем заключается в том, что вывод представляет собой файл, разделенный запятыми, и некоторые числа также разделены тысячами через ",". Дополнительный "," делает строки неравной длины.

Мысли и подходы

Я нашел несколько потенциальных подходов к этой проблеме:

  • Я подумал, что это может быть файл фиксированной ширины (входной), но я не мог использовать это как преимущество.
  • Такие функции, как fread et c. предлагают большую гибкость.
  • Здесь могут быть полезны строки чтения. Я прочитал руководство (https://cran.r-project.org/doc/contrib/de_Jonge+van_der_Loo-Introduction_to_data_cleaning_with_R.pdf), а часть на стр. 15 кажется актуальным, но я не могу заставить его работать.

Есть ли способ структурировать данные для желаемого результата? Или мне нужно извлекать таблицы другим способом? если да, как мне изменить приведенный выше код, чтобы он лучше соответствовал моим целям?

...