Categories
日常应用

各种被Teradata摧残的应付招数:SAS,R,文本文件导入导出

这篇文章基本上就是我被Teradata折腾的辛酸血泪史...TD有TD的长处,但是还不足以应付所有的分析任务。所以不得不面对各种把数据折腾出来折腾出去的任务...(好吧我还没开始被Hadoop虐呢,到时候务必又是一番惨死景象)。

Teradata文本导入导出

Teradata SQL Assistance直接输入输出

这个适用于本地安装了Teradata SQL Assistance的windows系统,然后我个人的建议是不要下载多于10w行的数据,要不会很烦。也不要上传多于1w行的数据,要teradata-export-csv不会更烦。

导出文本(csv)比较简单,直接选定 File->Export Result就可以了,然后任何运行SQL的输出都会输出到文本文件里面,主要就是 SELECT 出来的结果。

这个时候,TD会有提示"Future results will be Exported to a file"。

teradata-export-csv-2

类似的,可以选择File->Import Data来读入数据,这个时候会让你选择一个现成的文本(csv)文件,然后告诉TD怎么读就可以了。

举个例子,比如我们有一些邮政编码和城市名称的数据,格式如下:

County,Town,ZIP_CODE
Bronx,Bronx,10451
Bronx,Bronx,10452
Bronx,Bronx,10453
Bronx,Bronx,10454
Bronx,Bronx,10455
Bronx,Bronx,10456
Bronx,Bronx,10457
Bronx,Bronx,10458
Bronx,Bronx,10459
Bronx,Bronx,10460
Bronx,Bronx,10461

那么相应的code就可以写成:

CREATE MULTISET TABLE liyun_NY_DA_ZIP (
county CHAR(20)
,town CHAR(20)
,ZIP_CODE CHAR(5)
)

Insert into liyun_NY_DA_ZIP values (?,?,?); /* import data from csv */

Teradata的FastExport

这个目前还只在linux下试验成功过...

一段代码长成这个样子:

.logtable p_r_test_t.exp_log;
.logon mozart/username,password;
.begin export sessions 20;
.export outfile usa_all.txt mode record format text;
select cast(cast(x.byr_id as decimal(18) format 'ZZZZZZZZZZZZZZZZ9') as CHAR(18)),
',' (char(1)),
cast(cast(x.recency as decimal(4) format 'ZZZ9') as CHAR(4)),
',' (char(1)),
cast(cast(x.frequency as decimal(4) format 'ZZZ9') as CHAR(4)),
',' (char(1)),
cast(cast(x.monetary as decimal(8) format 'ZZZZZZZ9') as CHAR(8))
from p_r_test_t.dw_mbl_rfm_all x;
.end export;
.logoff;

然后存成一个脚本文件,比如your_fast_export_script.txt, 之后就可以直接在Shell里面调用了。

fexp < your_fast_export_script.txt

需要注意的是如果报各种稀奇古怪的错误,可能是编码尤其是换行符的问题...

FastLoad还米有试过。

Teradata导入导出到R里面

Teradata导出数据到R里面

如果想省心,那么就直接用TeradataR这个包。然后可能需要安装一下TD的ODBC driver: http://downloads.teradata.com/download/connectivity/odbc-driver/windows

简单的配置之后,就可以直接调用TeradataR了。

library(teradataR)
TDuid <- "xxx"
TDpwd <- "xxx"
connect_mozart = tdConnect(dsn = "xxxxx", uid=TDuid, pwd=TDpwd, database = "xxx")
#download the table and run summary locally
data_open <- tdQuery(" select * from xxxx order by 1,2,3,4,5,6;")
tdClose()

但是可惜的是这个包里面不提供导入回TD,只能去调用RODBC的原始命令。

R里面数据导入Teradata

#upload data
connect_mozart = odbcConnect(dsn = "xxx", uid=TDuid, pwd=TDpwd)
summary(aim_data)
sqlQuery(connect_mozart, "drop table xxxx;") #如果已经有这个表了
sqlSave(connect_mozart, aim_data, tablename="xxxx",rownames=F,fast=T)
odbcClose(connect_mozart)

这样就可以了。不过速度真心不怎么快(fast=F)会更慢。不建议使用大于1k行的R data.frame。

Teradata导入导出到SAS里面

Teradata导出数据到SAS里面

鉴于SAS可以直接用FastExport和FastLoad,这个速度就相当可观了。

proc sql noprint;
Connect To Teradata (user=&user password="&pwd" database=access_views tdpid=mozart mode=teradata);
create table xxx.xxxx as select * from connection to Teradata
(
select * from Xxxx.xxxx order by 1,2,3,4,5,6;

);

