【问题标题】:R - Sum Values That Match A Pattern/Character in Several Rows Across Several ColumnsR - 在多列的多行中匹配模式/字符的总和值
【发布时间】:2018-10-17 21:45:13
【问题描述】:

我希望对在任何“Y”列中具有相同字符的每个“M”列中的值求和,所以如果我的数据框如下所示:

X     M.1    M.2    M.3    Y.1     Y.2     Y.3
K3    21     6      11     L       N       X   
K8    31     1      29     N                         
K2    8      0      2      L       Q       Z

我想得到这个输出数据框:

Y     M.1    M.2    M.3
L     29     6      13
N     52     7      40
Q      8     0      2
X     21     6      11

如果它可以将 X 列中包含“Y”列中的特定字符的所有值包含在一列中,则奖励如下:

Y     M.1    M.2    M.3    X.all
L     29     6      13     K3,K2
N     52     7      40     K3,K8
Q      8     0      2      K2
X     29     6      13     K3

到目前为止,使用下面的 aggregate() 函数,我可以分别获得“Y”列中每个值的总和,但更喜欢一种更好的方法来制作一个包含所有总和的全新数据框

aggregate(cbind(df$M.1) ~ df$Y.1, data = df, sum)

非常感谢您的帮助!

【问题讨论】:

  • 现在 Y.2 和 Y.3 下有空白,很可能读入为 NA。这些人的计划是什么?
  • 在我正在使用的数据框中,Y.2 和 Y.3 列中有几个空白 - 没有专门针对它们的计划;您是否建议将空值转换为“NA”?
  • 完全取决于你需要对它们做什么以及你是否希望它们没有价值,这基本上就是NA的意思

标签: r dataframe sum aggregate multiple-columns


【解决方案1】:

如果你想使用tidyverse 函数,你可以在几个步骤中做一些争论。我正在分解它以查看中间结果。

关于缺失值:这取决于您。你没有dput 数据,所以当我用readr::read_table2 将你的数据作为文本读入时,空格会自动转换为NA。我在这里保留了那些缺失的值。

首先,tidyr::gather 为您提供一个长形数据框,首先将Y.1 等放在一个列中:

library(dplyr)
library(tidyr)

df %>%
  gather(key, value = Y, Y.1:Y.3) %>%
  head()
#> # A tibble: 6 x 6
#>   X       M.1   M.2   M.3 key   Y    
#>   <chr> <int> <int> <int> <chr> <chr>
#> 1 K3       21     6    11 Y.1   L    
#> 2 K8       31     1    29 Y.1   N    
#> 3 K2        8     0     2 Y.1   L    
#> 4 K3       21     6    11 Y.2   N    
#> 5 K8       31     1    29 Y.2   <NA> 
#> 6 K2        8     0     2 Y.2   Q

第二个gatherYs 和Ms 分成两列:

df %>%
  gather(key, value = Y, Y.1:Y.3) %>%
  gather(key2, value = M, M.1:M.3) %>%
  head()
#> # A tibble: 6 x 5
#>   X     key   Y     key2      M
#>   <chr> <chr> <chr> <chr> <int>
#> 1 K3    Y.1   L     M.1      21
#> 2 K8    Y.1   N     M.1      31
#> 3 K2    Y.1   L     M.1       8
#> 4 K3    Y.2   N     M.1      21
#> 5 K8    Y.2   <NA>  M.1      31
#> 6 K2    Y.2   Q     M.1       8

然后您可以分组,使用粘贴的字符串(例如K2,K3)创建一列,然后将数值相加。我把x.all放在分组里,这样总结后就不会掉了。

df %>%
  gather(key, value = Y, Y.1:Y.3) %>%
  gather(key2, value = M, M.1:M.3) %>%
  group_by(Y) %>%
  mutate(x.all = sort(X) %>% unique() %>% paste(collapse = ",")) %>%
  group_by(Y, key2, x.all) %>%
  summarise(sum = sum(M, na.rm = T)) %>%
  head()
#> # A tibble: 6 x 4
#> # Groups:   Y, key2 [6]
#>   Y     key2  x.all   sum
#>   <chr> <chr> <chr> <int>
#> 1 L     M.1   K2,K3    29
#> 2 L     M.2   K2,K3     6
#> 3 L     M.3   K2,K3    13
#> 4 N     M.1   K3,K8    52
#> 5 N     M.2   K3,K8     7
#> 6 N     M.3   K3,K8    40

