+关注

【R每日一贴】基本统计量计算

R

R具有非常强大的计算功能,R可以作为一个“大计算器”。基本统计量计算主要包括:均值计算、中位数计算、标准差计算、方差计算、协方差和相关系数计算。
R中如何完成上述计算,汇总如下。
基本统计量一:均值计算。使用mean()函数计算均值。
基本统计量二:中位数计算。使用median()函数计算中位数。
基本统计量三:标准差计算。使用sd()函数计算标准差。
基本统计量四:方差计算。使用var()函数计算方差。
基本统计量五:协方差计算。使用cov()函数计算协方差。
基本统计量六:相关系数计算。使用cor()函数计算相关系数。
关于R基本统计量计算,举例说明如下。
> data1 <- c(1, 3, 5, 7, 9)
> data1
[1] 1 3 5 7 9
> mean(data1)
[1] 5
> median(data1)
[1] 5
> sd(data1)
[1] 3.162278
> var(data1)
[1] 10
> data2  <- c(2, 4, 6, 8, 10)
> data2
[1]  2  4  6  8 10
> mean(data2)
[1] 6
> median(data2)
[1] 6
> sd(data2)
[1] 3.162278
> var(data2)
[1] 10
> cov(data1, data2)
[1] 10
> cor(data1, data2)
[1] 1

注意:关于基本统计量计算的函数对于数据缺失值敏感。某个变量的缺失值可能导致函数返回NA结果或是计算结果报错。数据中缺失值是否对统计结果产生严重影响?若是,计算机报错是正确的;若不是,可以通过设置参数na.rm=TRUE,告知R忽略缺失值。例如
> data3 <- c(1, 2, 3, NA)
> data3
[1]  1  2  3 NA
> mean(data3)
[1] NA
> mean(data3, na.rm=TRUE)
[1] 2
> sd(data3)
[1] NA
> sd(data3, na.rm=TRUE)
[1] 1

R基本统计量是统计分析的基本,一方面需要理解每个统计量的物理含义和统计意义,另一方面需要掌握如何基于R 计算各个基本统计量。
【想一想】
1基本统计量有哪些?每一种统计量表示什么含义?
2标准差和方差的计算公式是什么?
【做一做】
1查看women数据集,计算变量height和weight的均值、中位数、标准差、方差以及两者之间的协方差和相关系数。
2自定义峰度和偏度函数,计算上述变量的峰度和偏度。
继续阅读 »

R具有非常强大的计算功能,R可以作为一个“大计算器”。基本统计量计算主要包括:均值计算、中位数计算、标准差计算、方差计算、协方差和相关系数计算。
R中如何完成上述计算,汇总如下。
基本统计量一:均值计算。使用mean()函数计算均值。
基本统计量二:中位数计算。使用median()函数计算中位数。
基本统计量三:标准差计算。使用sd()函数计算标准差。
基本统计量四:方差计算。使用var()函数计算方差。
基本统计量五:协方差计算。使用cov()函数计算协方差。
基本统计量六:相关系数计算。使用cor()函数计算相关系数。
关于R基本统计量计算,举例说明如下。
> data1 <- c(1, 3, 5, 7, 9)
> data1
[1] 1 3 5 7 9
> mean(data1)
[1] 5
> median(data1)
[1] 5
> sd(data1)
[1] 3.162278
> var(data1)
[1] 10
> data2  <- c(2, 4, 6, 8, 10)
> data2
[1]  2  4  6  8 10
> mean(data2)
[1] 6
> median(data2)
[1] 6
> sd(data2)
[1] 3.162278
> var(data2)
[1] 10
> cov(data1, data2)
[1] 10
> cor(data1, data2)
[1] 1

注意:关于基本统计量计算的函数对于数据缺失值敏感。某个变量的缺失值可能导致函数返回NA结果或是计算结果报错。数据中缺失值是否对统计结果产生严重影响?若是,计算机报错是正确的;若不是,可以通过设置参数na.rm=TRUE,告知R忽略缺失值。例如
> data3 <- c(1, 2, 3, NA)
> data3
[1]  1  2  3 NA
> mean(data3)
[1] NA
> mean(data3, na.rm=TRUE)
[1] 2
> sd(data3)
[1] NA
> sd(data3, na.rm=TRUE)
[1] 1

R基本统计量是统计分析的基本,一方面需要理解每个统计量的物理含义和统计意义,另一方面需要掌握如何基于R 计算各个基本统计量。
【想一想】
1基本统计量有哪些?每一种统计量表示什么含义?
2标准差和方差的计算公式是什么?
【做一做】
1查看women数据集,计算变量height和weight的均值、中位数、标准差、方差以及两者之间的协方差和相关系数。
2自定义峰度和偏度函数,计算上述变量的峰度和偏度。 收起阅读 »

【R每日一贴】R结果重用?

R

R结果重用是指R的结果可以轻松保存,以作为进一步分析的输入信息,这是R的一个非常实用的特点,与其他统计软件(SAS,SPSS等)相对比,更能发现R对于结果处理的灵活性。
为了说明R结果重用这个命题,举例说明如下。
数据集:R自带的mtcars数据。
问题描述:探讨每加仑行驶的英里数mpg与车身重量wt之间的关系,或者说,通过wt去预测mpg。
问题求解:
第一步:执行回归,把结果保存到一个对象里面
lmfit <- lm(mpg ~ wt, data=mtcars)

第二步:查看对象结构和属性

查看对象结构

str(lmfit)

查看对象属性

attributes(lmfit)

第三步:查看对象内容

查看对象中的内容

lmfit

第四步:对象总结性描述

对象总结性描述,即对象摘要

summary(lmfit)
第五步:回归模型评估,即生成回归诊断图形
par(mfrow=c(2,2)) #四幅图形组合在一块
plot(lmfit)

第六步:绘制影响度量统计量图

计算影响度量统计量,并绘图

par(mfrow=c(1,1))
cooks<-cooks.distance(lmfit)
plot(cooks)

【想一想】
1 R中对象怎么理解?
2 影响度量统计量是什么意思?
【做一做】
1 查阅summary,lm和plot函数的R帮助文档和相关实例?
2 利用plot函数生成wt预测mpg的回归诊断图形?
继续阅读 »

R结果重用是指R的结果可以轻松保存,以作为进一步分析的输入信息,这是R的一个非常实用的特点,与其他统计软件(SAS,SPSS等)相对比,更能发现R对于结果处理的灵活性。
为了说明R结果重用这个命题,举例说明如下。
数据集:R自带的mtcars数据。
问题描述:探讨每加仑行驶的英里数mpg与车身重量wt之间的关系,或者说,通过wt去预测mpg。
问题求解:
第一步:执行回归,把结果保存到一个对象里面
lmfit <- lm(mpg ~ wt, data=mtcars)

第二步:查看对象结构和属性

查看对象结构

str(lmfit)

查看对象属性

attributes(lmfit)

第三步:查看对象内容

查看对象中的内容

lmfit

第四步:对象总结性描述

对象总结性描述,即对象摘要

summary(lmfit)
第五步:回归模型评估,即生成回归诊断图形
par(mfrow=c(2,2)) #四幅图形组合在一块
plot(lmfit)

第六步:绘制影响度量统计量图

计算影响度量统计量,并绘图

par(mfrow=c(1,1))
cooks<-cooks.distance(lmfit)
plot(cooks)

【想一想】
1 R中对象怎么理解?
2 影响度量统计量是什么意思?
【做一做】
1 查阅summary,lm和plot函数的R帮助文档和相关实例?
2 利用plot函数生成wt预测mpg的回归诊断图形? 收起阅读 »

MySQL索引原理及慢查询优化


MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库。虽然性能出色,但所谓“好马配好鞍”,如何能够更 好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”、“SQL语句优化”、“了解数据库原理”等要求。我们知道 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之重。

本人从13年7月份起,一直在美团核心业务系统部做慢查询的优化工作,共计十余个系统,累计解决和积累了上百个慢查询案例。随着业务的复杂性提升,遇到的问题千奇百怪,五花八门,匪夷所思。本文旨在以开发工程师的角度来解释数据库索引的原理和如何优化慢查询。

一个慢查询引发的思考
select

count(*)

from

task

where

status=2

and operator_id=20839

and operate_time>1371169729

and operate_time<1371174603

and type=2;


系统使用者反应有一个功能越来越慢,于是工程师找到了上面的SQL。

并且兴致冲冲的找到了我,“这个SQL需要优化,给我把每个字段都加上索引”

我很惊讶,问道“为什么需要每个字段都加上索引?”

“把查询的字段都加上索引会更快”工程师信心满满

“这种情况完全可以建一个联合索引,因为是最左前缀匹配,所以operate_time需要放到最后,而且还需要把其他相关的查询都拿来,需要做一个综合评估。”

“联合索引?最左前缀匹配?综合评估?”工程师不禁陷入了沉思。

多数情况下,我们知道索引能够提高查询效率,但应该如何建立索引?索引的顺序如何?许多人却只知道大概。其实理解这些概念并不难,而且索引的原理远没有想象的那么复杂。

MySQL索引原理

索引目的

索引的目的在于提高查询效率,可以类比字典,如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。如果没有索引,那么你可能需要把所有单词看一遍才能找到你想要的,如果我想找到m开头的单词呢?或者ze开头的单词呢?是不是觉得如果没有索引,这个事情根本无法完成?

索引原理

除了词典,生活中随处可见索引的例子,如火车站的车次表、图书的目录等。它们的原理都是一样的,通过不断的缩小想要获得数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是我们总是通过同一种查找方式来锁定数据。

数据库也是一样,但显然要复杂许多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查 询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数 据,1到100分成第一段,101到200分成第二段,201到300分成第三段......这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的,数据库实现比较复杂,数据保存在磁盘上,而为了提高性能,每次又可以把部分数据读入内 存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。

磁盘IO与预读

前面提到了访问磁盘,那么这里先简单介绍一下磁盘IO和预读,磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2=4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17=9ms左右,听起来还挺不错的,但要知道一台500-MIPS的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次IO的时间可以执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。下图是计算机硬件延迟的对比图,供大家参考:

104.png


考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。

索引的数据结构

前面讲了生活中索引的例子,索引的基本原理,数据库的复杂性,又讲了操作系统的相关知识,目的就是让大家了解,任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生。

详解b+树

107.jpg


如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色 所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

b+树的查找过程

如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1 的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

b+树性质

1.通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据 量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。

2.当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F) 这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以 只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

慢查询优化

关于MySQL索引原理是比较枯燥的东西,大家只需要有一个感性的认识,并不需要理解得非常透彻和深入。我们回头来看看一开始我们说的慢查询,了解完索引原理之后,大家是不是有什么想法呢?先总结一下索引的几大基本原则

建索引的几大原则

1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a=1 and b = 2 and c>3 and d=4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

2.=和in可以乱序,比如a=1 and b=2 and c=3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就 是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可

回到开始的慢查询

根据最左匹配原则,最开始的sql语句的索引应该是status、operator_id、type、operate_time的联合索引;其中status、operator_id、type的顺序可以颠倒,所以我才会说,把这个表的所有相关查询都找到,会综合分析;

比如还有如下查询
select * from task where status=0 and type=12 limit 10;

select count(*) from task where status=0 ;


那么索引建立成(status,type,operator_id,operate_time)就是非常正确的,因为可以覆盖到所有情况。这个就是利用了索引的最左匹配的原则

查询优化神器-explain命令

关于explain命令相信大家并不陌生,具体用法和字段含义可以参考官网explain-output,这里需要强调rows是核心指标,绝大部分rows小的语句执行一定很快(有例外,下面会讲到)。所以优化语句基本上都是在优化rows。

慢查询优化基本步骤

0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE

1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高

2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)

3.order by limit形式的sql语句让排序的表优先查

4.了解业务方使用场景

5.加索引时参照建索引的几大原则

6.观察结果,不符合预期继续从0分析

几个慢查询案例

下面几个例子详细解释了如何分析和优化慢查询

复杂语句写法

很多情况下,我们写SQL只是为了实现功能,这只是第一步,不同的语句书写方式对于效率往往有本质的差别,这要求我们对mysql的执行计划和索引原则有非常清楚的认识,请看下面的语句
select

distinct cert.emp_id

from

cm_log cl

inner join

(

select

emp.id as emp_id,

emp_cert.id as cert_id

from

employee emp

left join

emp_certificate emp_cert

on emp.id = emp_cert.emp_id

where

emp.is_deleted=0

) cert

on (

cl.ref_table='Employee'

and cl.ref_oid= cert.emp_id

)

or (

cl.ref_table='EmpCertificate'

and cl.ref_oid= cert.cert_id

)

where

cl.last_upd_date >='2013-11-07 15:03:00'

and cl.last_upd_date<='2013-11-08 16:00:00';

0.先运行一下,53条记录 1.87秒,又没有用聚合语句,比较慢

53 rows in set (1.87 sec)

1.explain

+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+

| 1 | PRIMARY | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where; Using temporary |

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 63727 | Using where; Using join buffer |

| 2 | DERIVED | emp | ALL | NULL | NULL | NULL | NULL | 13317 | Using where |

| 2 | DERIVED | emp_cert | ref | emp_certificate_empid | emp_certificate_empid | 4 | meituanorg.emp.id | 1 | Using index |

+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+


简述一下执行计划,首先mysql根据idx_last_upd_date索引扫描cm_log表获得379条记录;然后查表扫描了63727条记录,分为两部分,derived表示构造表,也就是不存在的表,可以简单理解成是一个语句形成的结果集,后面的数字表示语句的ID。derived2表示的是 ID=2的查询构造了虚拟表,并且返回了63727条记录。我们再来看看ID=2的语句究竟做了写什么返回了这么大量的数据,首先全表扫描employee表13317条记录,然后根据索引emp_certificate_empid关联emp_certificate表,rows=1表示,每个关联都只锁定了一条记录,效率比较高。获得后,再和cm_log的379条记录根据规则关联。从执行过程上可以看出返回了太多的数据,返回的 数据绝大部分cm_log都用不到,因为cm_log只锁定了379条记录。

如何优化呢?可以看到我们在运行完后还是要和cm_log做join,那么我们能不能之前和cm_log做join呢?仔细分析语句不难发现,其基本思想是如果cm_log的ref_table是EmpCertificate就关联emp_certificate表,如果ref_table是Employee就关联employee表,我们完全可以拆成两部分,并用union连接起来,注意这里用union,而不用union all是因为原语句有“distinct”来得到唯一的记录,而union恰好具备了这种功能。如果原语句中没有distinct不需要去重,我们就可以 直接使用union all了,因为使用union需要去重的动作,会影响SQL性能。

优化过的语句如下
select

emp.id

from

cm_log cl

inner join

employee emp

on cl.ref_table = 'Employee'

and cl.ref_oid = emp.id

where

cl.last_upd_date >='2013-11-07 15:03:00'

and cl.last_upd_date<='2013-11-08 16:00:00'

and emp.is_deleted = 0

union

select

emp.id

from

cm_log cl

inner join

emp_certificate ec

on cl.ref_table = 'EmpCertificate'

and cl.ref_oid = ec.id

inner join

employee emp

on emp.id = ec.emp_id

where

cl.last_upd_date >='2013-11-07 15:03:00'

and cl.last_upd_date<='2013-11-08 16:00:00'

and emp.is_deleted = 0


4.不需要了解业务场景,只需要改造的语句和改造之前的语句保持结果一致

5.现有索引可以满足,不需要建索引

6.用改造后的语句实验一下,只需要10ms 降低了近200倍!
+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+

| 1 | PRIMARY | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where |

| 1 | PRIMARY | emp | eq_ref | PRIMARY | PRIMARY | 4 | meituanorg.cl.ref_oid | 1 | Using where |

| 2 | UNION | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where |

| 2 | UNION | ec | eq_ref | PRIMARY,emp_certificate_empid | PRIMARY | 4 | meituanorg.cl.ref_oid | 1 | |

| 2 | UNION | emp | eq_ref | PRIMARY | PRIMARY | 4 | meituanorg.ec.emp_id | 1 | Using where |

| NULL | UNION RESULT | | ALL | NULL | NULL | NULL | NULL | NULL | |

+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+

53 rows in set (0.01 sec)

明确应用场景

