sed命令

问题

源数据:

1
2
3
4
5
6
7
drt=2013-09-02+22:14:28&at=7&cid=mDamz1b7WRJZ
drt=2013-09-02+22:14:29&at=3&cid=Q4ETETM9Hjx0
drt=2013-09-02+22:14:39&at=0&cid=ytBSubxEaFN6
drt=2013-09-02+22:14:40&at=3&cid=mDamz1b7WRJZ
drt=2013-09-02+22:14:42&at=6&cid=ytBSubxEaFN6
drt=2013-09-02+22:14:42&at=3&cid=mDamz1b7WRJZ
drt=2013-09-02+22:14:45&at=0&cid=66OwWFuVBMSS

希望把drt字段中的+号及后面的替换掉:

1
sed 's/\+[\d:]+&/&/g'

结果就是死活不工作。。。

解决

原来&在sed中也是元字符,之前从来没注意到这个,和普通正则还差异多多。最后就只是一个转义符的事情:

1
sed 's/\+/\&/g'

关于&的说明:

1
保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**。

还有:

  • sed的元字符里原来没有+号的。。。
  • \d发现也是无效的

最终就变成了:

1
sed 's/+[0-9:]*\&/\&/g'

附录

R中vector和names函数

问题

源数据保存在文件中,格式如下:

1
2
3
4
5
6
7
drt=2013-09-02+22:14:28&at=7&cid=mDamz1b7WRJZ
drt=2013-09-02+22:14:29&at=3&cid=Q4ETETM9Hjx0
drt=2013-09-02+22:14:39&at=0&cid=ytBSubxEaFN6
drt=2013-09-02+22:14:40&at=3&cid=mDamz1b7WRJZ
drt=2013-09-02+22:14:42&at=6&cid=ytBSubxEaFN6
drt=2013-09-02+22:14:42&at=3&cid=mDamz1b7WRJZ
drt=2013-09-02+22:14:45&at=0&cid=66OwWFuVBMSS

需要生成一个list,其下标是数据的字段名:drt,at,cid

解决

开始写成这样,很别扭的代码:

1
2
3
4
5
x <- scan(filename, what="", sep="&", nlines=1)
fields.name <- sapply(x, FUN=function(x){strsplit(x, split="=")[[1]][1]})
names(fields.name) <- NULL
names(fields.name) <- fields.name
fields.name <- as.list(fields.name)

借鉴tapply的源码,改成这样:

1
2
3
4
x <- scan(filename, what="", sep="&", nlines=1)
fields.name <- sapply(x, FUN=function(x){strsplit(x, split="=")[[1]][1]})
tmp.fields.name <- vector("list", length(fields.name)) # 生成一个空的list
names(tmp.fields.name) <- fields.name

BlueKai模式研究

产品

附录

http://www.bluekai.com/
http://www.iamniu.com/2013/03/30/about-bluekai/

rsync命令

从远程服务器复制文件到本地服务器以前通常会使用scp命令,例如:

1
scp -P 3000 user@host:/data2/bak/20130801/part-* /home/windows/data/20130825/

不过用脚本跑的时候,发现得到的文件不齐全,有些文件不知道为什么没有下载到本地,所以寻找可以不覆盖更新的命令。本以为scp有这样的参数的,不过没发现。后来就使用rsync命令了,如:

1
rsync -aPuv '-e ssh -p 3000' user@host:/data2/bak/20130825/part-* /home/windows/data/20130825/

rsync的参数解释:

  • P:显示进度条信息
  • u:update,只返回不同的文件

扩展:http://www.howtocn.org/rsync:use_rsync

使用R来获取网易公开课的下载地址

今天看到一个网易公开课的课程,想把它们下载下来,所以写了一个脚本去分析页面结构,把title和url解析出来。

脚本见https://github.com/cyy0523xc/R/blob/master/lib/cai_get_download_url.r

其他页面的数据应该也是一样的。

R编码规范

编码规范

google:http://google-styleguide.googlecode.com/svn/trunk/Rguide.xml

中文版:http://www.road2stat.com/rstyle/rstyle.html

https://docs.google.com/document/d/1esDVxyWvH8AsX-VJa-8oqWaHLs4stGlIbk8kLc5VlII/edit

补充规范

变量的特殊前缀

  • tmp: 临时变量
  • p: 函数参数

变量的后缀

后缀通常用来表示变量的类型,如:

  • lst,tb,vec, ft等
  • fn: 函数变量, 通常在例如tapply等函数中使用

技巧

  1. 在shell直接运行R脚本
1
2
3
#!/usr/bin/Rscript --slave
argv <- commandArgs(TRUE)
x <- as.numeric(argv[1])

然后:sudo chmod +x file.r

R资源列表

文档

