Как изменить сразу большое количество столбцов во фрейме данных в R с помощью пользовательской функции с pdftools и html-ссылками?

Извините, если он длинный или неправильно структурирован, это мой первый вопрос и первый крупный R-сторонний проект! Дайте мне знать, если мне нужно что-то изменить в своих вопросах в будущем.

В настоящее время я работаю с некоторыми данными о городском трафике, которые хранятся довольно странно. Вместо того, чтобы хранить данные для каждого перекрестка в загружаемых файлах csv, присутствует ссылка на веб-сайт, содержащий html-ссылки на все предыдущие исследования трафика в форме PDF.

Я извлек самый последний PDF-файл для каждого транспортного перекрестка, но мне не удается заставить мою функцию читать в PDF-файле и возвращать данные для работы при вводе в mapply и mutate. Ранее я писал функцию, способную принимать в качестве входных данных формат PDF и ссылку PDF и возвращать фрейм данных из 1 строки и 55 столбцов, содержащий все данные трафика для этого перекрестка.

Теперь я не могу заставить эту функцию работать в mapply / withmutate. Функция приведена ниже, но она принимает два входа: тип опроса, используемый для PDF-файла, и ссылку PDF на рассматриваемый PDF-файл, и возвращает фрейм данных, упомянутый выше.

При простом использовании mapply отдельно, я думаю, что функция использует весь столбец типа опроса / весь столбец ссылки PDF в целом для переменных в функции, а не перебирает все столбцы в цикле.

При использовании mapply внутри mutate кажется, что он правильно перебирает два столбца, но я не уверен, как использовать mutate для правильного добавления большого количества столбцов одновременно. В идеале я бы просто сделал вектор имен столбцов в правильном порядке и использовал бы mutate или какую-то похожую на мутацию функцию, чтобы назначить результаты mapply списку столбцов, т.е.

похожий на мутации (traffic_westwood, col-names = mapply(get_data, SURVEY_TYPE, PDF))

Таким образом, у меня есть два вопроса:

  1. Как работает mapply по отношению к приведенному ниже коду? Я неправильно понимаю, как mapply перебирает два столбца, или моя функция неправильно векторизована? Должен ли я использовать для этой задачи другую функцию цикла, отличную от mapply?

  2. Есть ли функция, которую я могу использовать для назначения результатов цикла по двум интересующим столбцам и извлечения данных PDF в список имен столбцов, а затем добавления этих столбцов в мой фрейм данных?

Раньше я пытался поместить вызов mapply в mutate, как обсуждалось выше, но для этого по-прежнему требуется способ назначить 55 столбцов списку имен.

Вдобавок я переключился на mapply, а не на lapply, когда понял, что проще перебрать два столбца, которые мне нужны, чем пытаться выполнить мою настраиваемую функцию для всех строк.

Также обратите внимание, что столбцы SURVEY_TYPE и столбцы PDF, используемые в вызове mapply, являются правильным размером с приемлемыми значениями в каждом слоте: я уже проверял это.

Обратите внимание, что мой код идеально работал бы с любым входным фреймом конечных данных любых размеров, содержащим следующие два столбца: SURVEY_TYPE и PDF. SURVEY_TYPE содержит "Авто", чтобы указать тип опроса автоматически, и "Вручную", чтобы указать тип опроса вручную. PDF содержит вектор строк / HTML-ссылок PDF для строки, где SURVEY_TYPE = 'Auto', и одну строку / HTML-ссылку PDF, если SURVEY_TYPE = 'Manual' для этой строки.

Примеры ссылки в формате PDF для ручного обзора дорожного движения в Лос-Анджелесе можно найти в сообщении об ошибке закрытых соединений, напечатанном ниже, которое должно содержать несколько таких ссылок.

