一、引言

近年来,随着分布式数据处理技术的不断革新,Hive、Spark、Kylin、Impala、Presto 等工具不断推陈出新,对大数据集合的计算和存储成为现实,数据仓库/商业分析部门日益成为各类企业和机构的标配。在这种背景下,是否能探索和挖掘数据价值,具备精细化数据运营的能力,就成为判定一个数据团队成功与否的关键。

在数据从后台走向前台的过程中,数据展示是最后一步关键环节。与冰冷的表格展示相比,将数据转化成图表并进行适当的内容组织,往往能更快速、更直观的传递信息,进而更好的提供决策支持。从结构化数据到最终的展示,需要通过一系列的探索和分析过程去完成产品思路的沉淀,这个过程也伴随着大量的数据二次处理。

上述这些场合 R 语言有着独特的优势。本文将基于美团到店餐饮技术部的精细化数据运营实践,介绍 R 在数据分析与可视化方面的工程能力,希望能够抛砖引玉,也欢迎业界同行给我们提供更多的建议。

二、数据运营产品分类与 R 的优势

2.1 数据运营产品分类

在企业数据运营过程中,考虑使用场景、产品特点、实施角色以及可利用的工具,大致可以将数据运营需求分为四类,如下表所示,数据运营需求分类:

产品 应用场景 产品特点 实施角色 工具
分析报告 对模式不固定的数据进行探索、组织与解释,形成一次性数据分析报告并提供决策支持 基于人对数据的解读;需求发散 数据分析师、数据工程师 Excel、SQL、R、Tableau 等
报表型产品 通过拖拽式或简单代码方式进行开发,对模式固定的数据组装和报表展现 开发效率高,开发门槛低;报表表达能力差 数据分析师 报表工具
定制式分析型产品 对固定模式的数据和分析方法,形成可重复式的数据分析产品并提供决策支持 开发效率较高,支持对数据的深度应用,开发过程可复用、可扩展,对有一定编程能力的开发者开发门槛较低;产品交互能力较弱 数据分析师、数据工程师 Python、R、Tableau 等
定制式展示型产品 对固定模式的数据进行产品的高度定制,通过强化交互和用户体验,满足个性化的数据展示需求 展现样式丰富、交互能力强;仅适合有前端能力的开发者,开发效率较低,数据二次处理能力较差 前端工程师 ECharts、Highcharts 等

2.2 R 在数据运营上的优势

如上节所述,在精细化数据运营过程中,经常需要使用高度定制的数据处理、可视化、分析等手段,这些过程 Excel、Tableau、企业级报表工具都无法面面俱到,而恰好是 R 的强项。一般来说,R 具备的如下特征,让其有了“数据分析领域的瑞士军刀”的名号:

  • 免费、开源、可扩展:截至到 2018-08-02,“The CRAN package repository features 12858 available packages. ”,CRAN 上的软件包涉及贝叶斯分析、运筹学、金融、基因分析、遗传学等方方面面,并在持续新增和迭代。
  • 可编程:R 本身是一门解释型语言,可以通过代码控制执行过程,并能通过 rPython、rJava 等软件包实现和 Python、Java 语言的互相调用。
  • 强大的数据操控能力:
    • 数据源接入:通过 RMySQL、SparkR、elastic 等软件包,可以实现从 MySQL、Spark、Elasticsearch 等外部数据引擎获取数据。
    • 数据处理:内置 vector、list、matrix、data.frame 等数据结构,并能通过 sqldf、tidyr、dplyr、reshape2 等软件包实现对数据的二次加工。
    • 数据可视化:ggplot2、plotly、dygraph 等可视化包可以实现高度定制化的图表渲染。
    • 数据分析与挖掘:R 本身是一门由统计学家发起的面向统计分析的语言,通过自行编程实现或者第三方软件包调用,可以轻松实现线性回归、方差分析、主成分分析等分析与挖掘功能。
  • 初具雏形的服务框架:
    • Web 编程框架:例如不精通前端和系统开发的同学,通过 shiny 软件包开发自己的数据应用。
    • 服务化能力:例如通过 rserve 包,可以实现 R 和其他语言通信的 C/S 架构服务。

对于以数据为中心的应用来说,Python 和 R 都是不错的选择,两门语言在发展过程中也互有借鉴。“越接近统计研究与数据分析,越倾向 R;越接近工程开发工程环境的人,越倾向 Python”,Python 是一个全能型“运动员”,R 则更像是一个统计分析领域的“剑客”,“Python 并未建立起一个能与 CRAN 媲美的巨大的代码库,R 在这方面具有绝对领先优势。统计学并不是 Python 的核心使命”。各技术网站上有大量“Python VS R ”的讨论,感兴趣的读者可以自行了解和作出选择。

三、R 的数据处理、可视化、可重复性数据分析能力

对于具备编程能力的分析师或者具备分析能力的开发人员来说,在进行一系列长期的数据分析工程时,使用 R 既可以满足“一次开发,终身受用”,又可以满足“调整灵活,图形丰富”的要求。下文将分别介绍 R 的数据处理能力、可视化能力和可重复性数据分析能力。

3.1 数据处理

在企业级数据系统中,数据清洗、计算和整合工作会通过数据仓库、Hive、Spark、Kylin 等工具完成。对于数据运营项目,虽然 R 操作的是结果数据集,但也不能避免需要在查询层进行二次数据处理。

在数据查询层,R 生态现成就存在众多的组件支持,例如可以通过 RMySQL 包进行 MySQL 库表的查询,可以使用 Elastic 包对 Elasticsearch 索引文档进行搜索。对于 Kylin 等新技术,在 R 生态的组件支持没有跟上时,可以通过使用 Python、Java 等系统语言进行查询接口封装,在 R 内部使用 rPython、rJava 组件进行第三方查询接口调用。通过查询组件获取的数据一般以 data.frame、list 等类型对象存在。

