• Posts tagged "yaml"

Blog Archives

R语言中的配置管理yaml

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员/Quant: Java,R,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-yaml

前言

我们做程序开发,经常需要编写配置文件。早期时,我们多用properites文件,之后用XML,现在很多的应用都用JSON,直到YAML的出场。YAML是专门用来写配置文件的语言,YAML的首要设计目的是为了方便人们读写,而JSON的首要设计目标是简单性和通用性。因此,YAML可以看作是JSON的自然超集,可以提高人们的可读性和更完整的信息模型,每个JSON文件也是一个有效的YAML文件。

目录

  1. YAML是什么
  2. YAML语法介绍
  3. R语言yaml包使用
  4. R语言config包使用
  5. 案例:ETL过程的配置管理

1. YAML是什么

YAML 是 YAML Ain’t Markup Language的缩写,是一种数据序列化语言YAML主页。YAML强调以数据为中心,设计目标包括易于阅读,为不同编程语言提供方便的数据交换,适合描述数据结构,标准接口支持通用工具,支持一次性处理,丰富的表达能力与可扩展性,易于使用。

YAML支持3种数据结构:

  • 键值表,键值对的集合,包括映射,哈希,字典。
  • 序列,为一组排列的值,包括数组,列表。
  • 常量,为单个的不可再分隔的值,包括字符串,布尔值,整数,浮点数,Null,时间,日期

2. YAML语法介绍

由于YAML是JSON的自然超集,所以我们每个YAML语法段,都可以用JSON进行表示。本文介绍 YAML 的语法,你通过在线 JS-YAML 解析器进行验证。

1. YAML文件使用 Unicode 编码作为字符标准编码,默认为UTF-8。
2. 使用“#”来表示注释内容。
3. 使用空格作为嵌套缩进工具,通常建议使用两个空格缩进,不建议使用 tab。
4. YAML文件后缀为 .yml,如:abc.yml 。
5. YAML文件可以由一或多个文档组成,文档间使用—(三个横线)在每文档开始作为分隔符。同时,文档也可以使用…(三个点号)作为结束符。
6. 键值表:使用 :(冒号)空格表示单个键值对,使用”{}”表示一个键值表,”? ” 问号+空格表示复杂的键。当键是一个列表或键值表时,就需要使用本符号来标记。


date: 2015-02-01                                    # 单个键值对
items: {no: 1234, descript: cpu, price: 800.00}     # 多个键值对

JSON输出


{ date: Sun Feb 01 2015 08:00:00 GMT+0800 (中国标准时间),
  items: { no: 1234, descript: 'cpu', price: 800 } }

7. 序列表示:使用 -(横线)单个空格表示单个列表项, 使用”[]”表示一组数据, 组合表示。每个结构都可以嵌套组成复杂的表示结构。

7.1 序列


--- # 序列
- blue                                              # 列表
- red
- green

JSON输出


[ 'blue', 'red', 'green' ]

7.2 嵌套结构


-
  - [blue, red, green]        
  - [Age, Bag]

JSON输出


[ [ [ 'blue', 'red', 'green' ], [ 'Age', 'Bag' ] ] ]

7.3 复合结构对象


--- # 复合结构
languages:
 - English
 - 中文
 - 日本語
websites:
 blog: fens.me
 YAML: yaml.org 
 R: www.r-project.org

JSON输出


{ languages: [ 'English', '中文', '日本語' ],
  websites: { blog: 'fens.me', YAML: 'yaml.org', R: 'www.r-project.org' } }

8. 文本块:使用 “|” 和文本内容缩进表示的块:保留块中已有的回车换行,相当于段落块。使用 “>” 和文本内容缩进表示的块:将块中回车替换为空格,最终连接成一行。使用定界符“”(双引号)、‘’(单引号)或回车表示的块:最终表示成一行。

8.1 段落: 每行保留回车


lines: |          
  北京市朝阳区
  ABC小区#292

JSON输出

{ lines: '北京市朝阳区\nABC小区#292\n' }

8.2 一行:最后一个回车


lines: > 
  北京市朝阳区
  ABC小区#292

JSON输出

{ lines: '北京市朝阳区 ABC小区#292\n' }

