当前位置: 首页 > 知识库问答 >
问题:

按行计算具有公共名称模式的列组的累积平均值

虞承泽
2023-03-14

我想在列名称中由模式定义的不同列集上按行计算累积平均值。

示例数据,两组列分别以ab开头:

a1 = c(1, 2, 3)
a2 = c(4, 5, 6)
a3 = c(7, 8, 9)
a4 = c(10, 11, 12)
b1 = c(10, 20, 30)
b2 = c(40, 50, 60)
b3 = c(70, 80, 90)
b4 = c(100, 110, 120)

df = data.frame(a1, a2, a3, a4, b1, b2, b3, b4)

> df
  a1 a2 a3 a4 b1 b2 b3  b4
1  1  4  7 10 10 40 70 100
2  2  5  8 11 20 50 80 110
3  3  6  9 12 30 60 90 120

第一组计算在名称以< code>a开头的列中执行:

a1_2a1a2

a1_3a1、a2a3 的平均值

< code>a1_4是< code>a1 、< code>a2 、< code>a3和< code>a4的平均值。

同样,我想对“< code>b列”执行相同的计算:< code>b1_2 、< code>b1_3和< code>b1_4的计算方式与< code>a1_2 、< code>a1_3和< code>a1_4完全相同。

我可以用以下代码生成a1_2b1_4。但在实际情况中,我有太多类似的变量要生成。

library(dplyr)
df %>% 
  rowwise() %>% 
  mutate(a1_2 = mean(c(a1, a2)),
         a1_3 = mean(c(a1, a2, a3)),
         a1_4 = mean(c(a1, a2, a3, a4)),
         b1_2 = mean(c(b1, b2)),
         b1_3 = mean(c(b1, b2, b3)),
         b1_4 = mean(c(b1, b2, b3, b4))) %>% 
  ungroup()

  a1 a2 a3 a4 b1 b2 b3  b4 a1_2 a1_3 a1_4 b1_2 b1_3 b1_4
1  1  4  7 10 10 40 70 100  2.5    4  5.5   25   40   55
2  2  5  8 11 20 50 80 110  3.5    5  6.5   35   50   65
3  3  6  9 12 30 60 90 120  4.5    6  7.5   45   60   75

如何更高效地执行这些计算,而不必手动逐个生成?这些生成的变量有一个模式,该模式用于计算多个变量的平均值。

我查了一个与我相关的问题(需要同时使用R中的Cross()创建多个新变量)。但是在这个问题中,作者生成的新变量与数据帧中的其他变量不相关,这与我遇到的问题不一样。

共有3个答案

孟浩慨
2023-03-14

这是基本R中的另一种方法:

  1. 定义一个小的包装函数,将 rowMeans 应用于以变量 v 开头的列集。
  2. 列根据名称“a”和“b”将原始 data.frame 与包装器的输出绑定(在具有许多不同变量名称的情况下,这可以推广)。
rowMeansAcc <- function(df, v) {
  m <- as.matrix(df[, grep(v, colnames(df))])
  m_mean <- sapply(1:ncol(m), \(i) rowMeans(m[, 1:i, drop = FALSE]))[, -1, drop = FALSE]
  colnames(m_mean) <- sprintf("%s1_%d", v, 2:ncol(m))
  m_mean  
}

cbind(df, rowMeansAcc(df, "a"), rowMeansAcc(df, "b"))

#>   a1 a2 a3 a4 b1 b2 b3  b4 a1_2 a1_3 a1_4 b1_2 b1_3 b1_4
#> 1  1  4  7 10 10 40 70 100  2.5    4  5.5   25   40   55
#> 2  2  5  8 11 20 50 80 110  3.5    5  6.5   35   50   65
#> 3  3  6  9 12 30 60 90 120  4.5    6  7.5   45   60   75

注意:我们可以稍微修改包装器函数,使其直接用于例如 mutate() 调用,


rowMeansAcc2 <- function(...) {
  m <- cbind(...)
  m_mean <- sapply(1:ncol(m), \(i) rowMeans(m[, 1:i, drop = FALSE]))[, -1, drop = FALSE]
  colnames(m_mean) <- sprintf("1_%d", 2:ncol(m))
  m_mean  
}

mutate(df, "a" = rowMeansAcc2(a1, a2, a3, a4), "b" = rowMeansAcc2(b1, b2, b3, b4))

