使用循环在各列中位数

时间:2018-10-30 21:53:23

标签: r

我有一个关于某些城镇居民按年龄划分的数据框。换句话说,我有这样的东西

Town        Aged18 Aged19 Aged20 Aged21 Aged22 Aged23 Aged24 Aged25 Total
Rome        2      3      5      10     15     25     20     18     98
Milan       15     19     21     25     25     20     35     15     175
Turin       14     8      8      10     15     25     24     6      110
Florence    10     15     15     10     25     10     5      5      95
Bologna     15     10     25     10     15     10     25     20     130

我想创建一个新列,指示每个城镇的居民分布中位数在哪个“年龄”列中。换句话说,我想创建这样的东西

Town    Aged18 Aged19 Aged20 Aged21 Aged22 Aged23 Aged24 Aged25 Total Median
Rome     2      3      5      10     15     25     20     18     98   23
Milan    15     19     21     25     25     20     35     15     175  22
Turin    14     8      8      10     15     25     24     6      110  22,5
Florence 10     15     15     10     25     10     5      5      95   21
Bologna  15     10     25     10     15     10     25     20     130  22

严格来说,我想创建一个循环,对每一列的内容求和,直到达到中位数为止。如果列的总数为奇数,则该位数对应于(n + 1)/ 2,而如果为偶数,它对应于(n / 2 +(n + 1)/ 2)/ 2。后者是我的数据帧中都灵的情况,我在22和23之间平均,因为它们分别对应于分别包含第55(110/2)和第56(111/2)个观测值的列。

因此,我希望新列不计算列值的中位数(这是我们可以通过rowMedian进行的操作),但是我希望它返回包含中位数观察值的列。

有人可以帮我吗?非常感谢,我希望这对尝试做类似事情的其他人也很有用。

1 个答案:

答案 0 :(得分:2)

这里是使用tidyverse的一种方法。

首先,我将数据收集为长格式,这通常简化了组分析。我还将“年龄”列转换为数字。

然后,对于每个Town,我计算该年龄段的累计人数。如果先前的累计计数少于一半,但我们将行标记为与中位数相对应,但当前的累计计数至少为一半。然后,针对特殊情况添加调整,其中Total为偶数,并且前一行累加等于一半。

library(tidyverse)
df_long <-
  df %>%
    gather(age, value, Aged18:Aged25) %>%
    mutate(age = str_remove(age, "Aged") %>% as.numeric()) %>%
    arrange(Town, age) %>%  # Probably not necessary but doesn't hurt
    group_by(Town) %>%
    mutate(cuml_count = cumsum(value),
           median     = lag(cuml_count < Total / 2, default = FALSE) & cuml_count >= Total / 2,
           median     = if_else(Total %% 2 == 0 & lag(cuml_count, default = FALSE) == Total / 2, 
                                TRUE, median))

这是视觉检查:

ggplot(df_long, aes(age, cuml_count/Total, color = median)) + geom_point() + facet_wrap(~Town)

enter image description here

最后,我们可以将原始表格与这些中位数结合起来

df2 <- df %>% 
  left_join(df_long,
            filter(median) %>%
            group_by(Town) %>%
            summarize(median = mean(age)))

输出:

> df2
      Town Aged18 Aged19 Aged20 Aged21 Aged22 Aged23 Aged24 Aged25 Total median
1     Rome      2      3      5     10     15     25     20     18    98   23.0
2    Milan     15     19     21     25     25     20     35     15   175   22.0
3    Turin     14      8      8     10     15     25     24      6   110   22.5
4 Florence     10     15     15     10     25     10      5      5    95   21.0
5  Bologna     15     10     25     10     15     10     25     20   130   22.0