【问题标题】:opposite of mutate_at dplyr与 mutate_at dplyr 相反
【发布时间】:2018-04-30 06:38:43
【问题描述】:

我们从dplyr 中知道mutate_at 函数允许我们对选定的多个列进行变异并对每个列应用一个函数。我需要相反的。我的意思是说,将多个函数应用于同一列或将同一函数多次应用于同一列。采取以下reproducible example

    > main <- structure(list(PolygonId = c(0L, 1L, 1612L, 3L, 2L, 1698L), Area = c(3.018892, 
1.995702, 0.582808, 1.176975, 2.277057, 0.014854), Perimeter = c(10.6415, 
8.6314, 4.8478, 6.1484, 9.2226, 0.6503), h0 = c(1000,500,700,1000,200,1200)), .Names = c("PolygonId", 
"Area", "Perimeter", "h0"), row.names = c(NA, 6L), class = "data.frame")

> main
  PolygonId     Area Perimeter     h0
1         0 3.018892   10.6415   1000
2         1 1.995702    8.6314    500
3      1612 0.582808    4.8478    700
4         3 1.176975    6.1484   1000
5         2 2.277057    9.2226    200
6      1698 0.014854    0.6503   1200

我只关心df main中的h0列。

预期结果:
h10 字段是 h0 + 10% of h0h_10h0 - 10% of h0

  PolygonId     Area Perimeter      h0     h10      h20    h_10   h_20
1         0 3.018892   10.6415    1000    1100     1200    900     800
2         1 1.995702    8.6314     500     550      600    450     400
3      1612 0.582808    4.8478     700     770      840    630     560
4         3 1.176975    6.1484    1000    1100     1200    900     800
5         2 2.277057    9.2226     200     220      240    180     160
6      1698 0.014854    0.6503    1200    1320     1440   1080     960

我通常会这样做::

calcH <- function(h, pc){
h + pc / 100 * h
}

new_main <- mutate ( main,
                     h10 = calcH(h0, 10),
                     h20 = calcH(h0, 20),
                     h_10 = calcH(h0, -10),
                     h_20 = calcH(h0, -20)
                    )

但这将是一个忙碌而冗长的代码,因为我必须以正反两种方式对1%, 2.5%, 5%, 7.5%, 10%, 12.5%, 15%... 30% 进行此计算。

【问题讨论】:

标签: r dplyr


【解决方案1】:

mutate_at 可以使用多个函数,但是它们需要作为命名函数存在于环境中(不能是匿名函数)所以像

pcts<-rep(c(1,2.5*1:12),2)*c(-1,1)
for(i in pcts){
    assign(gsub("-","_",paste0("h",i)),eval(parse(text=sprintf("function(x) x*(100+%f)/100",i))))    }

main %>% mutate_at(vars(h0),gsub("-","_",paste0("h",pcts)))

会工作

【讨论】:

  • 这行得通。但我无法理解代码,即 for 循环内部的代码。我理解 gsub() 部分,但不理解 eval 的东西。
  • eval(parse(text="some R code")) 将评估(即运行)引号内的 R 代码。 assign 将该代码的结果存储在一个变量中。所以 assign("h10",eval(parse(text="function(x) x*(100+10)/100" ))) 定义了一个名为 h10 的函数,它接受 x 并返回 x*(100+10)/100
  • 所以它是为我们想要定义的所有不同函数编写 h10
  • 啊!好,知道了。我们正在创建多个名为 h1、h2.5、h5 ... 等的函数,并使用 mutate_at 从最后一行调用它们。但是为什么会有sprintf()%f 而不是i。是 C/C++ 方式吗(使用 printf 和 sprintf 以及 %d、%f 等)?
  • 是的,和C++的方式一样。顺便说一句,使用 assign 和 eval 通常不被视为良好的 R 编程实践。有一个名为 zeallot 的新包可能用于创建命名函数,但我还没有尝试过。对于这个问题,我将遵循 Tino 使用的长格式方法,但形成更一般的问题,其中函数可能更加多样化,使用 mutate_at 和函数名称向量可能是最好的。