#>   a1 a2 a3 a4 b1 b2 b3  b4 a.1_2 a.1_3 a.1_4 b.1_2 b.1_3 b.1_4
#> 1  1  4  7 10 10 40 70 100   2.5   4.0   5.5    25    40    55
#> 2  2  5  8 11 20 50 80 110   3.5   5.0   6.5    35    50    65
#> 3  3  6  9 12 30 60 90 120   4.5   6.0   7.5    45    60    75
金霄
2023-03-14

这里是一个管道中的<code>dplyr</code>选项(感谢@jav的出色方法):

library(dplyr)
library(tidyr)
df %>%
  mutate(id = row_number()) %>%
  pivot_longer(cols = -id) %>%
  mutate(group = sub("^([[:alpha:]]*).*", "\\1", name),
         number = gsub(".*?([0-9]+).*", "\\1", name)) %>%
  group_by(id, group) %>%
  mutate(avg_value = cummean(value),
         col_name := paste0(group, min(number), "_", number)) %>%
  filter(number != 1) %>%
  pivot_wider(id_cols = id, names_from = col_name, values_from = avg_value) %>%
  group_by(id) %>%
  fill(everything(), .direction = "downup") %>%
  slice(1) %>%
  merge(df %>% mutate(id = row_number()), ., by = 'id') %>%
  select(-id)
#>   a1 a2 a3 a4 b1 b2 b3  b4 a1_2 a1_3 a1_4 b1_2 b1_3 b1_4
#> 1  1  4  7 10 10 40 70 100  2.5    4  5.5   25   40   55
#> 2  2  5  8 11 20 50 80 110  3.5    5  6.5   35   50   65
#> 3  3  6  9 12 30 60 90 120  4.5    6  7.5   45   60   75

创建于 2022-09-18,使用reprex v2.0.2

许俊贤
2023-03-14

我不知道如何使用突变来解决这个问题,但我可以向您展示一种使用data.table的方法

首先,我将解释我处理这个问题的方法:

