集算器与R语言的循环函数对比

  循环函数可以遍历数组或集合中的每个成员,可以将结构复杂的循环语句用简单的函数形式表达出来,可以减少代码量并提高可读性。集算器和R语言都支持循环函数,下面将对比两者用法上的异同。

1、生成数据

  生成1到10之间的奇数。

  集算器:x=to(1,10).step(2)

  代码中,to(1,10)生成了1到10之间的连续整数,step函数根据上一步计算结果间隔取数,最终结果是[1,3,4,5,7,9]。集算器的这种数据类型被称为序列。

  这段代码还有更简单的写法:x=10.step(2)

  R语言:x<-seq(from=1,to=10,by=2)

  这段代码直接从1开始间隔取数,直到10为止,计算结果是:c(1,3,4,5,9)。R语言的这种数据类型被称为向量。

  这段代码还有更简单的写法,即:x<-seq(1,10,2)

  比较:

(1)两者都可以实现本例,但集算器要分两步计算,理论上性能较差。R语言只需一步,性能较高;

(2)集算器的写法是按照序号从集合取数,具有通用性。比如某个字符串序列:A1=["a", "bc", "def"……],要取出A1里奇数位置的字符串时,集算器的写法不必改变,仍然是x=A1.step(2)。

  R语言是直接生成数据,所以性能更高。R语言也可以写出通用的表达式,比如同样的字符串向量:A1=c(“a”, “bc”, “def”……),要取出A1里奇数位置的字符串时,R语言的表达式可以是:x=A1[seq(1,length(A1),2)]

  (3)集算器的循环函数有个R语言所没有的特点:内置循环变量和运算符。其中,“~”代表循环变量,“#”代表循环计数,“[]”代表相对位置,“{}”代表相对区间。利用这些变量和运算符,集算器可以写出通用简洁的表达式,比如求集合A2=[2,3,4,5,6]各成员的平方:

  A2.(~*~)    /结果为[4,9,16,25,36],也可以表示为A2**A2,但后者不够直观通用。R语言则只能表示为A2*A2

  取前三个成员:

  A2.select(#<=3)     /结果为[2,3,4]

  取每个成员的上一个成员,组成新集合:

  A2.(~[-1])     /结果为[null,2,3,4,5]

  增长率:

  A2.((~ – ~[-1])/ ~[-1])   /结果为[null,0.5,0.33333333333,0.25,0.2]

  移动平均值:

  A2.(~{-1,1}.avg())  /结果为[2.5, 3.0, 4.0, 5.0, 5.5]

  总结:本例中,R语言既可以直接生成数据,也可以写出通用的表达式,比集算器更灵活,占用内存可以更少。

2、过滤记录

  循环函数的计算对象可以是成员为单值的数组或集合,也可以是成员为记录的二维结构化数据对象,事实上,后者才是循环函数的主要用途。比如:针对订单记录sales,过滤出2010年签订的金额大于2000的订单。

  说明:sales来源于文本文件,其部分数据如下:

 

  集算器:sales.select(ORDERDATE>=date(“2010-01-01″) && AMOUNT>2000)

  部分结果如下:

 

  R语言:sales[as.POSIXlt(sales$ORDERDATE)>=as.POSIXlt("2010-01-01") &sales$AMOUNT>2000,]

  部分结果如下:
 

  比较:

  (1)集算器和R语言都可以实现本功能,所不同的是集算器使用了select循环函数,而R语言直接使用下标,两者的本质并无差别。另外,R语言还可以用attach函数进一步简化表达式:sales[as.POSIXlt(ORDERDATE)>=as.POSIXlt("2010-01-01") & AMOUNT>2000,]。如此一来,两者的相似度就更高了。

  (2)除了查询,还有求序号、排序、排名、Top N、分组求和等。比如:求本案例中记录的序号。

  sales.pselect@a(ORDERDATE>=date(“2010-01-01″) && AMOUNT>2000)   /集算器

  which(as.POSIXlt(sales$ORDERDATE)>=as.POSIXlt(“2010-01-01″) &sales$AMOUNT>2000) /R语言

  比如:将记录按照SELLERID正排序,按照AMOUNT逆排序。

  sales.sort(SELLERID,AMOUNT:-1)    /集算器

  sales[order(sales$SELLERID,-sales$AMOUNT),]   /R语言

  比如:求AMOUNT最大的三条记录。

  sales.top(-AMOUNT;3)    /集算器

  head(sales[order(-sales$AMOUNT),],n=3)    /R语言

  (3)R语言有时用下标来计算,比如过滤;有时用函数来计算,比如求记录序号;有时是“数据集+函数+数据集”的形式,比如排序;有时是“函数+数据集+函数”的形式,比如Top N。这种多变的写法看似灵活,但会让程序员产生严重的混乱。相比之下,集算器总是“数据集+函数+函数……”这种对象式的访问形式,结构简单统一,更易于程序员掌握。

  比如,进行连续计算,先过滤再求TopN,集算器的算法如下:sales.select(ORDERDATE>=date(“2010-01-01″) && AMOUNT>2000).top(AMOUNT;3)

  R语言的算法如下:

  Mid<-sales[as.POSIXlt(sales$ORDERDATE)>=as.POSIXlt("2010-01-01") &sales$AMOUNT>2000,]

  head(Mid [order(Mid$AMOUNT),],n=3)

  可以看到,集算器更擅长表达多步骤的连续计算。

  总结:在本案例中,集算器在语法一致性和连续计算方面有一定的优势,对初学者更友好。

3、分组汇总

  对记录分组汇总时也常会用到循环函数,比如:按CLIENT和SELLERID分组,分组后对AMOUNT求和并求最大值。

  集算器:sales.groups(CLIENT,SELLERID;sum(AMOUNT),max(AMOUNT))

  部分结果如下:

 

  R语言:

  result1<-aggregate(sales[,4],sales[c(3,2)],sum)

  result2<-aggregate(sales[,4],sales[c(3,2)],max)

  result<-cbind(result1,result2[,3])

  部分结果如下:


 

  比较:

  (1)本案例要使用一种以上的汇总方法,集算器可直接实现本案例,但R语言内置的库函数不直接支持同时使用多种汇总方法,因此求和、求最大值要分为两步,最后再通过cbind拼合结果。另外,R语言在本案例中要占用更多的内存。

  (2)注意一下R语言中反人类的设计:sales[c(3,2)],这里的顺序是先SELLERID再CLIENT,但业务逻辑中的分组顺序却是先CLIENT再SELLERID,完全相反,而结果又会显示为先SELLERID再CLEINT。总之,业务逻辑、代码、计算结果,这三者无法统一起来。

  总结:本案例中,集算器在敏捷性、内存占用、统一性方面有优势。

4、求平方和

  用循环函数求集合v=[2,3,4,5]的平方和。

  注意:集算器和R语言都有现成函数可以求平方和,但这里我们用通用的循环函数来实现。

  集算器:v.loops(~~+~*~;0)

  R语言:Reduce(function(x,y) x+y*y, c(0,v))

  比较:

  (1)集算器和R语言都可以轻松实现本功能;

  (2)集算器使用了loops函数,这表示以零为初始值,对v中的每个成员依次进行计算,并返回最后的结果。其中,“~”代表当前这一步的成员,“~~”代表上一步的计算结果。比如:第一步的算法是0+2*2,第二步的算法是4+3*3,依此类推。最后的结果是54。

  R语言使用了Reduce函数,这表示对[0,2,3,4,5]中的成员依次计算,并将当次的计算结果代入下一次继续计算。第一步的算法是:0+2*2,第二步的算法是4+3*3,同集算器一样。

  (3)R语言使用了lambda表达式来实现本算法。这种写法无需定义函数名就可以直接使用,是匿名函数的一种。本例中,function(x,y)是函数的声明部分,定义了两个参数;x+y*y是函数体,实现具体算法;c(0,v)将0和v拼在了一起,即[0,2,3,4,5],这个集合中的每个成员都会依次参与运算。由于可以传入完整的函数,因此这种写法非常灵活,可以实现功能复杂的算法。

  集算器的写法可以理解为一种隐式的lambda表达式。这种写法本质和R语言的显式lambda表达式一样,但它只有表达式,既没有函数名,也无需声明函数,更不需要定义变量,因此结构更简单。本例中,“~”是内置的循环变量,无需定义;~~+~*~是表达式,用来实现具体算法;v是固定参数,参数中的每个成员都会依次参与运算。由于不能传入函数,因此理论上这种写法的灵活性和表达能力不如R语言。

  (4)集算器的写法理论上不够灵活,但它有~、~~、#、[]、{}等便利的内置变量和运算符,实际表达能力反而更强。比如集算器可以用“~~”直接表示上一步的计算结果,但在R语言中这需要用reduce函数和额外的变量配合才能实现。集算器可以用“#”直接表达当前的循环序号,而R语言就很难实现该功能。集算器也可以用“[]”来表达相对位置,比如~[1]可以表示下一个成员的值,Close[-1]可以表示上一条记录中Close字段的值。

  集算器还能用“{}”表示相对区间,比如{-1,1}可以表达前一位和后一位之间的三个成员,因此,用v.(~{-1,1}.avg())这种通用的表达式就可以计算移动平均值,而R语言需要用特定的函数来实现:filter(v/3, rep(1, 3),sides = 1),这里甚至没有“求平均值”这个函数,入门者很难理解。

  总结:本案例中,R语言的lambda表达式理论上更强大,但理解起来稍显困难,集算器的写法相对好理解。

5、跨行组运算

  这里有多支股票的日交易数据stock,请计算每支股票收盘价的日增长额。

  部分原始数据如下:

 

  集算器

  A10=stock.group(Code)

  A11=A10.(~.sort(Date))

  A12=A11.(~.derive((Close-Close[-1]):INC))

  R语言

  A10<-split(stock, stock $Code)

  for(I in 1:length(A10){

  A10[[i]][order(as.numeric(A10[[i]]$Date)),] #sort by Date in each group

  A10[[i]]$INC<-with(A10[[i]], Close-c(0,Close[- length (Close)])) #add a column, increased price

  }

  比较:

  (1)集算器和R语言都可以实现本案例,但集算器只用到了循环函数,性能高效代码简洁,而R语言需要用for语句手工书写代码,性能和可读性都较差;

  (2)本案例要进行两层循环:循环每一只股票,循环股票内的每一条记录。R语言的循环函数(包括lambda语法)没有内置的循环变量,只擅长表达最内层的循环,很难表达多层循环,即使能写也非常难懂。

  集算器的循环函数可以用“~”表达循环变量,还能嵌套使用,因此很擅长表达多层循环。比如代码中的A10.(~.sort(Date)),这实际是A10.(~.sort(~.Date))的简写,前一个“~”代表当前股票,后一个“~”代表当前股票的当前记录。

  (3)本案例是一个典型的有序算法,需要在每只股票里用当日收盘价减去上一日收盘价。集算器由于有#、[]、{}等便利的内置变量和运算符,因此很容易表达此类有序算法,比如:Close-Close[-1]就可以表达增长额。R语言也可以进行有序计算,但它缺乏循环序号、相对位置、相对区间等等便利功能,因此语法比较晦涩,比如增长额需要用Close-c(0,Close[- length (Close)])来表达。

  本案例的有序算法还比较简单,复杂些的算法用R语言的循环函数就更难表达了,往往需要多层的for循环才能实现,比如计算股票的连续上涨天数:

  A10<-split(stock, stock $Code)

  for(I in 1:length(A10){

    A10[[i]][order(as.numeric(A10[[i]]$Date)),] #sort by Date in each group

    A10[[i]]$INC<-with(A10[[i]], Close-c(0,Close[- length (Close)])) #add a column, increased price

    if(nrow(A10[[i]])>0){  #add a column, continuous increased days

      A10 [[i]]$CID[[1]]<-1

      for(j in 2:nrow(A3[[i]])){

        if(A10 [[i]]$INC[[j]]>0 ){

          A10 [[i]]$CID[[j]]<-A10 [[i]]$CID[[j-1]]+1

        }else{

          A10 [[i]]$CID[[j]]<-0

        }

      }

    }

  }

  集算器的算法依然简单易懂:

  A10=stock.group(Code)

  A11=A10.(~.sort(Date))

  A12=A11.(~.derive((Close-Close[-1]):INC), if(INC>0,CID=CID[-1]+1, 0):CID))

  总结:在进行多层循环或跨行组的计算时,集算器循环函数的运算性能更高,代码更易书写。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值