举这个例子的目的在于颠覆我们对列的区分度的认知,一般上我们认为区分度越高的列,越容易锁定更少的记录,但在一些特殊的情况下,这种理论是有局限性的
select

*

from

stage_poi sp

where

sp.accurate_result=1

and (

sp.sync_status=0

or sp.sync_status=2

or sp.sync_status=4

);

0.先看看运行多长时间,951条数据6.22秒,真的很慢

951 rows in set (6.22 sec)

1.先explain,rows达到了361万,type = ALL表明是全表扫描

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

| 1 | SIMPLE | sp | ALL | NULL | NULL | NULL | NULL | 3613155 | Using where |

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

2.所有字段都应用查询返回记录数,因为是单表查询0已经做过了951条

3.让explain的rows 尽量逼近951

看一下accurate_result = 1的记录数

select count(*),accurate_result from stage_poi group by accurate_result;

+----------+-----------------+

| count(*) | accurate_result |

+----------+-----------------+

| 1023 | -1 |

| 2114655 | 0 |

| 972815 | 1 |

+----------+-----------------+

我们看到accurate_result这个字段的区分度非常低,整个表只有-1,0,1三个值,加上索引也无法锁定特别少量的数据

再看一下sync_status字段的情况

select count(*),sync_status from stage_poi group by sync_status;

+----------+-------------+

| count(*) | sync_status |

+----------+-------------+

| 3080 | 0 |

| 3085413 | 3 |

+----------+-------------+

同样的区分度也很低,根据理论,也不适合建立索引

问题分析到这,好像得出了这个表无法优化的结论,两个列的区分度都很低,即便加上索引也只能适应这种情况,很难做普遍性的优化,比如当sync_status 0、3分布的很平均,那么锁定记录也是百万级别的

4.找业务方去沟通,看看使用场景。业务方是这么来使用这个SQL语句的,每隔五分钟会扫描符合条件的数据,处理完成后把sync_status这个字段变成1,五分钟符合条件的记录数并不会太多,1000个左右。了解了业务方的使用场景后,优化这个SQL就变得简单了,因为业务方保证了数据的不平衡,如果加上索引可以过滤掉绝大部分不需要的数据

5.根据建立索引规则,使用如下语句建立索引
alter table stage_poi add index idx_acc_status(accurate_result,sync_status);

6.观察预期结果,发现只需要200ms,快了30多倍。
952rows in set(0.20 sec)

我们再来回顾一下分析问题的过程,单表查询相对来说比较好优化,大部分时候只需要把where条件里面的字段依照规则加上索引就好,如果只是这种“无脑”优化的话,显然一些区分度非常低的列,不应该加索引的列也会被加上索引,这样会对插入、更新性能造成严重的影响,同时也有可能影响其它的查询语句。所以我们第4步调差SQL的使用场景非常关键,我们只有知道这个业务场景,才能更好地辅助我们更好的分析和优化查询语句。

无法优化的语句
select

c.id,

c.name,

c.position,

c.sex,

c.phone,

c.office_phone,

c.feature_info,

c.birthday,

c.creator_id,

c.is_keyperson,

c.giveup_reason,

c.status,

c.data_source,

from_unixtime(c.created_time) as created_time,

from_unixtime(c.last_modified) as last_modified,

c.last_modified_user_id

from

contact c

inner join

contact_branch cb

on c.id = cb.contact_id

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 10802

and oei.org_category = - 1

order by

c.created_time desc limit 0 ,

10;


还是几个步骤

0.先看语句运行多长时间,10条记录用了13秒,已经不可忍受

10 rows in set (13.06 sec)

1.explain

+----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+

| 1 | SIMPLE | oei | ref | idx_category_left_right,idx_data_id | idx_category_left_right | 5 | const | 8849 | Using where; Using temporary; Using filesort |

| 1 | SIMPLE | bu | ref | PRIMARY,idx_userid_status | idx_userid_status | 4 | meituancrm.oei.data_id | 76 | Using where; Using index |

| 1 | SIMPLE | cb | ref | idx_branch_id,idx_contact_branch_id | idx_branch_id | 4 | meituancrm.bu.branch_id | 1 | |

| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 108 | meituancrm.cb.contact_id | 1 | |

+----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+

从执行计划上看,mysql先查org_emp_info表扫描8849记录,再用索引idx_userid_status关联branch_user表,再用索引idx_branch_id关联contact_branch表,最后主键关联contact表。

rows返回的都非常少,看不到有什么异常情况。我们在看一下语句,发现后面有order by+limit组合,会不会是排序量太大搞的?于是我们简化SQL,去掉后面的order by和limit,看看到底用了多少记录来排序
select

count(*)

from

contact c

inner join

contact_branch cb

on c.id = cb.contact_id

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 10802

and oei.org_category = - 1

+----------+

| count(*) |

+----------+

| 778878 |

+----------+

1 row in set (5.19 sec)

发现排序之前居然锁定了778878条记录,如果针对70万的结果集排序,将是灾难性的,怪不得这么慢,那我们能不能换个思路,先根据contact的created_time排序,再来join会不会比较快呢?

于是改造成下面的语句,也可以用straight_join来优化
select

c.id,

c.name,

c.position,

c.sex,

c.phone,

c.office_phone,

c.feature_info,

c.birthday,

c.creator_id,

c.is_keyperson,

c.giveup_reason,

c.status,

c.data_source,

from_unixtime(c.created_time) as created_time,

from_unixtime(c.last_modified) as last_modified,

c.last_modified_user_id

from

contact c

where

exists (

select

1

from

contact_branch cb

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 10802

and oei.org_category = - 1

where

c.id = cb.contact_id

)

order by

c.created_time desc limit 0 ,

10;

验证一下效果预计在1ms内,提升了13000多倍!

```sql

10 rows in set (0.00 sec)

本以为至此大工告成,但我们在前面的分析中漏了一个细节,先排序再join和先join再排序理论上开销是一样的,为何提升这么多是因为有一个limit!大致执行过程是:mysql先按索引排序得到前10条记录,然后再去join过滤,当发现不够10条的时候,再次去10条,再次join,这显然在内层join过滤的数据非常多的时候,将是灾难的,极端情况,内层一条数据都找不到,mysql还傻乎乎的每次取10条,几乎遍历了这个数据表!

用不同参数的SQL试验下
select

sql_no_cache c.id,

c.name,

c.position,

c.sex,

c.phone,

c.office_phone,

c.feature_info,

c.birthday,

c.creator_id,

c.is_keyperson,

c.giveup_reason,

c.status,

c.data_source,

from_unixtime(c.created_time) as created_time,

from_unixtime(c.last_modified) as last_modified,

c.last_modified_user_id

from

contact c

where

exists (

select

1

from

contact_branch cb

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 2875

and oei.org_category = - 1

where

c.id = cb.contact_id

)

order by

c.created_time desc limit 0 ,

10;

Empty set (2 min 18.99 sec)


2 min 18.99 sec!比之前的情况还糟糕很多。由于mysql的nested loop机制,遇到这种情况,基本是无法优化的。这条语句最终也只能交给应用系统去优化自己的逻辑了。

通过这个例子我们可以看到,并不是所有语句都能优化,而往往我们优化时,由于SQL用例回归时落掉一些极端情况,会造成比原来还严重的后果。所以,第一:不要指望所有语句都能通过SQL优化,第二:不要过于自信,只针对具体case来优化,而忽略了更复杂的情况。

慢查询的案例就分析到这儿,以上只是一些比较典型的案例。我们在优化过程中遇到过超过1000行,涉及到16个表join的“垃圾SQL”,也遇到过线上 线下数据库差异导致应用直接被慢查询拖死,也遇到过varchar等值比较没有写单引号,还遇到过笛卡尔积查询直接把从库搞死。再多的案例其实也只是一些经验的积累,如果我们熟悉查询优化器、索引的内部原理,那么分析这些案例就变得特别简单了。

写在后面的话

本文以一个慢查询案例引入了MySQL索引原理、优化慢查询的一些方法论;并针对遇到的典型案例做了详细的分析。其实做了这么长时间的语句优化后才发现,任何数据库层面的优化都抵不上应用系统的优化,同样是MySQL,可以用来支撑Google/FaceBook/Taobao应用,但可能连你的个人网站都撑不住。套用最近比较流行的话:“查询容易,优化不易,且写且珍惜!”
继续阅读 »

MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库。虽然性能出色,但所谓“好马配好鞍”,如何能够更 好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”、“SQL语句优化”、“了解数据库原理”等要求。我们知道 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之重。

本人从13年7月份起,一直在美团核心业务系统部做慢查询的优化工作,共计十余个系统,累计解决和积累了上百个慢查询案例。随着业务的复杂性提升,遇到的问题千奇百怪,五花八门,匪夷所思。本文旨在以开发工程师的角度来解释数据库索引的原理和如何优化慢查询。

一个慢查询引发的思考
select

count(*)

from

task

where

status=2

and operator_id=20839

and operate_time>1371169729

and operate_time<1371174603

and type=2;


系统使用者反应有一个功能越来越慢,于是工程师找到了上面的SQL。

并且兴致冲冲的找到了我,“这个SQL需要优化,给我把每个字段都加上索引”

我很惊讶,问道“为什么需要每个字段都加上索引?”

“把查询的字段都加上索引会更快”工程师信心满满

“这种情况完全可以建一个联合索引,因为是最左前缀匹配,所以operate_time需要放到最后,而且还需要把其他相关的查询都拿来,需要做一个综合评估。”

“联合索引?最左前缀匹配?综合评估?”工程师不禁陷入了沉思。

多数情况下,我们知道索引能够提高查询效率,但应该如何建立索引?索引的顺序如何?许多人却只知道大概。其实理解这些概念并不难,而且索引的原理远没有想象的那么复杂。

MySQL索引原理

索引目的

索引的目的在于提高查询效率,可以类比字典,如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。如果没有索引,那么你可能需要把所有单词看一遍才能找到你想要的,如果我想找到m开头的单词呢?或者ze开头的单词呢?是不是觉得如果没有索引,这个事情根本无法完成?

索引原理

除了词典,生活中随处可见索引的例子,如火车站的车次表、图书的目录等。它们的原理都是一样的,通过不断的缩小想要获得数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是我们总是通过同一种查找方式来锁定数据。

数据库也是一样,但显然要复杂许多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查 询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数 据,1到100分成第一段,101到200分成第二段,201到300分成第三段......这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的,数据库实现比较复杂,数据保存在磁盘上,而为了提高性能,每次又可以把部分数据读入内 存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。

磁盘IO与预读

前面提到了访问磁盘,那么这里先简单介绍一下磁盘IO和预读,磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2=4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17=9ms左右,听起来还挺不错的,但要知道一台500-MIPS的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次IO的时间可以执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。下图是计算机硬件延迟的对比图,供大家参考:

104.png


考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。

索引的数据结构

前面讲了生活中索引的例子,索引的基本原理,数据库的复杂性,又讲了操作系统的相关知识,目的就是让大家了解,任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生。

详解b+树

107.jpg


如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色 所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

b+树的查找过程

如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1 的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

b+树性质

1.通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据 量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。

2.当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F) 这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以 只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

慢查询优化

关于MySQL索引原理是比较枯燥的东西,大家只需要有一个感性的认识,并不需要理解得非常透彻和深入。我们回头来看看一开始我们说的慢查询,了解完索引原理之后,大家是不是有什么想法呢?先总结一下索引的几大基本原则

建索引的几大原则

1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a=1 and b = 2 and c>3 and d=4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

2.=和in可以乱序,比如a=1 and b=2 and c=3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就 是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可

回到开始的慢查询

根据最左匹配原则,最开始的sql语句的索引应该是status、operator_id、type、operate_time的联合索引;其中status、operator_id、type的顺序可以颠倒,所以我才会说,把这个表的所有相关查询都找到,会综合分析;

比如还有如下查询
select * from task where status=0 and type=12 limit 10;

select count(*) from task where status=0 ;


那么索引建立成(status,type,operator_id,operate_time)就是非常正确的,因为可以覆盖到所有情况。这个就是利用了索引的最左匹配的原则

查询优化神器-explain命令

关于explain命令相信大家并不陌生,具体用法和字段含义可以参考官网explain-output,这里需要强调rows是核心指标,绝大部分rows小的语句执行一定很快(有例外,下面会讲到)。所以优化语句基本上都是在优化rows。

慢查询优化基本步骤

0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE

1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高

2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)

3.order by limit形式的sql语句让排序的表优先查

4.了解业务方使用场景

5.加索引时参照建索引的几大原则

6.观察结果,不符合预期继续从0分析

几个慢查询案例

下面几个例子详细解释了如何分析和优化慢查询

复杂语句写法

很多情况下,我们写SQL只是为了实现功能,这只是第一步,不同的语句书写方式对于效率往往有本质的差别,这要求我们对mysql的执行计划和索引原则有非常清楚的认识,请看下面的语句
select

distinct cert.emp_id

from

cm_log cl

inner join

(

select

emp.id as emp_id,

emp_cert.id as cert_id

from

employee emp

left join

emp_certificate emp_cert

on emp.id = emp_cert.emp_id

where

emp.is_deleted=0

) cert

on (

cl.ref_table='Employee'

and cl.ref_oid= cert.emp_id

)

or (

cl.ref_table='EmpCertificate'

and cl.ref_oid= cert.cert_id

)

where

cl.last_upd_date >='2013-11-07 15:03:00'

and cl.last_upd_date<='2013-11-08 16:00:00';

0.先运行一下,53条记录 1.87秒,又没有用聚合语句,比较慢

53 rows in set (1.87 sec)

1.explain

+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+

| 1 | PRIMARY | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where; Using temporary |

| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 63727 | Using where; Using join buffer |

| 2 | DERIVED | emp | ALL | NULL | NULL | NULL | NULL | 13317 | Using where |

| 2 | DERIVED | emp_cert | ref | emp_certificate_empid | emp_certificate_empid | 4 | meituanorg.emp.id | 1 | Using index |

+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+


简述一下执行计划,首先mysql根据idx_last_upd_date索引扫描cm_log表获得379条记录;然后查表扫描了63727条记录,分为两部分,derived表示构造表,也就是不存在的表,可以简单理解成是一个语句形成的结果集,后面的数字表示语句的ID。derived2表示的是 ID=2的查询构造了虚拟表,并且返回了63727条记录。我们再来看看ID=2的语句究竟做了写什么返回了这么大量的数据,首先全表扫描employee表13317条记录,然后根据索引emp_certificate_empid关联emp_certificate表,rows=1表示,每个关联都只锁定了一条记录,效率比较高。获得后,再和cm_log的379条记录根据规则关联。从执行过程上可以看出返回了太多的数据,返回的 数据绝大部分cm_log都用不到,因为cm_log只锁定了379条记录。

如何优化呢?可以看到我们在运行完后还是要和cm_log做join,那么我们能不能之前和cm_log做join呢?仔细分析语句不难发现,其基本思想是如果cm_log的ref_table是EmpCertificate就关联emp_certificate表,如果ref_table是Employee就关联employee表,我们完全可以拆成两部分,并用union连接起来,注意这里用union,而不用union all是因为原语句有“distinct”来得到唯一的记录,而union恰好具备了这种功能。如果原语句中没有distinct不需要去重,我们就可以 直接使用union all了,因为使用union需要去重的动作,会影响SQL性能。

优化过的语句如下
select

emp.id

from

cm_log cl

inner join

employee emp

on cl.ref_table = 'Employee'

and cl.ref_oid = emp.id

where

cl.last_upd_date >='2013-11-07 15:03:00'

and cl.last_upd_date<='2013-11-08 16:00:00'

and emp.is_deleted = 0

union

select

emp.id

from

cm_log cl

inner join

emp_certificate ec

on cl.ref_table = 'EmpCertificate'

and cl.ref_oid = ec.id

inner join

employee emp

on emp.id = ec.emp_id

where

cl.last_upd_date >='2013-11-07 15:03:00'

and cl.last_upd_date<='2013-11-08 16:00:00'

and emp.is_deleted = 0


4.不需要了解业务场景,只需要改造的语句和改造之前的语句保持结果一致

5.现有索引可以满足,不需要建索引

6.用改造后的语句实验一下,只需要10ms 降低了近200倍!
+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+

| 1 | PRIMARY | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where |

| 1 | PRIMARY | emp | eq_ref | PRIMARY | PRIMARY | 4 | meituanorg.cl.ref_oid | 1 | Using where |

| 2 | UNION | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where |

| 2 | UNION | ec | eq_ref | PRIMARY,emp_certificate_empid | PRIMARY | 4 | meituanorg.cl.ref_oid | 1 | |

| 2 | UNION | emp | eq_ref | PRIMARY | PRIMARY | 4 | meituanorg.ec.emp_id | 1 | Using where |

| NULL | UNION RESULT | | ALL | NULL | NULL | NULL | NULL | NULL | |

+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+

53 rows in set (0.01 sec)

明确应用场景

举这个例子的目的在于颠覆我们对列的区分度的认知,一般上我们认为区分度越高的列,越容易锁定更少的记录,但在一些特殊的情况下,这种理论是有局限性的
select

*

from

stage_poi sp

where

sp.accurate_result=1

and (

sp.sync_status=0

or sp.sync_status=2

or sp.sync_status=4

);

0.先看看运行多长时间,951条数据6.22秒,真的很慢

951 rows in set (6.22 sec)

1.先explain,rows达到了361万,type = ALL表明是全表扫描

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

| 1 | SIMPLE | sp | ALL | NULL | NULL | NULL | NULL | 3613155 | Using where |

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

2.所有字段都应用查询返回记录数,因为是单表查询0已经做过了951条

3.让explain的rows 尽量逼近951

看一下accurate_result = 1的记录数

select count(*),accurate_result from stage_poi group by accurate_result;

+----------+-----------------+

| count(*) | accurate_result |

+----------+-----------------+

| 1023 | -1 |

| 2114655 | 0 |

| 972815 | 1 |

+----------+-----------------+

我们看到accurate_result这个字段的区分度非常低,整个表只有-1,0,1三个值,加上索引也无法锁定特别少量的数据

再看一下sync_status字段的情况

select count(*),sync_status from stage_poi group by sync_status;

+----------+-------------+

| count(*) | sync_status |

+----------+-------------+

| 3080 | 0 |

| 3085413 | 3 |

+----------+-------------+

同样的区分度也很低,根据理论,也不适合建立索引

问题分析到这,好像得出了这个表无法优化的结论,两个列的区分度都很低,即便加上索引也只能适应这种情况,很难做普遍性的优化,比如当sync_status 0、3分布的很平均,那么锁定记录也是百万级别的

4.找业务方去沟通,看看使用场景。业务方是这么来使用这个SQL语句的,每隔五分钟会扫描符合条件的数据,处理完成后把sync_status这个字段变成1,五分钟符合条件的记录数并不会太多,1000个左右。了解了业务方的使用场景后,优化这个SQL就变得简单了,因为业务方保证了数据的不平衡,如果加上索引可以过滤掉绝大部分不需要的数据

5.根据建立索引规则,使用如下语句建立索引
alter table stage_poi add index idx_acc_status(accurate_result,sync_status);

6.观察预期结果,发现只需要200ms,快了30多倍。
952rows in set(0.20 sec)

我们再来回顾一下分析问题的过程,单表查询相对来说比较好优化,大部分时候只需要把where条件里面的字段依照规则加上索引就好,如果只是这种“无脑”优化的话,显然一些区分度非常低的列,不应该加索引的列也会被加上索引,这样会对插入、更新性能造成严重的影响,同时也有可能影响其它的查询语句。所以我们第4步调差SQL的使用场景非常关键,我们只有知道这个业务场景,才能更好地辅助我们更好的分析和优化查询语句。

无法优化的语句
select

c.id,

c.name,

c.position,

c.sex,

c.phone,

c.office_phone,

c.feature_info,

c.birthday,

c.creator_id,

c.is_keyperson,

c.giveup_reason,

c.status,

c.data_source,

from_unixtime(c.created_time) as created_time,

from_unixtime(c.last_modified) as last_modified,

c.last_modified_user_id

from

contact c

inner join

contact_branch cb

on c.id = cb.contact_id

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 10802

and oei.org_category = - 1

order by

c.created_time desc limit 0 ,

10;


还是几个步骤

0.先看语句运行多长时间,10条记录用了13秒,已经不可忍受

10 rows in set (13.06 sec)

1.explain

+----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+

| 1 | SIMPLE | oei | ref | idx_category_left_right,idx_data_id | idx_category_left_right | 5 | const | 8849 | Using where; Using temporary; Using filesort |

| 1 | SIMPLE | bu | ref | PRIMARY,idx_userid_status | idx_userid_status | 4 | meituancrm.oei.data_id | 76 | Using where; Using index |

| 1 | SIMPLE | cb | ref | idx_branch_id,idx_contact_branch_id | idx_branch_id | 4 | meituancrm.bu.branch_id | 1 | |

| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 108 | meituancrm.cb.contact_id | 1 | |

+----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+

从执行计划上看,mysql先查org_emp_info表扫描8849记录,再用索引idx_userid_status关联branch_user表,再用索引idx_branch_id关联contact_branch表,最后主键关联contact表。

rows返回的都非常少,看不到有什么异常情况。我们在看一下语句,发现后面有order by+limit组合,会不会是排序量太大搞的?于是我们简化SQL,去掉后面的order by和limit,看看到底用了多少记录来排序
select

count(*)

from

contact c

inner join

contact_branch cb

on c.id = cb.contact_id

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 10802

and oei.org_category = - 1

+----------+

| count(*) |

+----------+

| 778878 |

+----------+

1 row in set (5.19 sec)

发现排序之前居然锁定了778878条记录,如果针对70万的结果集排序,将是灾难性的,怪不得这么慢,那我们能不能换个思路,先根据contact的created_time排序,再来join会不会比较快呢?

于是改造成下面的语句,也可以用straight_join来优化
select

c.id,

c.name,

c.position,

c.sex,

c.phone,

c.office_phone,

c.feature_info,

c.birthday,

c.creator_id,

c.is_keyperson,

c.giveup_reason,

c.status,

c.data_source,

from_unixtime(c.created_time) as created_time,

from_unixtime(c.last_modified) as last_modified,

c.last_modified_user_id

from

contact c

where

exists (

select

1

from

contact_branch cb

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 10802

and oei.org_category = - 1

where

c.id = cb.contact_id

)

order by

c.created_time desc limit 0 ,

10;

验证一下效果预计在1ms内,提升了13000多倍!

```sql

