【问题标题】:How to sum every numeric column that start with the same name except the 2 last characters, in R?如何对R中除最后2个字符外以相同名称开头的每个数字列求和?
【发布时间】:2022-06-16 05:06:36
【问题描述】:

我有一个包含超过 100 列的数据框,有些是数字的,有些不是。
所有以“_f”或“_m”结尾的变量都是数字变量,我想总结所有以相同模式开头但以“_f”或“_m”结尾的对。

这是我的数据框中变量名称的示例:

xxxxxxxxxxxxx_age1_f
xxxxxxxxxxxxx_age1_m
xxxxxxxxxxxxx_age2_f
xxxxxxxxxxxxx_age2_m
xxxxxxxxxxxxx_age3_f
xxxxxxxxxxxxx_age3_m
yyyyyyyyyy_age1_f
yyyyyyyyyy_age1_m
yyyyyyyyyy_age2_f
yyyyyyyyyy_age2_m
yyyyyyyyyy_age3_f
yyyyyyyyyy_age3_m
yyyyyyyyyy_age4_f
yyyyyyyyyy_age4_m
yyyyyyyyyy_age5_f
yyyyyyyyyy_age5_m
zzzzzzzzzzzzzzzzzzzz_age1_f
zzzzzzzzzzzzzzzzzzzz_age1_m
zzzzzzzzzzzzzzzzzzzz_age2_f
zzzzzzzzzzzzzzzzzzzz_age2_m
zzzzzzzzzzzzzzzzzzzz_age3_f
zzzzzzzzzzzzzzzzzzzz_age3_m
text_var_11
text_var_222
text_var_33333

(我在这里用 x, y z 来抽象名称以使我的问题更清楚,它们实际上并不是那样命名的)

我的第一个解决方案是使用 dplyr::mutate() 对每一对求和,如下所示:

mutate( ... ) %>%
mutate( yyyyyyyyyy_age2 = yyyyyyyyyy_age2_f + yyyyyyyyyy_age2_m) %>%
mutate( yyyyyyyyyy_age3 = yyyyyyyyyy_age3_f + yyyyyyyyyy_age3_m) %>%
mutate( ... ) %>%

这可行,但必须有更智能的方法来做到这一点,而无需对所有变量对重复此操作。

寻找解决方案后,我发现最接近的是这个 Sum all columns whose names start with a pattern, by group

但是,由于两个原因,建议的解决方案在我的情况下不起作用:

  • substr() 不适用于我的问题,因为变量名称的长度发生了变化
  • 此方法假定我只有想要求和的变量,而在我的情况下,我还有许多其他变量不以“_f”或“_m”结尾并且不需要求和(并且无法求和)因为有些是文字)

我想可以修改解决方案以适用于我的案例,但我不确定如何。

编辑:这里是使用 dput 创建的示例数据

structure(list(Groups = c("xx", "xx", "xx"), xxxxx_age0_f = c(8, 
0, 7), xxxxx_age0_m = c(5, 0, 0), xxxxx_age1_f = c(1, 
0, 0), xxxxx_age1_m = c(3, 2, 0), xxxxx_age2_f = c(0, 
0, 2), xxxxx_age2_m = c(0, 1, 0), zzzz_age0_f = c(4, 
2, NA), zzzz_age0_m = c(3, 6, NA), zzzz_age1_f = c(0, 
0, NA), zzzz_age1_m = c(2, 0, NA), zzzz_age2_f = c(4, 
1, NA), zzzz_age2_m = c(3, 1, NA)), row.names = c(NA, -3L
), class = c("tbl_df", "tbl", "data.frame"))

ps:这个简化的例子只有一个分类变量,而我有数百个。

