2014-07-04 55 views
72

假设我想计算每个组内不同值的比例。例如,使用所述数据mtcars,如何计算的齿轮通过上午(自动/手动)数的相对频率一气呵成与dplyr相对频率/与dplyr的比例

library(dplyr) 
data(mtcars) 
mtcars <- tbl_df(mtcars) 

# count frequency 
mtcars %>% 
    group_by(am, gear) %>% 
    summarise(n = n()) 

# am gear n 
# 0 3 15 
# 0 4 4 
# 1 4 8 
# 1 5 5 

我想什么来实现:

am gear n rel.freq 
0 3 15  0.7894737 
0 4 4  0.2105263 
1 4 8  0.6153846 
1 5 5  0.3846154 
+0

这些百分比是你想要的实际数量吗?它们来自哪里,代数?啊,79%是15 /(15 + 4),21%是4 /(15 + 4),然后对于== == 1 62%是8 /(8 + 5)等等。 – Spacedman

+0

@Spacedman是的,这些是我想要的数字,弗兰克是正确的,他们通过am变量(79 + 21)和(62 + 38)总和为100%。 – jenswirf

+0

这真的好像是在寻找一个本地的dplyr实现''prop.table()'/'sweep()'。此外,在其他问题中,有些人[要求为变量或变量交互包含零计数](http://stackoverflow.com/questions/23778195/using-dplyr-for-frequency-counts-of- – smci

回答

143

试试这个:

mtcars %>% 
    group_by(am, gear) %>% 
    summarise (n = n()) %>% 
    mutate(freq = n/sum(n)) 

# am gear n  freq 
# 1 0 3 15 0.7894737 
# 2 0 4 4 0.2105263 
# 3 1 4 8 0.6153846 
# 4 1 5 5 0.3846154 

dplyr vignette

当您通过多个变量组,每个汇总剥离分组的一个级别。这使得渐进式汇总数据集变得很容易。

因此,summarise后,分组变量“齿轮”被剥离,然后将数据“仅”通过“AM”(只是groups检查它产生的数据)分组,在其上我们然后执行mutate计算。

“剥离”的结果当然取决于group_by调用中分组变量的顺序。这次我们很幸运,它剥离了所需的变量。您可能希望执行后续的group_by(am),以使您的代码更加明确。

为了四舍五入和美化,请参考@Tyler Rinker的好回答。

+3

我刚刚发现了这个解决方案,但是我不知道为什么'sum(n)'在'am'组而不是'gear'组上工作... – Spacedman

+4

请参阅[小插曲](http://cran.rstudio.com/web/packages/dplyr/vignettes/introduction.html):“当您通过多个变量进行分组时,每个摘要将剥离分组的一个级别。” – Henrik

+4

不错 - 如果你只是在'summarise'后面停下来,它确实说明哪些组会被留下。哦dplyr岩石... – Spacedman

19

@ Henrik的是易用性更好,因为这会令列字符,不再数字匹配,但你问什么...

mtcars %>% 
    group_by (am, gear) %>% 
    summarise (n=n()) %>% 
    mutate(rel.freq = paste0(round(100 * n/sum(n), 0), "%")) 

## am gear n rel.freq 
## 1 0 3 15  79% 
## 2 0 4 4  21% 
## 3 1 4 8  62% 
## 4 1 5 5  38% 

编辑因为Spacedman问它:-)

as.rel_freq <- function(x, rel_freq_col = "rel.freq", ...) { 
    class(x) <- c("rel_freq", class(x)) 
    attributes(x)[["rel_freq_col"]] <- rel_freq_col 
    x 
} 

print.rel_freq <- function(x, ...) { 
    freq_col <- attributes(x)[["rel_freq_col"]] 
    x[[freq_col]] <- paste0(round(100 * x[[freq_col]], 0), "%") 
    class(x) <- class(x)[!class(x)%in% "rel_freq"] 
    print(x) 
} 

mtcars %>% 
    group_by (am, gear) %>% 
    summarise (n=n()) %>% 
    mutate(rel.freq = n/sum(n)) %>% 
    as.rel_freq() 

## Source: local data frame [4 x 4] 
## Groups: am 
## 
## am gear n rel.freq 
## 1 0 3 15  79% 
## 2 0 4 4  21% 
## 3 1 4 8  62% 
## 4 1 5 5  38% 
+3

你总是可以用'format'方法创建一个S3“百分比”类,并添加一个百分号...... #overkill – Spacedman

+0

实现这个也许很有趣:http://stackoverflow.com/questions/13483430/how-to -make-rounded-percentageages-add-up-to-100 – Spacedman

+0

如果在这个例子中计算平均值,sd和SE,该怎么办? – user3655531

20

可以使用count()功能,但具有取决于版本的dplyr不同的行为:

  • dplyr 0.7.1:返回未分组表:你需要组再次am

  • dplyr < 0.7.1:再次返回分组表,因此无需组,虽然你可能想ungroup()供以后操作

dplyr 0.7.1

mtcars %>% 
    count(am, gear) %>% 
    group_by(am) %>% 
    mutate(freq = n/sum(n)) 

dplyr < 0.7。1

mtcars %>% 
    count(am, gear) %>% 
    mutate(freq = n/sum(n)) 

这导致成分组表,如果你想用它进行进一步的分析,这可能是与ungroup()删除分组属性很有用。

+0

这似乎是'dplyr' 0.7.1上的无效答案。它在“齿轮”上进行频率计算,而不是在“am”的每个级别内。 – Edwin

+1

好点,谢谢@Edwin !! – Matifou

2

这个回答基于Matifou的回答。

首先我对其进行了修改,以确保我没有通过使用scipen选项获得作为科学记数法列返回的freq列。

然后,我将答案乘以100得到一个百分比而不是十进制数,以使freq列更容易阅读为百分比。

getOption("scipen") 
options("scipen"=10) 
mtcars %>% 
count(am, gear) %>% 
mutate(freq = (n/sum(n)) * 100) 
2

这是一个在dplyr 0.7.1上实现Henrik解决方案的一般功能。

freq_table <- function(x, 
         group_var, 
         prop_var) { 
    group_var <- enquo(group_var) 
    prop_var <- enquo(prop_var) 
    x %>% 
    group_by(!!group_var, !!prop_var) %>% 
    summarise(n = n()) %>% 
    mutate(freq = n /sum(n)) %>% 
    ungroup 
} 
0

这里有一个函数来获取一对变量的行总数和列总数。

freq_tibble <- function(data, var1, var2) { 
    var1 <- rlang::enquo(var1) 
    var2 <- rlang::enquo(var2) 

    data %>% 
    dplyr::count(!!var1, !!var2) %>% 
    tidyr::spread(!!var2, n, fill = 0) %>% 
    dplyr::mutate(Total := rowSums(dplyr::select(., -!!var1))) %>% 
    dplyr::bind_rows(dplyr::bind_cols(!!rlang::quo_name(var1) := "Total", dplyr::summarize_if(., is.numeric, sum))) 
}