10 rows in set (0.00 sec)

本以为至此大工告成,但我们在前面的分析中漏了一个细节,先排序再join和先join再排序理论上开销是一样的,为何提升这么多是因为有一个limit!大致执行过程是:mysql先按索引排序得到前10条记录,然后再去join过滤,当发现不够10条的时候,再次去10条,再次join,这显然在内层join过滤的数据非常多的时候,将是灾难的,极端情况,内层一条数据都找不到,mysql还傻乎乎的每次取10条,几乎遍历了这个数据表!

用不同参数的SQL试验下
select

sql_no_cache c.id,

c.name,

c.position,

c.sex,

c.phone,

c.office_phone,

c.feature_info,

c.birthday,

c.creator_id,

c.is_keyperson,

c.giveup_reason,

c.status,

c.data_source,

from_unixtime(c.created_time) as created_time,

from_unixtime(c.last_modified) as last_modified,

c.last_modified_user_id

from

contact c

where

exists (

select

1

from

contact_branch cb

inner join

branch_user bu

on cb.branch_id = bu.branch_id

and bu.status in (

1,

2)

inner join

org_emp_info oei

on oei.data_id = bu.user_id

and oei.node_left >= 2875

and oei.node_right <= 2875

and oei.org_category = - 1

where

c.id = cb.contact_id

)

order by

c.created_time desc limit 0 ,

10;

Empty set (2 min 18.99 sec)


2 min 18.99 sec!比之前的情况还糟糕很多。由于mysql的nested loop机制,遇到这种情况,基本是无法优化的。这条语句最终也只能交给应用系统去优化自己的逻辑了。

通过这个例子我们可以看到,并不是所有语句都能优化,而往往我们优化时,由于SQL用例回归时落掉一些极端情况,会造成比原来还严重的后果。所以,第一:不要指望所有语句都能通过SQL优化,第二:不要过于自信,只针对具体case来优化,而忽略了更复杂的情况。

慢查询的案例就分析到这儿,以上只是一些比较典型的案例。我们在优化过程中遇到过超过1000行,涉及到16个表join的“垃圾SQL”,也遇到过线上 线下数据库差异导致应用直接被慢查询拖死,也遇到过varchar等值比较没有写单引号,还遇到过笛卡尔积查询直接把从库搞死。再多的案例其实也只是一些经验的积累,如果我们熟悉查询优化器、索引的内部原理,那么分析这些案例就变得特别简单了。

写在后面的话

本文以一个慢查询案例引入了MySQL索引原理、优化慢查询的一些方法论;并针对遇到的典型案例做了详细的分析。其实做了这么长时间的语句优化后才发现,任何数据库层面的优化都抵不上应用系统的优化,同样是MySQL,可以用来支撑Google/FaceBook/Taobao应用,但可能连你的个人网站都撑不住。套用最近比较流行的话:“查询容易,优化不易,且写且珍惜!” 收起阅读 »

【R每日一贴】R包那些事?


R包是R功能更强大,应用更灵活。什么是R包?R包是R函数、数据、预编译代码以一种定义完善的格式组成的集合。R包设计各种领域,比方说:统计学、生物信息学、金融学、机器学习、数据挖掘等。用户可以根据自己的问题域选择和使用合适的R包,当然,也可以在前人的基础,构建属于自己的R包,若是贡献出来,获得R社区的审核通过后,可以进一步扩充和完善R。
R软件安装成功后,自带一些R默认包,分别是:
1)  base
2)  datasets
3)  utils
4)  grDevices
5)  graphics
6)  stats
7)  methods

需要了解每一个包的详细情况,可以查看R帮助文档。
例如>
help(package="base")
#查阅base包详情
与R包相关的一些函数汇总
1).libPaths()
#显示库(计算上存储R包的目录)所在的位置。
2)library()
#显示库中有哪些包
3)search()
#告诉用户哪些包已经加载并可以使用。
4)install.packages("R包名")
#R包安装函数,包名一定要置于双引号里面,非常重要。
举例说明如下。
安装包gclus,这个包提供了增强型散点图的函数
>install.packages("gclus")

5)update.packages()
#更新已经安装的包
6)installed.packages()
#列出已经安装的包,以及版本号,依赖关系等信息
7)library("R包名")
#加载R包,若是没有安装,会提示该包不存在。
【想一想】
1 如何寻找适合自己问题域的R包?
2 如何确定是否安装了某一个R包?
【做一做】
1 安装与聚类相关的包?
2 查看datasets包里面提供了那些数据集?iris数据集怎么描述?
继续阅读 »

R包是R功能更强大,应用更灵活。什么是R包?R包是R函数、数据、预编译代码以一种定义完善的格式组成的集合。R包设计各种领域,比方说:统计学、生物信息学、金融学、机器学习、数据挖掘等。用户可以根据自己的问题域选择和使用合适的R包,当然,也可以在前人的基础,构建属于自己的R包,若是贡献出来,获得R社区的审核通过后,可以进一步扩充和完善R。
R软件安装成功后,自带一些R默认包,分别是:
1)  base
2)  datasets
3)  utils
4)  grDevices
5)  graphics
6)  stats
7)  methods

需要了解每一个包的详细情况,可以查看R帮助文档。
例如>
help(package="base")
#查阅base包详情
与R包相关的一些函数汇总
1).libPaths()
#显示库(计算上存储R包的目录)所在的位置。
2)library()
#显示库中有哪些包
3)search()
#告诉用户哪些包已经加载并可以使用。
4)install.packages("R包名")
#R包安装函数,包名一定要置于双引号里面,非常重要。
举例说明如下。
安装包gclus,这个包提供了增强型散点图的函数
>install.packages("gclus")

5)update.packages()
#更新已经安装的包
6)installed.packages()
#列出已经安装的包,以及版本号,依赖关系等信息
7)library("R包名")
#加载R包,若是没有安装,会提示该包不存在。
【想一想】
1 如何寻找适合自己问题域的R包?
2 如何确定是否安装了某一个R包?
【做一做】
1 安装与聚类相关的包?
2 查看datasets包里面提供了那些数据集?iris数据集怎么描述? 收起阅读 »

SAS学习笔记(5): SAS语言的数据管理功能(下)

SAS

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:
  1. 用SET和OUTPUT语句拆分数据集


有时我们需要根据某一分类原则把数据行分别存放到不同的数据集。比如,我们希望把数据集a中的所有男生的观测放到数据集am中,把所有女生的观测放到af中,可以使用如下程序:
data am af;

set a;

select(sex);

when ('男') output am;

when('女') output af;

otherwise put sex='有错';

end;

drop sex; /*去掉sex这一列*/

run;

proc print data=am;run;

proc print data=af;run;


OUTPUT语句是一个可执行语句,它使得当前观测被写到语句指定的数据集中。这样,我们根据SELECT的结果把不同性别分别放到了两个不同数据集中。

OUTPUT语句还可以用来强行写入数据集而不必象我们在数据步流程图中说明的那样等到数据步最后一个语句完成。数据步中有了OUTPUT语句后数据步流程中不再有自动写入观测的操作,而只能由OUTPUT语句指定输出。不指定数据集名的OUTPUT语句输出到第一个结果数据集。比如下面的程序生成一个包含1到10的及其平方的有10个观测的数据集:
data sq;

do i=1 to 10;

j=i*i;

 output;

end;

run;

proc print;run;


如果删去上面的OUTPUT语句则结果数据集中只有i=11,j=100的一个观测。
  1. 数据集的纵向合并


几个结构相同的数据集可以上下地连接到一起。
data classes;

set class1 class2 class3;

run;


有时我们需要在合并数据集时加入一个变量来指示每一个观测原来来自哪一个小数据集,这可以在SET语句的每一个数据集名后面加一个括号里面写上IN=变量名,变量名所给的变量取1表示观测来自此数据集,取0表示观测非来自此数据集。例如,我们把a数据集按男、女拆分成了am和af两个数据集并抛弃了性别变量,就可以用如下程序连接两个数据集并恢复性别信息:
data new;

set am(in=male) af(in=female);

if male=1 then sex='男';

if female=1 then sex='女';

run;

  1. 数据集的横向合并
    {{{
    data new;


merge ssa ssb ssc;

run;
}}}

若数据集的观测顺序不一样,一般应该采用按关键字合并的办法,排序过程如下:
proc sort data=ssa;

by name;

run;

proc sort data=ssb;

by name;

run;

proc sort data=ssc;

by name;

run;

  1. 用updata语句更新数据集


如果我们发现数据集中的某些数据值有错误或者现在的值已经改变了,我们可以从更正了的原始数据重新生成数据集,或者使用更有效的方法,即建立一个只包含新数据值的数据集,用此数据集修改原数据集。使用如下的DATA步中可以实现数据集的更新:

DATA 新数据集名;

UPDATE 原数据集 更新用数据集;

BY关键变量;

RUN;

例如,数据集C9501中王思明的语文成绩实际应该是91分,张红艺性别应为男,可以先生成如下的只包含更正数据值的数据集,不需要改的观测不列入,不需要改的变量不列入或取缺失值:
data upd;

input name $ sex $ math;

cards;

张红艺 男 .

王思明 . 91



run;


然后,把原数据集C9501和更新用数据集UPD均按姓名排序:
proc sort data=c9501;

by name;run;

proc sort data=upd;

by name;run;


最后用UPDATE和BY更新得到新数据集NEW,

data new;

update c9501 upd;

by name;

run;

  1. 用PROC SQL管理数据


用PROC SQL作查询的最简单的用法如下
PROC SQL;

SELECT 第一项,第二项,…,第n项

FROM数据集

WHERE 观测选择条件;

RUN;


其使用方法与SQL语言基本一致
继续阅读 »

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:
  1. 用SET和OUTPUT语句拆分数据集


有时我们需要根据某一分类原则把数据行分别存放到不同的数据集。比如,我们希望把数据集a中的所有男生的观测放到数据集am中,把所有女生的观测放到af中,可以使用如下程序:
data am af;

set a;

select(sex);

when ('男') output am;

when('女') output af;

otherwise put sex='有错';

end;

drop sex; /*去掉sex这一列*/

run;

