我正在争论一些数据,我们将这些数据排序到箱子中,并按批次计算每个分类箱的有限产量。
我有一个描述排序箱的元表。 行按升序测试顺序排列,一些排序标签带有非语法名称。
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
答案 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"
行。