通过间接引用列来修改数据框中的某些值

时间:2017-11-14 15:31:15

标签: r dplyr tidyverse rlang

我正在争论一些数据,我们将这些数据排序到箱子中,并按批次计算每个分类箱的有限产量。

我有一个描述排序箱的元表。 行按升序测试顺序排列,一些排序标签带有非语法名称。

sort_tbl <- tibble::tribble(~weight,   ~label,
                                  0, "fail A",
                                  0, "fail B",
                                  0, "fail C",
                                100,   "pass")
> sort_tbl
# A tibble: 4 x 2
  weight  label
   <dbl>  <chr>
1      0 fail A
2      0 fail B
3      0 fail C
4    100   pass

我有一个按排序箱限制产量的数据表,每批一行,每个分类一个col。因为这个表是从转置构造的,所以我们得到一个实例,其中一个特定的排序从未发生过很多,结果值是NA请注意,此表中的列按降序测试顺序排列。

yld_tbl <- tibble::tribble(  ~lot, ~pass, ~`fail C`, ~`fail B`, ~`fail A`,
                           "lot1",    NA,        NA,      0.00,        NA,
                           "lot2",    NA,      0.00,      0.80,        NA,
                           "lot3",  0.49,        NA,      0.50,      0.98,
                           "lot4",  0.70,      0.95,      0.74,      0.99)
> yld_tbl
# A tibble: 4 x 5
    lot  pass `fail C` `fail B` `fail A`
  <chr> <dbl>    <dbl>    <dbl>    <dbl>
1  lot1    NA       NA     0.00       NA
2  lot2    NA     0.00     0.80       NA
3  lot3  0.49       NA     0.50     0.98
4  lot4  0.70     0.95     0.74     0.99

一些缺失的值意味着100%的有限产量,而其他值则反映了未定义的值,因为我们在流程中的早期产量为零。 我的任务是根据需要用NA替换以前的1.00组。

如果后续的有限产量不是NA,则实现此操作的一种算法从左到右(降序测试顺序)将1.00替换为NA。在示例数据集的第一行中,由于缺少fail C,因此我们不会更改pass。但我们确实将fail A替换为1.00,因为fail B并不缺失。

正确的示例输出是:

> fill_ones(yld_tbl, sort_tbl)
# A tibble: 4 x 5
    lot  pass `fail C` `fail B` `fail A`
  <chr> <dbl>    <dbl>    <dbl>    <dbl>
1  lot1    NA       NA     0.00     1.00
2  lot2    NA     0.00     0.80     1.00
3  lot3  0.49     1.00     0.50     0.98
4  lot4  0.70     0.95     0.74     0.99

3 个答案:

答案 0 :(得分:1)

如果您将其视为&#34;首先将所有NA替换为1,然后将第一个0之后的所有1替换为NA,则此问题会变得更容易。&#34;

以下是两种方法,一种使用矩阵运算,另一种使用dplyr。

在矩阵方法中,您将值提取为数值矩阵,使用// FULL JOIN MUST COME HERE 查找需要用NA替换的位置,然后将其返回。

apply

使用dplyr / tidyr,您首先# extract as a matrix, with left-to-right bins m <- as.matrix(yld_tbl[, sort_tbl$label]) # replace NAs with 1 m[is.na(m)] <- 1 # find 1s happening after a zero in each row after_zero <- t(apply(m == 0, 1, cumsum)) & (m == 1) # replace them with NA m[after_zero] <- NA # return them in the table yld_tbl[, sort_tbl$label] <- m 列(使用gather()将其按所需顺序排列),替换NAs(arrange() / {{ 1}}正在完成与上面group_by相同的事情,并且mutate将它们恢复为宽格式。

apply

请注意,与基于矩阵的方法不同,这不会保留列的顺序。

答案 1 :(得分:0)

为了生成输出表,我编写了以下函数:

library(rlang)
library(dplyr)

fill_ones <- function(df, meta) {
  fail_labels <- meta[meta$weight == 0, ]$label
  last_val <- NULL
  for ( i in length(fail_labels):1) {
    if (is.null(last_val)) last_val <- df$pass
    else last_val <- eval_tidy(sym(fail_labels[[i+1]]), df)
    this_name <- sym(fail_labels[[i]])
    this_val  <- eval_tidy(this_name, df)
    this_val[intersect(which(!is.na(last_val)), which(is.na(this_val)))] <- 1
    df <- mutate(df, !!!new_definition(this_name, this_val))
  }
  df
}

此函数循环遍历meta中定义的失败排序,并计算对数据表df中相应列的更改。

调用sym(fail_labels[[i]])查找每列的名称,eval_tidy(..., df)提取数据框中的相应向量。

表达式intersect(which(!is.na(last_val)), which(is.na(this_val)))定义NA的子集,将被1.00替换。

使用mutate()覆盖整个列的新值。为了减少引用和取消引用的数量,我使用了new_definition()而不是:=

我不相信我已经达到了间接引用数据表中列的最简单语法。拥有非句法名称并没有帮助。此外,我们只需要修改有限数量的NA,但此解决方案会逐列重写每个数据条目。我没有找到一个好的语法来避免这种情况(没有转向data.table)。

如果有人有更好的方法,我很乐意听到。

答案 2 :(得分:0)

按照OP的方法从左到右填写缺失的1.00,可以使用melt()dcast()rleid()来实现:

library(data.table)
mDT <- melt(setDT(yld_tbl), id.var = "lot")
mDT[
  mDT[, grp := rleid(is.na(value)), by = lot][, .I[is.na(value) & grp > 1]]
  , value := 1][
    , dcast(.SD, lot ~ variable)]
    lot pass fail C fail B fail A
1: lot1   NA     NA   0.00   1.00
2: lot2   NA   0.00   0.80   1.00
3: lot3 0.49   1.00   0.50   0.98
4: lot4 0.70   0.95   0.74   0.99
5: lot5 0.95   0.95   1.00   1.00

数据

yld_tbl <- tibble::tribble(  ~lot, ~pass, ~`fail C`, ~`fail B`, ~`fail A`,
                             "lot1",    NA,        NA,      0.00,        NA,
                             "lot2",    NA,      0.00,      0.80,        NA,
                             "lot3",  0.49,        NA,      0.50,      0.98,
                             "lot4",  0.70,      0.95,      0.74,      0.99,
                             "lot5",  0.95,      0.95,        NA,        NA)

请注意其他"lot5"行。