proc print data=am;run;

proc print data=af;run;


OUTPUT语句是一个可执行语句,它使得当前观测被写到语句指定的数据集中。这样,我们根据SELECT的结果把不同性别分别放到了两个不同数据集中。

OUTPUT语句还可以用来强行写入数据集而不必象我们在数据步流程图中说明的那样等到数据步最后一个语句完成。数据步中有了OUTPUT语句后数据步流程中不再有自动写入观测的操作,而只能由OUTPUT语句指定输出。不指定数据集名的OUTPUT语句输出到第一个结果数据集。比如下面的程序生成一个包含1到10的及其平方的有10个观测的数据集:
data sq;

do i=1 to 10;

j=i*i;

 output;

end;

run;

proc print;run;


如果删去上面的OUTPUT语句则结果数据集中只有i=11,j=100的一个观测。
  1. 数据集的纵向合并


几个结构相同的数据集可以上下地连接到一起。
data classes;

set class1 class2 class3;

run;


有时我们需要在合并数据集时加入一个变量来指示每一个观测原来来自哪一个小数据集,这可以在SET语句的每一个数据集名后面加一个括号里面写上IN=变量名,变量名所给的变量取1表示观测来自此数据集,取0表示观测非来自此数据集。例如,我们把a数据集按男、女拆分成了am和af两个数据集并抛弃了性别变量,就可以用如下程序连接两个数据集并恢复性别信息:
data new;

set am(in=male) af(in=female);

if male=1 then sex='男';

if female=1 then sex='女';

run;

  1. 数据集的横向合并
    {{{
    data new;


merge ssa ssb ssc;

run;
}}}

若数据集的观测顺序不一样,一般应该采用按关键字合并的办法,排序过程如下:
proc sort data=ssa;

by name;

run;

proc sort data=ssb;

by name;

run;

proc sort data=ssc;

by name;

run;

  1. 用updata语句更新数据集


如果我们发现数据集中的某些数据值有错误或者现在的值已经改变了,我们可以从更正了的原始数据重新生成数据集,或者使用更有效的方法,即建立一个只包含新数据值的数据集,用此数据集修改原数据集。使用如下的DATA步中可以实现数据集的更新:

DATA 新数据集名;

UPDATE 原数据集 更新用数据集;

BY关键变量;

RUN;

例如,数据集C9501中王思明的语文成绩实际应该是91分,张红艺性别应为男,可以先生成如下的只包含更正数据值的数据集,不需要改的观测不列入,不需要改的变量不列入或取缺失值:
data upd;

input name $ sex $ math;

cards;

张红艺 男 .

王思明 . 91



run;


然后,把原数据集C9501和更新用数据集UPD均按姓名排序:
proc sort data=c9501;

by name;run;

proc sort data=upd;

by name;run;


最后用UPDATE和BY更新得到新数据集NEW,

data new;

update c9501 upd;

by name;

run;

  1. 用PROC SQL管理数据


用PROC SQL作查询的最简单的用法如下
PROC SQL;

SELECT 第一项,第二项,…,第n项

FROM数据集

WHERE 观测选择条件;

RUN;


其使用方法与SQL语言基本一致 收起阅读 »

SAS学习笔记(4): SAS语言的数据管理功能(上)

SAS
  1. SAS数据步的运行机制

data a;

put x= y= z=;

input x y;

z=x+y;

put x= y= z=;

cards;

10 20

100 200

;

run;

运行结果

x=. y=. z=.

x=10 y=20 z=30

x=. y=. z=.

x=100 y=200 z=300

x=. y=. z=.

NOTE: 数据集 WORK.A 有 2 个观测和 3 个变量。


从这个例子可以看出SAS数据步程序和普通程序的一个重大区别:SAS数据步如果有数据输入,比如用INPUT、SET、MERGE、UPDATE、MODIFY等语句读入数据,则数据步中隐含了一个循环,即数据步程序执行到最后一个语句后,会返回到数据步内的第一个可执行语句开始继续执行,直到读入数据语句(INPUT、SET、MERGE、UPDATE、MODIFY等)读入了数据结束标志为止才停止执行数据步,并把读入的各个观测写入在DATA语句中指定的数据集。如果没有数据输入而只是直接计算,则数据步程序不需要此隐含循环。
  1. 用input语句输入数据


从原始数据输入要使用input语句,数据的行写在cards语句和一个只有一个顶头的分号的行之间。

例如:自由格式
data a;

input name $ sex $ math chinese;

cards;

黎明 男 98 97

张红 女 94 98



run;


如果各数据行的各个数据项是上下对齐的,还可以使用INPUT语句的列方式。这时,除了在INPUT关键字后面列出变量名外,还需要在每个变量名(及$符)后面列出该变量在数据行中所占据的起始位置与结束位置。

列方式不要求数据项之间分开,所以经常用来输入紧缩格式的数据。比如,我们要输入一批身份证号码,但只输入其中的出生年、月、日信息,就可以用如下程序

1-3.jpg


如果需要完全原样地输入字符型数据(包括头尾空格、单独的小数点),可以用有格式输入,即在字符型变量名和$符后加上一个输入格式如CHAR1O.表示读入10个字符。

有特殊格式的数据需要用有格式输入,即在变量名后加格式名。其中最常见的是用来输入日期。数据中的日期写法经常是多种多样的,比如1998年10月9日可以写成"1998-10-9","19981009","9/10/98"等等,为读入这样的日期数据就需要为它指定特殊的日期输入格式。另外,日期数据在SAS中是按数值存储的,所以如果要显示日期值,也需要为它指定特殊的日期输出格式。例如:

1-4.jpg

  1. 变量属性


变量的属性包括:

(1)字符型还是数值型。INPUT语句读入字符型数据时要在变量名后面加$符。

(2)变量标签(LABEL)。可以给变量加一个长度不超过40个字符的标签(可以用汉字,不超过20个汉字),标签可以用在以后的报表中。

(3)变量存储长度(LENGTH)。数值型数据一般长度为8字节,也可以对取值范围小的变量规定较小的长度以节省存储空间。字符型变量长度为其能存储的字符个数,缺省为8个字节

数据步中的ATTRIB语句可以指定变量的这些属性。格式为:ATTRIB 变量名 属性 变量名 属性…;

可以同时指定多个变量的属性。属性为"属性名=属性值"这样的写法,可以为一个变量同时指定多个属性。见如下的例子

1-5.jpg

  1. 读入外部数据


文本格式:把原始数据放在一个普通的文本格式的文件中,然后用INFILE语句指定输入文件名。例如,我们可以把数据行单独生成一个文本文件aa.txt,假设放在了f:\中,可以用如下程序读入文件中的数据并生成数据集:
data zz;

infile 'f:\aa.txt';

input name $ sex $ age;

run;

proc print;

run;


注意INFILE语句要写在INPUT语句之前,有INFILE语句就不再有CARDS语句和空语句。INFILE关键字后面跟的是一个包含文件名的字符串,可以使用全路径名,如果只有文件名则在当前工作目录寻找。

微机格式的数据文件

可以使用SAS系统File菜单中的Import命令读入其它格式的文件。并且可以将操作步骤的程序保存下来留着下次用。

与大型数据库的接口

SAS提供了两种办法可以访问大型数据库。SAS/ACCESS 可以直接连接Oracle、SYbase、SQLServer等大型数据库。为了访问储存在这些数据库中的表,需要对数据库中的表在SAS中建立访问描述文件(access descriptor),和视图描述文件(view descriptor)。例如,在数据库服务器DBIN中有一个数据库Finance,其中有一个表Sales,用户名guest用密码anyone可以访问此库,就可以用以下程序在SAS中建立访问描述文件和视图文件:

1-6.jpg

1-6-1.jpg


其中大写的部分是固定的。这段程序首先生成了访问描述文件SASUSER. SALES. ACCESS,然后由此访问描述文件生成了视图文件SASUSER. SALESALL.VIEW。在SAS中视图文件和数据集的使用是一样的,可以使用数据集的地方都可以使用视图文件。
  1. 数据集的复制与修改


可以用SET语句把一个已有数据集复制到一个新数据集,同时还可以进行修改。如果只是复制数据集,也可以用SAS管理器(SAS Explorer)完成。比如要把数据集WORK.C9501复制为数据集SASUSER.CLS,只要用如下程序:
data sasuser.cls;

set c9501;

run;


在复制的同时我们还可以用SAS程序语句对生成的数据集进行修改。比如,我们把超过100分的语文成绩都改为100分,就可以用如下程序:
data c9501a;

set c9501;

if chinese>100 then chinese=100;

run;


在数据步中可以用KEEP语句或DROP语句指定要保留的变量或要丢弃的变量。

也可以指定一个条件取出数据集的某些行组成的子集。比如,我们希望取出数学分数90分以上,语文分数100分以上的学生的观测,可以用如下的"子集IF语句":

1-7.jpg


在用SET语句引入数据集时可以给引入的数据集加选项,选项放在数据集名后的括号内:

数据集名(数据集选项)

选项包括KEEP=,表示引入时只要指定的变量;DROP=,表示不引入指定的变量;OBS=,表示读取观测时读到指定的序号为止(是序号而不是观测数);FIRSTOBS=,表示从指定序号的观测开始读取而跳过之前的观测不读。
继续阅读 »
  1. SAS数据步的运行机制

data a;

put x= y= z=;

input x y;

z=x+y;

put x= y= z=;

cards;

10 20

100 200

;

run;

运行结果

x=. y=. z=.

x=10 y=20 z=30

x=. y=. z=.

x=100 y=200 z=300

x=. y=. z=.

NOTE: 数据集 WORK.A 有 2 个观测和 3 个变量。


从这个例子可以看出SAS数据步程序和普通程序的一个重大区别:SAS数据步如果有数据输入,比如用INPUT、SET、MERGE、UPDATE、MODIFY等语句读入数据,则数据步中隐含了一个循环,即数据步程序执行到最后一个语句后,会返回到数据步内的第一个可执行语句开始继续执行,直到读入数据语句(INPUT、SET、MERGE、UPDATE、MODIFY等)读入了数据结束标志为止才停止执行数据步,并把读入的各个观测写入在DATA语句中指定的数据集。如果没有数据输入而只是直接计算,则数据步程序不需要此隐含循环。
  1. 用input语句输入数据


从原始数据输入要使用input语句,数据的行写在cards语句和一个只有一个顶头的分号的行之间。

例如:自由格式
data a;

input name $ sex $ math chinese;

cards;

黎明 男 98 97

张红 女 94 98



run;


如果各数据行的各个数据项是上下对齐的,还可以使用INPUT语句的列方式。这时,除了在INPUT关键字后面列出变量名外,还需要在每个变量名(及$符)后面列出该变量在数据行中所占据的起始位置与结束位置。

列方式不要求数据项之间分开,所以经常用来输入紧缩格式的数据。比如,我们要输入一批身份证号码,但只输入其中的出生年、月、日信息,就可以用如下程序

1-3.jpg


如果需要完全原样地输入字符型数据(包括头尾空格、单独的小数点),可以用有格式输入,即在字符型变量名和$符后加上一个输入格式如CHAR1O.表示读入10个字符。

有特殊格式的数据需要用有格式输入,即在变量名后加格式名。其中最常见的是用来输入日期。数据中的日期写法经常是多种多样的,比如1998年10月9日可以写成"1998-10-9","19981009","9/10/98"等等,为读入这样的日期数据就需要为它指定特殊的日期输入格式。另外,日期数据在SAS中是按数值存储的,所以如果要显示日期值,也需要为它指定特殊的日期输出格式。例如:

1-4.jpg

  1. 变量属性


变量的属性包括:

(1)字符型还是数值型。INPUT语句读入字符型数据时要在变量名后面加$符。

(2)变量标签(LABEL)。可以给变量加一个长度不超过40个字符的标签(可以用汉字,不超过20个汉字),标签可以用在以后的报表中。

(3)变量存储长度(LENGTH)。数值型数据一般长度为8字节,也可以对取值范围小的变量规定较小的长度以节省存储空间。字符型变量长度为其能存储的字符个数,缺省为8个字节

数据步中的ATTRIB语句可以指定变量的这些属性。格式为:ATTRIB 变量名 属性 变量名 属性…;

可以同时指定多个变量的属性。属性为"属性名=属性值"这样的写法,可以为一个变量同时指定多个属性。见如下的例子

1-5.jpg

  1. 读入外部数据


文本格式:把原始数据放在一个普通的文本格式的文件中,然后用INFILE语句指定输入文件名。例如,我们可以把数据行单独生成一个文本文件aa.txt,假设放在了f:\中,可以用如下程序读入文件中的数据并生成数据集:
data zz;

infile 'f:\aa.txt';

input name $ sex $ age;

run;

proc print;

run;


注意INFILE语句要写在INPUT语句之前,有INFILE语句就不再有CARDS语句和空语句。INFILE关键字后面跟的是一个包含文件名的字符串,可以使用全路径名,如果只有文件名则在当前工作目录寻找。

微机格式的数据文件

可以使用SAS系统File菜单中的Import命令读入其它格式的文件。并且可以将操作步骤的程序保存下来留着下次用。

与大型数据库的接口

SAS提供了两种办法可以访问大型数据库。SAS/ACCESS 可以直接连接Oracle、SYbase、SQLServer等大型数据库。为了访问储存在这些数据库中的表,需要对数据库中的表在SAS中建立访问描述文件(access descriptor),和视图描述文件(view descriptor)。例如,在数据库服务器DBIN中有一个数据库Finance,其中有一个表Sales,用户名guest用密码anyone可以访问此库,就可以用以下程序在SAS中建立访问描述文件和视图文件:

1-6.jpg

1-6-1.jpg


其中大写的部分是固定的。这段程序首先生成了访问描述文件SASUSER. SALES. ACCESS,然后由此访问描述文件生成了视图文件SASUSER. SALESALL.VIEW。在SAS中视图文件和数据集的使用是一样的,可以使用数据集的地方都可以使用视图文件。
  1. 数据集的复制与修改


可以用SET语句把一个已有数据集复制到一个新数据集,同时还可以进行修改。如果只是复制数据集,也可以用SAS管理器(SAS Explorer)完成。比如要把数据集WORK.C9501复制为数据集SASUSER.CLS,只要用如下程序:
data sasuser.cls;

set c9501;

run;


在复制的同时我们还可以用SAS程序语句对生成的数据集进行修改。比如,我们把超过100分的语文成绩都改为100分,就可以用如下程序:
data c9501a;

set c9501;

if chinese>100 then chinese=100;

run;


在数据步中可以用KEEP语句或DROP语句指定要保留的变量或要丢弃的变量。

也可以指定一个条件取出数据集的某些行组成的子集。比如,我们希望取出数学分数90分以上,语文分数100分以上的学生的观测,可以用如下的"子集IF语句":

1-7.jpg


在用SET语句引入数据集时可以给引入的数据集加选项,选项放在数据集名后的括号内:

数据集名(数据集选项)

选项包括KEEP=,表示引入时只要指定的变量;DROP=,表示不引入指定的变量;OBS=,表示读取观测时读到指定的序号为止(是序号而不是观测数);FIRSTOBS=,表示从指定序号的观测开始读取而跳过之前的观测不读。 收起阅读 »

SAS学习笔记(3):SAS用作一般高级语言

SAS

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:

1 赋值语句

在SAS中用赋值语句计算一个值并存放到变量中。

格式为 变量名=表达式:

例如



2 输出语句

SAS数据步的输出一般是数据集,用赋值语句计算的结果会自动写入数据集。SAS也提供了一个PUT语句,可以象其它语言程序的PRINT,WRITE,printf等语句一样立即显示输出结果。PUT语句在关键字后面列出要输出的各项,每一项可以是变量名或字符串,不能为数值常量或表达式,各项之间用空格分开。PUT语句的输出结果显示在LOG窗口。例如:
data ;

x=1; y=sin(x);

put 'sin function value of' x ' is ' y;

run;

结果sin function value of 1 is 0.8414709848


PUT语句的输出项还可以指定具体列位置

put x 10-20 .6; X数值显示在第10-20列,保留6位小数

put x 20.6 y 20.8; X显示在1-20列,6位小数。Y显示在21-40列,8位小数

如果希望PUT语句的输出不产生换行,使下一个PUT的结果可以显示在同一行,只要在PUT语句结尾处加一个@符,