SAS里面数据导入Teradata

类似的,可以很快的把数据从SAS导入TD。

proc sql noprint;
Connect To Teradata (user=&user password="&pwd" database=access_views tdpid=vivaldi mode=teradata);

execute (drop table xxxxx.xxxx) by teradata;

execute
(
create table xxxxx.xxxx
(
USER_ID DECIMAL(18,0),
CMC_ID INTEGER,
CMC_START_DT DATE FORMAT 'YYYY/MM/DD',
SEGM_GROUP VARCHAR(255) CHARACTER SET UNICODE NOT CASESPECIFIC,
CHANNEL VARCHAR(5) CHARACTER SET UNICODE NOT CASESPECIFIC
) PRIMARY INDEX(USER_ID, cmc_id)
) by teradata;

Disconnect From Teradata;
quit;

proc append base=vivaldi.xxxx(fastload = yes fbufsize = 32000)
data=mz_sas.xxxx force;
run;

由于调用了fastexport和fastload,这速度明显改善,实测2G数据大概在5分钟左右,蛮快的了(6M/s)呢。

Categories
日常应用

七天搞定SAS(一):数据的导入、数据结构

标题有些噱头,不过这里的重点是: speak SAS in 7 days。也就是说,知识是现成的,我这里只是要学会如何讲这门语言,而不是如何边学SAS边学模型。顺便发现我最近喜欢写连载了,自从西藏回来后.....

之所以下定决定学SAS,是因为周围的人都在用SAS。为了和同事的沟通更有效率,还是多学一门语言吧。R再灵活,毕竟还是只有少数人能直接读懂。理论上语言是不应该成为障碍的~就像外语一样,多学一点总是好的,至少出门不发怵是不是?

最后一根稻草则是施老师传给我的一个link:http://blog.softwareadvice.com/articles/bi/3-career-secrets-for-data-scientists-1101712/,据说有数据分析师的职业秘笈...我就忍不住去看了看。其中一句话还是蛮有启发的:

如果有人问你要学什么工具,是SAS,R,EXCEL,SQL,SPSS还是?直接回答:所有。

这个答案一方面霸气,一方面也是,何必被工具束缚呢?

这东西宜突击不宜拖延,所以还是集中搞定吧。七天应该是个不错的时间段。

大致分配如下:
1. 熟悉SAS的数据结构,如基本的向量,数据集,数组;熟悉基本的数据类型,如文本,数字。
2. 熟悉基本的数据输入与输出。
3. 熟悉基本的逻辑语句:循环,判断
4. 熟悉基本的数据操作:筛选行列,筛选或计算变量,合并数据集,计算基本统计量,转置
5. 熟悉基本的文本操作函数
6. 熟悉基本的计量模型函数
7. 熟悉基本的macro编写,局部变量与全局变量

其实这大概也是按照我常用的R里面完成的任务来罗列的。基本计划是完成就可以大致了解SAS的语法了,其他的高级功能现用现学吧。

书籍方面,中文的抢了同事的一本《SAS编程与数据挖掘商业案例》,英文的找了一本「Applied Econometrics Using The SAS System」和「The Little SAS Book」,先这么看着吧。

后知后觉的补充:其实这一系列笔记都是先写再发布的,主要是方便我调整顺序什么的。事实证明绝大多数时间我在看(或者更直接的,抄)「The Little SAS Book」这本书,姚老师的《SAS编程与数据挖掘商业案例》简单看了一晚,作为对于SAS语法的预热。最后那本「Applied Econometrics Using The SAS System」更多是看具体模型的用法了,不是熟悉语法的问题了。例子都是第一本little book上的,很好用。

本系列连载文章:

-------笔记开始-------

SAS的数据类型

2013-12-09 16_41_10-The Little SAS Book(Fourth).PDF - Adobe Reader

首先,sas的编程大概就两块:Data和PROC,这个倒是蛮清晰的划分。然后目前关注data部分。

SAS的数据类型还真的只有两种:数字和文本。那么看来日期就要存成文本型了。变量名称后面加$代表文本型。

SAS的数据读入

手动输入这种就不考虑了,先是怎么从本地文件读入。比如我们有文本文件如下:

Lucky 2.3 1.9 . 3.0
Spot 4.6 2.5 3.1 .5
Tubs 7.1 . . 3.8
Hop 4.5 3.2 1.9 2.6
Noisy 3.8 1.3 1.8 1.5
Winner 5.7 . . .

然后SAS里面就可以用

