Как изменить сразу большое количество столбцов во фрейме данных в 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))
Таким образом, у меня есть два вопроса:
Как работает mapply по отношению к приведенному ниже коду? Я неправильно понимаю, как mapply перебирает два столбца, или моя функция неправильно векторизована? Должен ли я использовать для этой задачи другую функцию цикла, отличную от mapply?
Есть ли функция, которую я могу использовать для назначения результатов цикла по двум интересующим столбцам и извлечения данных 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, для работы со всеми различными типами ручных опросов. Спасибо за предложение из комментария о поиске минимального воспроизводимого набора данных: процесс запуска функции на игрушечных наборах данных, а не на полном, привел меня к обнаружению этого.