8.3 连续一行:无回车


lines:    
  “北京市朝阳区
  ABC小区#292”

JSON输出

{ lines: '“北京市朝阳区 ABC小区#292”' }

9. 数据类型:整数,浮点数,字符串,NULL,日期,布尔,时间。


--- # 数据类型
integer: 12345     # 整数标准形式
octal: 0o34        # 八进制表示,第二个是字母 o
hex: 0xFF          # 十六进制表示
 
float: 1.23e+3     # 浮点数
fixed: 13.67       # 固定小数
minmin: -.inf      # 表示负无穷
notNumber: .NaN    # 无效数字
 
null:              # 空值
boolean: [true, false] # 布尔值
string: '12345'    # 字符串
 
date: 2015-08-23   # 日期
datetime: 2015-08-23T02:02:00.1z  # 日期时间
iso8601: 2015-08-23t21:59:43.10-05:00  # iso8601 日期格式
spaced: 2015-08-23 21:59:43.10 -5      # ?

JSON输出


{ integer: 12345,
  octal: '0o34',
  hex: 255,
  float: 1230,
  fixed: 13.67,
  minmin: -Infinity,
  notNumber: NaN,
  null: null,
  boolean: [ true, false ],
  string: '12345',
  date: Sun Aug 23 2015 08:00:00 GMT+0800 (中国标准时间),
  datetime: '2015-08-23T02:02:00.1z',
  iso8601: Mon Aug 24 2015 10:59:43 GMT+0800 (中国标准时间),
  spaced: Mon Aug 24 2015 10:59:43 GMT+0800 (中国标准时间) }

10. 特殊字符:!(叹号),!!(双叹号)


--- # 特殊字符
isString: !!str 2015-08-23     # 强调是字符串不是日期数据
picture: !!binary |            # Base64  图片
  R0lGODlhDAAMAIQAAP//9/X
  17unp5WZmZgAAAOfn515eXv
  Pz7Y6OjuDg4J+fn5OTk6enp
  56enmleECcgggoBADs=

JSON输出


{ isString: '2015-08-23',
  picture: 
   [ 71,
     73,
     70,
     56,
     57,
     97,
     12,
     0,
     12,
     0,
     132,
     0,
     0,
     255,
     255,
     247,
     245,
     245,
     238,
     233,
     233,
     229,
     102,
     102,
     102,
     0,
     0,
     0,
     231,
     231,
     231,
     94,
     94,
     94,
     243,
     243,
     237,
     142,
     142,
     142,
     224,
     224,
     224,
     159,
     159,
     159,
     147,
     147,
     147,
     167,
     167,
     167,
     158,
     158,
     158,
     105,
     94,
     16,
     39,
     32,
     130,
     10,
     1,
     0,
     59 ] }

11. 下面是内置类型
!!int # 整数类型
!!float # 浮点类型
!!bool # 布尔类型
!!str # 字符串类型
!!binary # 也是字符串类型
!!timestamp # 日期时间类型
!!null # 空值
!!set # 集合
!!omap, !!pairs # 键值列表或对象列表
!!seq # 序列,也是列表
!!map # 键值表

11.1 omap


--- !!omap
- Mark: 65
- Sammy: 63
- Key: 58

JSON输出

[ { Mark: 65 }, { Sammy: 63 }, { Key: 58 } ]

11.2 set


--- !!set           # 注意,“?”表示键为列表,在这里列表为 null
? Mark
? Sammy
? Key

JSON输出

{ Mark: null, Sammy: null, Key: null }

12. 锚点与引用:使用 “&” 定义数据锚点(即要复制的数据),使用 “*” 引用上述锚点数据(即数据的复制目的地)。


hr:
   - Mark McGwire
   # Following node labeled SS
   - &SS 定义要复制的数据
   
rbi:
   - *SS # Subsequent occurrence   这里是数据复制目标
   - Ken Griffey

JSON输出


{ hr: [ 'Mark McGwire', '定义要复制的数据' ],
  rbi: [ '定义要复制的数据', 'Ken Griffey' ] }

YAML 语法结构还是挺简单的,主要是3个数据结构类型的组合使用。我们理解了核心语法后,接下来就可以使用R语言来调用YAML了,进行在R语言中的配置管理。