* Create a SAS data set named toads;
* Read the data file ToadJump.dat using list input;
DATA toads;
INFILE ’c:\MyRawData\ToadJump.dat’;
INPUT ToadName $ Weight Jump1 Jump2 Jump3;
RUN;
* Print the data to make sure the file was read correctly;
PROC PRINT DATA = toads;
TITLE ’SAS Data Set Toads’;
RUN;

这样就建立了一个名为toads的临时数据集,然后读入外部文件ToadJump.dat,然后告诉SAS有四个变量,其中第一个是文本型。这样就OK了。缺失值用一个点.标记。

偶尔数据没那么规范,比如长成:

----+----1----+----2----+----3----+----4
Columbia Peaches 35 67 1 10 2 1
Plains Peanuts 210 2 5 0 2
Gilroy Garlics 151035 12 11 7 6
Sacramento Tomatoes 124 85 15 4 9 1

那么就要有点类似正则表达式的感觉,告诉SAS更多的参数:

* Create a SAS data set named sales;
* Read the data file OnionRing.dat using column input;
DATA sales;
INFILE ’c:\MyRawData\OnionRing.dat’;
INPUT VisitingTeam $ 1-20 ConcessionSales 21-24 BleacherSales 25-28
OurHits 29-31 TheirHits 32-34 OurRuns 35-37 TheirRuns 38-40;
RUN;
* Print the data to make sure the file was read correctly;
PROC PRINT DATA = sales;
TITLE ’SAS Data Set Sales’;
RUN;

这样SAS就可以正确的读数据了—类似于excel的导入文本-固定宽度分隔。

再不规则的话,比如有日期型的:

Alicia Grossman 13 c 10-28-2008 7.8 6.5 7.2 8.0 7.9
Matthew Lee 9 D 10-30-2008 6.5 5.9 6.8 6.0 8.1
Elizabeth Garcia 10 C 10-29-2008 8.9 7.9 8.5 9.0 8.8
Lori Newcombe 6 D 10-30-2008 6.7 5.6 4.9 5.2 6.1
Jose Martinez 7 d 10-31-2008 8.9 9.510.0 9.7 9.0
Brian Williams 11 C 10-29-2008 7.8 8.4 8.5 7.9 8.0

那么接下来就是:

* Create a SAS data set named contest;
* Read the file Pumpkin.dat using formatted input;
DATA contest;
INFILE ’c:\MyRawData\Pumpkin.dat’;
INPUT Name $16. Age 3. +1 Type $1. +1 Date MMDDYY10.
(Score1 Score2 Score3 Score4 Score5) (4.1);
RUN;
* Print the data set to make sure the file was read correctly;
PROC PRINT DATA = contest;
TITLE ’Pumpkin Carving Contest’;
RUN;

就是说,name是一个长度为16的字符;age是长度为3、无小数点的数字;+1跳过空列;type是长度为1的文本;date是MMDDYY长度为10的日期;score1-5是长度为4,小数部分为1位的数字。

还有若干更复杂的,可以遇到时侯回来查手册。此外还有@可用来直接指定开始读的列。鉴于我接触的数据一般比较规范,这些就不细看了。

此外SAS可以指定开始读的行数,读取的行数等。

DATA icecream;
INFILE ’c:\MyRawData\IceCreamSales.dat’ FIRSTOBS = 3;
INPUT Flavor $ 1-9 Location BoxesSold;
RUN;

SAS读取CSV数据

以我最关心的CSV文件为例,如下数据:

Lupine Lights,12/3/2007,45,63,70,
Awesome Octaves,12/15/2007,17,28,44,12
"Stop, Drop, and Rock-N-Roll",1/5/2008,34,62,77,91
The Silveyville Jazz Quartet,1/18/2008,38,30,42,43
Catalina Converts,1/31/2008,56,,65,34

只需要:

DATA music;
INFILE ’c:\MyRawData\Bands.csv’ DLM = ’,’ DSD MISSOVER;
INPUT BandName :$30. GigDate :MMDDYY10. EightPM NinePM TenPM ElevenPM;
RUN;
PROC PRINT DATA = music;
TITLE ’Customers at Each Gig’;
RUN;

其实,貌似更简单的办法是:

DATA music;
INFILE ’c:\MyRawData\Bands.csv’ DLM = ’,’ DSD MISSOVER;
INPUT BandName :$30. GigDate :MMDDYY10. EightPM NinePM TenPM ElevenPM;
RUN;
PROC PRINT DATA = music;
TITLE ’Customers at Each Gig’;
RUN;

好吧,import果然更直接一点...excel文件也可以如法炮制。

SAS读取excel数据

* Read an Excel spreadsheet using PROC IMPORT;
PROC IMPORT DATAFILE = 'c:\MyExcelFiles\OnionRing.xls' DBMS=XLS OUT = sales;
RUN;
PROC PRINT DATA = sales;
TITLE 'SAS Data Set Read From Excel File';
RUN;