如 put x @;

PUT语句的输出结果缺省情况下被送到运行记录窗口。在PUT语句之前用FILE语句可以改变PUT语句的输出目的地。比如,在PUT语句之前用file print;

可以把PUT语句的输出转向到输出窗口。

在FILE语句中指定一个包含文件名的字符串可以把PUT语句的输出转向到此文件中。比如file 'temp.out';把后续的PUT语句输出转向到当前工作目录下的文件"tmp.out"中,生成输出文件tmp.out。

3 分支结构

格式一: IF 条件 THEN 语句;

Else 语句;

有时我们在条件成立时需要进行的操作无法用一个语句完成,这时可以使用SAS提供的复合语句功能:只要把若干个语句用"DO;"语句和"END;"语句包围起来,就可以把它们看作是一个语句,就可以用在需要指定一个语句的地方。
if x>0 then do;

put 'x是正数';

x=2*x;

put x=;

end;


格式二:

SAS的IF结构允许嵌套,但SAS不提供IF-ELSEIF-ELSE的多分支结构。SAS的SELECT结构提供了更为灵活的多分支结构,可以实现比其它语言的IF -ELSEIF -ELSE结构更强的功能。SELECT结构有两种基本用法,第一种为:

SELECT(选择表达式);

WHEN(值列表)语句;

WHEN(值列表)语句;

OTHERWISE 语句:

END;

另一种形式:

SELECT;

WHEN(条件)语句;

WHEN(条件)语句;

OTHERWISE语句:

END;

4 循环结构

计数DO循环
DO 计数变量=起始值TO结束值BY步长:

循环体语句……:

END;

在循环体中可以用LEAVE语句跳出循环,相当于C语言的break语句。

在循环体内用CONTINUE语句可以立即结束本轮循环并转入下一轮循环的判断与执行。

(2)当型循环

DO WHILE 循环继续条件:

循环体语句……:

END;

(3)直到型

DO UNTIL 循环退出条件:

循环体语句……:

END;

事实上,SAS的循环语句比上面所述还要灵活得多它在DO语句中可以指定一个循环列表,比如

循环变量i取5,7,1 1,14循环体被执行,当i取17时i的平方为289故循环体不被执行,循环结束。注意WHILE条件只作用于用逗号隔开的最后一项。

5 数组

SAS可以把一组同为数值型或同为字符型的变量合在一起,使用同一个名字称呼,用下标来区分。这与通常的程序设计语言中的数组略有区别,通常的程序设计语言中数组元素没有对应的变量名,而SAS数组每个元素都有自己的变量名。

一、数值型数组

定义:

ARRAY 数组名(维数说明)数组元素名列表(初始值表);

例如:array tests(3) math chinese english (0, 0, 0);

初始值表可以省略,这时其初始值为相应数组元素的值(如果其数组元素还没有值则初值为缺失值)

数组元素名列表可以省略,array tests(3)

也可以在说明维数时用"下标下界:下标上界"来说明一个其它的下标下界,如 ARRAY sales (95:97) yr95-yr97;这时sales(95)为yr95,sales(96)为yr96,sales(97)为yr97。

一维数组的维数说明还可以是一个星号,比如:

array tests(*) math chinese english (0, 0, 0);

二维数组定义,只要在维数说明中指定用逗号分开的两个下标界说明,例如:array table(2,2) x11 x12 x21 x22;注意,二维数组元素按行排列。

二、字符数组

定义:ARRAY 数组名(维数说明)$ 元素长度说明 数组元素名列表(初始值表);

三、临时数组

SAS也提供了与其它程序设计语言相同的数组,即数组元素只由数组名和序号决定,没有对应的变量名。这种数组叫做临时数组。

ARRAY数组名(维数说明) _TEMPORARY_ (初始值表)

6 函数

表示形式:

SUM(xl,x2,x3) SUM(OF xl x2 x3)或SUM(OF xl-x3)

1 数学函数

·ABS(x)求x的绝对值。

·MAX(xl,x2,…,xn)求所有自变量中的最大一个。

·MIN(xl,x2,…,xn)求所有自变量中的最小一个。

·MOD(x,y)求x除以y的余数。

·SQRT(x)求x的平方根。

·ROUND(x,eps)求x按照eps指定的精度四舍五入后的结果,比如ROUND (5654.5654,0.01)结果为5654.57,ROUND(5654.5654,10)结果为5650。

·CEEL(x)求大于等于x的最小整数。当x为整数时就是x本身,否则为x右边最近的整数。

·FLOOR(x)求小于等于x的最大整数。当x为整数时就是x本身,否则为x左边最近的整数。

·INT(x)求x扔掉小数部分后的结果。

·FUZZ(x)当x与其四舍五入整数值相差小于IE-12时取四舍五入。

·LOG(x)求x的自然对数。

·LOG10(x)求x的常用对数。

·EXP(x) 指数函数

·SIN(x),COS(x),TAN(x)求x的正弦、余弦、正切函数。

·ARSIN(y),ARCOS(y),ATAN(y)

·SINH(x),COSH(x),TANH(x)双曲正弦、余弦、正切。

此外还有符号函数SIGN,一阶导数函数DIGAMMA,二阶导数函数TRIGAMMA。

2 数组函数

·DIM(x) 求数组x第一维的元素的个数(注意当下界为1时元素个数与上界相同,否则元素个数不一定与上界相同)

·DIMk(x) 求数组x第k维的元素的个数。如DIM2(x)计算二维数组x第二维长度。

·LBOUND(x)求数组x第一维的下界。

·HBOUND(x)求数组x第一维的上界。

·LBOUNDk(x)求数组x第k维的下界。

·HBOUNDk(x)求数组x第k维的上界。

3 字符函数

·TRIM(s)返回去掉字符串s的尾随空格的结果。

UPCASE(s)把字符串s中小写字母转换为大写字母后的结果。
·LOWCASE(s)把字符串s中大写字母转换为小写字母后的结果。

·INDEX(s,sl)查找sl在s中出现的位置。找不到时返回0。

·RANK(s) 字符s的ASCII码值。

·BYTE(n)第n个ASCII码值的对应字符。

·REPEAT(s,n)字符表达式s重复n次。

·SUBSTR(s,p,n)从字符串s中的第p个字符开始抽取n个字符长的子串

·TRANWRD(s,sl,s2)从字符串s中把所有字符串sl替换成字符串s2后的结果。

4 日期和时间函数

·MDY(m,d,yr)生成yr年m月d日的SAS日期值

·YEAR(date)由SAS日期值date得到年

·MONTH(date)由SAS日期值date得到月

·DAY(date)由SAS日期值date得到日

·WEEKDAY(date)由SAS日期值date得到星期几

·QTR(date)由SAS日期值date得到季度值

·DHMS(d,h,m,s) 由SAS日期值d、小时h、分钟m、秒s生成SAS日期时间值

5 分布密度函数、分布函数

·分布函数值=CDF('分布',x<,参数表>);

·密度值= PDF('分布',x<,参数表>);

·概率值= PMF('分布',x<,参数表>);

·对数密度值= LOGPDF('分布',x<,参数表>);

·对数概率值= LOGPMF('分布',x<,参数表>);
继续阅读 »

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:

1 赋值语句

在SAS中用赋值语句计算一个值并存放到变量中。

格式为 变量名=表达式:

例如



2 输出语句

SAS数据步的输出一般是数据集,用赋值语句计算的结果会自动写入数据集。SAS也提供了一个PUT语句,可以象其它语言程序的PRINT,WRITE,printf等语句一样立即显示输出结果。PUT语句在关键字后面列出要输出的各项,每一项可以是变量名或字符串,不能为数值常量或表达式,各项之间用空格分开。PUT语句的输出结果显示在LOG窗口。例如:
data ;

x=1; y=sin(x);

put 'sin function value of' x ' is ' y;

run;

结果sin function value of 1 is 0.8414709848


PUT语句的输出项还可以指定具体列位置

put x 10-20 .6; X数值显示在第10-20列,保留6位小数

put x 20.6 y 20.8; X显示在1-20列,6位小数。Y显示在21-40列,8位小数

如果希望PUT语句的输出不产生换行,使下一个PUT的结果可以显示在同一行,只要在PUT语句结尾处加一个@符,

如 put x @;

PUT语句的输出结果缺省情况下被送到运行记录窗口。在PUT语句之前用FILE语句可以改变PUT语句的输出目的地。比如,在PUT语句之前用file print;

可以把PUT语句的输出转向到输出窗口。

在FILE语句中指定一个包含文件名的字符串可以把PUT语句的输出转向到此文件中。比如file 'temp.out';把后续的PUT语句输出转向到当前工作目录下的文件"tmp.out"中,生成输出文件tmp.out。

3 分支结构

格式一: IF 条件 THEN 语句;

Else 语句;

有时我们在条件成立时需要进行的操作无法用一个语句完成,这时可以使用SAS提供的复合语句功能:只要把若干个语句用"DO;"语句和"END;"语句包围起来,就可以把它们看作是一个语句,就可以用在需要指定一个语句的地方。
if x>0 then do;

put 'x是正数';

x=2*x;

put x=;

end;


格式二:

SAS的IF结构允许嵌套,但SAS不提供IF-ELSEIF-ELSE的多分支结构。SAS的SELECT结构提供了更为灵活的多分支结构,可以实现比其它语言的IF -ELSEIF -ELSE结构更强的功能。SELECT结构有两种基本用法,第一种为:

SELECT(选择表达式);

WHEN(值列表)语句;

WHEN(值列表)语句;

OTHERWISE 语句:

END;

另一种形式:

SELECT;

WHEN(条件)语句;

WHEN(条件)语句;

OTHERWISE语句:

END;

4 循环结构

计数DO循环
DO 计数变量=起始值TO结束值BY步长:

循环体语句……:

END;

在循环体中可以用LEAVE语句跳出循环,相当于C语言的break语句。

在循环体内用CONTINUE语句可以立即结束本轮循环并转入下一轮循环的判断与执行。

(2)当型循环

DO WHILE 循环继续条件:

循环体语句……:

END;

(3)直到型

DO UNTIL 循环退出条件:

循环体语句……:

END;

事实上,SAS的循环语句比上面所述还要灵活得多它在DO语句中可以指定一个循环列表,比如

循环变量i取5,7,1 1,14循环体被执行,当i取17时i的平方为289故循环体不被执行,循环结束。注意WHILE条件只作用于用逗号隔开的最后一项。

5 数组

SAS可以把一组同为数值型或同为字符型的变量合在一起,使用同一个名字称呼,用下标来区分。这与通常的程序设计语言中的数组略有区别,通常的程序设计语言中数组元素没有对应的变量名,而SAS数组每个元素都有自己的变量名。

一、数值型数组

定义:

ARRAY 数组名(维数说明)数组元素名列表(初始值表);

例如:array tests(3) math chinese english (0, 0, 0);

初始值表可以省略,这时其初始值为相应数组元素的值(如果其数组元素还没有值则初值为缺失值)

数组元素名列表可以省略,array tests(3)

也可以在说明维数时用"下标下界:下标上界"来说明一个其它的下标下界,如 ARRAY sales (95:97) yr95-yr97;这时sales(95)为yr95,sales(96)为yr96,sales(97)为yr97。

一维数组的维数说明还可以是一个星号,比如:

array tests(*) math chinese english (0, 0, 0);

二维数组定义,只要在维数说明中指定用逗号分开的两个下标界说明,例如:array table(2,2) x11 x12 x21 x22;注意,二维数组元素按行排列。

二、字符数组

定义:ARRAY 数组名(维数说明)$ 元素长度说明 数组元素名列表(初始值表);

三、临时数组

SAS也提供了与其它程序设计语言相同的数组,即数组元素只由数组名和序号决定,没有对应的变量名。这种数组叫做临时数组。

ARRAY数组名(维数说明) _TEMPORARY_ (初始值表)

6 函数

表示形式:

SUM(xl,x2,x3) SUM(OF xl x2 x3)或SUM(OF xl-x3)

1 数学函数

·ABS(x)求x的绝对值。

·MAX(xl,x2,…,xn)求所有自变量中的最大一个。

·MIN(xl,x2,…,xn)求所有自变量中的最小一个。

·MOD(x,y)求x除以y的余数。

·SQRT(x)求x的平方根。

·ROUND(x,eps)求x按照eps指定的精度四舍五入后的结果,比如ROUND (5654.5654,0.01)结果为5654.57,ROUND(5654.5654,10)结果为5650。

·CEEL(x)求大于等于x的最小整数。当x为整数时就是x本身,否则为x右边最近的整数。

·FLOOR(x)求小于等于x的最大整数。当x为整数时就是x本身,否则为x左边最近的整数。

·INT(x)求x扔掉小数部分后的结果。

·FUZZ(x)当x与其四舍五入整数值相差小于IE-12时取四舍五入。

·LOG(x)求x的自然对数。

·LOG10(x)求x的常用对数。

·EXP(x) 指数函数

·SIN(x),COS(x),TAN(x)求x的正弦、余弦、正切函数。

·ARSIN(y),ARCOS(y),ATAN(y)

·SINH(x),COSH(x),TANH(x)双曲正弦、余弦、正切。

此外还有符号函数SIGN,一阶导数函数DIGAMMA,二阶导数函数TRIGAMMA。

2 数组函数

·DIM(x) 求数组x第一维的元素的个数(注意当下界为1时元素个数与上界相同,否则元素个数不一定与上界相同)

·DIMk(x) 求数组x第k维的元素的个数。如DIM2(x)计算二维数组x第二维长度。

·LBOUND(x)求数组x第一维的下界。

·HBOUND(x)求数组x第一维的上界。

·LBOUNDk(x)求数组x第k维的下界。

·HBOUNDk(x)求数组x第k维的上界。

3 字符函数

·TRIM(s)返回去掉字符串s的尾随空格的结果。

UPCASE(s)把字符串s中小写字母转换为大写字母后的结果。
·LOWCASE(s)把字符串s中大写字母转换为小写字母后的结果。

·INDEX(s,sl)查找sl在s中出现的位置。找不到时返回0。

·RANK(s) 字符s的ASCII码值。

·BYTE(n)第n个ASCII码值的对应字符。

·REPEAT(s,n)字符表达式s重复n次。

·SUBSTR(s,p,n)从字符串s中的第p个字符开始抽取n个字符长的子串

·TRANWRD(s,sl,s2)从字符串s中把所有字符串sl替换成字符串s2后的结果。

4 日期和时间函数

·MDY(m,d,yr)生成yr年m月d日的SAS日期值

·YEAR(date)由SAS日期值date得到年

·MONTH(date)由SAS日期值date得到月

·DAY(date)由SAS日期值date得到日

·WEEKDAY(date)由SAS日期值date得到星期几

·QTR(date)由SAS日期值date得到季度值

·DHMS(d,h,m,s) 由SAS日期值d、小时h、分钟m、秒s生成SAS日期时间值

5 分布密度函数、分布函数

·分布函数值=CDF('分布',x<,参数表>);

·密度值= PDF('分布',x<,参数表>);

·概率值= PMF('分布',x<,参数表>);

·对数密度值= LOGPDF('分布',x<,参数表>);

·对数概率值= LOGPMF('分布',x<,参数表>); 收起阅读 »

SAS学习笔记(2):SAS语言构成

SAS

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:

1.1 SAS语句

SAS语言程序由数据步和过程步组成。数据步用来生成数据集、计算、整理数据,过程步用来对数据进行分析、报告。SAS语言的基本单位是语句,每个SAS语句一般由一个关键字(如DATA,PROC,INPUT,CARDS,BY)开头,包含SAS名字、特殊字符、运算符等,以分号结束。

SAS关键字是用于SAS语句开头的特殊单词,SAS语句除了赋值、累加、注释、空语句以外都以关键字开头。SAS名字在SAS程序中标识各种SAS成分,如变量、数据集、数据库,等等。SAS名字由1到8个字母、数字、下划线组成,第一个字符必须是字母或下划线。SAS关键字和SAS名字都不分大小写。

1.2 SAS表达式

SAS数据步程序中的计算用表达式完成。表达式把常量、变量、函数调用用运算符、括号连接起来得到一个计算结果。

