2

我正在处理一些我们分类失败的数据,并按批次计算每个分类箱的有限产量。

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

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

我有一个按分拣箱限制产量的数据表,每批一行,每个分拣箱一个列。因为这个表是从一个转置构造的,所以我们得到了一个特定排序从未发生过很多的实例,结果值为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% 的有限收益率,而另一些则反映了未定义的值,因为我们在流程的早期是零收益率。我的任务是酌情用 's替换前一组。NA1.00

完成此操作的一种算法从左到右(降序测试顺序)替换NA1.00if 随后的有限产量不是NA。在示例数据集的第一行中,我们没有更改,fail C因为pass缺少。但是我们确实替换fail A1.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
4

3 回答 3

1

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

这里有两种方法,一种使用矩阵运算,一种使用 dplyr。


在矩阵方法中,您会将值提取为数字矩阵,用于apply查找需要用 NA 替换的位置,然后返回它们。

# 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

使用 dplyr/tidyr,您首先gather()需要列(arrange()用于将它们按所需顺序排列),替换 NA(group_by/mutate完成与上述相同的事情apply),然后spread将它们恢复为宽格式。

library(dplyr)
library(tidyr)

yld_tbl %>%
  gather(label, value, -lot) %>%
  arrange(lot, match(label, sort_tbl$label)) %>%
  replace_na(list(value = 1)) %>%
  group_by(lot) %>%
  mutate(value = ifelse(cumsum(value == 0) > 0 & value == 1, NA, value)) %>%
  spread(label, value)

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

于 2017-11-15T14:40:34.320 回答
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)。

如果有人有更好的方法,我很想听听。

于 2017-11-14T15:31:15.187 回答
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"行。

于 2017-11-15T15:47:12.220 回答