【问题讨论】:

  • 我建议您提供一段数据框作为可重现的示例。这会增加您获得帮助的机会。
  • Minimal 可重复的例子是最好的。包括 2 或 3 列对,一列您不想求和,以及 3 或 4 行 - 这应该足以演示解决方案。 dput() 是共享数据的最佳方式,因为它是可复制/可粘贴的。 dput(your_data[1:3, c("relevant", "columns", "for", "example")]).
  • 按照@GregorThomas 的建议,使用dput 函数在此处插入数据样本!
  • starts_with() 将成为您的朋友。也就是说,这是为什么tidy 数据帧比不整洁的数据帧更好的经典示例。您的布局不整洁,因为列名包含创建摘要所需的信息。有了整洁的数据框,解决问题不仅更容易,而且解决方案更健壮。
  • 我已经添加了 dput 输出,感谢@GregorThomas 的建议

标签: r dplyr


【解决方案1】:

已更新,使用 OP 的实际示例数据:

inner_join(
  dat,
  dat %>% 
    select(Groups, ends_with(c("_f", "_m"))) %>% 
    pivot_longer(cols=!Groups) %>% 
    mutate(name = gsub("_.$",replacement = "",name)) %>% 
    group_by(Groups, name) %>% 
    summarize(value=sum(value,na.rm=T)) %>% 
    pivot_wider(id_cols ="Groups", "name"),
  by="Groups"
)

前一个例子,在 OP 的实际例子之前。

如果您的表格如下所示:

dat

# A tibble: 2 x 9
  zzzzzzzzzzzzzzzzzzzz_age1_f zzzzzzzzzzzzzzzzzzzz_age1_m zzzzzzzzzzzzzzzz~ zzzzzzzzzzzzzzz~ zzzzzzzzzzzzzzz~ zzzzzzzzzzzzzzz~ text_var_11 text_var_222 text_var_33333
                        <dbl>                       <dbl>             <dbl>            <dbl>            <dbl>            <dbl> <chr>       <chr>        <chr>         
1                      -0.709                       1.26               1.03             1.36           -0.140           -0.595 f           o            x             
2                      -0.202                       0.164             -1.28            -1.48           -0.380            0.874 a           p            m             

然后,您可以这样做:

dat %>% 
  select(ends_with(c("_f", "_m"))) %>% 
  pivot_longer(cols=everything()) %>% 
  mutate(name = gsub("_.$",replacement = "",name)) %>% 
  group_by(name) %>% 
  summarize(value=sum(value,na.rm=T))

要得到这个:

  name                       value
  <chr>                      <dbl>
1 zzzzzzzzzzzzzzzzzzzz_age1  0.510
2 zzzzzzzzzzzzzzzzzzzz_age2 -0.371
3 zzzzzzzzzzzzzzzzzzzz_age3 -0.240

现在,我不知道您想要的输出结构是什么,但是您上面的mutate 尝试表明您想要列绑定这些新的聚合列。这可以通过获取上面的中间结果并将其包装在bind_cols 中轻松完成,就像这样

bind_cols(
  dat,
  dat %>% 
    select(ends_with(c("_f", "_m"))) %>% 
    pivot_longer(cols=everything()) %>% 
    mutate(name = gsub("_.$",replacement = "",name)) %>% 
    group_by(name) %>% 
    summarize(value=sum(value,na.rm=T)) %>% 
    pivot_wider(id_cols ="name")
)

【讨论】:

  • 带上groups 列以备不时之需(或加入原始数据以获取它),转回宽,我认为这可能是 OP 正在寻找的。跨度>
  • 感谢@GregorThomas,我只是添加了那个编辑 - 你读懂了我的想法..
  • pivot_wider_spec() 中的错误:id_expand 必须是单个 TRUEFALSE。我认为您需要从 pivot_wider 中删除 , "name",对吗?
  • 我看到的一个缺点是您选择了“组”,它是我的示例中唯一的分类变量,但我实际上有数百个变量。
  • 另一个问题是它以组作为 id,但事实并非如此。如果 group 始终具有相同的值(例如 'xx',则它将所有行合并为 1。
【解决方案2】:

这并不能解决你所有的问题,但也许你可以尝试一下

df %>%
rowwise()

for i in c("age1", "age2", "age3"){

df %>%
z <- mutate({{i}} = sum(c_across(contains({{i}})))) %>%
print(z)
}

df %>% ungroup()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-29
    • 1970-01-01
    • 2015-10-23
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    相关资源
    最近更新 更多