然后将其恢复为宽形,并为不同的M 变量提供列:

df %>%
  gather(key, value = Y, Y.1:Y.3) %>%
  gather(key2, value = M, M.1:M.3) %>%
  group_by(Y) %>%
  mutate(x.all = sort(X) %>% unique() %>% paste(collapse = ",")) %>%
  group_by(Y, key2, x.all) %>%
  summarise(sum = sum(M, na.rm = T)) %>%
  spread(key = key2, value = sum)
#> # A tibble: 6 x 5
#> # Groups:   Y [6]
#>   Y     x.all   M.1   M.2   M.3
#>   <chr> <chr> <int> <int> <int>
#> 1 L     K2,K3    29     6    13
#> 2 N     K3,K8    52     7    40
#> 3 Q     K2        8     0     2
#> 4 X     K3       21     6    11
#> 5 Z     K2        8     0     2
#> 6 <NA>  K8       62     2    58

reprex package (v0.2.1) 于 2018 年 10 月 17 日创建

【讨论】:

    【解决方案2】:

    1) dplyr

    这是一个dplyr 方法,但以reshape 开头(它具有强大的功能,但其参数的工作方式无法记住)。

    library(dplyr)
    (df2 <- reshape(df1, varying = c("Y.1", "Y.2", "Y.3"), direction = "long") %>% 
      group_by(Y))
    # A tibble: 9 x 7
    # Groups:   Y [6]
    #  X       M.1   M.2   M.3  time Y        id
    #* <chr> <int> <int> <int> <dbl> <chr> <int>
    #1 K3       21     6    11     1 L         1
    #2 K8       31     1    29     1 N         2
    #3 K2        8     0     2     1 L         3
    #4 K3       21     6    11     2 N         1
    #5 K8       31     1    29     2 NA        2
    #6 K2        8     0     2     2 Q         3
    #7 K3       21     6    11     3 X         1
    #8 K8       31     1    29     3 NA        2
    #9 K2        8     0     2     3 Z         3
    

    在我们分别总结了Xc("M.1", "M.2", "M.3")之后,现在我们可以使用left_join了。

    left_join(
      summarise_at(df2, c("M.1", "M.2", "M.3"), .funs = sum),
      mmarise(df2, X = toString(unique(X)))
    )
    # A tibble: 6 x 5
    #  Y       M.1   M.2   M.3 X     
    #  <chr> <int> <int> <int> <chr> 
    #1 L        29     6    13 K3, K2
    #2 N        52     7    40 K8, K3
    #3 Q         8     0     2 K2    
    #4 X        21     6    11 K3    
    #5 Z         8     0     2 K2    
    #6 NA       62     2    58 K8
    

    2) 基础 R

    df2 开始,base R 的想法相同,但请注意NAs 丢失了。

    df2 <- reshape(df1, varying = c("Y.1", "Y.2", "Y.3"), direction = "long")
    
    merge(
      aggregate(cbind(M.1, M.2, M.3) ~ Y, df2, sum),
      aggregate(X ~ Y, df2, toString)
    )
    #  Y M.1 M.2 M.3      X
    #1 L  29   6  13 K3, K2
    #2 N  52   7  40 K8, K3
    #3 Q   8   0   2     K2
    #4 X  21   6  11     K3
    #5 Z   8   0   2     K2
    

    3) 数据表

    library(data.table)
    setDT(df1)
    df2 <- melt(df1, measure.vars = patterns("Y."), value.name = "Y")
    
    # I'm sure there must be a cleverer way than this
    df2[df2[, .(X = toString(unique(X))), by = Y], lapply(.SD, sum), .SDcols = c("M.1", "M.2", "M.3"), by = Y, on = "Y"]
    

    数据

    df1 <- structure(list(X = c("K3", "K8", "K2"), M.1 = c(21L, 31L, 8L), 
        M.2 = c(6L, 1L, 0L), M.3 = c(11L, 29L, 2L), Y.1 = c("L", 
        "N", "L"), Y.2 = c("N", NA, "Q"), Y.3 = c("X", NA, "Z")), .Names = c("X", 
    "M.1", "M.2", "M.3", "Y.1", "Y.2", "Y.3"), class = "data.frame", row.names = c(NA, 
    -3L))
    

    【讨论】:

    • 非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    相关资源
    最近更新 更多