根据条件匹配另一列的向量替换一列中的值
Replace values in one column based on a vector conditionally matching another column
我有以下数据框,我想用 NA 替换反射率值,具体取决于波长值是否落在被确定为不良测量值(badData 向量)的特定范围组中。
坏数据的范围可能会随着时间的推移而改变,所以我希望解决方案尽可能通用。
badData <- c(296:310, 330:335, 350:565)
df <- data.frame(wavelength = seq(300,360,5.008667),
reflectance = seq(-1,-61,-5.008667))
df
wavelength reflectance
300.0000 -1.000000
305.0087 -6.008667
310.0173 -11.017334
315.0260 -16.026001
320.0347 -21.034668
325.0433 -26.043335
330.0520 -31.052002
335.0607 -36.060669
340.0693 -41.069336
345.0780 -46.078003
350.0867 -51.086670
355.0953 -56.095337
我试过了
Data2 <- df %>%
mutate(reflectance = replace(reflectance,wavelength %in% badData, NA))
但是因为我试图用波长范围而不是精确值来做到这一点,所以这是行不通的。我在想我应该使用条件语句,但我不知道如何最有效地提供具有不同范围分组的向量。
输出数据集是因为波长 300.000 和 305.0087 介于 296 和 310 之间,波长 330.05620 介于 330 和 335 之间,而 350.0867 和 355.0953 介于 350:565。
wavelength reflectance
300.0000 NA
305.0087 NA
310.0173 -11.017334
315.0260 -16.026001
320.0347 -21.034668
325.0433 -26.043335
330.0520 NA
335.0607 -36.060669
340.0693 -41.069336
345.0780 -46.078003
350.0867 NA
355.0953 NA
我认为这会有所帮助。
library(TeachingDemos)
df$reflectance <- ifelse(296 %<% df$wavelength %<% 310 | 330 %<% df$wavelength %<% 335 | 350 %<% df$wavelength %<% 565, NA, df$reflectance)
> df
wavelength reflectance
1 300.0000 NA
2 305.0087 NA
3 310.0173 -11.01733
4 315.0260 -16.02600
5 320.0347 -21.03467
6 325.0433 -26.04333
7 330.0520 NA
8 335.0607 -36.06067
9 340.0693 -41.06934
10 345.0780 -46.07800
11 350.0867 NA
12 355.0953 NA
第一步是要认识到定义整数范围是行不通的。相反,我将使用数字对列表:
badData <- list(c(296,310), c(330,335), c(350,565))
我们要检查每个 $wavelength
是否在这三个范围内。支持更多范围。
我们可以做的第二件事是编写一个函数来检查值向量是否在一对或多对数字内。 (在这个例子中,我们“知道”它不会超过一个,但这并不重要。)
within_ranges <- function(x, lims) {
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
}
要了解它在做什么,让我们调试它,调用它,看看发生了什么。
debugonce(within_ranges)
within_ranges(df$wavelength, badData)
# debugging in: within_ranges(df$wavelength, badData)
# debug at #1: {
# Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <=
# lim[2]))
# }
让我们 运行 内部部分:
# Browse[2]>
lapply(lims, function(lim) lim[1] <= x & x <= lim[2])
# [[1]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
# [1] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
# [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE
所以第一个元素 (T,T,F,F,...) 是值 (x
) 是否落在第一个数字对 (296 到 310) 内;具有第二对的第二个元素(330 到 335);等等
Reduce(
部分在前两个参数上调用第一个参数,一个函数,保存return,然后运行在[=99上调用相同的函数=] 和第三个参数。它存储它,然后 运行 在 return 和第四个参数(如果存在)上使用相同的函数。它在提供的列表的整个长度上重复此操作。
在此示例中,函数是文字 |
(因为它是特殊的而被转义),因此它是 [[1]]
向量与 [[2]]
向量的“或”运算。如果添加 accumulate=TRUE
:
,您实际上可以看到发生了什么
# Browse[2]>
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]), accumulate=TRUE)
# [[1]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE
第一个return是第一个向量,未修改。第二个元素是原始[[2]]
向量与前面的return即this[[1]]
向量(与原始[[1]]
).第三个元素是原来的[[3]]
向量与前面的return进行或运算,即this[[2]]
。这会产生您期望的 TRUE
(1、2、7、11、12)三个分组。所以我们要[[3]]
这个元素,也就是不累加得到的:
# Browse[2]>
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE
好的,让我们Q
退出调试器,全力以赴:
within_ranges(df$wavelength, badData)
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE
这个输出看起来很眼熟。
(BTW: inside our function, we could also have used
rowSums(sapply(lims, ...)) > 0
and it would have worked just as well. For that, though, you need to realize that sapply
should be returning a matrix
with as many columns as we have rows of data in df
, odd if you aren't familiar.)
现在,我们可以NA
用dplyr
确定我们需要的东西:
df %>%
mutate(
reflectance = if_else(within_ranges(wavelength, badData), NA_real_, reflectance)
)
# wavelength reflectance
# 1 300.0000 NA
# 2 305.0087 NA
# 3 310.0173 -11.01733
# 4 315.0260 -16.02600
# 5 320.0347 -21.03467
# 6 325.0433 -26.04333
# 7 330.0520 NA
# 8 335.0607 -36.06067
# 9 340.0693 -41.06934
# 10 345.0780 -46.07800
# 11 350.0867 NA
# 12 355.0953 NA
编辑:或另一个dplyr
,使用你对replace
的第一个想法(不是我的第一个习惯,没有理由):
df %>%
mutate(
reflectance = replace(reflectance, within_ranges(wavelength, badData), NA_real_)
)
或基础 R:
df$reflectance <- ifelse(within_ranges(df$wavelength, badData), NA_real_, df$reflectance)
df
# wavelength reflectance
# 1 300.0000 NA
# 2 305.0087 NA
# 3 310.0173 -11.01733
# 4 315.0260 -16.02600
# 5 320.0347 -21.03467
# 6 325.0433 -26.04333
# 7 330.0520 NA
# 8 335.0607 -36.06067
# 9 340.0693 -41.06934
# 10 345.0780 -46.07800
# 11 350.0867 NA
# 12 355.0953 NA
备注:
- 我特别使用
NA_real_
,既是为了清楚(你知道有不同类型的 NA
吗?),部分是因为在使用 dplyr::if_else
时,它complain/fail 如果“真”和“假”参数的 类 不相同(NA
在技术上是 logical
,而不是 numeric
作为你的 reflectance
是);
- 我在第一个示例中使用
dplyr::if_else
,因为您已经在使用 dplyr
,但如果您选择放弃 dplyr
(或其他人放弃),那么base-R ifelse
也可以。 (它有它的责任,但它在这里似乎工作得很好。)
这是一个基于为 badData
和 tidyr::crossing
创建数据框的解决方案。使用 crossing
我们可以获得两个数据帧之间的所有组合。
badData <- data.frame(start= c(296,330,350),end=c(310.01,335,565))
library(dplyr)
library(tidyr)
library(data.table)
df %>% crossing(badData) %>%
mutate(Flag=ifelse(data.table::between(wavelength,start,end),1,0)) %>%
arrange(wavelength,desc(Flag)) %>% #Make sure 1 'if exist' at the 1st row for each wavelength before run distinct
distinct(wavelength,.keep_all=T) %>%
mutate(reflectance_upd=ifelse(Flag==1,NA,reflectance))
wavelength reflectance start end Flag reflectance_upd
1 300.0000 -1.000000 296 310.01 1 NA
2 305.0087 -6.008667 296 310.01 1 NA
3 310.0173 -11.017334 296 310.01 0 -11.01733
4 315.0260 -16.026001 296 310.01 0 -16.02600
5 320.0347 -21.034668 296 310.01 0 -21.03467
6 325.0433 -26.043335 296 310.01 0 -26.04333
7 330.0520 -31.052002 330 335.00 1 NA
8 335.0607 -36.060669 296 310.01 0 -36.06067
9 340.0693 -41.069336 296 310.01 0 -41.06934
10 345.0780 -46.078003 296 310.01 0 -46.07800
11 350.0867 -51.086670 350 565.00 1 NA
12 355.0953 -56.095337 350 565.00 1 NA
dplyr::between()
怎么样?
library(dplyr)
df %>%
mutate(
reflectance = case_when(
between(wavelength, 296, 310) ~ NA_real_,
between(wavelength, 330, 335) ~ NA_real_,
between(wavelength, 350, 565) ~ NA_real_,
TRUE ~ reflectance
)
)
我有以下数据框,我想用 NA 替换反射率值,具体取决于波长值是否落在被确定为不良测量值(badData 向量)的特定范围组中。
坏数据的范围可能会随着时间的推移而改变,所以我希望解决方案尽可能通用。
badData <- c(296:310, 330:335, 350:565)
df <- data.frame(wavelength = seq(300,360,5.008667),
reflectance = seq(-1,-61,-5.008667))
df
wavelength reflectance
300.0000 -1.000000
305.0087 -6.008667
310.0173 -11.017334
315.0260 -16.026001
320.0347 -21.034668
325.0433 -26.043335
330.0520 -31.052002
335.0607 -36.060669
340.0693 -41.069336
345.0780 -46.078003
350.0867 -51.086670
355.0953 -56.095337
我试过了
Data2 <- df %>%
mutate(reflectance = replace(reflectance,wavelength %in% badData, NA))
但是因为我试图用波长范围而不是精确值来做到这一点,所以这是行不通的。我在想我应该使用条件语句,但我不知道如何最有效地提供具有不同范围分组的向量。
输出数据集是因为波长 300.000 和 305.0087 介于 296 和 310 之间,波长 330.05620 介于 330 和 335 之间,而 350.0867 和 355.0953 介于 350:565。
wavelength reflectance
300.0000 NA
305.0087 NA
310.0173 -11.017334
315.0260 -16.026001
320.0347 -21.034668
325.0433 -26.043335
330.0520 NA
335.0607 -36.060669
340.0693 -41.069336
345.0780 -46.078003
350.0867 NA
355.0953 NA
我认为这会有所帮助。
library(TeachingDemos)
df$reflectance <- ifelse(296 %<% df$wavelength %<% 310 | 330 %<% df$wavelength %<% 335 | 350 %<% df$wavelength %<% 565, NA, df$reflectance)
> df
wavelength reflectance
1 300.0000 NA
2 305.0087 NA
3 310.0173 -11.01733
4 315.0260 -16.02600
5 320.0347 -21.03467
6 325.0433 -26.04333
7 330.0520 NA
8 335.0607 -36.06067
9 340.0693 -41.06934
10 345.0780 -46.07800
11 350.0867 NA
12 355.0953 NA
第一步是要认识到定义整数范围是行不通的。相反,我将使用数字对列表:
badData <- list(c(296,310), c(330,335), c(350,565))
我们要检查每个 $wavelength
是否在这三个范围内。支持更多范围。
我们可以做的第二件事是编写一个函数来检查值向量是否在一对或多对数字内。 (在这个例子中,我们“知道”它不会超过一个,但这并不重要。)
within_ranges <- function(x, lims) {
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
}
要了解它在做什么,让我们调试它,调用它,看看发生了什么。
debugonce(within_ranges)
within_ranges(df$wavelength, badData)
# debugging in: within_ranges(df$wavelength, badData)
# debug at #1: {
# Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <=
# lim[2]))
# }
让我们 运行 内部部分:
# Browse[2]>
lapply(lims, function(lim) lim[1] <= x & x <= lim[2])
# [[1]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
# [1] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
# [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE
所以第一个元素 (T,T,F,F,...) 是值 (x
) 是否落在第一个数字对 (296 到 310) 内;具有第二对的第二个元素(330 到 335);等等
Reduce(
部分在前两个参数上调用第一个参数,一个函数,保存return,然后运行在[=99上调用相同的函数=] 和第三个参数。它存储它,然后 运行 在 return 和第四个参数(如果存在)上使用相同的函数。它在提供的列表的整个长度上重复此操作。
在此示例中,函数是文字 |
(因为它是特殊的而被转义),因此它是 [[1]]
向量与 [[2]]
向量的“或”运算。如果添加 accumulate=TRUE
:
# Browse[2]>
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]), accumulate=TRUE)
# [[1]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE
第一个return是第一个向量,未修改。第二个元素是原始[[2]]
向量与前面的return即this[[1]]
向量(与原始[[1]]
).第三个元素是原来的[[3]]
向量与前面的return进行或运算,即this[[2]]
。这会产生您期望的 TRUE
(1、2、7、11、12)三个分组。所以我们要[[3]]
这个元素,也就是不累加得到的:
# Browse[2]>
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE
好的,让我们Q
退出调试器,全力以赴:
within_ranges(df$wavelength, badData)
# [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE
这个输出看起来很眼熟。
(BTW: inside our function, we could also have used
rowSums(sapply(lims, ...)) > 0
and it would have worked just as well. For that, though, you need to realize that
sapply
should be returning amatrix
with as many columns as we have rows of data indf
, odd if you aren't familiar.)
现在,我们可以NA
用dplyr
确定我们需要的东西:
df %>%
mutate(
reflectance = if_else(within_ranges(wavelength, badData), NA_real_, reflectance)
)
# wavelength reflectance
# 1 300.0000 NA
# 2 305.0087 NA
# 3 310.0173 -11.01733
# 4 315.0260 -16.02600
# 5 320.0347 -21.03467
# 6 325.0433 -26.04333
# 7 330.0520 NA
# 8 335.0607 -36.06067
# 9 340.0693 -41.06934
# 10 345.0780 -46.07800
# 11 350.0867 NA
# 12 355.0953 NA
编辑:或另一个dplyr
,使用你对replace
的第一个想法(不是我的第一个习惯,没有理由):
df %>%
mutate(
reflectance = replace(reflectance, within_ranges(wavelength, badData), NA_real_)
)
或基础 R:
df$reflectance <- ifelse(within_ranges(df$wavelength, badData), NA_real_, df$reflectance)
df
# wavelength reflectance
# 1 300.0000 NA
# 2 305.0087 NA
# 3 310.0173 -11.01733
# 4 315.0260 -16.02600
# 5 320.0347 -21.03467
# 6 325.0433 -26.04333
# 7 330.0520 NA
# 8 335.0607 -36.06067
# 9 340.0693 -41.06934
# 10 345.0780 -46.07800
# 11 350.0867 NA
# 12 355.0953 NA
备注:
- 我特别使用
NA_real_
,既是为了清楚(你知道有不同类型的NA
吗?),部分是因为在使用dplyr::if_else
时,它complain/fail 如果“真”和“假”参数的 类 不相同(NA
在技术上是logical
,而不是numeric
作为你的reflectance
是); - 我在第一个示例中使用
dplyr::if_else
,因为您已经在使用dplyr
,但如果您选择放弃dplyr
(或其他人放弃),那么base-Rifelse
也可以。 (它有它的责任,但它在这里似乎工作得很好。)
这是一个基于为 badData
和 tidyr::crossing
创建数据框的解决方案。使用 crossing
我们可以获得两个数据帧之间的所有组合。
badData <- data.frame(start= c(296,330,350),end=c(310.01,335,565))
library(dplyr)
library(tidyr)
library(data.table)
df %>% crossing(badData) %>%
mutate(Flag=ifelse(data.table::between(wavelength,start,end),1,0)) %>%
arrange(wavelength,desc(Flag)) %>% #Make sure 1 'if exist' at the 1st row for each wavelength before run distinct
distinct(wavelength,.keep_all=T) %>%
mutate(reflectance_upd=ifelse(Flag==1,NA,reflectance))
wavelength reflectance start end Flag reflectance_upd
1 300.0000 -1.000000 296 310.01 1 NA
2 305.0087 -6.008667 296 310.01 1 NA
3 310.0173 -11.017334 296 310.01 0 -11.01733
4 315.0260 -16.026001 296 310.01 0 -16.02600
5 320.0347 -21.034668 296 310.01 0 -21.03467
6 325.0433 -26.043335 296 310.01 0 -26.04333
7 330.0520 -31.052002 330 335.00 1 NA
8 335.0607 -36.060669 296 310.01 0 -36.06067
9 340.0693 -41.069336 296 310.01 0 -41.06934
10 345.0780 -46.078003 296 310.01 0 -46.07800
11 350.0867 -51.086670 350 565.00 1 NA
12 355.0953 -56.095337 350 565.00 1 NA
dplyr::between()
怎么样?
library(dplyr)
df %>%
mutate(
reflectance = case_when(
between(wavelength, 296, 310) ~ NA_real_,
between(wavelength, 330, 335) ~ NA_real_,
between(wavelength, 350, 565) ~ NA_real_,
TRUE ~ reflectance
)
)