将远程数据源包含在 R 包中的最佳实践?

问题描述 投票:0回答:1

在编写 R 包时,烘焙一些数据集通常是一种很好的做法,通常用于教育/教程目的。 Hadley Wickham 关于 R 包的书中有一章介绍了这一点:https://r-pkgs.org/data.html

我有一个用例,其中有几个数据集我也想通过包提供,但它们实际上是远程的。我经常使用 {dbplyr} 并编写大量 R 代码来与我的数据库交互,但就其本质而言,它们必须是远程的。

所以,我经常做类似的事情



production <- DBI::dbConnect(
  RPostgres::Postgres(),
  host     = Sys.getenv("POSTGRES_SERVER"),
  dbname   = Sys.getenv("PRODUCTION_DATABASE"),
  user     = Sys.getenv("POSTGRES_USER"),
  password = Sys.getenv("POSTGRES_PASSWORD")
)

my_table <- dplyr::tbl(production, dbplyr::in_schema("our_database", "my_table"))

head(my_table)

是否可以在我的自定义 R 包中包含这样的内容:

head(my_pkg::my_table)

据我所知,哈德利的书中并没有真正涉及这个案例。 也许第 7.2 节 - 内部数据

r dbi r-package dbplyr
1个回答
0
投票

如何最好地解决这个问题取决于数据的敏感性、目标受众以及您想要提供信息的目的。以下是您可以尝试的四种不同方法。

在每种情况下,我都将它们编写为

demo_function
,您的用户在熟悉您的包时可能会调用它们。我假设您可能会将这些内容包含在小插图或文档中。

请注意,要将数据包含在 GitHub 存储库中,文件大小有限制。


1)模拟数据库连接

dbplyr 包含用于模拟数据库连接的选项。

  • 敏感性 - 您可以创建自己的安全数据示例数据提取
  • 观众 - 公众
  • 目的 - 测试或演示数据库翻译。
demo_function = function(){

  data(starwars)
  # simulated connection
  remote_df = tbl_lazy(starwars, con = simulate_postgres())
  # computation
  out = mutate(remote_df, substring_col = grepl("Luke", name)))
  return(out)
}

2) SQLite 数据库

SQLite 在 R 中可用。您可以在包的 extdata 文件夹中包含 SQLite 数据库文件。

  • 敏感性 - 您可以创建自己的安全数据示例数据提取
  • 观众 - 公众
  • 目的 - 演示连接到数据库进行基本分析

软件包开发期间的设置:

path = system.file("extdata", "testing", package = "my.new.package")
db_path = file.path(path, "testing_sqlite.db")
db_conn = DBI::dbConnect(RSQLite::SQLite(), db_path)

# add table
data(iris)
DBI::dbWriteTable(db_conn, "iris_table", iris)

用途:

demo_function = function(){
  path = system.file("extdata", "testing", package = "my.new.package")
  db_path = file.path(path, "testing_sqlite.db")
  db_conn = DBI::dbConnect(RSQLite::SQLite(), db_path)

  my_table = dplyr::tbl(db_conn, "iris")
  return(my_table)
}

3)演示代码

您可以编写一个演示 R 文件,供用户自定义以进行自己的分析。

  • 敏感性 - 包中没有数据,演示中没有凭据
  • 观众 - 内部焦点,但对公众安全
  • 目的 - 设置用户开始遵循您的示例

脚本保存到extdata:

# INSTRUCTIONS:
# Edit the lines of this file to provide the required connection details.

production <- DBI::dbConnect(
  RPostgres::Postgres(),
  host     = Sys.getenv("POSTGRES_SERVER"),
  dbname   = Sys.getenv("PRODUCTION_DATABASE"),
  user     = Sys.getenv("POSTGRES_USER"),
  password = Sys.getenv("POSTGRES_PASSWORD")
)

my_table <- dplyr::tbl(production, dbplyr::in_schema("our_database", "my_table"))

head(my_table)

辅助功能:

demo_function = function(){
  folder = getwd()
  
  # locations
  to_dir = file.path(normalizePath(folder), "demo_script")
  from_dir = system.file("extdata", "demo_script", package = "my.new.package")
  
  # copy
  file.copy(
    list.files(from_dir, full.names = TRUE), 
    to_dir, 
    recursive = TRUE
  )
}

4)单独的凭证文件

您可以将凭据保存在不属于包的单独文件中。该文件可以与包一起提供给内部用户。当包运行时,如果发现错误/警告/提示(您的选择),它将使用凭证文件。

  • 敏感性 - 包中没有数据,演示中没有凭据
  • 观众 - 内部焦点,但对公众安全
  • 目的 - 为用户提供内部数据快速入门
demo_function = function(host = NULL, dbname = NULL, user = NULL, password = NULL, db = NULL){
  # load credential file if exists  
  credentials = "C:/expected/folder/credential_file.txt"
  if(file.exists(credentials)){
    con = file(credentials, "r")
    host = readLines(con, n = 1)
    dbname = readLines(con, n = 1)
    user = readLines(con, n = 1)
    password = readLines(con, n = 1)
    close(con)
  }
  
  # prompt if NULL
  if(is.null(host)){
    host = readline("What is the value of host?")  
  }
  if(is.null(dbname)){
    dbname = readline("What is the value of dbname?")  
  }
  if(is.null(user)){
    user = readline("What is the value of user?")  
  }
  if(is.null(password)){
    password  = readline("What is the value of password?")  
  }

  # return connection
  production <- DBI::dbConnect(
    RPostgres::Postgres(),
      host     = host,
      dbname   = dbname,
      user     = user,
      password = password
  )
}
© www.soinside.com 2019 - 2024. All rights reserved.