如果需要SAS永久存着这些数据,则需要先指定libname:

LIBNAME plants ’c:\MySASLib’;
DATA plants.magnolia;
INFILE ’c:\MyRawData\Mag.dat’;
INPUT ScientificName $ 1-14 CommonName $ 16-32 MaximumHeight
AgeBloom Type $ Color $;
RUN;

后期就可以直接调用啦:

LIBNAME example ’c:\MySASLib’;
PROC PRINT DATA = example.magnolia;
TITLE ’Magnolias’;
RUN;

SAS 读取Teradata数据

最后就是从teradata里面读数据,可以利用teradata fastexport特性:

libname tra Teradata user=terauser pw=XXXXXX server=boom;
proc freq data=tra.big(dbsliceparm=all);
table x1-x3;
run;

等价于:

proc sql;
connect to teradata(user=terauser password=XXXXXX server=boom dbsliceparm=all);
select * from connection to teradata
(select * from big);
quit;

暂时没有fastload的需求,就先这样吧。可以参见SAS的TD手册:http://support.sas.com/resources/papers/teradata.pdf

本系列连载文章:

 

Categories
日常应用

关于R的若干SQL等价问题

以前总是觉得不同的计算机语言之间只是语法问题,思路其实还是差不多的--后来才知道不尽然如此。比如用惯了R作分析,切换到其他语言顿时觉得效率降低了好多,尤其是很多一行命令在R里面就可以搞定的时候-思维习惯了一定程度的跳跃,常用的操作(尤其是数据整理!)封装成函数之后工作效率那叫一个倍增啊!结合knitr,原来的时候生成定期报告的效率极其之高,基本属于10倍以上的时间节省。

现在公司的数据平台是teradata,典型的SQL结构,各种join。在这么大的数据量下,不可能直接取数据到本机来分析,只能借助SQL进行一定程度的降维。而后剩下的收尾分析工作,可以由R完成。至于两者之间分工的界限在哪里,我还在摸索一个效率最高的平衡点。不得不吐槽一下,SQL的逻辑思维方式真心没效率,完全是为了数据库性能和空间单位平衡而设计的,做分析的时候就额外的痛苦许多——90%以上的时间都用来琢磨怎么鼓捣出来自己需要的数据格式,全在数据清理上了!

抱怨完毕,除了祈祷hadoopR和oracle连接起来彻底摆脱SQL阴影之外,暂时只能跟SQL硬战。下面说说最近常见的几个相同功能在R和SQL里面分别的实现方法。

1. 生成新变量

多见的明确的任务啊。如果是数值型,比如变量D是其他三个变量ABC的显性函数f(A,B,C),最简单诸如D=A+B+C,在R和SQL里面都是直接写。

  • R:
    my_dataframe$D <- my_dataframe$A+ my_dataframe$B + my_dataframe$C

    (当然还有更elegant的with()函数)

  • SQL(以select为例):
    SELECT A,B,C, A+B+C D from my_datatable;

    然后如果f()稍稍复杂的话,R的可以定义函数的优势就明显了,SQL只有macro模式显然不足够灵活强大。如,

R:

generate_D <- function(VarA=A, VarB=B, VarC=C) {
VarD <- VarA * VarB *(VarB %*% VarC)
return( VarD)}
my_dataframe$D <- generate_D(my_dataframe$A, my_dataframe$B, my_dataframe$C)

注:%*%代表向量内积或矩阵乘法,这里为一个数字。理论上这里可以调用任何R中函数。

如果新变量是字符型,R的优势就更明显了,字符串操作函数例如substr()取字符串其中一段,paste()连接多个字符串,grep()和sub()查找替换类,自然比SQL灵活的多。还是那句话,只要能用函数写出来,R都可以方便地搞定。你问我拿SQL跟R比这个有意思么?明显SQL就不是为了这个功能专门设计的啊。好吧,常见的生成新变量的情况:有条件的生成新变量,比如年龄分组等,基本就是按照若干已知条件生成一个新的变量。这里,SQL的case when确实方便,比如年龄分为老中青三组:

SQL:

SELECT CASE WHEN AGE>50 THEN 'old'
WHEN AGE between 25 and 50 THEN 'mid'
ELSE 'young'
END AGE_GROUP
FROM my_datatable

而R中,我一直用一种最笨的办法-刚刚搜了一下发现其实我的办法还是挺好用的。