3. R语言yaml包使用

R语言中,有多个包都可以进行YAML的数据解析,我来介绍2个包一个是yaml包,另一个是config包,下面我们分别介绍一下。2个包都能解析YAML文件,但设计目标是不同的。

  • yaml包,是专门用来解析YAML语法的包,核心功能就是解析YAML文件到R对象,再把R对象生成YAML文件。
  • config包,是专门用来进行配置管理的。用于项目配置管理的场景,当开发,测试和生产环境同一个参数需要配置不同的值,使用config包使用起来比yaml包更简单。

开发所使用的系统环境

  • Win10 64bit
  • R: 3.6.1 x86_64-w64-mingw32/x64 b4bit

先让我来学习一下yaml包,yaml包安装和使用,都很容易。首先,我们先进行安装。


> install.packages("yaml")
> library(yaml)

查看yaml包,发现一共就5个函数

  • read_yaml,读取一个YAML文件
  • write_yaml,写入到一个YAML文件
  • as.yaml,转换R对象为YAML字符串
  • yaml.load,转换YAML字符串到R对象
  • yaml.load_file,读文件转换YAML到R对象,看了一个代码和read_yaml()函数没有什么区别

接下来,我们新建一个abc.yml文件,用于描述一个订单。


invoice: 31223
date   : 2020-01-23
bill-to: &id001
    given  : 小明
    address:
        lines: |
            北京市朝阳区
            ABC小区 #292
        city    : 北京
ship-to: *id001
product:
    - sku         : BL394D
      quantity    : 4
      price       : 450.00
    - sku         : BL4438H
      quantity    : 1
      price       : 2392.00
tax  : 251.42
total: 4443.52

使用yaml包,来加载这个文件。

> abc<-read_yaml("abc.yml",fileEncoding = "UTF-8")

查看R语言结构,就是映射为R语言中的list结构。

在R语言中,修改结果。


# 修改日期
abc$date<- '2019-01-01'

# 去掉第二件商品
abc$product[2]<-NA

输出保存为新的文件abc2.yml

write_yaml(abc,"abc2.yml",fileEncoding = "UTF-8")

打开文件abc2.yml


invoice: 31223
date: '2019-01-01'
bill-to:
  given: 小明
  address:
    lines: |
      北京市朝阳区
      ABC小区 #292
    city: 北京
ship-to:
  given: 小明
  address:
    lines: |
      北京市朝阳区
      ABC小区 #292
    city: 北京
product:
- sku: BL394D
  quantity: 4
  price: 450.0
- .na
tax: 251.42
total: 4443.52

我们可以进行JSON转换,再通过JSON的格式进行观察。在R语言中JSON和R的详细介绍,请参考文章R和JSON的傻瓜式编程


# 加载JSON库
> library(RJSONIO)
> abcj<-toJSON(abc)

# 查看JSON
> cat(abcj)
{
 "invoice": 31223,
"date": "2019-01-01",
"bill-to": {
 "given": "小明",
"address": {
 "lines": "北京市朝阳区\nABC小区 #292\n",
"city": "北京" 
} 
},
"ship-to": {
 "given": "小明",
"address": {
 "lines": "北京市朝阳区\nABC小区 #292\n",
"city": "北京" 
} 
},
"product": [
 {
 "sku": "BL394D",
"quantity": 4,
"price":      450 
},
null 
],
"tax":   251.42,
"total":  4443.52 
}

4. R语言config包使用

config包,是RStudio公司开发的,是专门用来进行配置管理的。用于项目配置管理的场景,区别开发,测试和生产环境可能需要不同的值,使用起来比yaml更简单。

首先是安装。


> install.packages("config")
> library(config)

使用config包,我们新一个datasource.yml。用来描述,在软件项目开发时,开发环境(default),测试环境(test),生产环境(production)对于datafile这个参数,会用于不同的文件。


# 开发环境
default:
  time: 5
  datafile: "data-dev.csv"

# 测试环境
test:
  time: 30
  datafile: "data-test.csv"

# 生产环境
production:
  inherits: test
  datafile: "data-prod.csv"

