【问题标题】:How do I sweep specific columns with dplyr?如何使用 dplyr 扫描特定列?
【发布时间】:2015-04-02 15:15:17
【问题描述】:

对我的数据类型来说,一个非常常见的操作是对所有列应用归一化因子。这可以使用sweepscale 有效地完成:

normalized = scale(data, center = FALSE, scale = factors)
# or
normalized = sweep(data, 2, factors, `/`)

在哪里

data = structure(list(A = c(3L, 174L, 6L, 1377L, 537L, 173L),
    B = c(1L, 128L, 2L, 1019L, 424L, 139L),
    C = c(3L, 66L, 2L, 250L, 129L, 40L),
    D = c(4L, 57L, 4L, 251L, 124L, 38L)),
    .Names = c("A", "B", "C", "D"),
    class = c("tbl_df", "data.frame"), row.names = c(NA, -6L))

factors = c(A = 1, B = 1.2, C = 0.8, D = 0.75)

但是,当我的数据前面有其他列时,如何使用 dplyr 执行此操作?我可以在单独的语句中执行此操作,但我希望在 one 管道中执行此操作。这是我的数据:

data = structure(list(ID = c(1, 2, 3, 4, 5, 6),
    Type = c("X", "X", "X", "Y", "Y", "Y"),
    A = c(3L, 174L, 6L, 1377L, 537L, 173L),
    B = c(1L, 128L, 2L, 1019L, 424L, 139L),
    C = c(3L, 66L, 2L, 250L, 129L, 40L),
    D = c(4L, 57L, 4L, 251L, 124L, 38L)),
    .Names = c("ID", "Type", "A", "B", "C", "D"),
    class = c("tbl_df", "data.frame"), row.names = c(NA, -6L))

我想改变数据列而不触及前两列。通常我可以用mutate_each 做到这一点;但是,我如何无法将归一化因子传递给该函数:

data %>% mutate_each(funs(. / factors), A:D)

这毫不奇怪,假设我想将每一列除以factors,而不是每一列除以其匹配因子。

【问题讨论】:

  • 也许这有帮助data %>% list(as.list(factors)) %>% Reduce(/, .)
  • @akrun 不,这根本行不通。
  • 我做了以下实验。我为factors 创建了一个df 并尝试了mutate_each。结果似乎还好。但是,我想这不是你所追求的。 factors <- data.frame(A = 1, B = 1.2, C = 0.8, D = 0.75); mutate_each(data, funs(. / factors$.), A:D)
  • @akrun 任务完成。 :)
  • 另一种选择,虽然不如 jazzuro 的答案那么有效和简洁,但它是使用 do,如 data %>% do(data.frame(.[1:2], sweep(.[-c(1:2)], 2, factors, /)))

标签: r dplyr


【解决方案1】:

鉴于 akrun 的鼓励,让我在这里发布我所做的作为答案。我只是直观地认为您可能想要求 R 指示具有相同名称的列来执行此操作mutate_each。例如,如果. 表示列A,我认为来自另一个data.frame 的另一个名为A 的列可能是dplyr 可能喜欢的东西。所以,我为factors 创建了一个数据框,然后使用mutate_each。看来结果是对的。由于我没有技术背景,恐怕我无法真正提供任何解释。希望你不要介意。

factors <- data.frame(A = 1, B = 1.2, C = 0.8, D = 0.75)

mutate_at(data, vars(A:D), funs(. / foo$.))

# By the time I answered this question, the following was working.
# But mutate_each() is now deprecated.

# mutate_each(data, funs(. / factors$.), A:D)

#  ID Type    A           B      C          D
#1  1    X    3   0.8333333   3.75   5.333333
#2  2    X  174 106.6666667  82.50  76.000000
#3  3    X    6   1.6666667   2.50   5.333333
#4  4    Y 1377 849.1666667 312.50 334.666667
#5  5    Y  537 353.3333333 161.25 165.333333
#6  6    Y  173 115.8333333  50.00  50.666667

编辑

这也有效。鉴于数据框是列表的一种特殊情况,这也许不足为奇。

# Experiment
foo <- list(A = 1, B = 1.2, C = 0.8, D = 0.75)

mutate_at(data, vars(A:D), funs(. / foo$.))

# mutate_each(data, funs(. / foo$.), A:D)

#  ID Type    A           B      C          D
#1  1    X    3   0.8333333   3.75   5.333333
#2  2    X  174 106.6666667  82.50  76.000000
#3  3    X    6   1.6666667   2.50   5.333333
#4  4    Y 1377 849.1666667 312.50 334.666667
#5  5    Y  537 353.3333333 161.25 165.333333
#6  6    Y  173 115.8333333  50.00  50.666667

【讨论】:

  • 这就是我想要的。但是,我发现它完全令人困惑和不直观。这是一个糟糕的 API - 与 dplyr 所追求的相反。
  • @KonradRudolph 我很高兴听到这就是你所追求的。我理解你的沮丧。我最初在做一些完全不同的事情。然后,我只是想“愚蠢”一点,看看会发生什么。我想这表明 API 对广泛的用户来说不一定是直观的。顺便说一句,我从你的问题中学到了一些新东西。非常感谢。
  • @KonradRudolph 这就是我使用data.table - setDT(data)[, names(factors) := Map("/", .SD, factors), .SDcols = names(factors)] 的方式 - 不确定这是否会清除您的直觉障碍。
  • @jazzurro 有没有办法用mutate_at 做到这一点?我尝试过替换,但它不起作用。看起来mutate_each 将被弃用。
  • @EricKrantz 您必须学习如何使用该功能。我更新了我的答案。看看吧。
【解决方案2】:

来自dplyr 1.0.0,你可以这样做:

data %>%
 rowwise() %>%
 mutate(across(A:D)/factors)

     ID Type      A       B      C      D
  <dbl> <chr> <dbl>   <dbl>  <dbl>  <dbl>
1     1 X         3   0.833   3.75   5.33
2     2 X       174 107.     82.5   76   
3     3 X         6   1.67    2.5    5.33
4     4 Y      1377 849.    312.   335.  
5     5 Y       537 353.    161.   165.  
6     6 Y       173 116.     50     50.7 

【讨论】:

  • 是的,很好看。 dplyr 1.0.0 填补了其 API 中的许多(尽管不是全部)突出空白,几乎完全取代了基本的 data.frame 功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-10
  • 2015-11-23
  • 2017-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多