R 代码优化:for 循环和写入数据库

R code optimization: For loop and writing to a database

我正在尝试优化我在 两个 方面编写的简单 R 代码:

1) 循环

2) 将数据写入我的 PostgreSQL 数据库

For 1) 我知道应该不惜一切代价避免循环,建议使用 lapply 但我我不清楚如何使用 lapply.

翻译我的代码

对于 2) 我在下面所做的工作有效但我不确定这是最有效的方法(例如这样做与将所有数据绑定到 R 数据框,然后将整个数据框加载到我的 PostgreSQL 数据库中的方法。)

编辑:我用下面的可重现示例更新了我的代码。

for (i in 1:100){

   search <- paste0("https://github.com/search?o=desc&p=", i, &q=R&type=Repositories)

   download.file(search, destfile ='scrape.html',quiet = TRUE)

   url <- read_html('scrape.html')

   github_title <- url%>%html_nodes(xpath="//div[@class=mt-n1]")%>%html_text()

   github_link <- url%>%html_nodes(xpath="//div[@class=mt-n1]//@href")%>%html_text()

   df <- data.frame(github_title, github_link )

   colnames(df) <- c("title", "link")

   dbWriteTable(con, "my_database", df, append = TRUE, row.names = FALSE)

   cat(i)
}

非常感谢您的所有意见!

首先,lapply 在任何方面都比使用 for loop 的等效代码快,这是一个应该被彻底驳斥的神话。多年来,这个问题已经得到解决,for loops 在任何情况下都应该比等效的 lapply 快。

我将使用 for loop 进行可视化,因为您似乎觉得这更直观。但是请注意,我主要在 T-sql 中工作,可能需要进行一些转换。

n <- 1e5
outputDat <- vector('list', n)
for (i in 1:10000){
  id <- element_a[i]
  location <- element_b[i]
  language <- element_c[i]
  date_creation <- element_d[i]
  df <- data.frame(id, location, language, date_creation)
  colnames(df) <- c("id", "location", "language", "date_creation")
  outputDat[[i]] <- df
}
## Combine data.frames
outputDat <- do.call('rbind', outputDat)
#Write the combined data.frame into the database.
##dbBegin(con)   #<= might speed up might not.
dbWriteTable(con, "my_database", df, append = TRUE, row.names = FALSE)
##dbCommit(con)  #<= might speed up might not.

使用 Transact-SQL,您也可以将整个字符串组合成一个 insert into 语句。在这里,我将偏离并使用 apply 来遍历行,因为在这种情况下它更具可读性。如果操作得当,for 循环同样快速。

#Create the statements. here 
statement <- paste0("('", apply(outputDat, 1, paste0, collapse = "','"), "')", collapse = ",\n") #\n can be removed, but makes printing nicer.
##Optional: Print a bit of the statement
# cat(substr(statement, 1, 2000))

##dbBegin(con)   #<= might speed up might not.
dbExecute(con, statement <- paste0(
'
/*
  SET NOCOCUNT ON seems to be necessary in the DBI API.
  It seems to react to 'n rows affected' messages. 
  Note only affects this method, not the one using dbWriteTable
*/
--SET NOCOUNT ON 
INSERT INTO [my table] values ', statement))
##dbCommit(con)   #<= might speed up might not.

请注意,正如我评论的那样,这可能无法正确上传 table,因为 DBI 包似乎有时会失败这种交易,如果它导致一条或多条关于n rows affected

最后但并非最不重要的一点是,一旦做出声明,就可以将其从 R 复制并粘贴到任何直接访问数据库的 GUI 中,例如使用 writeLines(statement, 'clipboard') 或写入文本文件(如果您的数据包含很多行,则文件更多 stable)。在极少数异常情况下,如果出于某种原因 DBI 或替代 R 包似乎 运行 无缘无故地过慢,那么最后的手段可能会更快。由于这似乎有点像个人项目,所以这可能足以供您使用。