使用config包读取datasource.yml。


# 默认情况会加载default配置
> dev <- config::get(file = "datasource.yml")
> dev
$time
[1] 5

$datafile
[1] "data-dev.csv"

attr(,"config")
[1] "default"
attr(,"file")
[1] "c:\\work\\R\\config\\datasource.yml"

当我们在生产环境时,可以在R的运行时环境配置好全局的环境变量,这个时候就可以直接取对prod的配置信息了。


# 设置全局变量
> Sys.setenv(R_CONFIG_ACTIVE = "production")

# 判断是否production被激活
> config::is_active("production")
[1] TRUE

# 获得配置
> prod <- config::get(file = "datasource.yml")
> prod
$time
[1] 30
$inherits
[1] "test"
$datafile
[1] "data-prod.csv"
attr(,"config")
[1] "production"
attr(,"file")
[1] "c:\\work\\R\\config\\datasource.yml"

所以,我们看到config包,提供了对于yaml的在软件管理的场景的使用方法,很简单,更方便。

5. 案例:ETL过程的配置管理

现在,我们已经完全了解了yaml的语法,以及程序如何调用的使用方法了,那么接下来就是真正地发挥YAML的作用了,把YAML的配置管理用于具体的业务逻辑中。

我设计了一个真实的场景,做数据项目的时候,经常需要对数据进行各种变形处理,就是常说的ETL过程。先加载数据,然后数据处理,最后输出数据,进行可视化。

数据的连续处理过程,在这个思路上其实也出现了一大批软件,通过界面拖拽就实现的功能操作。从软件设计的角度来说,数据连续处理的过程,是非常适合用配置管理的。以配置的方法简化了软件开发的复杂度,又能让用户自己参与其中,用户体验那叫一个爽。

我们就可以用YAML格式,来做为不同软件的核心数据交换结构。基于这个文字型的数据结构,用户可以自己编写,因为易读易懂。同时,基于这个文字型的数据结构,程序可以解析关键的配置信息,这样就打通人和程序的对话格式。把原本看起来很复杂的过程,进行了抽象,便利地实现,用户可参与的配置任务。

我设计一个task.yml的文件,用于描述一个数据处理的过程。


version: v1.0              # 定义版本

resource:                   # 数据源
  - database: &mysql1
      dbname: yaml
      host: localhost
      port: 3306
      username: yaml
      password: yaml
  - file: &file1 
      path: C:/work/demo/test.csv   
      file_encoding: 'utf-8'

data_input:                 # 数据读取
  method: mysql             # 数据读取方法,格式为 method: 方法名
  dataref: *mysql1
  param:                    
    table_name: iris_table
    columns: Sepal.Length

data_analysis:     
  method: mean              # 分析方法,格式为 method: 方法名
  param:                    # 参数
    na_rm: FALSE            # 具体参数格式 参数名: 参数值

data_output:
  method: csv               # 数据输出方法,格式为 method: 方法名
  dataref: *file1

配置解释:

  • version:定义配置文件版本
  • resource:定义数据源,数据库,文件等
  • data_input:定义数据数据输入,引用数据源
  • data_analysis: 定义数据处理方法,计算平均值
  • data_output:定义数据数据输出,引用数据源

运行R语言的代码,就可以执行上面的配置过程,下面的代码是只是一个代码结构,涉及到很多具体的函数调用细节,等下次在详细介绍。


library(magrittr)
task<-read_yaml("task.yml",fileEncoding = "UTF-8")
task

run<-function(task){
  data_input<-function(obj){}          #数据加载
  data_analysis<-function(dat,obj){}   #数据处理
  data_output<-function(dat,obj){}     #数据输出
  
  data_input(task$data_input)  %>% 
    data_analysis(a,task$data_analysis) %>% 
    data_output(task$data_output)
}

# 运行函数
run(task)

本文对YAML进行比较完整的介绍,配置管理工作是软件开发中非常重要的,现在工具越来越便利,把程序员可以从大量复杂的场景剥离出来,只专注于代码本身,可以极大地提高开发效率,并保证程序的质量。把YAML试试用起来吧。

转载请注明出处:
http://blog.fens.me/r-yaml

打赏作者