>

  • 首先,对于以a开头的列,您要查找(a1, a2)的平均值、的平均值(a1, a2, a3)等。我将简化为仅调用这些(1,2)的平均值、(1,2,3)的平均值等。从表面上看,我们可以重新措辞,因为您需要跨列分组的累积平均值

    我们可以尝试按组使用 cummean,但这仅适用于行。

    因此,我们只需将数据集重设为长格式,执行<code>cummean

    导入包:

       library(data.table)
       library(dplyr)
    

    将您的数据帧转换为data.table格式:

       setDT(df)
    

    向数据框添加行号:

       df[, row_id := .I]
    

    重新调整为长格式:

       df2 = melt.data.table(df, id.vars = "row_id")
    

    此时,您的数据如下所示:

        row_id variable value
     1:      1       a1     1
     2:      2       a1     2
     3:      3       a1     3
     4:      1       a2     4
     5:      2       a2     5
     6:      3       a2     6
     7:      1       a3     7
     8:      2       a3     8
     9:      3       a3     9
    10:      1       a4    10
    11:      2       a4    11
    12:      3       a4    12
    13:      1       b1    10
    14:      2       b1    20
    15:      3       b1    30
    16:      1       b2    40
    17:      2       b2    50
    18:      3       b2    60
    19:      1       b3    70
    20:      2       b3    80
    21:      3       b3    90
    22:      1       b4   100
    23:      2       b4   110
    24:      3       b4   120
    

    让我们从数字中分离出字母,以便我们可以按字母创建组:

       df2[, group := substr(variable, 1, 1)]
       df2[, number := as.numeric(gsub("[[:alpha:]]", "", variable))]
    

    在这一点上,我们有:

        row_id variable value group number
     1:      1       a1     1     a      1
     2:      2       a1     2     a      1
     3:      3       a1     3     a      1
     4:      1       a2     4     a      2
     5:      2       a2     5     a      2
     6:      3       a2     6     a      2
     7:      1       a3     7     a      3
     8:      2       a3     8     a      3
     9:      3       a3     9     a      3
    10:      1       a4    10     a      4
    11:      2       a4    11     a      4
    12:      3       a4    12     a      4
    13:      1       b1    10     b      1
    14:      2       b1    20     b      1
    15:      3       b1    30     b      1
    16:      1       b2    40     b      2
    17:      2       b2    50     b      2
    18:      3       b2    60     b      2
    19:      1       b3    70     b      3
    20:      2       b3    80     b      3
    21:      3       b3    90     b      3
    22:      1       b4   100     b      4
    23:      2       b4   110     b      4
    24:      3       b4   120     b      4
    

    现在,我们可以通过row_id分组来获取累积值均值:

       df2[, avg_val :=cummean(value), by=c("row_id", "group")]
    

    然后我们创建您的列命名约定,如下所示:

       df2[, col_name := paste0(group, min(number), "_", number)]
    

    在这一点上,我们有:

        row_id variable value group number avg_val col_name
     1:      1       a1     1     a      1     1.0     a1_1
     2:      2       a1     2     a      1     2.0     a1_1
     3:      3       a1     3     a      1     3.0     a1_1
     4:      1       a2     4     a      2     2.5     a1_2
     5:      2       a2     5     a      2     3.5     a1_2
     6:      3       a2     6     a      2     4.5     a1_2
     7:      1       a3     7     a      3     4.0     a1_3
     8:      2       a3     8     a      3     5.0     a1_3
     9:      3       a3     9     a      3     6.0     a1_3
    10:      1       a4    10     a      4     5.5     a1_4
    11:      2       a4    11     a      4     6.5     a1_4
    12:      3       a4    12     a      4     7.5     a1_4
    13:      1       b1    10     b      1    10.0     b1_1
    14:      2       b1    20     b      1    20.0     b1_1
    15:      3       b1    30     b      1    30.0     b1_1
    16:      1       b2    40     b      2    25.0     b1_2
    17:      2       b2    50     b      2    35.0     b1_2
    18:      3       b2    60     b      2    45.0     b1_2
    19:      1       b3    70     b      3    40.0     b1_3
    20:      2       b3    80     b      3    50.0     b1_3
    21:      3       b3    90     b      3    60.0     b1_3
    22:      1       b4   100     b      4    55.0     b1_4
    23:      2       b4   110     b      4    65.0     b1_4
    24:      3       b4   120     b      4    75.0     b1_4
    

    我们可以去掉number=1的行,因为它们代表原始数据。从那里,我们可以将其转换为宽格式,并合并回原始数据:

       df2 = df2[number != 1]
       result = dcast.data.table(df2, row_id ~ col_name, value.var = "avg_val")
    
       result = merge(df, result, by = "row_id")
    
       result[, row_id := NULL]
    

    最终的结果是:

       a1 a2 a3 a4 b1 b2 b3  b4 a1_2 a1_3 a1_4 b1_2 b1_3 b1_4
    1:  1  4  7 10 10 40 70 100  2.5    4  5.5   25   40   55
    2:  2  5  8 11 20 50 80 110  3.5    5  6.5   35   50   65
    3:  3  6  9 12 30 60 90 120  4.5    6  7.5   45   60   75
    

  •  类似资料:
    • 问题内容: 我正在尝试使用下面的代码来计算用户输入的一组值的平均值,并将其显示在中,但它无法正常工作。假设用户输入7、4和5,该程序在应显示5.3时显示平均值。 代码有什么问题? 问题答案: 当您拥有增强的for循环时,为什么还要对索引使用笨拙的for循环?

    • 我试图使用下面的代码来计算用户输入的一组值的平均值,并将其显示在中,但它不能正常工作。例如,用户输入7、4和5,程序显示1作为平均值,而它应该显示5.3

    • 我有一个由66个变量的10299个观测值组成的数据框。其中一些变量共享一个通用的列名,我想计算每个观测值的这些变量的平均值。 具有以下矩阵,列名: 我想得到: 我尝试了循环,命令,但没有得到所需的结果。 抱歉,如果这个问题看起来太基本了,我已经在谷歌上查过可能的解决方案,但没有找到任何解决方案。

    • 问题内容: Y1961 Y1962 Y1963 Y1964 Y1965 Region 0 82.567307 83.104757 83.183700 83.030338 82.831958 US 1 2.699372 2.610110 2.587919 2.696451 2.846247 US 2 14.131355 13.690028 13.599516 13.649176 13.649046

    • 问题内容: 任何人都知道如何计算这些列之一的平均值(在Linux上)? 例如:mean(第2栏) 问题答案: Awk: 读为: 对于每一行,将第2列添加到变量“总计”中。 在文件末尾,打印“总计”除以记录数。

    • 我们的教授在一个文本文件中给了我们一份982个数字的列表,我们已经阅读了文件中的文本,并打印出了一些关于数字的信息。到目前为止,除了奇数的总数之外,我的一切都是正确的(她给了我们正确的答案)。我不知道如何得到奇数的平均值,即48201.56。 我一直得到的结果是97354,这很奇怪,因为我用的方法和所有数字的平均值和偶数的平均值是一样的。 我想知道为什么“总平均数”的答案不是48201.56。谢谢