<<The Art of R Programing>> – exercise
 
 Exercise #1
 
 Target: 图像操作
 
 Details
 
 Achieve
# 导入 pixmap library 用于操作图像数据
if ("pixmap" %in% .packages(all.available = TRUE)) {
	library(pixmap)
} else {
	install.packages(pixmap)
	library(pixmap)
}
img <- read.pnm("test.pgm")
plot(img)
loc <- locator()
if (length(loc$x) == 0) {
	quit()
}
area <- list()
area$rows <- seq(from = ifelse(length(loc$y) < 1, 0,
    img@size[1] - loc$y[1]), to = ifelse(length(loc$y) < 2, 
    img@size[1], img@size[1] - loc$y[2]), by = 1)
area$columns <- seq(from = ifelse(length(loc$x) < 1, 0,
    loc$x[1]), to = ifelse(length(loc$x) < 2,
    img@size[2], loc$x[2]), by = 1)
print(area)
#根据 locator得到的像素区域进行增加噪声
blurImgArea <- function(img, area, noise)
{
	nRows <- length(area$rows)
	nCols <- length(area$columns)
	randomNoiseMatrix <- matrix(data = runif(nRows * nCols, min = 0, max = 1),
        nrow = nRows, ncol = nCols)
	
	img@grey[area$rows, area$columns] <- (1 - noise) *
        img@grey[area$rows, area$columns] + noise * randomNoiseMatrix
	return(img)
}
plot(blurImgArea(img, area, 0.75))
 
 Exercise #2
 
 Target:求距离矩阵最小值
 
 Details
 
 Achieve
# 通过输入一个距离矩阵,找出最小距离及其行列号(舍弃mat[i, i] == 0的结果)
# 通过对距离矩阵进行遍历进行两两比较我们可以很快就能得到答案
# ,但这种方法效率不高,没有利用到距离矩阵mat[i, j] == mat[j, i]的有效信息
# 我们先对每行处理 找出每行的最小元素,然后综合所有行结果找出解集
# 利用mat[i, j] == mat[j, i]的信息我们可以舍弃
# 每行的行号之前的列元素进行求解
# 由于apply函数会丢弃行号的信息,所以我们可以重新构造矩阵为其添加上
addRowTag <- function(mat)
{
	return(cbind(mat, 1:nrow(mat)))
}
rowMin <- function(rVec)
{
	n <- length(rVec)
	currRow <- rVec[n]
	minColIndex <- which.min(rVec[(currRow + 1):(n - 1)])
	return(c(currRow + minColIndex, rVec[currRow + minColIndex]))
}
# 返回值为 [行号, 列号, 最小元素值]
getDistanceInfo <- function(mat)
{
	mat <- addRowTag(mat)
	n <- nrow(mat)
	
	# 返回2xn矩阵 每列包含最小元素的列和该最小元素值
	rowsMinInfo <- apply(mat[-n, ], 1, rowMin)
	# 综合所有元素值找出最小元素值
	i <- which.min(rowsMinInfo[2, ]) #行号
	j <- rowsMinInfo[1, i] # 列号
	return(c(i, j, rowsMinInfo[2, i]))
}
testMat <- matrix(c(0, 12, 13, 8, 20,
	12, 0, 15, 28, 88,
	13, 15, 0, 6, 9,
	8, 28, 6, 0, 33,
	20, 88, 9, 33, 0), nrow = 5)
print(getDistanceInfo(testMat))
 
 Exercise #3
 
 Target:获取单词的索引列表
 
 Details
 
 Achieve
# 读取文件 为文件出现的单词创建索引
indexWords <- function(file)
{
	# 读取文件内容
	contents <- scan(file, what="")
	#将所有非单词字符替换为空格
	contents <- gsub("[^a-zA-Z]", " ", contents)
	# 得到所有单词的列表
	words <- strsplit(contents, split=" ")
	# 快捷函数 
	# 将字符串向量内容转换为小写
	words <- lapply(words, function(strs) {
		# 移除空字符串
		strs <- strs[strs != ""]
		return(sapply(strs, tolower))
	})
	# 将列表转换为向量
	words <- unlist(words)
	
	indexs <- list()
	for (i in 1:length(words))
	{
		indexs[[words[i]]] <- c(indexs[[words[i]]], i)
	}
	return(indexs)
}
sortListByAplpha <- function(wrdList)
{
	keys <- names(wrdList)
	index <- order(keys)
	return(wrdList[index])
}
sortListByRate <- function(wrdList)
{
	rates <- sapply(wrdList, length)
	return(wrdList[order(rates)])
}
print(indexWords("test.txt"))
 
 Exercise #4
 
 Target: 鲍鱼数据-关于性别的Logistic回归模型分析
 
 Details
 
 Achieve
