将总计 table 更改为基于总计行的百分比 table

Change a table of totals to a table of percentages based on total row

我有这样的数据:

a <- data.frame("Color" = c("Blue", "Red", "Green", "Total"),
                "N_Likes" = c(5, 4, 1, 10),
                "N_Dislikes" = c(2, 4, 2, 8))

看起来像这样:

  Color N_Likes N_Dislikes
1  Blue       5          2
2   Red       4          4
3 Green       1          2
4 Total      10          8

此数据是总计,我想将其转换为百分比。

我想把它转换成这样:

  Color N_Likes N_Dislikes
1  Blue       50%          25%
2   Red       40%          50%
3 Green       10%          25%
4 Total      100%          100%

其中 table 中的每个值都是基于总数的百分比。
我知道我可以手动完成并执行此操作,但是有没有办法轻松进行此更改?

更新

此外,如果有 NA 值,我想忽略并保留它们:

  Color N_Likes N_Dislikes  N_Neutral
1  Blue       5          2          1
2   Red       4          4         NA
3 Green       1          2          2
4 Total      10          8          3

会导致:

  Color   N_Likes   N_Dislikes   N_Neutral
1  Blue       50%          25%      33.33%
2   Red       40%          50%          NA
3 Green       10%          25%      66.66%
4 Total      100%          100%       100%

您可以使用 lapply

遍历数字列
col_idx <- sapply(a, is.numeric) # find positions of numeric columns
a[, col_idx] <- lapply(a[, col_idx], function(x) {
  ifelse(is.na(x), NA, paste0(x / max(x, na.rm = TRUE) * 100, "%"))
})
a
#  Color N_Likes N_Dislikes
#1  Blue     50%        25%
#2   Red     40%        50%
#3 Green     10%        25%
#4 Total    100%       100%

使用dplyr

library(dplyr)
a %>% mutate_if(is.numeric, ~sprintf("%3.0f%%", .x / .x[length(.x)] * 100))
#  Color N_Likes N_Dislikes
#1  Blue     50%        25%
#2   Red     40%        50%
#3 Green     10%        25%
#4 Total    100%       100%

修改后的数据用NAs

df %>% mutate_if(is.numeric, ~if_else(!is.na(.x), sprintf("%3.0f%%", .x / .x[length(.x)] * 100), "NA"))

另一种 dplyr 解决方案:

a <- data.frame("Color" = c("Blue", "Red", "Green", "Total"),
                "N_Likes" = c(5, 4, 1, 10),
                "N_Dislikes" = c(2, 4, 2, 8))

library(dplyr)

a %>% mutate_at(vars(matches("N")), ~paste0(round(100*./last(.), 2), "%"))

#     Color N_Likes N_Dislikes
#   1  Blue     50%        25%
#   2   Red     40%        50%
#   3 Green     10%        25%
#   4 Total    100%       100%

我使用 last(.) 的前提是 Total 始终位于数据框的最后一行。

对于NA的情况你可以使用:

a %>% mutate_at(vars(matches("N")), 
                ~ifelse(is.na(.), "NA", paste0(round(100*./last(.), 2), "%")))

如果你想有"NA"(字符值),或者你可以使用:

a %>% mutate_at(vars(matches("N")), 
                ~ifelse(is.na(.), NA, paste0(round(100*./last(.), 2), "%")))

你想要一个合适的 NA(缺失值;不是字符串 "NA")