#This function obtains a data frame with rows of 55 entries for each intersection withmanual data.
#Automatic data only intersections have blank rows for now.
get_data <- function(survey_type, pdf_link){

  #Return data frame of empty row if type was auto. I'm fine on this part.
  if(survey_type == 'Auto'){
    ...
    return(data.frame(#Whatever I want for auto, empty 1 row 55 cols for now))
  }

  #Now we read in this entries manual pdf data.

  #Read in the first pdf.
  pdf1 <- pdf_text(toString(pdf_link))


  #Get the handle for this file..
  file1 <- file("Traffic_Data_Files/pdf1.txt", 'w')

  #Write the PDF contents to the file.
  write(pdf1, file = file1, sep = '\t')

  #Close the file and reopen it.
  close(file1)
  file1 <- file("Traffic_Data_Files/pdf1.txt", 'r')

  #Here is where I had code to get the data frame we will return and extract the 
  #data to it. Note that #the data frame will be 1 row and 55 columns and will  
  #be called 'intersection1'.
  #I have tested this code, which works on a single file to return such data.

  #Return the info for this intersection.
  return(intersection1)

}

#Use lapply on the traffic data.
manual_intersections <- mutate(traffic_westwood, Data = mapply(get_data, SURVEY_TYPE, PDF))

В идеале я хотел бы напрямую добавить 55 строк заполненной информации в текущий фрейм данных. Конечно, я мог бы просто получить новый фрейм данных со строками и при необходимости добавить его к старому в качестве промежуточного шага.

Следующее сообщение об ошибке возникает, когда я сохраняю вызов mapply вне вызова mutate в последней строке выше (мне кажется, что он не может сказать, что столбцы являются векторами, и выполняет первую проверку для SURVEY_TYPE == 'Auto' по первому компоненту столбца, а не поэлементно):

Error in open.connection(con, "rb") : cannot open the connection
In addition: Warning messages:
1: In if (survey_type == "Auto") { :
  the condition has length > 1 and only the first element will be used
2: In open.connection(con, "rb") :
  cannot open URL 'http://navigatela.lacity.org/dot/traffic_data/manual_counts/46972_WESWEY96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/46974_KINWES96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/Gayley.Weyburn.180927-NDSMAN.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12599_GAYVET96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/gaylec06.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12690_BROLEC95.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12691_BROXTON.WEYBURN07.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12995_HILLEC95.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/13531_LECWES96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/13997_VETWIL081021.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/SELWIL080319.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/16896_GLELIN96.pdf, http [... truncated]

Когда я вызываю mapply в режиме mutate, как показано в коде, и пытаюсь назначить вызов одной переменной, на первый взгляд я считаю, что он не может назначить всю результирующую строку одной записи столбца. Таким образом, мой вопрос о том, как я могу назначить большое количество столбцов списку имен, используя произвольную настраиваемую функцию в функции mutate или подобной функции mutate:

Error in eval(substitute(expr), envir, enclos) : 
  more columns than column names
In addition: Warning message:
closing unused connection 3 (http://navigatela.lacity.org/dot/traffic_data/manual_counts/46972_WESWEY96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/46974_KINWES96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/Gayley.Weyburn.180927-NDSMAN.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12599_GAYVET96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/gaylec06.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12690_BROLEC95.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12691_BROXTON.WEYBURN07.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/12995_HILLEC95.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/13531_LECWES96.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/13997_VETWIL081021.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/SELWIL080319.pdf, http://navigatela.lacity.org/dot/traffic_data/manual_counts/16896_GLELIN [... truncated]
'''

EDIT: Further examination of my code for creating a minimal example revealed other problems. I will update when I am farther along

1 ответ

Решение

Я понял, почему мой код не работает. Оказывается, что PDF-файлы, хотя и написаны в одном и том же формате, читаются PDFTools по-разному, поскольку между кажущимися идентичными форматами таблиц / заголовков существует несколько явных различий в интервале. Это то, что вызывает сообщение об ошибке для второй части проблемы, поскольку количество строк, которые я предполагал в функции для чтения в CSV из файла, неверно для этих PDF-файлов с различным форматом.

Таким образом, мне нужно определить, в каких PDF-файлах не работает моя функция, создать второй или, возможно, несколько типов ручных опросов и адаптировать функцию, считывающую данные из PDFS, для работы со всеми различными типами ручных опросов. Спасибо за предложение из комментария о поиске минимального воспроизводимого набора данных: процесс запуска функции на игрушечных наборах данных, а не на полном, привел меня к обнаружению этого.

Другие вопросы по тегам