我有一个数据集,我想针对b
的每个唯一条目返回列a
中最频繁的条目,其中两列都是字符向量。如果b
中的两个条目对于a
中的唯一条目来说是同样频繁,那么我想在单独的列中返回b
的两个条目(以下为期望的输出)。 / p>
这类似于问here的问题,但是该问题的答案全部使用tidyverse。我是tidyverse的厌恶者,因为tidyverse对象破坏了我项目中的其他内容。寻找基本的R解决方案(并希望避免谈论tidyverse的优点)。
我的数据如下:
a <- as.character(c(rep(1:3,4)))
b <- c("A","A","A",
"B","B","B",
"A","B","A",
"A","B","B")
df <- data.frame(a,b)
a b
1 A
2 A
3 A
1 B
2 B
3 B
1 A
2 B
3 A
1 A
2 B
3 B
所需的输出:
group match_1 match_2
1 A <NA>
2 B <NA>
3 A B
答案 0 :(得分:3)
library(dplyr)
# library(tidyr)
df %>%
count(a, b) %>%
group_by(a) %>%
filter(n == max(n)) %>%
mutate(r = row_number()) %>%
tidyr::spread(r, b) %>%
select(-n)
# # A tibble: 3 x 3
# # Groups: a [3]
# a `1` `2`
# <fct> <fct> <fct>
# 1 1 A <NA>
# 2 2 B <NA>
# 3 3 A B
然后您只需要重命名列即可。
Base R变体:
reshape(do.call(rbind.data.frame, by(df, df$a, function(x) {
tb <- table(x$b)
tb <- tb[ tb == max(tb) ]
data.frame(a = x$a[1], b = names(tb), r = seq_along(tb))
})), timevar = "r", idvar = "a", direction = "wide")
# a b.1 b.2
# 1 1 A <NA>
# 2 2 B <NA>
# 3.1 3 A B
我将其分解,因为并非所有内容都直观:
by
函数返回一个list
(特殊格式,但仍然只是一个列表)。如果我们查看a
的单个实例,那么我们来看看会发生什么。我将跳到a == "3"
,因为那是重复的:
by(df, df$a, function(x) { browser(); 1; })
# Called from: FUN(data[x, , drop = FALSE], ...)
# Browse[1]>
debug at #1: [1] 1
# Browse[2]>
Called from: FUN(data[x, , drop = FALSE], ...)
# Browse[1]>
debug at #1: [1] 1
# Browse[2]>
Called from: FUN(data[x, , drop = FALSE], ...)
# Browse[1]>
debug at #1: [1] 1
# Browse[2]>
x
# a b
# 3 3 A
# 6 3 B
# 9 3 A
# 12 3 B
# Browse[2]>
( tb <- table(x$b) )
# A B
# 2 2
好的,现在我们有了每个{b
的计数。意识到这里可能有更多的东西,例如:
# A B C
# 2 2 1
所以我将把这个命名向量简化为只有最高值的向量:
# Browse[2]>
( tb <- tb[ tb == max(tb) ] ) # no change here, but had there been a third value in 'b' ...
# A B
# 2 2
最后,我们希望by
捕获data.frame
(以后可以合并)。我们保证a
是一个可能重复的值,所以a[1]
;我们确保names(tb)
具有所有“有趣的”值,并且r
是reshape
的助手,以后:
# Browse[2]>
data.frame(a = x$a[1], b = names(tb), r = seq_along(tb))
# a b r
# 1 3 A 1
# 2 3 B 2
现在,我们在内部进行了探索,让我们总结一下。
by(df, df$a, function(x) {
tb <- table(x$b)
tb <- tb[ tb == max(tb) ]
data.frame(a = x$a[1], b = names(tb), r = seq_along(tb))
})
# df$a: 1
# a b r
# 1 1 A 1
# ------------------------------------------------------------
# df$a: 2
# a b r
# 1 2 B 1
# ------------------------------------------------------------
# df$a: 3
# a b r
# 1 3 A 1
# 2 3 B 2
这看起来很尴尬,但是如果您深入了解(使用dput
),您会发现它只是重新分类的list
。现在,我们可以将它们合并为一个框架:
do.call(rbind.data.frame, by(df, df$a, function(x) {
tb <- table(x$b)
tb <- tb[ tb == max(tb) ]
data.frame(a = x$a[1], b = names(tb), r = seq_along(tb))
}))
# a b r
# 1 1 A 1
# 2 2 B 1
# 3.1 3 A 1
# 3.2 3 B 2
顺便说一句:data.frame
和rbind.data.frame
的默认情况下都为您提供factor
个。如果您不想要它们,则:
do.call(rbind.data.frame, c(by(df, df$a, function(x) {
tb <- table(x$b)
tb <- tb[ tb == max(tb) ]
data.frame(a = x$a[1], b = names(tb), r = seq_along(tb),
stringsAsFactors = FALSE)
}), stringsAsFactors=FALSE))
# a b r
# 1 1 A 1
# 2 2 B 1
# 3.1 3 A 1
# 3.2 3 B 2
然后重塑。我承认这是其中最脆弱的部分(至少对我而言)。我不是reshape
用户,我倾向于tidyr::spread
或data.table::dcast
,但这是base-R,目前可以使用。 reshape
的使用本身就是一个教程,因此在这里不再赘述。有许多尝试提供更用户友好的重塑工具({reshape2
,tidyr
,data.table
都摆在了前面,但不可能是唯一的)。< / p>
答案 1 :(得分:2)
另一个base
替代方案。
创建列联表并转换为数据框:as.data.frame(table(df))
。
使用ave
按组选择具有最大值的行。
使用ave
创建一个“时间”变量,以“区分同一组中的多个记录”(请参见?reshape
)。
reshape
相关变量要广泛。
d <- as.data.frame(table(df))
d2 <- d[d$Freq == ave(d$Freq, d$a, FUN = max), ]
d2$time <- ave(d2$a, d2$a, FUN = seq_along)
reshape(d2[ , c("a", "b", "time")], idvar = "a", direction = "wide")
# a b.1 b.2
# 1 1 A <NA>
# 3 3 A B
# 5 2 B <NA>
如果需要,请按“ a”排序。
答案 2 :(得分:1)
我们可以在base R
tbl <- table(df)
ifelse(tbl[,1] == tbl[,2], toString(colnames(tbl)), colnames(tbl)[max.col(tbl)])