# 读取鲍鱼数据
# 鲍鱼数据每一行表示:
# Gender,Length,Diameter,Height,WholeWt,ShuckedWt,ViscWt,ShellWt,Rings
# 中文解释:	 		
#  性别  	长度	直径	 高度	实际重量 去壳重量 脏器重量 壳重  年龄
# 附加描述:	
# in('M', 'F', 'I')
# 对鲍鱼数据每一列(除性别)用Logistic模型进行回归分析,对鲍鱼性别Gender进行预测
logisticPredict <- function(dataFile)
{
	# 读取鲍鱼数据
	data <- read.csv(dataFile, header=T, stringsAsFactors=T)
	
	# 鲍鱼数据包含(M, F, I),分别表示 公 母 幼儿(暂时无性别特征)
	# 去除性别为幼儿的数据
	data <- data[data$Gender != "I", ] 
	
	print(data)	
	# 对鲍鱼数据的某列进行Logistic回归分析
	predictResult <- function(cData)
	{
		return (glm(data$Gender ~ cData, family=binomial)$coefficient)
	}
	# 对鲍鱼数据除性别列外所以列进行Logistic回归分析,并返回结果集
	return(sapply(data[, -1], predictResult))	
}
print(logisticPredict('AbaloneWithHeader.data'))
 
 Appendix
 
 Exercise #5
 
 Target: 提取子表
 
 Details
初识table的构造
R中的table由一个表示因子水平频数的向量或矩阵,表示因子组合的向量或列表构成,通过传参任何能够提取因子组合的数据结构给table函数就能构造一个table> ct <- table(c("M", "F", "F", "I"))
> print(ct)
F I M 
2 1 1
> ct <- table(c("M", "F", "F", "I"), c(20, 22, 19, 22))
> print(ct)
   
    19 20 22
  F  1  0  1
  I  0  0  1
  M  0  1  0
> ct <- table(list(sex=c("M", "F", "F", "I"), age=c(20, 22, 19, 22)))
> print(ct)
   age
sex 19 20 22
  F  1  0  1
  I  0  0  1
  M  0  1  0
> ct <- table(data.frame(sex=c("M", "F", "F", "I"), age=c(20, 22, 19, 22)))
> print(ct)
   age
sex 19 20 22
  F  1  0  1
  I  0  0  1
  M  0  1  0
> ct <- table(c("M", "F", "F", "I"), c(20, 22, 19))
Error in table(c("M", "F", "F", "I"), c(20, 22, 19)) : 
  所有参数的长度都应一致
> ct <- table(data.frame(sex=c("M", "F", "F", "I"), age=c(20, 22, 19)))
Error in data.frame(sex = c("M", "F", "F", "I"), age = c(20, 22, 19)) : 
  参数值意味着不同的行数: 4, 3
将维度相同所有因子作为输入(可转化为data.frame的数据结构)
内部计算因子水平频数矩阵或数组
内部计算得到因子水平的向量或列表
组合二者得到table
提取子表
通过以上知识我们知道,table由不同空间的水平(包含该水平的标识)和由该数据结构计算的组合水平频数构成,我们通过提供水平元组(level1, level2, level3, ...)来提取它的频数,比如:> ct <- table(name=c("Marry", "Lisa", "Jack"), sex=c("F", "F", "M"), age=c(18, 21, 19))
> ct["Lisa", "F", "21"]
[1] 1
> ct <- table(name=c("Marry", "Lisa", "Jack"), sex=c("F", "F", "M"), age=c(18, 21, 19))
> subtable(ct, list(name=c("Marry", "Lisa"), sex=c("F", "M"), age=c("18", "21")))
$name
[1] 2
$sex
[1] 2
$age
[1] 2
 'table' int [1:2, 1:2, 1:2] 1 0 0 0 0 1 0 0
 - attr(*, "dimnames")=List of 3
  ..$ name: chr [1:2] "Marry" "Lisa"
  ..$ sex : chr [1:2] "F" "M"
  ..$ age : chr [1:2] "18" "21"
