按基数返回最常用的字符串

时间:2018-11-16 16:30:24

标签: r

我有一个数据集,我想针对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

3 个答案:

答案 0 :(得分:3)

docendo discimus's answer继续:

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)具有所有“有趣的”值,并且rreshape的助手,以后:

# 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.framerbind.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::spreaddata.table::dcast,但这是base-R,目前可以使用。 reshape的使用本身就是一个教程,因此在这里不再赘述。有许多尝试提供更用户友好的重塑工具({reshape2tidyrdata.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)])