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

发布于:2024-04-26 ⋅ 阅读:(23) ⋅ 点赞:(0)

《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

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

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

查找重复的行:使用函数 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
## 是没有保存的
## 要保存就赋值

Note:这样操作是不会显示组内重复的
例如:Coder=A,则有两行Subject=1Response=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

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

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

# 忽略 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

扩充: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)
}
小扩充
(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

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

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

# 删去第二行
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

最简单的方法是使用 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

要重新计算所有因子列的水平,使用 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