NULL
, , age = 18
       sex
name    F M
  Marry 1 0
  Lisa  0 0
, , age = 21
       sex
name    F M
  Marry 0 0
  Lisa  1 0
 
 Achieve
ct <- read.table("ct.txt", header=T)
cttab <- table(ct)
# 提取 table的子集
subtable <- function(tab, subDims)
{
	tabArray <- unclass(tab)
	
	# 获取每个维度的因子
	dcargs <- list(tabArray)
	nDims <- length(subDims)
	for (i in 1:nDims)
	{
		dcargs[[i+1]] <- subDims[[i]]
	}
	subArray <- do.call("[", dcargs)
	
	dim <- lapply(subDims, length)
	subArray <- array(subArray, dim=dim, subDims)
	class(subArray) <- "table"
	return(subArray)
}
print(subtable(cttab, list(Vote.for.X=c("No", "Yes"), Vote.for.Last.Time=c("No", "Yes"))))
 
 Exercise #6
 
 Target: 在表中找寻频数最大的单元格
 
 More
方案一
如果我们对R中as.data.frame(tab)的行为一无所知的话,我们可以通过table的数据结构,自行编写代码构造上述data.frame,首先我们需要获取table中所有空间的水平,构造所有可能的水平元组(通过"向量"的积构造),将该水平元组集合构造为data.frame,然后通过所有元组访问table获取频数,构造频数向量,最后将该频数向量增加到data.frame中,最后通过order函数排序 
 Achieve
ct <- read.table("ct.txt", header=T)
ct <- table(ct)
# 获取"向量"积的集合
combineVec <- function(m, v)
{
	if (!is.vector(v))
	{
		return(NA)
	}
	if (is.null(m))
	{
		return(v)
	}
	nCol <- ifelse(is.vector(m), length(m), ncol(m))
	print(nCol)
	nFrame <- NULL
	for (i in 1:nCol)
	{
		for (j in 1:length(v))
		{
			nFrame <- cbind(nFrame, c(ifelse(is.vector(m), m[i], m[, i]), v[j]))
		}
	}
	return(nFrame)
}
tabdom <- function(tab)
{
	if ("Freq" %in% dimnames(tab))
	{
		# 简单处理 如果有"Freq"列冲突直接返回
		return(NA)
	}
		
	# 将tab转为data.frame	
	levels <- dimnames(tab)
	frame <- levels[[1]]
	for (i in 2:length(levels))
	{
		frame <- combineVec(frame, levels[[i]])
	}
	# 转置:使得行向量为水平元组
	frame <- t(frame)
	colnames(frame) <- names(levels)
	frame <- data.frame(frame)
	freqs <- NULL
	
	# 构造频数向量
	for (i in 1:nrow(frame))
	{
		
		doArgs <- list(tab)
		for (j in 1:ncol(frame))
		{
			doArgs[[j+1]] <- as.character(frame[i, j])
		}
		freqs <- c(freqs, do.call("[", doArgs))
	}
	frame$Freq <- freqs
	frame <- frame[order(freqs, decreasing=T), ]	
	return(frame)
}
print(tabdom(ct))
方案二
如果我们R中as.data.frame(table)足够了解,该功能实现将会特别简单,R中的as.data.frame(table)做了我们方案一的大部分工作,它会构造一个每一行为水平元组和该水平元组在table的频数(该频数列名为Freq)的data.frame数据框,为了完成我们的功能,我们知道,只需要将table通过as.data.frame转化,然后按照Freq列排序即可 
 Achieve
ct <- read.table("ct.txt", header=T)
ct <- table(ct)
tabdom <- function(tab)
{
	frame <- as.data.frame(tab)
	frame <- frame[order(frame$Freq, decreasing=T), ]
	return(frame)
}
print(tabdom(ct))
 
 Appendix
      Next Post