zoukankan      html  css  js  c++  java
  • Advanced R之构造子集

    转发请声明出处:http://www.cnblogs.com/lizichao/p/4794733.html

    构造子集

      R构造子集的操作功能强大而且速度快。精通构造子集者可以用简洁的方式表达复杂的操作,很少有其他语言能做到这一点。构造子集学习起来比较困难,因为需要掌握一系列相互关联的概念:

    • 3种构造子集操作符。
    • 6类子集。
    • 不同对象(比如向量、列表、因子、矩阵、数据框)行为上的重要不同。
    • 联合使用构造子集与赋值。

      本章将帮助掌握构造子集,让我们从最简单的构造子集开始:用[构造原子向量的子集。然后逐渐扩展你的知识,首先学习更加复杂的数据类型(比如数组和列表),然后是其他构造子集操作符,[[和$。接着你将学习如何组合使用构造子集和赋值来修改对象的一部分,最后,你将看到很多有用的应用。

      构造子集与str()相辅相成。str()展示任意对象的结构,构造子集让你选取你所感兴趣的部分。

    测试

      做下下面的测试,以确定你是否需要阅读本章。如果可以很快想到答案,你可以跳过本章。可以在answers中检查你的答案是否正确。

    1. 构造向量的子集时,使用正整数、负整数、一个逻辑向量或者一个字符向量,结果会如何?
    2. 当作用于列表时,[,[[,$有何不同?
    3. 什么时候使用drop = FALSE
    4. 如果x是一个矩阵,那么x[] <- 0会发生什么?这与x <- 0有何不同?
    5. 如何使用命名向量来重新标记分类变量?
    概述
    • 数据类型部分以学习[开始。首先学习可以用来构造原子向量子集的6种数据类型,然后学习这6种数据类型如何构造列表、矩阵、数据框和S3对象的子集。
    • 构造子集操作符部分扩展关于构造子集操作符的知识,包括[[和$,关注点在于结构简化与保留的重要规律。
    • 构造子集与赋值部分你将学习局部赋值的技巧,即联合使用构造子集和赋值来改变对象的一部分。
    • 应用部分介绍8种重要但是不明显的构造子集应用,这些应用在数据分析中经常用到。

    数据类型

      构造原子向量子集是最简单的,然后归纳到高维和其他更加复杂的对象。我们从[开始,[是最常见的构造子集操作符。构造子集操作符部分将介绍[[和$,它们是另外2种主要的构造子集操作符。

    原子向量

      让我们看下对于一个简单的向量x,用不同数据类型来构造其子集会有何不同。

    x <- c(2.1, 4.2, 3.3, 5.4)

      注意,向量中每个元素小数点后的数字,表明该元素在向量中的位置。

      可以用5样东西来构造向量的子集:

    •  正整数,这时将得到对应位置的元素:

      x[c(3, 1)]
      #> [1] 3.3 2.1
      x[order(x)]
      #> [1] 2.1 3.3 4.2 5.4
      
      # Duplicated indices yield duplicated values
      x[c(1, 1)]
      #> [1] 2.1 2.1
      
      # Real numbers are silently truncated to integers
      x[c(2.1, 2.9)]
      #> [1] 4.2 4.2
    • 负整数,这时会忽略对应位置的元素:

      x[-c(3, 1)]
      #> [1] 4.2 5.4

      不能混合使用正整数和负整数: 

    x[c(-1, 2)]
    #> Error in x[c(-1, 2)]: only 0's may be mixed with negative subscripts
    • 逻辑向量,这时会选择对应的逻辑值为真的元素。这可能是最有用的构造子集方式,因为你可以利用创建逻辑向量的表达式:

      x[c(TRUE, TRUE, FALSE, FALSE)]
      #> [1] 2.1 4.2
      x[x > 3]
      #> [1] 4.2 3.3 5.4

      如果逻辑向量的长度,比子集所在向量的长度短,逻辑向量将被循环复制直到达到相同的长度。

      x[c(TRUE, FALSE)]
      #> [1] 2.1 3.3
      # Equivalent to
      x[c(TRUE, FALSE, TRUE, FALSE)]
      #> [1] 2.1 3.3

      缺失值所在的下标,一般对应的也会得到一个缺失值:

      x[c(TRUE, TRUE, NA, FALSE)]
      #> [1] 2.1 4.2  NA
    • ,此时返回原向量。这对于向量来说没有用,但是对于矩阵、数据框和数组非常有用。当联合使用构造子集与赋值时也比较有用。

      x[]
      #> [1] 2.1 4.2 3.3 5.4
    • 0,此时返回一个长度为0的向量。这通常不是你的意图,但是对于产生测试数据是有用的。

      x[0]
      #> numeric(0)
      如果向量是命了名的,你也可以使用:
    • 字符向量,此时返回匹配的元素。

      (y <- setNames(x, letters[1:4]))
      #>   a   b   c   d 
      #> 2.1 4.2 3.3 5.4
      y[c("d", "c", "a")]
      #>   d   c   a 
      #> 5.4 3.3 2.1
      
      # Like integer indices, you can repeat indices
      y[c("a", "a", "a")]
      #>   a   a   a 
      #> 2.1 2.1 2.1
      
      # When subsetting with [ names are always matched exactly
      z <- c(abc = 1, def = 2)
      z[c("a", "d")]
      #> <NA> <NA> 
      #>   NA   NA

    列表

      构造列表的子集与构造原子向量子集的方式相同。对列表使用[构造子集总是得到一个列表;[[和$得到列表的内容,如下所述。

    矩阵和数组

      对于高维数据结构,有3种方式构造子集:

    • 使用多个向量。
    • 使用单个向量。
    • 使用矩阵。

      构造矩阵(2维)和数组(>2维)子集最常见的方式,是对1维情况下构造子集的简单拓展:对于每个维度提供一个1维的下标,用逗号分隔。用空格构造子集此时比较有用,因为它允许你保留所有的行或者列。

    a <- matrix(1:9, nrow = 3)
    colnames(a) <- c("A", "B", "C")
    a[1:2, ]
    #>      A B C
    #> [1,] 1 4 7
    #> [2,] 2 5 8
    a[c(T, F, T), c("B", "A")]
    #>      B A
    #> [1,] 4 1
    #> [2,] 6 3
    a[0, -2]
    #>      A C
      默认情况下,[会简化结果,即将结果的维度降到可能的最低维度。参见结构简化与保留部分介绍的如何避免这种情况。

      因为矩阵和数组是通过向量实现的,即具有特殊属性的向量,所以可以用一个向量来对它们构造子集。这种情况下,矩阵和数组会表现的像向量。R中的数组以列为首要存储顺序。

    (vals <- outer(1:5, 1:5, FUN = "paste", sep = ","))
    #>      [,1]  [,2]  [,3]  [,4]  [,5] 
    #> [1,] "1,1" "1,2" "1,3" "1,4" "1,5"
    #> [2,] "2,1" "2,2" "2,3" "2,4" "2,5"
    #> [3,] "3,1" "3,2" "3,3" "3,4" "3,5"
    #> [4,] "4,1" "4,2" "4,3" "4,4" "4,5"
    #> [5,] "5,1" "5,2" "5,3" "5,4" "5,5"
    vals[c(4, 15)]
    #> [1] "4,1" "5,3"
      也可以利用整形矩阵来对更高维度的数据结构构造子集(或者,如果是命名的数据结构,可以用字符矩阵)。对于子集所在的数组,矩阵的每一行确定了值的位置,每一列对应一个维度。也就是说,用2列的矩阵来构造矩阵的子集,用3列的矩阵构造3维数组的矩阵。得到的结果是向量:
    vals <- outer(1:5, 1:5, FUN = "paste", sep = ",")
    select <- matrix(ncol = 2, byrow = TRUE, c(
      1, 1,
      3, 1,
      2, 4
    ))
    vals[select]
    #> [1] "1,1" "3,1" "2,4"

    数据框

      数据框具有列表和矩阵的特征:如果用一个向量对数据框构造子集,数据框表现的像列表;如果使用2个向量,它表现的像矩阵。

    df <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])
    
    df[df$x == 2, ]
    #>   x y z
    #> 2 2 2 b
    df[c(1, 3), ]
    #>   x y z
    #> 1 1 3 a
    #> 3 3 1 c
    
    # There are two ways to select columns from a data frame
    # Like a list:
    df[c("x", "z")]
    #>   x z
    #> 1 1 a
    #> 2 2 b
    #> 3 3 c
    # Like a matrix
    df[, c("x", "z")]
    #>   x z
    #> 1 1 a
    #> 2 2 b
    #> 3 3 c
    
    # There's an important difference if you select a single 
    # column: matrix subsetting simplifies by default, list 
    # subsetting does not.
    str(df["x"])
    #> 'data.frame':    3 obs. of  1 variable:
    #>  $ x: int  1 2 3
    str(df[, "x"])
    #>  int [1:3] 1 2 3

    S3对象

      S3对象由原子向量、数组、列表组成,所以你可以使用上述方法以及str()得到的信息,来构造S3对象的子集。

    S4对象

      对于S4对象,还有另外2种构造子集的运算符:@(等同于$),slot()(等同于[[)。@比$有更加严格的限制,即如果槽不存在会返回错误。这些内容在the OO field guide介绍。

    练习

    1. 下列代码试图构造数据框子集,请修复其中的错误:

      mtcars[mtcars$cyl = 4, ]
      mtcars[-1:4, ]
      mtcars[mtcars$cyl <= 5]
      mtcars[mtcars$cyl == 4 | 6, ]
    2. 对于x <- 1:5;为何x[NA]得到5个缺失值?(思路:这与x[NA_real_]为何不同?)

    3. upper.tri()返回什么?为何可以使用它对矩阵构造子集?对于这种情况,我们是否需要额外的构造子集规则来描述它?

      x <- outer(1:5, 1:5, FUN = "*")
      x[upper.tri(x)]
    4. 为何mtcars[1:20]返回错误?它与mtcars[1:20, ]为何不同?

    5. 自己动手写一个函数,取矩阵对角线上的元素(该函数应该类似于diag(x),其中x是矩阵)。

    6. df[is.na(df)] <- 0什么意思?为何可以这样使用?

    构造子集操作符

      另外2种构造子集操作符是[[和$。[[类似于[,只不过[[只返回一个单独的值,而且允许构造列表的子集。$是[[的简化,它利用字符构造子集,很有用。

      当构造列表子集时需要使用[[。因为[应用到列表时总是得到一个列表:而不是列表的内容。为了得到列表内容,需要使用[[:

    “如果x是拉货的火车,x[[5]]是5号车厢中的货物;x[4:6]是4到6号车厢。”

    @RLangTip

      因为[[只返回一个值,必须使用正整数或者字符串来使用[[:

    a <- list(a = 1, b = 2)
    a[[1]]
    #> [1] 1
    a[["a"]]
    #> [1] 1
    
    # If you do supply a vector it indexes recursively
    b <- list(a = list(b = list(c = list(d = 1))))
    b[[c("a", "b", "c", "d")]]
    #> [1] 1
    # Same as
    b[["a"]][["b"]][["c"]][["d"]]
    #> [1] 1

      因为数据框是列组成的列表,所以要使用[[来提取数据框的一列:mtcars[[1]]mtcars[["cyl"]]

      S3对象和S4对象可以覆盖[和[[操作符的标准行为,所以对于不同的对象会有不同的表现。关键的不同点往往在于如何选择结构简化或者保留,以及默认情况。

    结构简化和保留

      理解子集结构简化和保留的区别非常重要。结构简化是指返回的子集的数据结构尽可能简单,只要能表示输出即可,对于交互来说这很有用因为这样往往可以得到想要的结果。结构保留是指输入与输出的结构相同,在编程中这往往更好些,因为结果一直是同类型的。构造矩阵和数据框子集时,忽略drop = FALSE是个最常见的编程错误。(在测试时可以正常工作,但是对于只有一列的数据框,会以不可预测且不清楚的方式失败。)

      不幸的是,对不同数据类型,如何在简化与保留之间切换那,这里总结了下表。

      简化 保留
    向量 x[[1]] x[1]
    列表 x[[1]] x[1]
    因子 x[1:4, drop = T] x[1:4]
    数组 x[1, ] or x[, 1] x[1, , drop = F] or x[, 1, drop = F]
    数据框
    x[, 1] or x[[1]] x[, 1, drop = F] or x[1]

      结构保留对于所有的数据类型都相同:输出与输入类型相同。不同数据类型的结构简化行为略有不同,如下:

    • 原子向量:移除名称。

      x <- c(a = 1, b = 2)
      x[1]
      #> a 
      #> 1
      x[[1]]
      #> [1] 1
    • 列表:返回列表中的对象,而不是单个元素的列表。

      y <- list(a = 1, b = 2)
      str(y[1])
      #> List of 1
      #>  $ a: num 1
      str(y[[1]])
      #>  num 1
    • 因子:丢掉所有没有用到的因子水平。

      z <- factor(c("a", "b"))
      z[1]
      #> [1] a
      #> Levels: a b
      z[1, drop = TRUE]
      #> [1] a
      #> Levels: a
    • 矩阵和数组:如果某个维度的长度为1,丢掉该维度。

      a <- matrix(1:4, nrow = 2)
      a[1, , drop = FALSE]
      #>      [,1] [,2]
      #> [1,]    1    3
      a[1, ]
      #> [1] 1 3
    • 数据框:如果输出是单个列,返回一个向量而非数据框。

      df <- data.frame(a = 1:2, b = 1:2)
      str(df[1])
      #> 'data.frame':    2 obs. of  1 variable:
      #>  $ a: int  1 2
      str(df[[1]])
      #>  int [1:2] 1 2
      str(df[, "a", drop = FALSE])
      #> 'data.frame':    2 obs. of  1 variable:
      #>  $ a: int  1 2
      str(df[, "a"])
      #>  int [1:2] 1 2

    $

      $是个简化符号,x$y等同于x[["y", exact = FALSE]]。它常被用在数据框中访问变量,比如mtcars$cyl or diamonds$carat

      对于$常见的一个错误是,当将列名存储到一个变量,尝试并使用$和这个变量来访问数据:

    var <- "cyl"
    # Doesn't work - mtcars$var translated to mtcars[["var"]]
    mtcars$var
    #> NULL
    
    # Instead use [[
    mtcars[[var]]
    #>  [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

      $与[[之间有一个重要的不同,即$支持部分匹配:

    x <- list(abc = 1)
    x$a
    #> [1] 1
    x[["a"]]
    #> NULL

      如果要禁止这种情况,可以使用全局选项warnPartialMatchDollarTRUE。使用时要小心:这可能影响你加载的其他代码(比如从包中加载的代码)。

    缺失值/下标越界

       当下标越界时(OOB),[和[[的行为略有不同,比如,对于一个长度为4的向量,试图取第5个元素,或者使用NA或NULL构造子集:

    x <- 1:4
    str(x[5])
    #>  int NA
    str(x[NA_real_])
    #>  int NA
    str(x[NULL])
    #>  int(0)

      下表总结了利用[和[[,在不同类型下标、下标越界情况下,构造原子向量和列表子集的结果。

    操作符 下标 原子向量 列表
    [ OOB NA list(NULL)
    [ NA_real_ NA list(NULL)
    [ NULL x[0] list(NULL)
    [[ OOB Error Error
    [[ NA_real_ Error NULL
    [[ NULL Error Error

      如果输入向量是命名的,那么下标越界、缺失值或者NULL,对应结果的名称为"<NA>"

    测试

    1. 给定一个线性模型,比如mod <- lm(mpg ~ wt, data = mtcars),提取残差的自由度。从模型摘要(summary(mod))中提取R squared。

    构造子集和赋值

      所有子集构造符都可以与赋值操作联合使用,来修改输入变量指定的值。

    x <- 1:5
    x[c(1, 2)] <- 2:3
    x
    #> [1] 2 3 3 4 5
    
    # The length of the LHS needs to match the RHS
    x[-1] <- 4:1
    x
    #> [1] 2 4 3 2 1
    
    # Note that there's no checking for duplicate indices
    x[c(1, 1)] <- 2:3
    x
    #> [1] 3 4 3 2 1
    
    # You can't combine integer indices with NA
    x[c(1, NA)] <- c(1, 2)
    #> Error in x[c(1, NA)] <- c(1, 2): NAs are not allowed in subscripted assignments
    # But you can combine logical indices with NA
    # (where they're treated as false).
    x[c(T, F, NA)] <- 1
    x
    #> [1] 1 4 3 1 1
    
    # This is mostly useful when conditionally modifying vectors
    df <- data.frame(a = c(1, 10, NA))
    df$a[df$a < 5] <- 0
    df$a
    #> [1]  0 10 NA

      利用空来构造子集,并联合使用赋值操作比较有用,因为这会保留原始对象的类和结构。比较下列2个表达式。第一个,mtcars仍然是一个数据框。第二个,mtcars变成了一个列表。

    mtcars[] <- lapply(mtcars, as.integer)
    mtcars <- lapply(mtcars, as.integer)

      对于列表,可以联合使用构造子集、赋值、NULL来移除列表的一部分。利用[和list(NULL)添加一个字符NULL到列表中:

    x <- list(a = 1, b = 2)
    x[["b"]] <- NULL
    str(x)
    #> List of 1
    #>  $ a: num 1
    
    y <- list(a = 1)
    y["b"] <- list(NULL)
    str(y)
    #> List of 2
    #>  $ a: num 1
    #>  $ b: NULL

    应用

      利用上述基本规则,可以创建很多有用的应用。下列描述的是一些最重要的应用。其中一些基本的技巧已经包装到了更加准确的函数中(比如subset(),merge()plyr::arrange()),但是理解其中的构造子集原理是很有用的。这样当你遇到新的情况而现有函数无法解决时,你知道该怎么做。

    查阅表(字符构造子集)

       字符匹配是创建查询表的一种强大方式。比如你要转换缩略词:

    x <- c("m", "f", "u", "f", "f", "m", "m")
    lookup <- c(m = "Male", f = "Female", u = NA)
    lookup[x]
    #>        m        f        u        f        f        m        m 
    #>   "Male" "Female"       NA "Female" "Female"   "Male"   "Male"
    unname(lookup[x])
    #> [1] "Male"   "Female" NA       "Female" "Female" "Male"   "Male"
    
    # Or with fewer output values
    c(m = "Known", f = "Known", u = "Unknown")[x]
    #>         m         f         u         f         f         m         m 
    #>   "Known"   "Known" "Unknown"   "Known"   "Known"   "Known"   "Known"

      如果不想要结果中的名称,使用unname()将其移出。

    手动匹配和合并(整形构造子集)

      你可能有一个更加复杂的查询表,它包含多个列。假设我们有一个成绩向量,以及一个描述成绩属性的数据框:

    grades <- c(1, 2, 2, 3, 1)
    
    info <- data.frame(
      grade = 3:1,
      desc = c("Excellent", "Good", "Poor"),
      fail = c(F, F, T)
    )

      我们想复制信息表,需要对于grades中每个值创建一行。有2种方式实现,一是使用match()和整形构造子集,或者使用rownames和字符构造子集:

    grades
    #> [1] 1 2 2 3 1
    
    # Using match
    id <- match(grades, info$grade)
    info[id, ]
    #>     grade      desc  fail
    #> 3       1      Poor  TRUE
    #> 2       2      Good FALSE
    #> 2.1     2      Good FALSE
    #> 1       3 Excellent FALSE
    #> 3.1     1      Poor  TRUE
    
    # Using rownames
    rownames(info) <- info$grade
    info[as.character(grades), ]
    #>     grade      desc  fail
    #> 1       1      Poor  TRUE
    #> 2       2      Good FALSE
    #> 2.1     2      Good FALSE
    #> 3       3 Excellent FALSE
    #> 1.1     1      Poor  TRUE

      如果有多个列需要匹配,你需要将它们折叠到一个单独的列(使用interaction()paste()或者 plyr::id())。也可以用merge()或者plyr::join(),作用是一样的——要了解如何实现的可以查阅源代码。

    随机取样/放回取样(整数构造子集)

      对向量和数据框,可以使用整数下标实现随机取样或者有放回取样。sample()生成一个下标向量,然后构造子集来访问相应值:

    df <- data.frame(x = rep(1:3, each = 2), y = 6:1, z = letters[1:6])
    
    # Set seed for reproducibility
    set.seed(10)
    
    # Randomly reorder
    df[sample(nrow(df)), ]
    #>   x y z
    #> 4 2 3 d
    #> 2 1 5 b
    #> 5 3 2 e
    #> 3 2 4 c
    #> 1 1 6 a
    #> 6 3 1 f
    # Select 3 random rows
    df[sample(nrow(df), 3), ]
    #>   x y z
    #> 2 1 5 b
    #> 6 3 1 f
    #> 3 2 4 c
    # Select 6 bootstrap replicates
    df[sample(nrow(df), 6, rep = T), ]
    #>     x y z
    #> 3   2 4 c
    #> 4   2 3 d
    #> 4.1 2 3 d
    #> 1   1 6 a
    #> 4.2 2 3 d
    #> 3.1 2 4 c

      sample()的参数控制取样的数量,以及是否是有放回取样。

    排序(整数构造子集)

      order()以向量为输入,返回一个整形向量,该整形向量描述了该向量应该如何排序:

    x <- c("b", "c", "a")
    order(x)
    #> [1] 3 1 2
    x[order(x)]
    #> [1] "a" "b" "c"

      为断绝关系,你可以在order()中使用额外的变量,还可以使用decreasing = TRUE将升序修改为降序。缺省情况下,所有缺省值会被放在向量最后;然而可以使用na.last = NA移除缺省值,或者使用na.last = FALSE将缺省值放在前面。

      对于2维和高维数据结构,利用order()和整形构造子集,可以方便的按照对象的行或者列排序:

    # Randomly reorder df
    df2 <- df[sample(nrow(df)), 3:1]
    df2
    #>   z y x
    #> 3 c 4 2
    #> 1 a 6 1
    #> 2 b 5 1
    #> 4 d 3 2
    #> 6 f 1 3
    #> 5 e 2 3
    
    df2[order(df2$x), ]
    #>   z y x
    #> 1 a 6 1
    #> 2 b 5 1
    #> 3 c 4 2
    #> 4 d 3 2
    #> 6 f 1 3
    #> 5 e 2 3
    df2[, order(names(df2))]
    #>   x y z
    #> 3 2 4 c
    #> 1 1 6 a
    #> 2 1 5 b
    #> 4 2 3 d
    #> 6 3 1 f
    #> 5 3 2 e

      sort()可以更准确对向量排序,但是不够灵活,同样plyr::arrange()可以对数据框进行更准确的排序,但是同样不够灵活。

    扩展合计数量(整形构造子集)

      有时你会得到这样一个数据框,它的相同的行被折叠到了一行,并且有一列表明行的数量。rep()和整形构造子集可以很容易将折叠的数据分开,这通过一个重复的行索引实现:

    df <- data.frame(x = c(2, 4, 1), y = c(9, 11, 6), n = c(3, 5, 1))
    rep(1:nrow(df), df$n)
    #> [1] 1 1 1 2 2 2 2 2 3
    df[rep(1:nrow(df), df$n), ]
    #>     x  y n
    #> 1   2  9 3
    #> 1.1 2  9 3
    #> 1.2 2  9 3
    #> 2   4 11 5
    #> 2.1 4 11 5
    #> 2.2 4 11 5
    #> 2.3 4 11 5
    #> 2.4 4 11 5
    #> 3   1  6 1

    移除数据框的列(字符构造子集)

      有2种方式从数据框中移除列。可以通过将某一列设置为NULL来实现:

    df <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])
    df$z <- NULL

      或者可以通过构造子集来获取想要的列:

    df <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])
    df[c("x", "y")]
    #>   x y
    #> 1 1 3
    #> 2 2 2
    #> 3 3 1

      如果你知道哪些列是不需要的,可以使用设置操作来得到想要的列:

    df[setdiff(names(df), "z")]
    #>   x y
    #> 1 1 3
    #> 2 2 2
    #> 3 3 1

    根据条件选择行(逻辑构造子集)

      因为可以方便的合并多个列的条件,所以逻辑构造子集可能是提取数据框行的最常用方式。

    mtcars[mtcars$gear == 5, ]
    #>     mpg cyl  disp  hp drat    wt qsec vs am gear carb
    #> 27 26.0   4 120.3  91 4.43 2.140 16.7  0  1    5    2
    #> 28 30.4   4  95.1 113 3.77 1.513 16.9  1  1    5    2
    #> 29 15.8   8 351.0 264 4.22 3.170 14.5  0  1    5    4
    #> 30 19.7   6 145.0 175 3.62 2.770 15.5  0  1    5    6
    #> 31 15.0   8 301.0 335 3.54 3.570 14.6  0  1    5    8
    mtcars[mtcars$gear == 5 & mtcars$cyl == 4, ]
    #>     mpg cyl  disp  hp drat    wt qsec vs am gear carb
    #> 27 26.0   4 120.3  91 4.43 2.140 16.7  0  1    5    2
    #> 28 30.4   4  95.1 113 3.77 1.513 16.9  1  1    5    2

      记得使用&和|,而不是&&和||,&&和||在条件语句中更加有用。不要忘记摩根定律(De Morgan’s laws),它可以用来简化否定:

    • !(X & Y)等同于!X | !Y
    • !(X | Y)等同于!X & !Y

      例如,!(X & !(Y | Z))可以简化为!X | !!(Y|Z),进一步简化为!X | Y | Z

      subset()是一个特殊的简化了的构造数据框子集的函数,用它可以少敲几下键盘,因为不需要重复数据框的名字。你将在非标准计算(non-standard evaluation)中看到它是如何工作的。

    subset(mtcars, gear == 5)
    #>     mpg cyl  disp  hp drat    wt qsec vs am gear carb
    #> 27 26.0   4 120.3  91 4.43 2.140 16.7  0  1    5    2
    #> 28 30.4   4  95.1 113 3.77 1.513 16.9  1  1    5    2
    #> 29 15.8   8 351.0 264 4.22 3.170 14.5  0  1    5    4
    #> 30 19.7   6 145.0 175 3.62 2.770 15.5  0  1    5    6
    #> 31 15.0   8 301.0 335 3.54 3.570 14.6  0  1    5    8
    subset(mtcars, gear == 5 & cyl == 4)
    #>     mpg cyl  disp  hp drat    wt qsec vs am gear carb
    #> 27 26.0   4 120.3  91 4.43 2.140 16.7  0  1    5    2
    #> 28 30.4   4  95.1 113 3.77 1.513 16.9  1  1    5    2

    布尔代数与设置操作(逻辑和整形构造子集)

      需要注意设置操作(整形构造子集)与布尔代数(布尔构造子集)之间的等效性。在某些情况下使用设置操作更加有效:

    • 试图找出第一个(或者最后一个)TRUE

    • 当有比较少的TRUEs和非常多的FALSEs;设置操作可能更快而且需要更少的内存。

      which()可以将一个逻辑表示转换为整形表示。基础R中没有反转操作但是可以很容易创建一个:

    x <- sample(10) < 4
    which(x)
    #> [1]  3  7 10
    
    unwhich <- function(x, n) {
      out <- rep_len(FALSE, n)
      out[x] <- TRUE
      out
    }
    unwhich(which(x), 10)
    #>  [1] FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE

      让我们来创建2个逻辑向量和对等的整形向量,然后看下布尔值与设置操作的关系。

    (x1 <- 1:10 %% 2 == 0)
    #>  [1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE
    (x2 <- which(x1))
    #> [1]  2  4  6  8 10
    (y1 <- 1:10 %% 5 == 0)
    #>  [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE
    (y2 <- which(y1))
    #> [1]  5 10
    
    # X & Y <-> intersect(x, y)
    x1 & y1
    #>  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
    intersect(x2, y2)
    #> [1] 10
    
    # X | Y <-> union(x, y)
    x1 | y1
    #>  [1] FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE
    union(x2, y2)
    #> [1]  2  4  6  8 10  5
    
    # X & !Y <-> setdiff(x, y)
    x1 & !y1
    #>  [1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE
    setdiff(x2, y2)
    #> [1] 2 4 6 8
    
    # xor(X, Y) <-> setdiff(union(x, y), intersect(x, y))
    xor(x1, y1)
    #>  [1] FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE FALSE
    setdiff(union(x2, y2), intersect(x2, y2))
    #> [1] 2 4 6 8 5

      开始学习构造子集时的一个常见错误是使用x[which(y)]替代x[y]。这儿which()什么都没干:它将逻辑构造子集转换成整形构造子集,但是结果完全一样。同样要注意x[-which(y)]并不等同于x[!y]:如果y全是FALSE,which(y)会得到integer(0),而integer(0)还是integer(0),所以你没有获取任何值,而不是所有值。通常应该避免将逻辑构造子集转换成整形构造子集,除非你想这么做,比如获取第一个或者最后一个TRUE。

    练习

    1. 如何随机排列数据框中的列?(在随机预测中这很重要。)你能否在一步内同时随机排列行和列?
    2. 如何随机选取数据框中的m行?如果要求这m行是连续的那(比如,一个首行,一个结束行,以及其中所有的行)?
    3. 如何将数据框的列按照字母表排序?

    答案

    1. 正整数选取对应位置的元素,负整数去除对应位置的元素;逻辑向量保留TRUE对应位置的元素;字符向量按照名称匹配选取元素。
    2. [选取子列表。它总是得到一个列表;如果使用[和一个正整数来构造子集,会得到一个长度为1的列表。[[选取列表中的元素。$是为了方便起见的缩写符号:x$y等同于x[["y"]]
    3. 如果构造矩阵、数据或者数据框的子集时,想保留数据结构,使用drop = FALSE。在函数内容构造子集时,你应该总是这么做。
    4. 如果x是矩阵,x[] <- 0将替换每个元素为0,行和列的数量不变,x <- 0会将矩阵替换为0。
    5. 一个命名的向量可以作为一个简单的查询表:c(x = 1, y = 2, z = 3)[c("y", "z", "x")]
  • 相关阅读:
    Linux系统下/tmp目录文件重启后自动删除,不重启自动删除10天前的/TMP的文件(转)
    Docker 镜像加速器
    RabbitMQ集群和高可用配置
    k8s如何管理Pod(rc、rs、deployment)
    微信开放平台开发(1) 语义理解
    微信开放平台开发(2) 微信登录
    微信电话
    微信支付开发(1) 微信支付URL配置
    微信支付开发(2) 微信支付账号体系
    微信支付开发(3) JS API支付
  • 原文地址:https://www.cnblogs.com/lizichao/p/4794733.html
Copyright © 2011-2022 走看看