SAS常量主要有数值型、字符型两种,并且还提供了用于表达日期、时间的数据类型。例如
数值型:1 2,-7.5,2.5E-10
字符型:'Beijing',"Li Ming","李明"
日期型:'13JUL1998'd
时间型:'14:20't
日期时间型:' 13JUL1998:14:20:32' dt
SAS中用一个单独的小数点来表示缺失值常量。


SAS变量的基本类型有两种:数值型和字符型。日期、时间等变量存为数值型。SAS的数值型变量可以存储任意整数、定点实数、浮点实数,一般不关心其区别。数值型变量在数据集中的存贮一般使用8个字节。SAS的字符型变量缺省的长度是8个字符,但是如果在INPUT语句中输入字符型变量时指定了长度则不受此限制。可以用LENGTH语句直接指定变量长度,LENGTH语句一般应出现在变量定义之前,格式为:
LENGTH 变量名 $ 长度;

例如:length name $ 20;

SAS运算符包括算术、比较、逻辑等运算符。

逻辑运算符 = ^= > < >= <= IN

EQ NE GT LT GE LE

IN是一个SAS特有的比较运算符,用来检查某个变量的取值是否在一个给定列表中,比如:

prov in ('北京','天津','上海')

逻辑运算符: &(AND) l (OR) ^(NOT)

连接两个字符串的l l(两个连续的l号)

取两个运算值中较大一个的<>(比如3 <>5结果为5),取两个运算值中较小一个的><


1.3 SAS程序规则

SAS程序由语句构成。每个语句以分号结尾。因为分号作为语句结束标志,所以SAS语句不需要单独占一行,一个语句可以写到多行,也可以在一行连续写几个语句。SAS语言中只要允许用一个空格的地方就可以加入任意多个空白f空格、制表符、回

车,允许用空格的地方是名字周围、运算符周围。

SAS关键字和名字大小写不分,但字符型数据值要区分大小写,比如"Beijing"和"BEIJING"被认为是不同的数据值。

在SAS程序中可以加入注释,注释使用C语言语法,用//在两端界定注释,这种注释可以出现在任何允许加入空格的位置,可以占多行。

SAS程序包括数据步和过程步两种结构,每一个步是一段相对完整的可以单独运行的程序。数据步用来生成、整理数据和自编程计算,过程步调用SAS己编好的处理过程对数据进行处理。自己用SAS编程序进行计算主要在数据步中进行。

SAS数据步以DATA语句开头,以RUN语句结尾。DATA步中可以使用INPUT,CARDS,INFILE,SET,MERGE等语句指定数据来源输入数据,也可以用赋值、分支、循环等编程结构直接生成数据或对输入的数据进行修改。
继续阅读 »

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:

1.1 SAS语句

SAS语言程序由数据步和过程步组成。数据步用来生成数据集、计算、整理数据,过程步用来对数据进行分析、报告。SAS语言的基本单位是语句,每个SAS语句一般由一个关键字(如DATA,PROC,INPUT,CARDS,BY)开头,包含SAS名字、特殊字符、运算符等,以分号结束。

SAS关键字是用于SAS语句开头的特殊单词,SAS语句除了赋值、累加、注释、空语句以外都以关键字开头。SAS名字在SAS程序中标识各种SAS成分,如变量、数据集、数据库,等等。SAS名字由1到8个字母、数字、下划线组成,第一个字符必须是字母或下划线。SAS关键字和SAS名字都不分大小写。

1.2 SAS表达式

SAS数据步程序中的计算用表达式完成。表达式把常量、变量、函数调用用运算符、括号连接起来得到一个计算结果。

SAS常量主要有数值型、字符型两种,并且还提供了用于表达日期、时间的数据类型。例如
数值型:1 2,-7.5,2.5E-10
字符型:'Beijing',"Li Ming","李明"
日期型:'13JUL1998'd
时间型:'14:20't
日期时间型:' 13JUL1998:14:20:32' dt
SAS中用一个单独的小数点来表示缺失值常量。


SAS变量的基本类型有两种:数值型和字符型。日期、时间等变量存为数值型。SAS的数值型变量可以存储任意整数、定点实数、浮点实数,一般不关心其区别。数值型变量在数据集中的存贮一般使用8个字节。SAS的字符型变量缺省的长度是8个字符,但是如果在INPUT语句中输入字符型变量时指定了长度则不受此限制。可以用LENGTH语句直接指定变量长度,LENGTH语句一般应出现在变量定义之前,格式为:
LENGTH 变量名 $ 长度;

例如:length name $ 20;

SAS运算符包括算术、比较、逻辑等运算符。

逻辑运算符 = ^= > < >= <= IN

EQ NE GT LT GE LE

IN是一个SAS特有的比较运算符,用来检查某个变量的取值是否在一个给定列表中,比如:

prov in ('北京','天津','上海')

逻辑运算符: &(AND) l (OR) ^(NOT)

连接两个字符串的l l(两个连续的l号)

取两个运算值中较大一个的<>(比如3 <>5结果为5),取两个运算值中较小一个的><


1.3 SAS程序规则

SAS程序由语句构成。每个语句以分号结尾。因为分号作为语句结束标志,所以SAS语句不需要单独占一行,一个语句可以写到多行,也可以在一行连续写几个语句。SAS语言中只要允许用一个空格的地方就可以加入任意多个空白f空格、制表符、回

车,允许用空格的地方是名字周围、运算符周围。

SAS关键字和名字大小写不分,但字符型数据值要区分大小写,比如"Beijing"和"BEIJING"被认为是不同的数据值。

在SAS程序中可以加入注释,注释使用C语言语法,用//在两端界定注释,这种注释可以出现在任何允许加入空格的位置,可以占多行。

SAS程序包括数据步和过程步两种结构,每一个步是一段相对完整的可以单独运行的程序。数据步用来生成、整理数据和自编程计算,过程步调用SAS己编好的处理过程对数据进行处理。自己用SAS编程序进行计算主要在数据步中进行。

SAS数据步以DATA语句开头,以RUN语句结尾。DATA步中可以使用INPUT,CARDS,INFILE,SET,MERGE等语句指定数据来源输入数据,也可以用赋值、分支、循环等编程结构直接生成数据或对输入的数据进行修改。 收起阅读 »

SAS学习笔记(1):SAS基本概念

SAS

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:
  1. SAS数据集


SAS数据集(SAS Datasets)可以看作由若干行和若干列组成的表格,类似于一个矩阵,但各列可以取不同的类型值,比如整数值、浮点值、时间值、字符串、货币值等等。其逻辑结构如下:

1-1.jpg


数据集的每一行叫做一个观测(Observation),每列叫做一个变量(Variable)。SAS数据集等价于关系数据库系统中的一个表。
2. SAS数据库

SAS数据集是各种特殊格式的SAS文件中最重要的一种。另一种重要的SAS文件是SAS目录(Catalog),用来保存各种不能表示成行列结构表格形式的数据,比如系统设置、图象、声音等。多个SAS文件可以放在一起,称为一个SAS数据库(Library)。一个SAS数据库实际是磁盘上的一个子目录(特殊情况下一个数据库可以由几个子目录组成)。为了把库名和子目录联系起来,使用LIBNAME语句。比如,我们在C:\Y1995子目录中保存了几个SAS数据集,可以用如下语句把库名MYLIB与子目录C:\Y1995联系起来:
libname mylib " c:\y1995 "

法二(图形界面操作):
单击新建逻辑库按步骤填写。

有三个预定义的SAS数据库:
1)WORK数据库:临时库,存放在其中的SAS文件叫临时文件,当退出SAS系统时会被自动删除。
2)SASUSER库:保存与用户个人设置有关的文件,它是永久的,即退出SAS时文件不会被删除。
3)SASHELP库:保存与SAS帮助系统、例子有关的文件,是永久的。
临时数据集可以用单水平名,即只有数据集名,如C9501,也可以命名为WORK. C9501。而永久数据集名由两部分组成:库名.数据集名,比如放在MYLIB库中的数据集TEACH必须用MYLIB.TEACH表示。
3 不需编程的SAS应用/INSIGHT模块

选“Solutions - Analysis - Interactive Data Analysis"菜单。SAS/INSIGHT提供了一个类似于电子表格的数据窗口来管理数据集。

INSIGHT模块提供了十分方便的数据探索功能。 在分析菜单栏下直接进行选择操作。
继续阅读 »

网友水墨点滴学习李东风的《统计软件教程》笔记,大家可以学习参考下:
  1. SAS数据集


SAS数据集(SAS Datasets)可以看作由若干行和若干列组成的表格,类似于一个矩阵,但各列可以取不同的类型值,比如整数值、浮点值、时间值、字符串、货币值等等。其逻辑结构如下:

1-1.jpg


数据集的每一行叫做一个观测(Observation),每列叫做一个变量(Variable)。SAS数据集等价于关系数据库系统中的一个表。
2. SAS数据库

SAS数据集是各种特殊格式的SAS文件中最重要的一种。另一种重要的SAS文件是SAS目录(Catalog),用来保存各种不能表示成行列结构表格形式的数据,比如系统设置、图象、声音等。多个SAS文件可以放在一起,称为一个SAS数据库(Library)。一个SAS数据库实际是磁盘上的一个子目录(特殊情况下一个数据库可以由几个子目录组成)。为了把库名和子目录联系起来,使用LIBNAME语句。比如,我们在C:\Y1995子目录中保存了几个SAS数据集,可以用如下语句把库名MYLIB与子目录C:\Y1995联系起来:
libname mylib " c:\y1995 "

法二(图形界面操作):
单击新建逻辑库按步骤填写。

有三个预定义的SAS数据库:
1)WORK数据库:临时库,存放在其中的SAS文件叫临时文件,当退出SAS系统时会被自动删除。
2)SASUSER库:保存与用户个人设置有关的文件,它是永久的,即退出SAS时文件不会被删除。
3)SASHELP库:保存与SAS帮助系统、例子有关的文件,是永久的。
临时数据集可以用单水平名,即只有数据集名,如C9501,也可以命名为WORK. C9501。而永久数据集名由两部分组成:库名.数据集名,比如放在MYLIB库中的数据集TEACH必须用MYLIB.TEACH表示。
3 不需编程的SAS应用/INSIGHT模块

选“Solutions - Analysis - Interactive Data Analysis"菜单。SAS/INSIGHT提供了一个类似于电子表格的数据窗口来管理数据集。

INSIGHT模块提供了十分方便的数据探索功能。 在分析菜单栏下直接进行选择操作。 收起阅读 »

Spark vs. Pig 时间缩短8倍,计算节约45%


Apache Pig是在HDFS和MapReduce之上的数据流处理语言,它将数据流处理自动转换为一个DAG(有向无环图)的MapReduce作业流去执行,为数据分析人员提供了更简单的海量数据操作接口。但是在DAG的作业流中,作业之间存在冗余的磁盘读写、网络开销以及多次资源申请,使得Pig任务存在严重的性能问题。大数据处理新贵Spark凭借其对DAG运算的支持、Cache机制和Task多线程池模型等优势,相比于MapReduce更适合用于DAG作业流的实现。腾讯TDW Spark平台基于社区最新Spark版本进行深度改造,在性能、稳定和规模方面都得到了极大的提高,为大数据分析任务提供了有力的支持。
本文将介绍DAG模型的作业流计算任务在TDW Spark与Pig上的实现对比,相比于Pig,TDW Spark执行时间缩短8倍,计算节约45%。

介绍
Apache Pig是一个基于Hadoop平台的数据流并行执行平台,它包含了一个用于描述数据流的语言,称为Pig Latin。该语言借鉴了SQL和map/reduce两者的优点,既具有类似SQL的灵活可变式性,又有过程式语言的数据流特点,该语言的编译器会将用户书写的Pig Latin脚本转换成一系列MapReduce运算,提供更高层次的抽象将开发者从具体的编程中解放出来,为复杂的海量数据并行计算提供了一个简单的操作接口。

最近风生水起的大数据处理新贵Spark是一个类Hadoop的通用并行计算框架。与Hadoop相比,Spark提供了DAG模型调度来支持复杂的计算任务,通过减少中间结果的磁盘读写和网络传输来获得更佳的性能;提供了cache机制,增加了对迭代计算的支持。Spark还引进了名为RDD(弹性分布式数据集)的分布式内存抽象,使得用户在编写Spark程序时可以像Pig Latin过程式语言这样,轻松操作分布式数据集。

前面介绍了Pig会把Pig Latin脚本翻译成多个MapReduce作业来协作完成,而多个作业之间存在着冗余的磁盘读写开销、网络传输开销和多次资源申请过程。借助于Spark天生支持DAG模型调度和过程式语言编程等特点,本文尝试把一个复杂作业流的Pig计算任务用TDW Spark去实现,并进行性能对比。

实例描述
PV(Page View)和UV(Unique Visitor)统计是数据分析人员在产品运营中使用最为广泛的统计数据之一。本文将以手Q平台公众帐号的推送文章PV、UV统计作为例子,输入数据可以抽象为三张表:手Q平台文章推送表,该表记录了手Q平台的文章下推数据;公众帐号表,记录了各个公众帐号的详细信息;手Q平台推送文章点击表,该表记录了推送文章的点击数据。

先将公众账号表分别和文章推送表、推送文章点击表进行关联过滤数据,再对关联后的新表进行groupby等操作进行数据统计。统计的结果表数据包括:整个手Q平台全部公众帐号以及各个公众帐号的总推送文章量、总推送用户量、推送文章的PV、推送文章的UV、点击率。

Pig解决方案
由于篇幅有限,Pig脚本的处理逻辑不详细介绍,这里主要从数据流的角度描述Pig脚本对数据的加工流程,如下图:

如上数据流图,这是一个存在复杂数据流操作的Pig脚本,由于Pig提供了管道式的数据处理方式,使得数据处理流程简洁直观。通过Pig的客户端日志,可以得到Pig Latin编译器把Pig脚本翻译成的MapReduce Job DAG图:

Pig脚本最终被翻译成一个包含14个MapReduce作业的DAG作业流图来共同完成计算。需要注意,当MapReduce作业的输入输出存在关联时,作业之间是通过HDFS作为中间层来进行协作的,即MR1的结果要先写到HDFS,然后MR2再从HDFS读取MR1的结果来进行运算。14个MapReduce作业意味着14次读写HDFS,同时,MapReduce作业和map/reduce Task的启动也是需要消耗集群调度资源开销的。这样网络和磁盘的开销、以及MapReduce作业和Task启动的调度资源开销,将会使得整个作业流运行时间增大,作业成本增加,随着Job DAG图越复杂,这样的缺点就越明显。

Spark解决方案
Spark提供了Scala过程式语言的编程方式,跟Pig Latin类似,Spark具有join、grouby、union等数据处理算子,distinct、sum等操作也可以通过对RDD进行简单变换操作来实现。因此用Spark来实现上述的Pig脚本是比较容易的,用Spark编程接口实现上述的业务逻辑如下图所示:

与Pig的实现方式相比,Spark在以下方面优化了作业的运行时间和计算成本:

DAG模型调度:Spark的DAG编程模型会把Spark作业自动切分成多个Stage,Stage内部再转化为Task任务集,Stage之间的数据依赖通过Shuffle传递。相比Pig的MapReduce作业流,Spark减少了HDFS作为中间层的读写开销,整个Spark作业只需要读、写HDFS各一次。

资源调度优化:Spark通过DAG编程模型把原来的14个MapReduce作业合成了一个Spark作业。同时,Spark作业在启动后会申请所需的全部Executor资源,所有Stage的Tasks以线程的方式运行,共用Executors,相比于MapReduce作业流方式,Spark减少了Job和Task的资源调度开销。

Cache机制:Spark的RDD提供Cache特性,使得RDD的数据可以被重复利用而不用重新计算。
更细粒度的数据操作:例如大表join小表的时候可以通过Spark的广播机制把小表先进行广播到各个计算节点,然后再实现join功能。

效果对比
在本文实现的Spark作业中,Stage的Task数由200-2000不等,本测试将使用100、200、400个Executor,每个Executor使用10G内存(内存太少的话Executor会做full GC从而影响性能)做测试,对比不同Executor下的运行时间和计算成本:

对比Spark和Pig的运行结果,Spark的运行时间和计算成本比Pig都有明显减少,DAG模型减少了HDFS读写、cache减少重复数据的读取,这两个优化即能减少作业运行时间又能降低成本;而资源调度次数的减少能提高作业的运行效率。

