前言
R语言是一门很好入门的语言,但用在生产环境下,如果没有规范的项目结构以及相应的开发流程,最后项目的开发效率以及可维护性都会大打折扣。本文从笔者实践角度给大家分享一下。
相信大家一般在写R代码的时候,是这样写的。
library
当然在代码量在百行左右的情况下,这样写很清晰。但随着代码量的增加,弊端也会越发凸显。以下从几方面进行优化:
- 项目结构
推荐多子项目且项目间互有关联
下面进行说明:
1.将数据库配置写入一个config文件中
统一管理修改数据库配置,方便后续增加以及维护。推荐使用yaml进行配置,示例如下:
MYSQL_APP:
server: IP
type: mysql
port: 3306
user: 用户名
password: 密码
SQLSERVER_APP:
server: IP
type: sqlserver
port: 1433
user: 用户名
password: 密码
那怎么使用呢?
这个时候就可以在utils的common.R中增加一个公共function,示例如下:
MysqlDBConnect <- function(driver_fun, server, dbname, yaml_path = './'){
server_obj <- read_yaml('sql.yaml')$server
host <- server_obj$server
port <- server_obj$port
user <- server_obj$user
password <- server_obj$password
dbConnect(driver_fun(),host=host, port=port,dbname=dbname,user=user,password=password)
}
# 在Script中调用就好了
mysql_con <- MysqlDBConnect(RMySQL::MySQL, "SQLSERVER_APP", 'xx_database')
2. 数据模型
这里可能会比较抽象,因为可能大家和笔者一样直接就写SQL,然后传入数据库取到数据。但这样SQL很难维护,可能同一张表字段改了,就导致好多个SQL都需要修改字段。还有可能每个SQL都需要写完整,但其中部分SQL可以复用,减少代码冗余。一般笔者会在model.R中这样写:
TABLE1 <- "SELECT
account_id AS sfdc_account_id,
account_id AS zuora_account_id
FROM sfdc_account"
在model里,笔者推荐只做SELECT, FROM以及字段映射也就是AS。然后在中再加入component中再根据具体需要加入其它关键字。例如:
xx_var <- 'hhh'
custome_model_sql <- TABLE1 %>%
paste(
.,
sprintf("WHERE xx == '%s'", xx_var),
sprintf("GROUP BY account_id")
)
custome_model_data <- dbGetQuery(mysql_con,
custome_model_sql)
这里也引申出一个coding时的技巧。尽量复用代码,SQL部分相似的就写到一起,然后将变化的部分与相同的部分再进行组装。utils的思想也是大致一样的。比如在好几个应用里都需要解析身份证号。那在common.R中写一个统一的身份证解析函数,就可以复用了。结合笔者上一篇R语言dplyr的高级技巧就可以组装出极其灵活的数据流得到最后期望的结果,并且易维护的代码。
3. components与main
笔者在实践中是这样理解的,components中主要写不同步骤的代码,最后main中步骤代码按顺序连接起来。可能这样说较为抽象。下面举个例子:
通常数据分析也好,数据处理也好。开发流程一般如下图:
![3b414e0d9eadd1708f631b38bcc95b1e.png](https://img-blog.csdnimg.cn/img_convert/3b414e0d9eadd1708f631b38bcc95b1e.png)
那在components中就可以这样写:
|--components
||--import.R(导入数据)
||--transfrom_01.R(数据清洗01)
||--transfrom_02.R(数据清洗02)
||--visualise.R(可视化)
||--model_01.R(建模01)
||--model_02.R(建模02)
||--output.R(输出)
||--output.Rmd(Markdown文件)
在main中就会像:
|--main
||--app01_main.R
||--app02_main.R
app01_main.R
source('./components/import.R')
source('./components/transfrom_01.R')
source('./components/model_01.R')
source('./components/output.R.R')
app02_main.R
source('./components/import.R')
source('./components/transfrom_02.R')
source('./components/model_02.R')
source('./components/output.R.R')
这样在main中就可以做到根据需求随意组合component,只需要在main中传入特定的参数就可以改变component输出。如有需要即可将参数存入数据库中,在main中for循环就可以输出需要的结果。
4. crontab_job
这个里面主要是写shell脚本,将有需要进行固定时间输出的R脚本放入其中:
app01_main.sh
#!/bin/bash
cd /home/.../crontab_job/
source /etc/profile
echo "starting app01_main job"
R CMD BATCH /home/.../main/app01_main.R
echo "ending app01_main job"
# 如脚本之间有前后依赖关系,写在一起就可以顺序执行
echo "starting app02_main job"
R CMD BATCH /home/.../main/app02_main.R
echo "ending app02_main job"
然后再放入/etc/crontab中,设置一下运行时间搞定。
10 8 * * * user /home/.../crontab_job/app01_main.sh> /home/.../crontab_job/app01_main.log 2>&1 &
5.test
这块可能在做分析的同学中不太常见,但是在工程化中测试很重要。具体怎么做测试可以参考
Testing · R packagesr-pkgs.had.co.nz大致就是将utils或者components,进行测试。保证代码的健壮性。
6.结语
本篇介绍了R语言项目的完整结构,以及对应的参考示例。希望能对大家有所帮助,如果有疑问或建议也欢迎在评论区与我讨论。
7.补充资料
主要放的是SQL以及R语言的代码规范
SQL style guide by Simon Holywell
SQL Style Guide
Google’s R Style Guide
Style guide · Advanced R.
The tidyverse style guide