• R语言 数据的整理与清洗(Data Frame 篇下)


    《Cookbook for R》 Manipulating Data ~ Dataframe

    Comparing data frames 比较数据框

    当你想比较两个或多个数据框,并从中找到:
    1、重复出现在多个数据框中的行
    2、或仅出现在一个数据框中的行

    先构建三个示例数据框

    dfA <- data.frame(Subject=c(1,1,2,2), Response=c("X","X","X","X"))
    dfA
    #>   Subject Response
    #> 1       1        X
    #> 2       1        X
    #> 3       2        X
    #> 4       2        X
    
    dfB <- data.frame(Subject=c(1,2,3), Response=c("X","Y","X"))
    dfB
    #>   Subject Response
    #> 1       1        X
    #> 2       2        Y
    #> 3       3        X
    
    dfC <- data.frame(Subject=c(1,2,3), Response=c("Z","Y","Z"))
    dfC
    #>   Subject Response
    #> 1       1        Z
    #> 2       2        Y
    #> 3       3        Z
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    要进行比较,首先将三个数据框与一列连接起来,该列标识每行来自哪个源数据框

    dfA$Coder <- "A"
    dfB$Coder <- "B"
    dfC$Coder <- "C"
    
    df <- rbind(dfA, dfB, dfC)                    # 将三个数据框合并
    # rbind 合并的前提是列名相同,按列将行依次粘起来
    df <- df[,c("Coder", "Subject", "Response")]  # 重新排个序
    df
    #>    Coder Subject Response
    #> 1      A       1        X
    #> 2      A       1        X
    #> 3      A       2        X
    #> 4      A       2        X
    #> 5      B       1        X
    #> 6      B       2        Y
    #> 7      B       3        X
    #> 8      C       1        Z
    #> 9      C       2        Y
    #> 10     C       3        Z
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    查找重复的行:使用函数 dupsBetweenGroups()
    可以找到哪些行在不同的组之间重复

    dupRows <- dupsBetweenGroups(df, "Coder")
    
    # 将它单独列在数据框边上
    cbind(df, dup=dupRows)
    #>    Coder Subject Response   dup
    #> 1      A       1        X  TRUE
    #> 2      A       1        X  TRUE
    #> 3      A       2        X FALSE
    #> 4      A       2        X FALSE
    #> 5      B       1        X  TRUE
    #> 6      B       2        Y  TRUE
    #> 7      B       3        X FALSE
    #> 8      C       1        Z FALSE
    #> 9      C       2        Y  TRUE
    #> 10     C       3        Z FALSE
    ## 是没有保存的
    ## 要保存就赋值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Note:这样操作是不会显示组内重复的
    例如:Coder=A,则有两行Subject=1和Response=X,但它们未标记为重复

    查找唯一的行

    cbind(df, unique=!dupRows)
    #>    Coder Subject Response unique
    #> 1      A       1        X  FALSE
    #> 2      A       1        X  FALSE
    #> 3      A       2        X   TRUE
    #> 4      A       2        X   TRUE
    #> 5      B       1        X  FALSE
    #> 6      B       2        Y  FALSE
    #> 7      B       3        X   TRUE
    #> 8      C       1        Z   TRUE
    #> 9      C       2        Y  FALSE
    #> 10     C       3        Z   TRUE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    subset()拆分数据框:将原始的数据框分出来

    # 将结果保存在df中
    dfDup <- cbind(df, dup=dupRows)
    
    dfA <- subset(dfDup, Coder=="A", select=-Coder)
    dfA
    #>   Subject Response   dup
    #> 1       1        X  TRUE
    #> 2       1        X  TRUE
    #> 3       2        X FALSE
    #> 4       2        X FALSE
    
    dfB <- subset(dfDup, Coder=="B", select=-Coder)
    dfB
    #>   Subject Response   dup
    #> 5       1        X  TRUE
    #> 6       2        Y  TRUE
    #> 7       3        X FALSE
    
    dfC <- subset(dfDup, Coder=="C", select=-Coder)
    dfC
    #>    Subject Response   dup
    #> 8        1        Z FALSE
    #> 9        2        Y  TRUE
    #> 10       3        Z FALSE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    忽略列:可以忽略一个或多个列
    方法是从传递给函数的数据框中删除该列

    # 忽略 Subject 这一列 
    dfNoSub <- subset(df, select=-Subject)
    dfNoSub
    #>    Coder Response
    #> 1      A        X
    #> 2      A        X
    #> 3      A        X
    #> 4      A        X
    #> 5      B        X
    #> 6      B        Y
    #> 7      B        X
    #> 8      C        Z
    #> 9      C        Y
    #> 10     C        Z
    
    # 检查重复
    dupRows <- dupsBetweenGroups(dfNoSub, "Coder")
    
    # 将结果合并进先前的数据框
    cbind(df, dup=dupRows)
    #>    Coder Subject Response   dup
    #> 1      A       1        X  TRUE
    #> 2      A       1        X  TRUE
    #> 3      A       2        X  TRUE
    #> 4      A       2        X  TRUE
    #> 5      B       1        X  TRUE
    #> 6      B       2        Y  TRUE
    #> 7      B       3        X  TRUE
    #> 8      C       1        Z FALSE
    #> 9      C       2        Y  TRUE
    #> 10     C       3        Z FALSE
    
    • 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

    扩充:dupsBetweenGroups()函数是如何写出来的

    # 函数定义如下
    dupsBetweenGroups <- function (df, idcol) {
        # df: 数据框
        # idcol: 用于确定每行所属的列
    
        # 获取用于查找匹配的列
        datacols <- setdiff(names(df), idcol)    # setdiff(x,y) 以x为标准,找出x、y的差集
    
        # idcol进行分类, 再按datacols分类. 保存排序,方便后面取消排序
        sortorder <- do.call(order, df) 
        df <- df[sortorder,]
    
        # 先把重复的行找出来 (first copy is not marked)
        dupWithin <- duplicated(df) 
    
        # 把每个组内的重复行去掉, 再在组间找重复行
        # 需要用duplicated()从上到下再从下到上扫描  because first copy is not marked.
        dupBetween = rep(NA, nrow(df))
        dupBetween[!dupWithin] <- duplicated(df[!dupWithin,datacols])
        dupBetween[!dupWithin] <- duplicated(df[!dupWithin,datacols], fromLast=TRUE) | dupBetween[!dupWithin]
    
        # ============= 用先前非NA值来替换NA值 ==============
        # 这就是为什么要提前分类 - 这使我们高效率地完成这部分工作
    
        # 获得非NA值的索引
        goodIdx <- !is.na(dupBetween)
    
        # 仅来自x的非NA值
        # 添加一个前导NA用于后续对该列向量进行索引
        goodVals <- c(NA, dupBetween[goodIdx])
    
        # 避免索引值为0
        fillIdx <- cumsum(goodIdx)+1
    
        # 原始向量, now with gaps filled
        dupBetween <- goodVals[fillIdx]
    
        # 撤销原来的排序
        dupBetween[sortorder] <- dupBetween
    
        # 返回各组中重复条目的向量
        return(dupBetween)
    }
    
    • 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
    小扩充
    (x <- c(sort(sample(1:20, 9)), NA))
    # x  1  3  4  5  6 10 12 17 20 NA
    (y <- c(sort(sample(3:23, 7)), NA))
    # y  3  6  7  9 10 11 18 NA
    
    # 把x、y不重复的元素都放到一起
    union(x, y)
    #  [1]  1  3  4  5  6 10 12 17 20 NA  7  9 11 18
    
    # 把x、y取交集
    intersect(x, y)
    # [1]  3  6 10 NA
    
    # 以x为标准,把x、y的差集找到
    setdiff(x, y)
    # 1  4  5 12 17 20
    
    # 以y为标准,把y、x的差集找到
    setdiff(y, x)
    # [1]  7  9 11 18
    
    # 检查两个对象是否相等
    setequal(x, y)
    # [1] FALSE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    Re-computing the levels of all factor columns in a data frame 数据框:重新计算因子列的level

    有时候,在读入数据并对其进行清理后,会因子列中存在一些不该存在的level

    示例数据
    这里 d 有一个空白行,当它被读入时,因子列具有’ ’ ,这不应该是数据的一部分

    d <- read.csv(header = TRUE, text='
    x,y,value
    a,one,1
    ,,5
    b,two,4
    c,three,10
    ')
    
    d
    #>   x     y value
    #> 1 a   one     1
    #> 2             5
    #> 3 b   two     4
    #> 4 c three    10
    
    str(d)
    #> 'data.frame':	4 obs. of  3 variables:
    #>  $ x    : Factor w/ 4 levels "","a","b","c": 2 1 3 4
    #>  $ y    : Factor w/ 4 levels "","one","three",..: 2 1 4 3
    #>  $ value: int  1 5 4 10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    即使删除了空行,因子仍然以空字符串 " " 作为一个等级

    # 删去第二行
    d <- d[-2,]
    d
    #>   x     y value
    #> 1 a   one     1
    #> 3 b   two     4
    #> 4 c three    10
    
    str(d)
    #> 'data.frame':	3 obs. of  3 variables:
    #>  $ x    : Factor w/ 4 levels "","a","b","c": 2 3 4
    #>  $ y    : Factor w/ 4 levels "","one","three",..: 2 4 3
    #>  $ value: int  1 4 10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    最简单的方法是使用 droplevels() 函数

    d1 <- droplevels(d)
    str(d1)
    #> 'data.frame':	3 obs. of  3 variables:
    #>  $ x    : Factor w/ 3 levels "a","b","c": 1 2 3
    #>  $ y    : Factor w/ 3 levels "one","three",..: 1 3 2
    #>  $ value: int  1 4 10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    要重新计算所有因子列的水平,使用 vapply() 和 is.factor() 来找出哪些列是因子
    然后使用该信息和 lapply 循环将 factor() 函数应用于这些列。

    # 找出哪些列是因子
    factor_cols <- vapply(d, is.factor, logical(1))
    
    # 应用 factor() 函数到这些列, 然后赋值返回结果d
    d[factor_cols] <- lapply(d[factor_cols], factor)
    str(d)
    #> 'data.frame':	3 obs. of  3 variables:
    #>  $ x    : Factor w/ 3 levels "a","b","c": 1 2 3
    #>  $ y    : Factor w/ 3 levels "one","three",..: 1 3 2
    #>  $ value: int  1 4 10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    Anaconda下的pkgs占用空间13G,如何安全的清理(已解决)
    Numpy入门[11]——生成数组的函数
    2022年最新最全软件测试面试题大全
    C++数据结构稀疏矩阵运算(含加减乘及快速转置)
    【PAT甲级】1136 A Delayed Palindrome
    鸿蒙LiteOs读源码教程+向LiteOS中添加一个简单的基于线程运行时的短作业优先调度策略
    基于Python实现的神经网络分类MNIST数据集
    接口测试时遇到接口加密了该如何处理?
    Spring Boot面试必问:启动流程
    【Kubernetes 系列】一文学会Kubernetes Service安全的暴露应用
  • 原文地址:https://blog.csdn.net/yangyulu1998/article/details/138181165