对比Spark在不同计算资源下的结果,可以看到随着使用的Executor数目增多,Spark的运行时间得到了减少,但是计算成本也随之增大。当Executor数从100翻倍到200,再到200翻倍到400,运行时间并没有得到线性增加,这是由两个因素导致的:(1)每个Task的运行时间并不是完全相等的,例如某些Task处理的数据量比其他Task多,这可能导致Stage的最后时刻因某些Task未结束而无法启动下一个Stage;(2)部分Stage的Task少于400个,因此多出的Executor并没有起到并行执行Task的作用。另一方面,Spark作业是一直占有Executor的,当Stage的Task个数少于Executor时,空闲的Executor也是占用计算成本的,于是会导致计算成本的增加。因以在Spark中运行时间和计算成本是需要开发者根据实际情况去权衡的。

小结
在实际的生产任务中,绝大多数的Pig脚本都会转换成包含多个MapReduce作业的DAG作业流去执行,任务的处理逻辑越复杂,MapReduce作业流的性能问题就会越严重,最终影响任务的运行时间和计算成本。针对这些任务,如果利用Spark的迭代计算和内存计算优势,将会大幅降低运行时间和计算成本。TDW目前已经维护了千台规模的Spark集群,并且会在资源利用率、稳定性和易用性等方面做进一步的提升和改进,为业务提供更有利的支持。
继续阅读 »

Apache Pig是在HDFS和MapReduce之上的数据流处理语言,它将数据流处理自动转换为一个DAG(有向无环图)的MapReduce作业流去执行,为数据分析人员提供了更简单的海量数据操作接口。但是在DAG的作业流中,作业之间存在冗余的磁盘读写、网络开销以及多次资源申请,使得Pig任务存在严重的性能问题。大数据处理新贵Spark凭借其对DAG运算的支持、Cache机制和Task多线程池模型等优势,相比于MapReduce更适合用于DAG作业流的实现。腾讯TDW Spark平台基于社区最新Spark版本进行深度改造,在性能、稳定和规模方面都得到了极大的提高,为大数据分析任务提供了有力的支持。
本文将介绍DAG模型的作业流计算任务在TDW Spark与Pig上的实现对比,相比于Pig,TDW Spark执行时间缩短8倍,计算节约45%。

介绍
Apache Pig是一个基于Hadoop平台的数据流并行执行平台,它包含了一个用于描述数据流的语言,称为Pig Latin。该语言借鉴了SQL和map/reduce两者的优点,既具有类似SQL的灵活可变式性,又有过程式语言的数据流特点,该语言的编译器会将用户书写的Pig Latin脚本转换成一系列MapReduce运算,提供更高层次的抽象将开发者从具体的编程中解放出来,为复杂的海量数据并行计算提供了一个简单的操作接口。

最近风生水起的大数据处理新贵Spark是一个类Hadoop的通用并行计算框架。与Hadoop相比,Spark提供了DAG模型调度来支持复杂的计算任务,通过减少中间结果的磁盘读写和网络传输来获得更佳的性能;提供了cache机制,增加了对迭代计算的支持。Spark还引进了名为RDD(弹性分布式数据集)的分布式内存抽象,使得用户在编写Spark程序时可以像Pig Latin过程式语言这样,轻松操作分布式数据集。

前面介绍了Pig会把Pig Latin脚本翻译成多个MapReduce作业来协作完成,而多个作业之间存在着冗余的磁盘读写开销、网络传输开销和多次资源申请过程。借助于Spark天生支持DAG模型调度和过程式语言编程等特点,本文尝试把一个复杂作业流的Pig计算任务用TDW Spark去实现,并进行性能对比。

实例描述
PV(Page View)和UV(Unique Visitor)统计是数据分析人员在产品运营中使用最为广泛的统计数据之一。本文将以手Q平台公众帐号的推送文章PV、UV统计作为例子,输入数据可以抽象为三张表:手Q平台文章推送表,该表记录了手Q平台的文章下推数据;公众帐号表,记录了各个公众帐号的详细信息;手Q平台推送文章点击表,该表记录了推送文章的点击数据。

先将公众账号表分别和文章推送表、推送文章点击表进行关联过滤数据,再对关联后的新表进行groupby等操作进行数据统计。统计的结果表数据包括:整个手Q平台全部公众帐号以及各个公众帐号的总推送文章量、总推送用户量、推送文章的PV、推送文章的UV、点击率。

Pig解决方案
由于篇幅有限,Pig脚本的处理逻辑不详细介绍,这里主要从数据流的角度描述Pig脚本对数据的加工流程,如下图:

如上数据流图,这是一个存在复杂数据流操作的Pig脚本,由于Pig提供了管道式的数据处理方式,使得数据处理流程简洁直观。通过Pig的客户端日志,可以得到Pig Latin编译器把Pig脚本翻译成的MapReduce Job DAG图:

Pig脚本最终被翻译成一个包含14个MapReduce作业的DAG作业流图来共同完成计算。需要注意,当MapReduce作业的输入输出存在关联时,作业之间是通过HDFS作为中间层来进行协作的,即MR1的结果要先写到HDFS,然后MR2再从HDFS读取MR1的结果来进行运算。14个MapReduce作业意味着14次读写HDFS,同时,MapReduce作业和map/reduce Task的启动也是需要消耗集群调度资源开销的。这样网络和磁盘的开销、以及MapReduce作业和Task启动的调度资源开销,将会使得整个作业流运行时间增大,作业成本增加,随着Job DAG图越复杂,这样的缺点就越明显。

Spark解决方案
Spark提供了Scala过程式语言的编程方式,跟Pig Latin类似,Spark具有join、grouby、union等数据处理算子,distinct、sum等操作也可以通过对RDD进行简单变换操作来实现。因此用Spark来实现上述的Pig脚本是比较容易的,用Spark编程接口实现上述的业务逻辑如下图所示:

与Pig的实现方式相比,Spark在以下方面优化了作业的运行时间和计算成本:

DAG模型调度:Spark的DAG编程模型会把Spark作业自动切分成多个Stage,Stage内部再转化为Task任务集,Stage之间的数据依赖通过Shuffle传递。相比Pig的MapReduce作业流,Spark减少了HDFS作为中间层的读写开销,整个Spark作业只需要读、写HDFS各一次。

资源调度优化:Spark通过DAG编程模型把原来的14个MapReduce作业合成了一个Spark作业。同时,Spark作业在启动后会申请所需的全部Executor资源,所有Stage的Tasks以线程的方式运行,共用Executors,相比于MapReduce作业流方式,Spark减少了Job和Task的资源调度开销。

Cache机制:Spark的RDD提供Cache特性,使得RDD的数据可以被重复利用而不用重新计算。
更细粒度的数据操作:例如大表join小表的时候可以通过Spark的广播机制把小表先进行广播到各个计算节点,然后再实现join功能。

效果对比
在本文实现的Spark作业中,Stage的Task数由200-2000不等,本测试将使用100、200、400个Executor,每个Executor使用10G内存(内存太少的话Executor会做full GC从而影响性能)做测试,对比不同Executor下的运行时间和计算成本:

对比Spark和Pig的运行结果,Spark的运行时间和计算成本比Pig都有明显减少,DAG模型减少了HDFS读写、cache减少重复数据的读取,这两个优化即能减少作业运行时间又能降低成本;而资源调度次数的减少能提高作业的运行效率。

对比Spark在不同计算资源下的结果,可以看到随着使用的Executor数目增多,Spark的运行时间得到了减少,但是计算成本也随之增大。当Executor数从100翻倍到200,再到200翻倍到400,运行时间并没有得到线性增加,这是由两个因素导致的:(1)每个Task的运行时间并不是完全相等的,例如某些Task处理的数据量比其他Task多,这可能导致Stage的最后时刻因某些Task未结束而无法启动下一个Stage;(2)部分Stage的Task少于400个,因此多出的Executor并没有起到并行执行Task的作用。另一方面,Spark作业是一直占有Executor的,当Stage的Task个数少于Executor时,空闲的Executor也是占用计算成本的,于是会导致计算成本的增加。因以在Spark中运行时间和计算成本是需要开发者根据实际情况去权衡的。

小结
在实际的生产任务中,绝大多数的Pig脚本都会转换成包含多个MapReduce作业的DAG作业流去执行,任务的处理逻辑越复杂,MapReduce作业流的性能问题就会越严重,最终影响任务的运行时间和计算成本。针对这些任务,如果利用Spark的迭代计算和内存计算优势,将会大幅降低运行时间和计算成本。TDW目前已经维护了千台规模的Spark集群,并且会在资源利用率、稳定性和易用性等方面做进一步的提升和改进,为业务提供更有利的支持。 收起阅读 »

【R每日一贴】R画直方图?


问题描述:R创建数据的直方图?
解决方案:使用函数hist(x),x是一个数值型向量。
举例说明
> matrix1 <- matrix(c(1,1,2,3), nrow=2, ncol=2, byrow=TRUE)
> layout(matrix1)
> library(MASS)
Warning message:
程辑包‘MASS’是用R版本3.1.1 来建造的 
> attach(Cars93)
> hist(MPG.city)
> hist(MPG.city, 20)
> hist(MPG.city, 20, main="City MPG(1993)", xlab="MPG")
> detach(Cars93)

1031-1.jpg

【想一想】
1 直方图有什么用?包括那些信息?
【做一做】
1查阅hist()函数帮助文档和应用实例?
继续阅读 »

问题描述:R创建数据的直方图?
解决方案:使用函数hist(x),x是一个数值型向量。
举例说明
> matrix1 <- matrix(c(1,1,2,3), nrow=2, ncol=2, byrow=TRUE)
> layout(matrix1)
> library(MASS)
Warning message:
程辑包‘MASS’是用R版本3.1.1 来建造的 
> attach(Cars93)
> hist(MPG.city)
> hist(MPG.city, 20)
> hist(MPG.city, 20, main="City MPG(1993)", xlab="MPG")
> detach(Cars93)

1031-1.jpg

【想一想】
1 直方图有什么用?包括那些信息?
【做一做】
1查阅hist()函数帮助文档和应用实例? 收起阅读 »

【R每日一贴】R对行和列求和?

R

问题描述:R对行和列求和?
解决方案:R使用rowSums函数对行求和,使用colSums函数对列求和。
举例说明
> matrix1 <- matrix(1:4, nrow=2, ncol=2, dimnames=list(c("r1", "r2"), c("c1", "c2")))
> matrix1
c1 c2
r1  1  3
r2  2  4
> rowSums(matrix1)
r1 r2 
4  6 
> colSums(matrix1)
c1 c2 
3  7 
> rbind(matrix1, sum=colSums(matrix1))
c1 c2
r1   1  3
r2   2  4
sum  3  7

【想一想】
1 如何计算行和列的和?
【做一做】
1 实战上述实例?并且把行的和添加到矩阵?
继续阅读 »

问题描述:R对行和列求和?
解决方案:R使用rowSums函数对行求和,使用colSums函数对列求和。
举例说明
> matrix1 <- matrix(1:4, nrow=2, ncol=2, dimnames=list(c("r1", "r2"), c("c1", "c2")))
> matrix1
c1 c2
r1  1  3
r2  2  4
> rowSums(matrix1)
r1 r2 
4  6 
> colSums(matrix1)
c1 c2 
3  7 
> rbind(matrix1, sum=colSums(matrix1))
c1 c2
r1   1  3
r2   2  4
sum  3  7

【想一想】
1 如何计算行和列的和?
【做一做】
1 实战上述实例?并且把行的和添加到矩阵? 收起阅读 »

【R每日一贴】列表那些事(二)?

R

关于R列表,继续谈论它的事情。
事情三:删除列表元素。将NULL赋值给需要删除的元素,就实现从列表中删除元素。举例说明如下。
> daxue <- list("清华大学", "广东科技学院", "中山大学", "华南理工大学", "深圳大学")
> daxue
[[1]]
[1] "清华大学"

[[2]]
[1] "广东科技学院"

[[3]]
[1] "中山大学"

[[4]]
[1] "华南理工大学"

[[5]]
[1] "深圳大学"

> daxue[[1]] <- NULL
> daxue
[[1]]
[1] "广东科技学院"

[[2]]
[1] "中山大学"

[[3]]
[1] "华南理工大学"

[[4]]
[1] "深圳大学"

> daxue[c(1, 2)] <- NULL
> daxue
[[1]]
[1] "华南理工大学"

[[2]]
[1] "深圳大学"

事情四:列表换为向量。列表转为向量,即把列表中所有元素“展平”赋值给一个向量,使用unlist()函数。举例说明如下。
> lst1 <- list(1, 2, 3)
> class(lst1)
[1] "list"
> mean(lst1)
[1] NA
Warning message:
In mean.default(lst1) : 参数不是数值也不是逻辑值:回覆NA
> vc1 <- unlist(lst1)
> class(vc1)
[1] "numeric"
> mean(vc1)
[1] 2

说明:一些函数,参数需要向量,因而若是列表数据类型,需要进行列表换为向量的预处理。
事情五:移除列表中的NULL元素。举例说明如下。
> lst2 <- list(0, 1, NULL, 360, NULL)
> lst2
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
NULL

[[4]]
[1] 360

[[5]]
NULL

> logic1 <- sapply(lst2, is.null)
> logic1
[1] FALSE FALSE  TRUE FALSE  TRUE
> lst2[logic1] <- NULL
> lst2
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 360

提示:也可以使用条件来移除列表中的元素。举例说明如下。
> lst3 <- list(-1, 0, 1, 2)
> lst3
[[1]]
[1] -1

[[2]]
[1] 0

[[3]]
[1] 1

[[4]]
[1] 2

> lst3[lst3 < 0] <- NULL
> lst3
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 2

【想一想】
1 为什么要进行列表向量化,如何转换?
2 R中NULL表示什么意思?和NA、NaN有什么区别?
【做一做】
1 自建一个列表,删除列表中一个或者某几个元素?
继续阅读 »

关于R列表,继续谈论它的事情。
事情三:删除列表元素。将NULL赋值给需要删除的元素,就实现从列表中删除元素。举例说明如下。
> daxue <- list("清华大学", "广东科技学院", "中山大学", "华南理工大学", "深圳大学")
> daxue
[[1]]
[1] "清华大学"

[[2]]
[1] "广东科技学院"

[[3]]
[1] "中山大学"

[[4]]
[1] "华南理工大学"

[[5]]
[1] "深圳大学"

> daxue[[1]] <- NULL
> daxue
[[1]]
[1] "广东科技学院"

[[2]]
[1] "中山大学"

[[3]]
[1] "华南理工大学"

[[4]]
[1] "深圳大学"

> daxue[c(1, 2)] <- NULL
> daxue
[[1]]
[1] "华南理工大学"

[[2]]
[1] "深圳大学"

事情四:列表换为向量。列表转为向量,即把列表中所有元素“展平”赋值给一个向量,使用unlist()函数。举例说明如下。
> lst1 <- list(1, 2, 3)
> class(lst1)
[1] "list"
> mean(lst1)
[1] NA
Warning message:
In mean.default(lst1) : 参数不是数值也不是逻辑值:回覆NA
> vc1 <- unlist(lst1)
> class(vc1)
[1] "numeric"
> mean(vc1)
[1] 2

说明:一些函数,参数需要向量,因而若是列表数据类型,需要进行列表换为向量的预处理。
事情五:移除列表中的NULL元素。举例说明如下。
> lst2 <- list(0, 1, NULL, 360, NULL)
> lst2
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
NULL

[[4]]
[1] 360

[[5]]
NULL

> logic1 <- sapply(lst2, is.null)
> logic1
[1] FALSE FALSE  TRUE FALSE  TRUE
> lst2[logic1] <- NULL
> lst2
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 360

提示:也可以使用条件来移除列表中的元素。举例说明如下。
> lst3 <- list(-1, 0, 1, 2)
> lst3
[[1]]
[1] -1

[[2]]
[1] 0

[[3]]
[1] 1

[[4]]
[1] 2

> lst3[lst3 < 0] <- NULL
> lst3
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 2

【想一想】
1 为什么要进行列表向量化,如何转换?
2 R中NULL表示什么意思?和NA、NaN有什么区别?
【做一做】
1 自建一个列表,删除列表中一个或者某几个元素? 收起阅读 »

