Chapter 4 有序因子与无序因子 因子是一种向量对象,它给自己的组件指定了一个离散的分类(分组), 它的组件由其他等长的向量组成。R提供了有序因子和无序因子。因子的“实 际”应用是与模型公式相联系的(参见),我们在这里先看 4.1 个特例 假设,我们有从澳大利亚来的30个税务会计作为样本他们各自所在省份由下面 的字符向量指定 state <-c(" tas","sa",qld","nsw","nsw","nt","wa","wa qld",vic",nsw",vic,qld",qld","sa","tas sa,nt",wa,vic",qld",nsw ns青 "sa","act","nsw","vic","vic","act") 在这里提示读者:在一个字符向量中,“排序的”是值按照字母顺序被排序。 此时用函数 factor o创建一个因子 函数 print()对因子的处理与其它对象略有不同 state [1] tas sa qld nswnsw nt wa wa qld vic nsw vic qldqld sa [16] tas sa nt wa vic qld nsw nsw wa sa act nsw vicvic act Levels: act nsw nt q ld sa tas vica 函数1 evels()可以用来观察因子中有多少不同的水平。 42函数 tapply(与 ragged数组 继续前面的例子,假设我们拥有这些会计的收入数据 (60,49,40,61,64, 61,61,61,58,51,48,65,49,49,41,48,52,46 59,46,58,43) 处的有序是指其因子水平顺序是指定的,而无序则表明因子的水平没有经过处理,一般是按 照字母顺序排列的,所以称有序因子为定序因子似乎更恰当
Chapter 4 有序因子与无序因子 因子1 是一种向量对象,它给自己的组件指定了一个离散的分类(分组), 它的组件由其他等长的向量组成。R提供了有序因子和无序因子。因子的“实 际”应用是与模型公式相联系的(参见),我们在这里先看 4.1 一个特例 假设,我们有从澳大利亚来的30个税务会计作为样本他们各自所在省份由下面 的字符向量指定 > state <- c("tas", "sa", "qld", "nsw", "nsw", "nt", "wa", "wa", "qld", "vic", "nsw", "vic", "qld", "qld", "sa", "tas", "sa", "nt", "wa", "vic", "qld", "nsw", "nsw", "wa", "sa", "act", "nsw", "vic", "vic", "act") 在这里提示读者:在一个字符向量中,“排序的”是值按照字母顺序被排序。 此时用函数factor()创建一个因子 > statef <- factor(state) 函数print()对因子的处理与其它对象略有不同。 > statef [1] tas sa qld nsw nsw nt wa wa qld vic nsw vic qld qld sa [16] tas sa nt wa vic qld nsw nsw wa sa act nsw vic vic act Levels: act nsw nt qld sa tas vic wa 函数levels()可以用来观察因子中有多少不同的水平。 4.2 函数tapply()与ragged数组 继续前面的例子,假设我们拥有这些会计的收入数据 > incomes <- c(60, 49, 40, 61, 64, 60, 59, 54, 62, 69, 70, 42, 56, 61, 61, 61, 58, 51, 48, 65, 49, 49, 41, 48, 52, 46, 59, 46, 58, 43) 1此处的有序是指其因子水平顺序是指定的,而无序则表明因子的水平没有经过处理,一般是按 照字母顺序排列的,所以称有序因子为定序因子似乎更恰当。 12
CHAPTER4.有序因子与无序因子 此时通过函数 tapply()可以计算个省份会计收入的样本均值 incmeans <-tapply (incomes, statef, mean) 包含所得均值的向量在显示时由其水平标记 VIC 44.50057.33355.50053.60055.00060.50056.00052.250 函数 tapply(的作用是对它第一个参数的组件中所包含的每个组应用一个 函数,本例中是对 comes应用函数mean(),而 Incomes的水平由 tapply(的 第二个参数 statef定义。函数的结果是一个长度与因子水平数相等的结构。上 面的例子是比较一般的情况,即 Incomes与 statef是两个单独变量时 tapply()的 应用方法。更详细的资料读者可以通过帮助文档查询。 假设我们还要进一步的对各省税务会计收入均值的标准误进行计算。我们需 要编写一个简单的R函数来计算任何给定向量的标准误。由于R内建一个计算样 本方差的函数var(),所以我们要做的只是写一个一行的函数,并通过赋值语 句指定函数的名称 stderr < function(x) sqrt(var(x)/length(x)) (函数的编写将在稍后的章节讲述,参见)赋值完成后我们就可以这样计算 标准误了: incster <-tapply(incomes, statef, stderr) 所求得的值为 inciter d VIc 1.54.31024.54.10612.73860 244 作为一个练习,你可能还想得到收入均值95%的置信区间。可以通过下面的 方法完成:先用 tapply()应用函数1 ength(得到样本长度,然后用函数qt(来 获得t分布的分位点 函数 tapply(可以通过多类别的方法处理更复杂的向量索引。例如,我们 可能像依据省份和性别来分割税务会计的数据,在一个简单的例子中(只有 个类别),我们的思路可以是这样的:根据类别中不同的项,向量中的值被分 成不同的组,然后,函数被分别应用于每一个组。返回指是函数结果的向量 由类别的水平标记。 个向量和一个标记用的因子合并有时会成为一个 ragged array,因为子类 别的大小可能是不规则的。当子类别的大小全都相同时,合并过程中会自动完 成索引,而且这样显然会更有效率,正如我们在下一章将要看到的那样。 4.3有序因子 因子的水平按照字母顺序存储,不过如果被明确的指定,他们将按照指定的顺 序存储。有时因子的水平具有其原始的顺序,而且这种顺序可以在我们的统计 分析中被用到,所以我们需要一定的方法来记录这种顺序。函数 ordered()可 以创建这种有序因子,但是这种有序因子同因子是有差别的。在多数情况下 有序因子和无序因子的差别仅仅是前者在输出结果是其水平,不过在拟合线性 模型时,两种因子是有实质差异的
CHAPTER 4. 有序因子与无序因子 13 此时通过函数tapply()可以计算个省份会计收入的样本均值 > incmeans <- tapply(incomes, statef, mean) 包含所得均值的向量在显示时由其水平标记 act nsw nt qld sa tas vic wa 44.500 57.333 55.500 53.600 55.000 60.500 56.000 52.250 函数tapply()的作用是对它第一个参数的组件中所包含的每个组应用一个 函数,本例中是对incomes应用函数mean(),而incomes的水平由tapply()的 第二个参数statef定义。函数的结果是一个长度与因子水平数相等的结构。上 面的例子是比较一般的情况,即incomes与statef是两个单独变量时tapply()的 应用方法。更详细的资料读者可以通过帮助文档查询。 假设我们还要进一步的对各省税务会计收入均值的标准误进行计算。我们需 要编写一个简单的R函数来计算任何给定向量的标准误。由于R内建一个计算样 本方差的函数var(),所以我们要做的只是写一个一行的函数,并通过赋值语 句指定函数的名称 > stderr <- function(x) sqrt(var(x)/length(x)) (函数的编写将在稍后的章节讲述,参见)赋值完成后我们就可以这样计算 标准误了: > incster <- tapply(incomes, statef, stderr) 所求得的值为 > incster act nsw nt qld sa tas vic wa 1.5 4.3102 4.5 4.1061 2.7386 0.5 5.244 2.6575 作为一个练习,你可能还想得到收入均值95%的置信区间。可以通过下面的 方法完成:先用tapply()应用函数length()得到样本长度,然后用函数qt()来 获得t分布的分位点。 函数tapply()可以通过多类别的方法处理更复杂的向量索引。例如,我们 可能像依据省份和性别来分割税务会计的数据,在一个简单的例子中(只有一 个类别),我们的思路可以是这样的:根据类别中不同的项,向量中的值被分 成不同的组,然后,函数被分别应用于每一个组。返回指是函数结果的向量, 由类别的水平标记。 一个向量和一个标记用的因子合并有时会成为一个ragged array,因为子类 别的大小可能是不规则的。当子类别的大小全都相同时,合并过程中会自动完 成索引,而且这样显然会更有效率,正如我们在下一章将要看到的那样。 4.3 有序因子 因子的水平按照字母顺序存储,不过如果被明确的指定,他们将按照指定的顺 序存储。有时因子的水平具有其原始的顺序,而且这种顺序可以在我们的统计 分析中被用到,所以我们需要一定的方法来记录这种顺序。函数ordered() 可 以创建这种有序因子,但是这种有序因子同因子是有差别的。在多数情况下, 有序因子和无序因子的差别仅仅是前者在输出结果是其水平,不过在拟合线性 模型时,两种因子是有实质差异的
Chapter 5 数组和矩阵 51数组 数组可以看成一个由递增下标表示的数据项的集合,例如数值。R语言对创建 和处理数组及其特例矩阵提供了简单而方便的功能,尤其是对矩阵 个矩阵就是一个2维数组。维数向量中的值规定了下标k的上限。下限 般为1。如果一个向量需要在R中以数组的方式被处理,则必须含有一个维数向 量作为它的dim属性 假设,例如,z是一个由1500个元素组成的向量。下面的赋值语句 >dim(z)<-c(3,5,100) 使它具有dim属性,并且将被当作一个3×5×100的数组进行处理 在更简单和一般化的赋值过程中还可以用到像 matrix()和 array(这样的函 数。读者可以参考() 当数据向量中的值被赋给数组中的值时,将遵循与 FORTRAN相同的原 则一”主列顺序”,即第一个下标变化的最快,最后的下标变化最慢。 例如,一个数组a的维数向量是c(3,4,2),则它包含24个数据项,这些数据 项在数据向量中的顺序是a[1,1,1],a[2,1,1],.,a[2,4,2],a[3,4,2]。 52数组的索引和数组的子块 正如上面所提到的,数组中的单个元素可以通过下标来指定,下标由逗号分 隔,写在括号内。 更一般的,我们可以通过在下标的位置给出一个索引向量来指定一个数组的 子块,不过如果在任何一个索引位置上给出空的索引向量,则相当于选取了这 个下标的全部范围 继续上面的例子,a[2,,]是一个4×2的数组,维数向量为c(4,2),数据向 量中包含下面这些值 c(a[2,1,1],a[2,2,1],a[2,3,1],a[2,4,1] a[2,1,2],a[2,2,2],a[2,3,2],a[2,4,2]) 其顺序与给出顺序的相同。 a[,,J代表了整个数组,相当于省略所有下标,单独使用a。 对任意数组,比如说Z,其维数向量都可以用dim()来指代(在赋值语句的 任意一侧)
Chapter 5 数组和矩阵 5.1 数组 数组可以看成一个由递增下标表示的数据项的集合,例如数值。R语言对创建 和处理数组及其特例矩阵提供了简单而方便的功能,尤其是对矩阵。 一个矩阵就是一个2维数组。维数向量中的值规定了下标k的上限。下限一 般为1。如果一个向量需要在R中以数组的方式被处理,则必须含有一个维数向 量作为它的dim属性。 假设,例如,z是一个由1500个元素组成的向量。下面的赋值语句 > dim(z) <- c(3,5,100) 使它具有dim属性,并且将被当作一个3 × 5 × 100的数组进行处理。 在更简单和一般化的赋值过程中还可以用到像matrix()和array()这样的函 数。读者可以参考() 当数据向量中的值被赋给数组中的值时,将遵循与FORTRAN相同的原 则—”主列顺序”,即第一个下标变化的最快,最后的下标变化最慢。 例如,一个数组a的维数向量是c(3,4,2),则它包含24个数据项,这些数据 项在数据向量中的顺序是a[1,1,1], a[2,1,1],..., a[2,4,2], a[3,4,2]。 5.2 数组的索引和数组的子块 正如上面所提到的,数组中的单个元素可以通过下标来指定,下标由逗号分 隔,写在括号内。 更一般的,我们可以通过在下标的位置给出一个索引向量来指定一个数组的 子块,不过如果在任何一个索引位置上给出空的索引向量,则相当于选取了这 个下标的全部范围。 继续上面的例子,a[2,,]是一个4 × 2的数组,维数向量为c(4,2),数据向 量中包含下面这些值 c(a[2,1,1], a[2,2,1], a[2,3,1], a[2,4,1], a[2,1,2], a[2,2,2], a[2,3,2], a[2,4,2]) 其顺序与给出顺序的相同。 a[,,]代表了整个数组,相当于省略所有下标,单独使用a。 对任意数组,比如说Z,其维数向量都可以用dim()来指代(在赋值语句的 任意一侧)。 14
CHAPTER5.数组和矩阵 而且,如果一个给出的数组名称中只有一个下标或单个索引向量,那么,只 有数据向量中的相应值会被用到;这种情况下维数向量是被忽略的。不过,如 果给出的单个索引不是向量而是一个数组,就不在我们上面的讨论范围内了, 而是我们下面将要讨论的。 53索引数组 除了在任意一个下标位置使用索引向量之外,数组还可以在下标处使用索引数 组,这样可以把数值向量中不规则的一组值赋值到数组中,也可以把数组中的 组不规则的值释放到一个向量中。 我们可以用一个矩阵的例子来解释这个过程,在有双下标索引的数组的情况 下,一个索引矩阵将包含两列和所需的行数。索引向量中的项是双下标索引数 组的行索引和列索引。假定我们有一个4×5的数组x,并且希望完成下面的工 作 以向量的形式释放元素x[1,3],X[2,2]和x[3,1] ·将数组中的这些元素用0替换 此时,我们需要一个3×2的下标数组,请看下面的例子 >x < array(1: 20, dim=c (4, 5))# Generate a 4 by 5 array [,1][,2][,3][,4][,5] [1,] [3 234 6101418 7111519 >i< array(c(1:3,3:1),dim=c(3,2) 1123 [,1 >x[i] [1]963 >xLi]<-0# Replace those elements by zeros [,1][,2][,3][,4][,5] [1,] 501317 [2,]2010141 [3,]071115 8121620 作为一个不太重要的例子,假定,我们要为一个由因子b1ocks( b levels)和 varietles ( v levels)定义的区组设计生成一个设计矩阵。进一步的,我们假设实验中包 括n个计划(plot),我们可以进行如下操作 >Xb < matrix o, n, b) Xv < matrix(o, n, v)
CHAPTER 5. 数组和矩阵 15 而且,如果一个给出的数组名称中只有一个下标或单个索引向量,那么,只 有数据向量中的相应值会被用到;这种情况下维数向量是被忽略的。不过,如 果给出的单个索引不是向量而是一个数组,就不在我们上面的讨论范围内了, 而是我们下面将要讨论的。 5.3 索引数组 除了在任意一个下标位置使用索引向量之外,数组还可以在下标处使用索引数 组,这样可以把数值向量中不规则的一组值赋值到数组中,也可以把数组中的 一组不规则的值释放到一个向量中。 我们可以用一个矩阵的例子来解释这个过程,在有双下标索引的数组的情况 下,一个索引矩阵将包含两列和所需的行数。索引向量中的项是双下标索引数 组的行索引和列索引。假定我们有一个4 × 5的数组X,并且希望完成下面的工 作 • 以向量的形式释放元素X[1,3], X[2,2] 和 X[3,1] • 将数组中的这些元素用0替换 此时,我们需要一个3 × 2的下标数组,请看下面的例子 > x <- array(1:20,dim=c(4,5)) # Generate a 4 by 5 array. > x [,1] [,2] [,3] [,4] [,5] [1,] 1 5 9 13 17 [2,] 2 6 10 14 18 [3,] 3 7 11 15 19 [4,] 4 8 12 16 20 > i <- array(c(1:3,3:1),dim=c(3,2)) > i [,1] [,2] [1,] 1 3 [2,] 2 2 [3,] 3 1 > x[i] [1] 9 6 3 > x[i] <- 0 # Replace those elements by zeros. > x [,1] [,2] [,3] [,4] [,5] [1,] 1 5 0 13 17 [2,] 2 0 10 14 18 [3,] 0 7 11 15 19 [4,] 4 8 12 16 20 > 作为一个不太重要的例子,假定,我们要为一个由因子blocks(b levels) 和varieties (v levels) 定义的区组设计生成一个设计矩阵。进一步的,我们假设实验中包 括n个计划(plot),我们可以进行如下操作: > Xb <- matrix(0, n, b) > Xv <- matrix(0, n, v)
CHAPTER5.数组和矩阵 >ib<- bind (1: n, blocks) >xb[ib]<-1 Xy X <- bind (Xb, Xv) 构建关联矩阵,比如叫N,我们可以使用 >N<- crossprod (Xb, Xv) 不过,构件这个矩阵,更简单直接的方法是使用函数tabe() >N<- table(blocks, varieties 54函数 array 除了通过赋予一个向量以dim属性,我们还可以用函数 array来从向量构建数 组。函数形式为 >Z<-array(data_vector, dim_vector) 例如,若向量h包含24,或者更少个数值,那么命令 >Z<-array(h, dim=c(3, 4, 2)) 将用h的数值在Z中创建一个3×4×2的数组。如果h的大小恰好是24,那么命令 的效果等同于 >dim(Z)<-c(3,4,2) 不过,如果h的大小小于24,它的值将被重复使用直到凑足24个。(参见)作 为一个极端但是很常见的例子 z<- (0,c(3,4,2)) 使E成为一个全零的数组。 此时,dim(z)代表维数向量c(3,4,2),Z[1:24]代表数据向量,Z]和Z都 代表整个数组。 数组可以在算数表达式中使用,结果也是一个数组,这个数组由数据向量逐 个元素的运算后组成,,通常参与运算的对象应当具有相同的dim属性。而且 这将作为最终结果的维数向量。所以如果A,B,C是相似的数组,那么 >D<-2*A*B+C+1 令D成为一个与数据向量相似的数组,而且很明显的,结果将是逐个元素进行 运算后得出的。不过,涉及到向量和数组混合运算的法则还需要更进一步而且 更精确的说明 5.41向量,数组的混合运算,重复使用规则 向量,数组混合运算的精确法则会让人感觉有些怪异,而且很难在参考书中找 到。按照经验,我们发现下面的这些规则是值得信赖的一些参考 表达式从左到右被扫描 参与运算的任意对象如果大小不足,都将被重复使用直到与其他参与运 算的对象等长 有且只有较短的向量和数组在运算中相遇时,所有的数组必须具有相 同的dim属性,或者返回一个错误。( As long as short vectors and arrays only are encountered, the arrays must all have the same dim attribute or
CHAPTER 5. 数组和矩阵 16 > ib <- cbind(1:n, blocks) > iv <- cbind(1:n, varieties) > Xb[ib] <- 1 > Xv[iv] <- 1 > X <- cbind(Xb, Xv) 构建关联矩阵,比如叫N,我们可以使用 > N <- crossprod(Xb, Xv) 不过,构件这个矩阵,更简单直接的方法是使用函数table(): > N <- table(blocks, varieties) 5.4 函数array() 除了通过赋予一个向量以dim属性,我们还可以用函数array来从向量构建数 组。函数形式为 > Z <- array(data_vector, dim_vector) 例如,若向量h包含24,或者更少个数值,那么命令 > Z <- array(h, dim=c(3,4,2)) 将用h的数值在Z中创建一个3 × 4 × 2的数组。如果h的大小恰好是24,那么命令 的效果等同于 > dim(Z) <- c(3,4,2) 不过,如果h的大小小于24,它的值将被重复使用直到凑足24个。(参见)作 为一个极端但是很常见的例子 > Z <- array(0, c(3,4,2)) 使E成为一个全零的数组。 此时,dim(Z)代表维数向量c(3,4,2),Z[1:24]代表数据向量,Z[]和Z都 代表整个数组。 数组可以在算数表达式中使用,结果也是一个数组,这个数组由数据向量逐 个元素的运算后组成,,通常参与运算的对象应当具有相同的dim属性。而且 这将作为最终结果的维数向量。所以如果A,B,C是相似的数组,那么 > D <- 2*A*B + C + 1 令D成为一个与数据向量相似的数组,而且很明显的,结果将是逐个元素进行 运算后得出的。不过,涉及到向量和数组混合运算的法则还需要更进一步而且 更精确的说明。 5.4.1 向量,数组的混合运算,重复使用规则 向量,数组混合运算的精确法则会让人感觉有些怪异,而且很难在参考书中找 到。按照经验,我们发现下面的这些规则是值得信赖的一些参考。 • 表达式从左到右被扫描 • 参与运算的任意对象如果大小不足,都将被重复使用直到与其他参与运 算的对象等长 • 有且只有较短的向量和数组在运算中相遇时,所有的数组必须具有相 同的dim属性,或者返回一个错误。(As long as short vectors and arrays only are encountered, the arrays must all have the same dim attribute or an error results.)