R扩展:http://cran.r-project.org/doc/manuals/R-exts.html

文章

炼数成金 R中国用户组http://r.dataguru.cn/
编写R包C扩展的核心指引http://www.dataguru.cn/article-1178-1.html
关于在R programming中避免显式循环的一些方法http://www.dataguru.cn/article-3284-1.html

书籍刊物

数据科学http://kan.weibo.com/kan/3444217594966746
R语言小站http://kan.weibo.com/kan/3484733640942053

课程

北美18名校的数据挖掘,数据分析,人工智能及机器学习课程汇总http://kan.weibo.com/con/3547413957114530?_from=text

移动广告数据挖掘

从有米遇到的实际情况,来谈自己对移动广告数据挖掘的一点认识。

为什么需要数据挖掘?

我知道我广告费的一半浪费掉了,但是我不知道哪一半。

这是某位大师说的话,事实上的确如此,可能还不止一半。

对于我们来说,数据挖掘就是用来指导或者实现广告的精准投放,提升广告的转化率,从而提升开发者收益,形成平台的良性循环。


什么时候需要数据挖掘?

在我看来,数据挖掘应该是一种锦上添花,而非雪中送炭。广告的转化率对于我们来说是至关重要的,所以我们会重点关注这方面数据。对于积分墙(介绍:http://www.youmi.net/page/product/#wall),大家也许会觉得,用户是奔着积分去下应用的,转化率会很高,不过中间还是有很多因数使得用户没有完成步骤,我们数据挖掘的核心任务就是要把影响转化率的主要因数找出来,以驱动产品改进决策。对于我们来说,可能看见钉子就想锤,看见数据就想挖,但是这样未必总是好的。对于一个企业来说,特别是各种资源都相对紧张的初创企业,做一件事情的成本和收益永远是其要考虑的,毫无疑问,数据挖掘是需要付出成本的,包括时间成本和人力成本等。作为企业决策者就需要衡量投入的成本和获得的收益是否符合增长的预期?如果把这部分的成本放到其他方面会不会获得更好的收益?

上周末听郭同学讲了这么个情况:

1
数据挖掘服务对于小电商肯定是需要的,但是对于小电商,他们的样本数太少,而且对于他们企业来说,主要矛盾并不是怎么挖掘提升转化率,而是怎么开拓新用户。

具体从有米的实际情况来说,也是这样的。我们是国内第一家移动广告平台,到现在已经三年多,但是真正的数据挖掘也是最近才开始的。并不是我们不重视数据挖掘,只是有很多现实的情况:

  • 媒介流量是否足够大
  • 广告量是否足够多(如果广告量不够,那么就算挖掘的模型再完美,那很可能就是无米之炊,很难用来指导广告投放)
  • 公司资源是否允许(在创业的前三年,市场在迅速的扩大,公司也在快速的扩张,内部的各种资源都紧张,自然要放在解决主要矛盾上)

基于这些情况,我们的重心也自然不在数据挖掘上,当然也不是完全没有数据挖掘,简单的还是有的。

所以,对企业来说,特别是初创企业,数据挖掘很多时候是重要不紧急的任务。


是否需要高深的算法?

对这个问题,常常让我想起大学时参加的数学建模竞赛:全国赛的时候,大家可能都喜欢使用那些看起来很高深的算法,例如神经网络,遗传算法之类;不过到了美国赛道时候,我们使用的模型并不复杂。一个模型的好与不好,我觉得并不是用算法的复杂度来衡量的,在满足现实条件时,能解决实际问题的模型,就是好的模型。我们关注的是解决问题本身,而不是算法。

好的模型通常是简单,而且应该易于理解的。

对我们来说,高深的挖掘技术,暂时也还没有用上,不过可以列举一些简单有效的案例给大家。


怎么进行数据挖掘?

这里最重要的一步就是要把每一个步骤到下一个步骤的转化率计算出来,这些计算都很简单,下面给出两个实例(数据是经过简化的)。

积分墙不同接入点下的转化率分析

https://docs.google.com/spreadsheet/ccc?key=0AtdtDh06pqwEdGZUZWQzVFd1OG43UHpXVzA2SnE3dWc&usp=sharing

从数据或者图像至少可以看出几个问题:

  • 从启动安装到安装完成这一步的转化率明显比其他步骤低很多
  • 展示到点击的这个转化率也普遍不高
  • 从请求到展示这个步骤中,在cmwap的环境下特别低
  • 从启动下载到下载完成,在cmnet和cmwap环境下也明显偏低
  • 在wifi环境下,可以看到有两个转化率是明显比总的转化率高的,但是其他的却没有。

看出了问题,我们再跟问题去寻找问题发生的原因。。。

积分墙下安装包大小对在不同网络环境下的影响

数据如下:

https://docs.google.com/spreadsheet/ccc?key=0AtdtDh06pqwEdDlacnZ6YVdBT1pLRy1UR1NfMDV6YkE&usp=sharing

从这份数据我们也可以得出一些产品或者投放上的优化建议,不过这次重点不关注这个。从图像上看,包的大小和转化率基本呈线性关系。有了这个线性模型,我们就可以去计算,怎么分配不同的广告包,以达到我们收益的最大化。


扩展阅读

这两个都是视频,值得看。

R function tapply

tapply

1
tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)

