R 通过间接引用列来修改数据框中的某些值
我正在争论一些数据,我们将失败的数据分类到各个分类箱中,然后逐批计算每个分类箱的有限收益率 我有一个描述排序箱的元表。行以升序测试顺序排列,一些排序标签带有非语法名称 一些缺失值意味着100%的有限收益率,而另一些则反映了未定义的值,因为我们在流程的早期是零收益率我的任务是将前一组R 通过间接引用列来修改数据框中的某些值,r,dplyr,tidyverse,rlang,R,Dplyr,Tidyverse,Rlang,我正在争论一些数据,我们将失败的数据分类到各个分类箱中,然后逐批计算每个分类箱的有限收益率 我有一个描述排序箱的元表。行以升序测试顺序排列,一些排序标签带有非语法名称 一些缺失值意味着100%的有限收益率,而另一些则反映了未定义的值,因为我们在流程的早期是零收益率我的任务是将前一组NA替换为1.00,视情况而定。 实现这一点的一种算法是从左到右(测试顺序降序),如果后续的限制产量不是NA,则将NA替换为1.00。在示例数据集的第一行中,我们不更改fail C,因为缺少pass。但是我们确实用1.
NA
替换为1.00
,视情况而定。
实现这一点的一种算法是从左到右(测试顺序降序),如果后续的限制产量不是NA
,则将NA
替换为1.00
。在示例数据集的第一行中,我们不更改fail C
,因为缺少pass
。但是我们确实用1.00
替换fail A
,因为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
>填充(yld、排序)
#一个tibble:4x5
批次通过“不合格C”`不合格B``不合格A`
1 lot1 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
为了生成输出表,我编写了以下函数:
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
}
库(rlang)
图书馆(dplyr)
填充元素如果您将其视为“首先用1替换所有NAs,然后用NA替换第一个0之后的所有1”,则此问题会变得更容易
这里有两种方法,一种使用矩阵运算,另一种使用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
请注意,与基于矩阵的方法不同,这不会保留列的顺序。按照OP的方法从左到右填充缺失的1.00,这可以使用melt()
、dcast()
和rleid()
实现:
资料
yld\u tbl我不知道你是如何填写1的。你能明确给出条件吗?@denis我更新了问题以更明确地解释这一点。如果两个后续的NA
s,例如lot5 NA 0.95 NA
将成为lot5 NA 0.95 1.00 1.00
,会发生什么情况?@Uwe,是的,在您的示例中,后面的NA
将变成1.00
。但是隐含的通过率是0.95
,因此示例行需要阅读:lot5 0.95 0.95 NA
变成lot5 0.95 0.95 1.00 1.00
。谢谢!这些方法看起来很有趣。两者都避免显式地在列上循环。我更喜欢这个。传入的数据以垂直列开始,因此我可以使用排列(…,fill=1.00)
在传入的过程中执行replace\u na()
操作。我有一个按顺序排列的列名表,因此对列重新排序只是一个select(lot,!!!syms(sort\u tbl$label))
。我试图了解cumsum(value==0)
正在做什么。数值向量
yld_tbl$值
将转换为逻辑向量。我猜cumsum()
会因为>0
而强制将其恢复为数值?因此,我们在一个批次中添加零的数量,如果有,我们将值==1
条目替换为NA
。那太圆滑了!这是正确的-‘cumsum(x==0)’将是到目前为止的零的数量,‘cumsum(x==0)>0’意味着“至少有一个零”。“在第一个某某之后,做这个”是一个很好用的成语,谢谢。我个人还没有使用DT,但是有这样一个语法的例子很好。惊人的组合。
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
}
# 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
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)
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)