是否有更好的方法对列表中的每个值进行group_by?

时间:2019-10-30 23:41:55

标签: r dplyr purrr

我正在尝试找到最好的方法来遍历数据帧的每一列,按该列分组并产生摘要。 这是我的尝试:

library(tidyverse)
data = data.frame(
  a = sample(LETTERS[1:3], 100, replace=TRUE),
  b = sample(LETTERS[1:8], 100, replace=TRUE),
  c = sample(LETTERS[3:15], 100, replace=TRUE),
  d = sample(LETTERS[16:26], 100, replace=TRUE),
  value = rnorm(100)
)

myfunction <- function(x) {
  groupVars <- select_if(x, is.factor) %>% colnames()
  results <- list()
  for(i in 1:length(groupVars)) {
  results[[i]] <- x %>%
    group_by_at(.vars = vars(groupVars[i])) %>%
    summarise(
      n = n()
    ) 
  }
  return(results)
}

test <- myfunction(data)

该函数返回:

[[1]]
# A tibble: 3 x 2
  a         n
  <fct> <int>
1 A        37
2 B        34
3 C        29
...
...
...

我的问题是,这是最好的方法吗?有没有办法避免使用for循环?我可以使用purrr并以某种方式映射吗?

谢谢

3 个答案:

答案 0 :(得分:2)

一种选择是使用map

library(tidyverse)
map(data[1:4], ~data.frame(x = {{.x}}) %>% count(x))
#$a
## A tibble: 3 x 2
#  x         n
#  <fct> <int>
#1 A        39
#2 B        32
#3 C        29
#
#$b
## A tibble: 8 x 2
#  x         n
#  <fct> <int>
#1 A        14
#2 B        11
#3 C        16
#4 D        10
#5 E        12
#6 F        10
#7 G        13
#8 H        14
#...

输出为list。请注意,我忽略了data的最后一列,因为这里似乎无关紧要。


如果您要根据原始list中的列来命名data.frame data中的列,我们可以使用imap

imap(data[1:4], ~tibble(!!.y := {{.x}}) %>% count(!!sym(.y)))
#$a
## A tibble: 3 x 2
#  a         n
#  <fct> <int>
#1 A        23
#2 B        35
#3 C        42
#
#$b
## A tibble: 8 x 2
#  b         n
#  <fct> <int>
#1 A        15
#2 B        10
#3 C        13
#4 D         5
#5 E        19
#6 F         9
#7 G        13
#8 H        16
#...

或者利用tibble::enframe(感谢@camille)

imap(data[1:4], ~enframe(.x, value = .y) %>% count(!!sym(.y)))

答案 1 :(得分:1)

您可以根据列和字母来重塑数据和分组。这样就为您提供了一个数据框,而不是它们的列表,但是如果您确实需要split,则可以获取该列表。

set.seed(123)
library(tidyverse)
data = data.frame(
  a = sample(LETTERS[1:3], 100, replace=TRUE),
  b = sample(LETTERS[1:8], 100, replace=TRUE),
  c = sample(LETTERS[3:15], 100, replace=TRUE),
  d = sample(LETTERS[16:26], 100, replace=TRUE),
  value = rnorm(100)
)

data %>%
  pivot_longer(cols = -value, names_to = "column", values_to = "letter") %>%
  group_by(column, letter) %>%
  summarise(n = n())
#> # A tibble: 35 x 3
#> # Groups:   column [4]
#>    column letter     n
#>    <chr>  <fct>  <int>
#>  1 a      A         33
#>  2 a      B         32
#>  3 a      C         35
#>  4 b      A          8
#>  5 b      B         11
#>  6 b      C         12
#>  7 b      D         14
#>  8 b      E          8
#>  9 b      F         17
#> 10 b      G         16
#> # … with 25 more rows

reprex package(v0.3.0)于2019-10-30创建

答案 2 :(得分:0)

您可以简单地致电:

apply(data, 2,table)

如果需要,您可以删除最后一个列表元素。