把变量X(通常为向量)通过函数FUN作用在INDEX这个因子变量上,返回值可以根据simplify参数设置。simplify = T(默认)返回array,simplify = F则返回list。

实例

计算各个省份的人均收入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> ins <- list(year=c(2011, 2012, 2012, 2013, 2013), province=c("GZ", "GZ", "BG", "BG", "GZ"), income=c(10, 12, 13, 12, 15)) # 定义数据
> ins
$year
[1] 2011 2012 2012 2013 2013
$province
[1] "GZ" "GZ" "BG" "BG" "GZ"
$income
[1] 10 12 13 12 15
> tapply(ins$income, ins$province, mean)
BG GZ
12.50000 12.33333

计算各个省份在各个年份的平均收入

1
2
3
4
> tapply(ins$income, list(ins$province, ins$year), mean)
2011 2012 2013
BG NA 13 12
GZ 10 12 15

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function (X, INDEX, FUN = NULL, ..., simplify = TRUE)
{
FUN <- if (!is.null(FUN))
match.fun(FUN)
if (!is.list(INDEX)) # 如果分组因子不是list,则自动转为list
INDEX <- list(INDEX)
nI <- length(INDEX)
if (!nI)
stop("'INDEX' is of length zero")
namelist <- vector("list", nI)
names(namelist) <- names(INDEX)
extent <- integer(nI)
nx <- length(X)
one <- 1L
group <- rep.int(one, nx) # 构造一个重复向量
ngroup <- one
for (i in seq_along(INDEX)) { # 对分组因子列表的下标进行循环处理
index <- as.factor(INDEX[[i]])
if (length(index) != nx)
stop("arguments must have same length")
namelist[[i]] <- levels(index)
extent[i] <- nlevels(index)
# 注意这里计算分组的算法:计算组合后的分组情况
group <- group + ngroup * (as.integer(index) - one)
ngroup <- ngroup * nlevels(index)
}
if (is.null(FUN))
return(group)
# 分组数据,完成映射(FUN)
ans <- lapply(X = split(X, group), FUN = FUN, ...) # split按照分组因子切割变量X
index <- as.integer(names(ans))
if (simplify && all(unlist(lapply(ans, length)) == 1L)) {
ansmat <- array(dim = extent, dimnames = namelist)
ans <- unlist(ans, recursive = FALSE)
}
else {
ansmat <- array(vector("list", prod(extent)), dim = extent,
dimnames = namelist)
}
if (length(index)) {
names(ans) <- NULL
ansmat[index] <- ans
}
ansmat
}

这个源码有可以优化的地方,例如循环体里面的:

1
2
3
namelist[[i]] <- levels(index)
extent[i] <- nlevels(index)
ngroup <- ngroup * nlevels(index)

实际上nlevels(index)只是对levels(index)取length,所以这三行代码实质上调用了levels函数三次(在一次循环体里面),调用nlevels和length两次。可以修改成这样:

1
2
3
namelist[[i]] <- levels(index)
extent[i] <- length(namelist[[i]])
ngroup <- ngroup * extent[i]

有时间的话,可以考虑把tapply改成C语言实现

R function apply

apply

apply(X, MARGIN, FUN, …)

参数说明

1
2
3
4
X: array or matrix
MARGIN: 1表示按行计算,2表示按列计算,c(1, 2)表示对行和列同时作用,就会对每个元素都产生作用
FUN: 作用函数
...: 作用函数的参数

实例

按行计算

1
2
3
4
5
6
7
> a
[,1] [,2] [,3] [,4]
[1,] 1 3 2 1
[2,] 2 1 3 2
> apply(a, 1, function(x)sum(x)) # 可以简化成:apply(a, 1, sum)
[1] 7 8

按列计算

1
2
3
> apply(a, 2, function(x)sum(x))
[1] 3 4 5 3

行列同时作用

1
2
3
4
> apply(a, c(1,2), function(x)sum(x))
[,1] [,2] [,3] [,4]
[1,] 1 3 2 1
[2,] 2 1 3 2

扩展参数

1
2
3
4
5
6
> apply(a, 1, function(x, t)x+t, 10) #
[,1] [,2]
[1,] 11 12
[2,] 13 11
[3,] 12 13
[4,] 11 12