按列值将数学计算应用于 DF 的所有行

Apply Math calculation to all rows of DF by Column Values

我想应用一个数学计算 (Occ_1+1)/(Totl_1+Unique_words) , (Occ_2+1)/(Totl_2+Unique_words)(Occ_3+1)/(Totl_3+Unique_words) 并创建一个新列作为 Probability_1, Probability_2, Probability_3

现在我正在分别进行每项计算并将它们组合在一起。

例如:(Occ_1+1) 我正在做 sapply(df$Occ_1, function(x){x+1})

我几乎有 50 Occ_50 Totl_,所以如果我单独进行所有计算,我的代码会变得很长。
有没有办法一次完成所有计算。

样本 DF 直到 Occ_3Totl_3

 word        Occ_1  Occ_2  Occ_3  Totl_1 Totl_2 Totl_3 Unique_words
  <chr>      <int>  <int>  <int>  <int>  <int>  <int>        <int>
 1 car          0     1     0     11      9      7           17
 2 saturn       2     0     2     11      9      7           17
 3 survival     1     2     0     11      9      7           17
 4 baseball     1     1     0     11      9      7           17
 5 color        0     0     1     11      9      7           17
 6 muscle       0     1     0     11      9      7           17

这实际上就是所谓的函数矢量化,它可以显着提高代码的性能。

但首先,为了让您了解以后的问题,使用 dput

提供示例数据要容易得多
dput(df)

那么想回答问题的人可以简单地使用输出:

df <- dget(structure(list(word = structure(c(2L, 5L, 6L, 1L, 3L, 4L), .Label = c("baseball", 
"car", "color", "muscle", "saturn", "survival"), class = "factor"), 
    Occ_1 = c(0L, 2L, 1L, 1L, 0L, 0L), Occ_2 = c(1L, 0L, 2L, 
    1L, 0L, 1L), Occ_3 = c(0L, 2L, 0L, 0L, 1L, 0L), Totl_1 = c(11L, 
    11L, 11L, 11L, 11L, 11L), Totl_2 = c(9L, 9L, 9L, 9L, 9L, 
    9L), Totl_3 = c(7L, 7L, 7L, 7L, 7L, 7L), Unique_words = c(17L, 
    17L, 17L, 17L, 17L, 17L), Probability_1 = c(0.0357142857142857, 
    0.107142857142857, 0.0714285714285714, 0.0714285714285714, 
    0.0357142857142857, 0.0357142857142857), Probability_2 = c(0.0769230769230769, 
    0.0384615384615385, 0.115384615384615, 0.0769230769230769, 
    0.0384615384615385, 0.0769230769230769), Probability_3 = c(0.0416666666666667, 
    0.125, 0.0416666666666667, 0.0416666666666667, 0.0833333333333333, 
    0.0416666666666667)), row.names = c(NA, -6L), class = "data.frame"))

无论如何,这里有一个方法可以做你想做的事:

df$Probability_1 <- (df$Occ_1 + 1) / (df$Totl_1 + df$Unique_words)
df$Probability_2 <- (df$Occ_2 + 1) / (df$Totl_2 + df$Unique_words)
df$Probability_3 <- (df$Occ_3 + 1) / (df$Totl_3 + df$Unique_words)

或者如果你喜欢 dplyr:

library("dplyr")
df_new <- df %>% 
  mutate(
    Probability_1 = (Occ_1 + 1) / (Totl_1 + Unique_words),
    Probability_2 = (Occ_2 + 1) / (Totl_2 + Unique_words),
    Probability_3 = (Occ_3 + 1) / (Totl_3 + Unique_words)        
  )

更新

我错过了问题的重点。它实际上是关于 OccTotl 变量的数量。我会用 for 循环来解决这个问题,它应该仍然非常有效:

for(i in gsub("^Occ_", "", grep("^Occ_*", colnames(df), value = TRUE))) {
  df[paste0("Probability_", i)] <- 
    (df[paste0("Occ_", i)] + 1) / (df[paste0("Totl_", i)] + df$Unique_words)
}

我只是将所有 Occ..Tot.. 列聚集在一起并执行所需的算法

occ_cols <- grep("^Occ", names(df))
tot_cols <- grep("^Totl", names(df))

df[paste0("Probability_", 1:length(occ_cols))] <- 
      (df[occ_cols] + 1)/(df[tot_cols] + df$Unique_words)

df
#      word Occ_1 Occ_2 Occ_3 Totl_1 Totl_2 Totl_3 Unique_words Probability_1
#1      car     0     1     0     11      9      7           17    0.03571429
#2   saturn     2     0     2     11      9      7           17    0.10714286
#3 survival     1     2     0     11      9      7           17    0.07142857
#4 baseball     1     1     0     11      9      7           17    0.07142857
#5    color     0     0     1     11      9      7           17    0.03571429
#6   muscle     0     1     0     11      9      7           17    0.03571429

#  Probability_2 Probability_3
#1    0.07692308    0.04166667
#2    0.03846154    0.12500000
#3    0.11538462    0.04166667
#4    0.07692308    0.04166667
#5    0.03846154    0.08333333
#6    0.07692308    0.04166667

但是,请确保所有 Occ..Tot.. 列的顺序相同。对于此示例,我们有 Occ_1Occ_2Occ_3,然后是 Totl_1Totl_2Totl_3

我将提出与其他两个答案不同的方法。我认为你在这里使用了错误的数据格式,即你的数据在应该很长的时候很宽。如果您不熟悉这些术语,您应该查看网上的大量解释。在我看来,最好的是 this one.

使用 tidyr 包,我会这样解决你的问题:

library(tidyverse)

第一步是将 Occ 和 Totl 列分成 2 个数据框,稍后我们将它们合并在一起。使用 gather 函数,我将这些列转换为键值对。我们正在从键中提取数值,以便稍后可以将 Occ_1 匹配到 Totl_1。

df_occ <- df %>%
  gather(group, occ, contains("Occ")) %>%
  select(word, group, occ) %>%
  mutate(group = str_extract(group, "[0-9]") %>% as.integer())

df_totl <- df %>%
  gather(group, totl, contains("Totl")) %>%
  select(word, group, totl) %>%
  mutate(group = str_extract(group, "[0-9]") %>% as.integer())

一旦我们有了这两个数据框,我们就会将它们重新合并在一起。我们从原始数据框中取出 wordUnique_words 列,然后按组添加 Occ 数据框,最后添加 Totl 数据框。终于,我们一行代码就可以完成你想要的计算了。

df_merge <- df %>%
  select(word, Unique_words) %>%
  left_join(df_occ, by = 'word') %>%
  left_join(df_totl, by = c('word', 'group')) %>%
  mutate(prob = (occ + 1) / (totl + Unique_words))

如果要将其转换回宽格式,可以使用 gather 函数的反函数,即 spread.

df_wide <- df_merge %>%
  select(word, group, prob) %>%
  mutate(group = paste0("Prob_", group)) %>%
  spread(group, prob)

这种方法的优点:

  1. 您的代码更易读,每个操作都在自己的行中,并且避免了方括号(这通常会产生难以阅读的代码)。
  2. 您的代码显示了中间步骤。
  3. 该方法更加灵活,希望也能简化其他处理步骤。