【解决方案2】:

我喜欢使用长数据表示来解决这类问题:

library(dplyr)
library(tidyr)

# create data frame with join helper and multiplier-values:
bla <- data.frame(mult = seq(-.1, .1, .01), 
                  join = TRUE)

# join, calculate values, create names, transform to wide:
main %>% 
  mutate(join = TRUE) %>% 
  left_join(bla) %>% 
  mutate(h0 = h0*(1+mult),
         mult = sub(x = paste0("h", mult*100), pattern = "-", replacement = "_")) %>% 
  select(-join) %>% 
  spread(mult, h0)

【讨论】:

  • 这是我见过的最性感的代码。它工作得很好,完全符合我的预期。谢谢!
【解决方案3】:

这在基础 R 中很容易。想法是创建一个具有所需百分比的向量,遍历该向量并计算您的指标,即

v1 <- c(1, seq(2.5, 30, by = 2.5), seq(-30, -2.5, by = 2.5), -1)
sapply(v1, function(i) calcH(main$h0, i))

【讨论】:

  • 它按预期使输出稍微偏离了界限。在我的原始 df 中获得的输出是num [1:2796, 1:26]。现在跟踪列真的很麻烦。我确实将 colnames 和 rownames 应用于输出向量,但如果我可以将原始 df 本身的输出作为新列获得,那就太好了。为我执行进一步的操作会更容易。
【解决方案4】:

这是另一种类似于@andyyy 的方法,但使用rlang 代替:

library(dplyr)
library(rlang)

percent <- c(1, 2.5*1:12)

calc_expr <- function(percent_vec){
  parse_exprs(paste(paste0("h0+(",percent_vec,"/100*h0)"), collapse = ";"))
}

main %>%
  mutate(!!!calc_expr (percent), !!!calc_expr (percent*-1)) %>%
  setNames(c(colnames(main), paste0("h", percent), paste0("h_", percent)))

结果:

  PolygonId     Area Perimeter   h0   h1   h2.5   h5   h7.5  h10  h12.5  h15  h17.5  h20  h22.5  h25  h27.5
1         0 3.018892   10.6415 1000 1010 1025.0 1050 1075.0 1100 1125.0 1150 1175.0 1200 1225.0 1250 1275.0
2         1 1.995702    8.6314  500  505  512.5  525  537.5  550  562.5  575  587.5  600  612.5  625  637.5
3      1612 0.582808    4.8478  700  707  717.5  735  752.5  770  787.5  805  822.5  840  857.5  875  892.5
4         3 1.176975    6.1484 1000 1010 1025.0 1050 1075.0 1100 1125.0 1150 1175.0 1200 1225.0 1250 1275.0
5         2 2.277057    9.2226  200  202  205.0  210  215.0  220  225.0  230  235.0  240  245.0  250  255.0
6      1698 0.014854    0.6503 1200 1212 1230.0 1260 1290.0 1320 1350.0 1380 1410.0 1440 1470.0 1500 1530.0
   h30  h_1  h_2.5  h_5  h_7.5 h_10 h_12.5 h_15 h_17.5 h_20 h_22.5 h_25 h_27.5 h_30
1 1300  990  975.0  950  925.0  900  875.0  850  825.0  800  775.0  750  725.0  700
2  650  495  487.5  475  462.5  450  437.5  425  412.5  400  387.5  375  362.5  350
3  910  693  682.5  665  647.5  630  612.5  595  577.5  560  542.5  525  507.5  490
4 1300  990  975.0  950  925.0  900  875.0  850  825.0  800  775.0  750  725.0  700
5  260  198  195.0  190  185.0  180  175.0  170  165.0  160  155.0  150  145.0  140
6 1560 1188 1170.0 1140 1110.0 1080 1050.0 1020  990.0  960  930.0  900  870.0  840

注意事项:

使用百分比向量,我使用paste0parse_exprs 构造多个表达式,然后使用!!! 取消引用并将它们拼接到mutate。最后,使用setNames重命名列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 2018-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多