My_dataframe$AGE_GROUP <- 'young'
My_dataframe[My_dataframe$AGE > 50,]$AGE_GROUP <- 'mid'
My_dataframe[(My_dataframe$AGE >=25 )& (My_datafame$AGE<= 50),]$AGE_GROUP <- 'mid'

当然也可以用ifelse()或者transform的方法,我倒是觉得没有这种笨办法清晰简洁易读,易于回头看代码。ifelse那堆括号哦!没有高亮匹配会死人的。

这里边界值随意,不考虑直接除法取整的情况。两种分类时可以直接用逻辑型简化,一行出结果;另,数值型离散化转换为factor型其实可以简单的用一个函数cut()搞定..(多谢yihui一语道破天机)

2. 分组加总等数据整理统计

要知道在很多时候,什么都比不上基本的求和均值方差有用,偶尔来个计数最大最小值就不错了。SQL一个group by 就神马都搞定了,比如对每组顾客购买的图书本书去重、求和。
SQL:

SELECT sum(TA.quantity) quantity ,
TB.book_type
FROM Table_A TA
OUTER JOIN Table_B TB
ON TA.book_id = TB.book_id
GROUP BY book_type

 

SELECT user_group, SUM(book_quantity) quantity, count(distinct book_id) sold_book
FROM my_datatable
GROUP BY user_group

那么相对应的,在R中,我们的解决策略是万能的data.table()。
R:

book_stat <- data.table(my_dataframe)[,list(quantity=sum(book_quantity), sold_book = length(unique(book_id))), by="user_group]

也不麻烦对嘛~可是,R里面还是有可以调用多种函数的优势哦。嘻嘻。

3. 表的连接和数据混合

咳咳,thanks to 著名的三大范式,SQL语句永远逃不掉各种各样的连接,内外左右,inner join, outer join, left join, right join 写来写去有没有!R里面呢,类似于SAS,有个神奇的merge()函数。每次看到讲left join 的教程示例的时候都觉得真心罗嗦难懂,相比而言R的merge()函数简洁明了了许多有木有!

依旧,假设我们第一个表, 两个字段 book_id, book_quantity, 然后第二个表两个字段,book_id, book_type,包含的是书的分类信息。现在需要分类统计书的数量。

SQL:

这里用外链接,既如果图书在TB中没有分类信息,会自动归于NULL这一列。

用R呢,嘻嘻,很简单。

Book_stat <- merge(TableA, TableB, by="book_id", all.x=T, all.y=T)

这里其实可以简写all=TRUE (T 在R中等价于逻辑值TRUE),只是为了更清晰所以把x,y分开了。多明显啊,我就是要保留两个表中所有的观测对象,如果任意表缺失标记为NA即可。很简单的,merge()的参数和四大连接的关系就是:
INNER JOIN 等价于 merge(all=F)
LEFT JOIN 等价于 merge(all.x=T, all.y=F)
RIGHT JOIN 等价于 merge(all.x=F, all.y=T)
OUTER JOIN 等价于 merge(all=T)

嗯啊,反正对我来说,这个更好理解...

至于SQL的where和having条件,基本就是R中对于行的选择,不再赘述,参见新变量生成那里对于行的选择。TOP或者limit也可以通过head或者直接指定行序号n:m来搞定。其他的常见的就不多了吧...过去两周的时间,我基本就在用R的整理数据框架思路来实现SQL语句撰写的煎熬中度过,多少次烦的时候都想直接砸了显示器或者哀叹如果能导出到R里面该多好...磨合期啊。

注:我现在理解SQL架构还有一项主要的feature就是索引,哈希表是个很强大的东东。这东西某种程度上类似R中factor类型的数据,但是貌似水要深很多,为了提升性能值得继续好好研究。

注2:NOSQL架构拯救分析师啊

注3:数据整理绝对是最耗分析师时间的活,如果思路不清晰不知道想要进入分析模型的数据长什么样子,那就真的悲剧了,往往一天两天就是徒劳。这也是我的小册子新版第一章加的就是数据整理,血泪的教训啊。
另外一个耗时间的就是excel或者word中作图配文字,这个绝对需要knitr来拯救-亲,对于每个分类统计是很简单,但是对于每个分类都画图的话,您难道还准备告诉excel作循环?然后一张张复制粘贴到word里面?省省时间吧,knitr会save your life的,绝对是工欲善其事,必先利其器。分析不是也不应该是体力活哦。下周的上海R沙龙,一定要好好称赞一下knitr,相比于 reproducible research,它对于业界的意义就在于没有BI系统之前,自动写报告...轻量化高效工具!

注4:ipad果然不适合码代码...有typo或不满排版的,容我稍后电脑上修改。

注5:开始研究RHadoop,各种沦落伤不起啊。