【R每日一贴】列表那些事(一)?

R

列表是R一种数据结构。
关于R列表,谈谈这些事。
事情一:列表创建。使用list()函数创建列表。举例说明如下。
> lst1 <- list(0, 1, 360)
> lst1
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 360

> print(lst1)
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 360

说明:列表允许包含不同类型的元素。例如。
> lst2 <- list(0, "1", TRUE, 1 + 1i, mean)
> lst2
[[1]]
[1] 0

[[2]]
[1] "1"

[[3]]
[1] TRUE

[[4]]
[1] 1+1i

[[5]]
function (x, ...) 
UseMethod("mean")
<bytecode: 0x07f4cdb4>
<environment: namespace:base>

另外,可以通过先创建空列表,然后填充该列表来创建一个列表。例如。
> lst3 <- list()
> lst3[[1]] <- 1
> lst3[[2]] <- "360"
> lst3
[[1]]
[1] 1

[[2]]
[1] "360"

说明:列表元素可以命名。例如。
> lst4 <- list(id=1, sex="F")
> lst4
$id
[1] 1

$sex
[1] "F"

事情二:列表元素获取。 lst[[n]]从列表中选定第n个元素,lst[c(n1, n2, n3…nk)]返回一个列表,列表元素由相应位置的值确定。举例说明如下。
> years <- list(1984, 2000, 2004, 2008, 2010, 2013)
> print(years)
[[1]]
[1] 1984

[[2]]
[1] 2000

[[3]]
[1] 2004

[[4]]
[1] 2008

[[5]]
[1] 2010

[[6]]
[1] 2013

> years[[2]]
[1] 2000
> years[c(4,6)]
[[1]]
[1] 2008

[[2]]
[1] 2013

注意:lst[[n]]和lst[n]有重大差别,前者获取列表第n个元素,后者返回一个列表,元素列表中第n个位置所对应的元素。例如。
> years[[1]]
[1] 1984
> class(years[[1]])
[1] "numeric"
> years[1]
[[1]]
[1] 1984

> class(years[1])
[1] "list"

关于列表,后续谈论列表元素的删除、列表转换为向量等事情。
【想一想】
1 有哪些方法可以创建列表?结合实际工作谈谈。
2 如何获取列表中的元素?lst[n]和lst[[n]]有什么区别?
【做一做】
1 创建一个星期列表,获取列表第二个元素。
继续阅读 »

列表是R一种数据结构。
关于R列表,谈谈这些事。
事情一:列表创建。使用list()函数创建列表。举例说明如下。
> lst1 <- list(0, 1, 360)
> lst1
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 360

> print(lst1)
[[1]]
[1] 0

[[2]]
[1] 1

[[3]]
[1] 360

说明:列表允许包含不同类型的元素。例如。
> lst2 <- list(0, "1", TRUE, 1 + 1i, mean)
> lst2
[[1]]
[1] 0

[[2]]
[1] "1"

[[3]]
[1] TRUE

[[4]]
[1] 1+1i

[[5]]
function (x, ...) 
UseMethod("mean")
<bytecode: 0x07f4cdb4>
<environment: namespace:base>

另外,可以通过先创建空列表,然后填充该列表来创建一个列表。例如。
> lst3 <- list()
> lst3[[1]] <- 1
> lst3[[2]] <- "360"
> lst3
[[1]]
[1] 1

[[2]]
[1] "360"

说明:列表元素可以命名。例如。
> lst4 <- list(id=1, sex="F")
> lst4
$id
[1] 1

$sex
[1] "F"

事情二:列表元素获取。 lst[[n]]从列表中选定第n个元素,lst[c(n1, n2, n3…nk)]返回一个列表,列表元素由相应位置的值确定。举例说明如下。
> years <- list(1984, 2000, 2004, 2008, 2010, 2013)
> print(years)
[[1]]
[1] 1984

[[2]]
[1] 2000

[[3]]
[1] 2004

[[4]]
[1] 2008

[[5]]
[1] 2010

[[6]]
[1] 2013

> years[[2]]
[1] 2000
> years[c(4,6)]
[[1]]
[1] 2008

[[2]]
[1] 2013

注意:lst[[n]]和lst[n]有重大差别,前者获取列表第n个元素,后者返回一个列表,元素列表中第n个位置所对应的元素。例如。
> years[[1]]
[1] 1984
> class(years[[1]])
[1] "numeric"
> years[1]
[[1]]
[1] 1984

> class(years[1])
[1] "list"

关于列表,后续谈论列表元素的删除、列表转换为向量等事情。
【想一想】
1 有哪些方法可以创建列表?结合实际工作谈谈。
2 如何获取列表中的元素?lst[n]和lst[[n]]有什么区别?
【做一做】
1 创建一个星期列表,获取列表第二个元素。 收起阅读 »

【R语言读书会】《R实战》读书笔记(第一章)

R

第一章 R简介

本章概要

1安装R

2理解R语言

3运行R程序

本章所介绍的内容概括如下。

一个典型的数据分析步骤如图1所示。



图1:典型数据分析步骤

简而言之,现今的数据分析要求我们从多种数据源中获取数据、数据合并、标注、清洗和分析,并且把分析的结果进行展示,形成报告或者系统,辅助决策。R能够满足现今数据分析的要求。

为什么用R?

R是一个适合统计分析和绘图的环境与语言。它是开源、免费的,获得世界范围社区支持。统计分析和绘图工具已经很多了,例如:SPSS,SAS,Excel,Stata和Minitab等,为什么还要用R呢?R的诸多特性,可以回答这个问题。

1)R是免费的,大多数统计工具都是商业化的,即要花钱。

2)R是一个功能强大全面的统计分析平台,一个典型的数据分析过程都可以用R来完成。

3)R具有强大的绘图能力。

4)R是一个能够进行交互式数据分析和探索的平台。

5)R是一个以容易且直观的方式编写统计方法的无与伦比的平台。

6)R实现了很多高级统计方法。

7)R可以在多个系统上运行。

8)R对于不想编程的人,提供GUI,实现R所能做的事情。

获得和安装R

从网址http://cran.r-project.org获得R安装程序。

R工作

R是一个有大小写之分的解释型语言。你可以在命令提示符(>)每次输入一条命令或者在一个源文件中写好所有命令集。

R的赋值符号是”<-“。


x <- rnorm(5) # 向量x包含5个服从标准正态分布的随机数
R在Windows系统的GUI如图2所示。



图2:Windows系统下GUI

R实例一,数据集为10个婴儿年龄(单位:月)和体重(单位:千克)。

程序清单如下:


Age <- c(1, 3, 5, 2, 11, 9, 3, 9, 12, 3)


Weight <- c(4.4, 5.3, 7.2, 5.2, 8.5, 7.3, 6.0, 10.4, 10.2, 6.1)


mean(Weight)
[1] 7.06


sd(Weight)
[1] 2.077498


cor(Age, Weight)
[1] 0.9075655


plot(Age, Weight)
关于Age与Weight的散点图如图3所示。



图3:10个婴儿的Age-Weight散点图

退出界面,执行如下函数。


q()
R帮助系统很强大,通过帮助可以获得R中已安装包的函数的细节、参考和实例。R 中获得帮助的方法。

方法一:help.start()

方法二:help(“foo”)或者?foo

方法三:help.search(“foo”)或者??foo

方法四:example(“foo”)

方法五:RSiteSearch(“foo”)

方法六:apropos(“foo”,mode=”function”)

方法七:data()

方法八:vignette()

方法九:vignette(“foo”)

R 工作空间是R当前工作环境和用户定义的所有对象。(向量、列表、数组、矩阵、数据框 和函数等)

管理R空间的一些函数。

函数一:getwd()

函数二:setwd(“mydirectory”)

函数三:list()

函数四:rm(objectname)

函数五:help(options)

函数六:options()

函数七:history(#)

函数八:savehistory(“myfile”)

函数九:loadhistory(“myfile”)

函数十:save.image(“myfile”)

函数十一:load(“myfile”)

函数十二:save(objectlist, file=”myfile”)

R实例二,数据集20个服从标准均匀分布的随机数


setwd(“E://myproject”)


options()


options(digits=3)


x <- runif(20)


summary(x)


hist(x)


savehistory(x)


save.image()


q()
R脚本输入方法,即source(“myscript.R”)

文本输出方法,即sink(“myfilename”)

图形输出方法,即pdf(“xxx.pdf”)或者png(“xxx.png”)或者jpeg(“xxx.jpg”)或者bmp(“xxx.bmp”)等。

R包

R功能之所以强大,原因之一就是具有非常多的功能扩展包。R包下载地址如下:

http://cran.r-project.org/web/packages/

为什么要用R包。R包是R函数、数据,可编译代码的聚合。

.libPaths() #显示包安装路径

library() # 查看已安装的包

search() #告知那些包已装载和可以使用

R软件安装完毕后,自带包(标准包)有base,datasets,utils,grDevices,graphics,stats和methods。

安装包方法,即 install.packages(“packagename”)

更新包方法,即update.packages()

了解已安装包的详细信息方法,即installed.packages()

加载包(前提是该包已经安装)的方法,即library(“packagename”)

对新安装包的学习和认知方法,即help(package=”packagename”)

批处理

适合重复性工作

形式:R CMD BATCH options infile outfile

结果重用

f分析的结果可以保存,并作为下一次的输入。

R实例三,数据集R自带的mtcars。

程序清单:


lmfit <- lm(mpg ~ wt, data=mtcars)


summary(lmfit)


plot(lmfit)


predict(lmfit, mynewdata)
对待大数据集

R所处理的一切都视为对象代之,并且是把对象加载到内存中进行操作和处理。因而,R能处理多大的数据集取决于作业环境的内存容量。

使用R进行数据分析,需要考虑两点,一是数据的大小;二是统计分析的方法。R能够处理GB级到TB级数据。但特殊过程特殊处理。

R实例

秉承”学以致用、活学活用“的原则,以一个综合实例融合上述内容。

R综合实例,程序清单如下。


help.start()


library()


install.package(“vcd”)


help(package=”vcd”)


library(vcd)


help(Arthritis)


Arthritis


example(Arthritis)


q()
运行效果如图4所示。



总结

1 )R的优势。

2 )因R很适合理解数据,吸引很多用户群(学生、研究人员、统计学者、数据分析师等)。

3​)RGUI、R安装、R包、R程序、R帮助、R结果保存、R批处理等内容。

Resoure:

http://www.wangluqing.com/2014 ... ote2/
《R in action》第一部分的第一章

PPV课原创文章,转载请注明来自PPV课

版权所有,违者必究
继续阅读 »

第一章 R简介

本章概要

1安装R

2理解R语言

3运行R程序

本章所介绍的内容概括如下。

一个典型的数据分析步骤如图1所示。



图1:典型数据分析步骤

简而言之,现今的数据分析要求我们从多种数据源中获取数据、数据合并、标注、清洗和分析,并且把分析的结果进行展示,形成报告或者系统,辅助决策。R能够满足现今数据分析的要求。

为什么用R?

R是一个适合统计分析和绘图的环境与语言。它是开源、免费的,获得世界范围社区支持。统计分析和绘图工具已经很多了,例如:SPSS,SAS,Excel,Stata和Minitab等,为什么还要用R呢?R的诸多特性,可以回答这个问题。

1)R是免费的,大多数统计工具都是商业化的,即要花钱。

2)R是一个功能强大全面的统计分析平台,一个典型的数据分析过程都可以用R来完成。

3)R具有强大的绘图能力。

4)R是一个能够进行交互式数据分析和探索的平台。

5)R是一个以容易且直观的方式编写统计方法的无与伦比的平台。

6)R实现了很多高级统计方法。

7)R可以在多个系统上运行。

8)R对于不想编程的人,提供GUI,实现R所能做的事情。

获得和安装R

从网址http://cran.r-project.org获得R安装程序。

R工作

R是一个有大小写之分的解释型语言。你可以在命令提示符(>)每次输入一条命令或者在一个源文件中写好所有命令集。

R的赋值符号是”<-“。


x <- rnorm(5) # 向量x包含5个服从标准正态分布的随机数
R在Windows系统的GUI如图2所示。



图2:Windows系统下GUI

R实例一,数据集为10个婴儿年龄(单位:月)和体重(单位:千克)。

程序清单如下:


Age <- c(1, 3, 5, 2, 11, 9, 3, 9, 12, 3)


Weight <- c(4.4, 5.3, 7.2, 5.2, 8.5, 7.3, 6.0, 10.4, 10.2, 6.1)


mean(Weight)
[1] 7.06


sd(Weight)
[1] 2.077498


cor(Age, Weight)
[1] 0.9075655


plot(Age, Weight)
关于Age与Weight的散点图如图3所示。



图3:10个婴儿的Age-Weight散点图

退出界面,执行如下函数。


q()
R帮助系统很强大,通过帮助可以获得R中已安装包的函数的细节、参考和实例。R 中获得帮助的方法。

方法一:help.start()

方法二:help(“foo”)或者?foo

方法三:help.search(“foo”)或者??foo

方法四:example(“foo”)

方法五:RSiteSearch(“foo”)

方法六:apropos(“foo”,mode=”function”)

方法七:data()

方法八:vignette()

方法九:vignette(“foo”)

R 工作空间是R当前工作环境和用户定义的所有对象。(向量、列表、数组、矩阵、数据框 和函数等)

管理R空间的一些函数。

函数一:getwd()

函数二:setwd(“mydirectory”)

函数三:list()

函数四:rm(objectname)

函数五:help(options)

函数六:options()

函数七:history(#)

函数八:savehistory(“myfile”)

函数九:loadhistory(“myfile”)

函数十:save.image(“myfile”)

函数十一:load(“myfile”)

函数十二:save(objectlist, file=”myfile”)

R实例二,数据集20个服从标准均匀分布的随机数


setwd(“E://myproject”)


options()


options(digits=3)


x <- runif(20)


summary(x)


hist(x)


savehistory(x)


save.image()


q()
R脚本输入方法,即source(“myscript.R”)

文本输出方法,即sink(“myfilename”)

图形输出方法,即pdf(“xxx.pdf”)或者png(“xxx.png”)或者jpeg(“xxx.jpg”)或者bmp(“xxx.bmp”)等。

R包

R功能之所以强大,原因之一就是具有非常多的功能扩展包。R包下载地址如下:

http://cran.r-project.org/web/packages/

为什么要用R包。R包是R函数、数据,可编译代码的聚合。

.libPaths() #显示包安装路径

library() # 查看已安装的包

search() #告知那些包已装载和可以使用

R软件安装完毕后,自带包(标准包)有base,datasets,utils,grDevices,graphics,stats和methods。

安装包方法,即 install.packages(“packagename”)

更新包方法,即update.packages()

了解已安装包的详细信息方法,即installed.packages()

加载包(前提是该包已经安装)的方法,即library(“packagename”)

对新安装包的学习和认知方法,即help(package=”packagename”)

批处理

适合重复性工作

形式:R CMD BATCH options infile outfile

结果重用

f分析的结果可以保存,并作为下一次的输入。

R实例三,数据集R自带的mtcars。

程序清单:


lmfit <- lm(mpg ~ wt, data=mtcars)


summary(lmfit)


plot(lmfit)


predict(lmfit, mynewdata)
对待大数据集

R所处理的一切都视为对象代之,并且是把对象加载到内存中进行操作和处理。因而,R能处理多大的数据集取决于作业环境的内存容量。

使用R进行数据分析,需要考虑两点,一是数据的大小;二是统计分析的方法。R能够处理GB级到TB级数据。但特殊过程特殊处理。

R实例

秉承”学以致用、活学活用“的原则,以一个综合实例融合上述内容。

R综合实例,程序清单如下。


help.start()


library()


install.package(“vcd”)


help(package=”vcd”)


library(vcd)


help(Arthritis)


Arthritis


example(Arthritis)


q()
运行效果如图4所示。



总结

1 )R的优势。

2 )因R很适合理解数据,吸引很多用户群(学生、研究人员、统计学者、数据分析师等)。

3​)RGUI、R安装、R包、R程序、R帮助、R结果保存、R批处理等内容。

Resoure:

http://www.wangluqing.com/2014 ... ote2/
《R in action》第一部分的第一章

PPV课原创文章,转载请注明来自PPV课

版权所有,违者必究 收起阅读 »