另外 R 本身也拥有比较完备的二次数据处理能力。例如可以通过 sqldf 使用 sql 对 data.frame 对象进行数据处理,可以使用 reshape2 进行宽格式和窄格式的转化,可以使用 stringr 完成各种字符串处理,其他如排序、分组处理、缺失值填充等功能,也都具备完善的语言本身和生态的支持。

3.2 数据可视化

数据可视化是数据探索过程和结果呈现的关键环节,而 “R is a free software environment for statistical computing and graphics. ”,绘图(可视化)系统也是 R 的最大优势之一。

目前 R 主流支持的有三套可视化系统:

  • 内置系统:包括有 base、grid 和 lattice 三个内置发行包,支持以相对比较朴素的方式完成图形绘制。
  • ggplot2:由 RStudio 的首席科学家 Hadley Wickham 开发,ggplot2 通过一套图形语法支持,支持通过图层叠加以组合的方式支持高度定制的可视化。这一理念也逐步影响了包括 Plotly、阿里 AntV 等国内外数据可视化解决方案。截至到 2018-08-02,CRAN 已经落地了 40 个 ggplot2 扩展包,参考链接
  • htmlwidgets for R:这一系统是在 RStudio 支持下于 2016 年开始逐步发展壮大,提供基于 JavaScript 可视化的 R 接口。htmlwidgets for R 作为前端可视化(for 前端工程师)和数据分析可视化(for 数据工程师)的桥梁,发挥了两套技术领域之间的组合优势。截至到 2018-08-02,经过两年多的发展,目前 CRAN 上已经有 101 个基于 htmlwidgets 开发的第三方包,参考链接

实际数据运营分析过程中,可以固化常规的图表展现和可视化分析过程,实现代码复用,提高开发效率。下图是美团到店餐饮技术部数据团队积累的部分可视化组件示例:

《美团 R 语言数据运营实战》
美团 R 语言数据运营实战

图1 可视化组件示例

基于可视化组件库,一个可视化过程只需要一行代码即可完成,能极大提升开发效率。上图中最后的四象限矩阵分析示例图的代码如下:

vis_4quadrant(iris, 'Sepal.Length', 'Petal.Length', label = 'Species', tooltip = 'tooltip', title = '', xtitle = '萼片长度', ytitle = '花瓣长度', pointSize = 1, annotationSize = 1)

茲再附四象限矩阵分析可视化组件的函数声明:

vis_4quadrant <- function(df, x, y,
  label = '', tooltip = '', title = '', xtitle = '', ytitle = '',
  showLegend = T, jitter = T, centerType = 'mean',
  pointShape = 19, pointSize = 5, pointColors = collocatcolors2,
  lineSize = 0.4, lineType = 'dashed', lineColor = 'black',
  annotationFace = 'sans serif', annotationSize = 5, annotationColor = 'black', annotationDeviationRatio = 15,
  gridAnnotationFace = 'sans serif', gridAnnotationSize = 6, gridAnnotationColor = 'black', gridAnnotationAlpha = 0.6,
  titleFace = 'sans serif', titleSize = 12, titleColor = 'black',
  xyTitleFace = 'sans serif', xyTitleSize = 8, xyTitleColor = 'black',
  gridDesc = c('A 区', 'B 区', 'C 区', 'D 区'), dataMissingInfo = '数据不完整', renderType = 'widget') {

  # 绘制分组散点图
  #
  # Args:
  #   df: 数据框;必要字段;需要进行图形绘制的数据,至少应该有三列
  #   x: 字符串;必要字段;映射到 X 轴的列名,对应 df 的某一列,此列必须是数值类型或日期类型
  #   y: 字符串;必要字段;映射到 Y 轴的列名,对应 df 的某一列
  #   label: 字符串;映射到点上的文字注释
  #   tooltip: 字符串;映射到点上的悬浮信息
  #   title: 字符串;标题
  #   xtitle: 字符串;X 轴标题
  #   ytitle: 字符串;Y 轴标题
  #   showLegend: bool;定义分区图例是否展示
  #   jitter: bool;定义是否扰动
  #   centerType: 字符串;定义中心点类型,mean 代表平均值,median 代表中位数
  #   pointShape: 整形;定义点型
  #   pointSize: 数值;定义点大小
  #   lineSize: 数值;定义线宽
  #   lineType: 字符串;定义线型
  #   lineColor: 字符串;定义线色
  #   annotationFace: 字符串;定义注释字体
  #   annotationSize: 数值;定义注释字体大小
  #   annotationColor: 字符串;定义注释字体颜色
  #   annotationDeviationRatio: 数值;定义注释文本向上偏移系数
  #   gridAnnotationFace: 字符串;定义网格注释字体
  #   gridAnnotationSize: 数值;定义网格注释字体大小
  #   gridAnnotationColor: 字符串;定义网格注释字体颜色
  #   gridAnnotationAlpha: 数值;定义网格注释文本透明度
  #   titleFace: 字符串;定义标题字体
  #   titleSize: 数值;定义标题字体大小
  #   titleColor: 字符串;定义标题字体颜色
  #   xyTitleFace: 字符串;定义 X、Y 轴标题字体
  #   xyTitleSize: 数值;定义 X、Y 轴标题字体大小
  #   xyTitleColor: 字符串;定义 X、Y 轴标题字体颜色
  #   gridDesc: 长度为 4 的字符串向量
  #   dataMissingInfo: 字符串;数据问题提示文本
  #   renderType: 字符串;定义渲染结果类型,widget 对应 htmlwidget 组件,html 对应 html 内容