在 R 中的 For 循环中执行特定于案例的编辑
Performing Case Specific Edits in For Loop in R
我的调查数据需要我执行几个特定案例的重新编码,但我想避免为每个重新编码创建一行新代码(因为会有几十个)。我希望有一种方法可以使用人行横道重新编码数据,该人行横道仅根据 su_id 和需要重新编码的 Q# 重新编码所需的值。
su_id <- 100001:100010
Q1 <- c(1, 2, 5, 6, 2, 3, 4, 2, 1, 6)
Q2 <- c(2, 4, 6, 4, 3, 6, 2, 1, 6, 5)
data <- data.frame(su_id, Q1, Q2)
su_id <- c( 100004, 100010, 100003, 100006, 100009)
var <- c("Q1", "Q1", "Q2", "Q2", "Q2")
newVal <- c(4, 4, 5, 5, 5)
cw <- data.frame(su_id, var, newVal)
#data:
su_id Q1 Q2
1 100001 1 2
2 100002 2 4
3 100003 5 6
4 100004 6 4
5 100005 2 3
6 100006 3 6
7 100007 4 2
8 100008 2 1
9 100009 1 6
10 100010 6 5
#Crosswalk:
su_id var newVal
1 100004 Q1 4
2 100010 Q1 4
3 100003 Q2 5
4 100006 Q2 5
5 100009 Q2 5
我开始尝试重复这样的事情,但显然这不会成功,但希望这能让我了解我想要完成的事情。任何人都可以建议 how/if 这是可能的吗?
su_idToChange <- cw$su_id
varToChange <- cw$var
newValToChange <- cw$newVal
for(i in su_idToChange) {
data_new <- data %>%
mutate(across(all_of(varToChange), case_when(su_id %in% su_idToChange
~ coalesce(deframe(cw[cw$var == "Q1" | cw$var == "Q2", ][-1])[.], .))))
}
谢谢!
如果我对你的问题理解正确,那么你正在尝试执行如下操作:
my_change_fun <- function(data, cw) {
for (i in seq_len(nrow(cw))) {
data[data$su_id == cw[i, 1], cw[i, 2]] <- cw[i, 3]
}
data
}
my_change_fun(data, cw)
#> su_id Q1 Q2
#> 1 100001 1 2
#> 2 100002 2 4
#> 3 100003 5 5
#> 4 100004 4 4
#> 5 100005 2 3
#> 6 100006 3 5
#> 7 100007 4 2
#> 8 100008 2 1
#> 9 100009 1 5
#> 10 100010 4 5
由 reprex package (v2.0.1)
于 2021-08-30 创建
这里有一个 tidyverse
您可以使用的解决方案:
library(dplyr)
library(tidyr)
cw %>%
pivot_wider(names_from = var, values_from = newVal) %>%
right_join(data, by = "su_id") %>%
mutate(across(ends_with(".x"), ~ coalesce(.x, get(gsub("\.x", "\.y", cur_column()))))) %>%
select(!ends_with(".y")) %>%
arrange(su_id) %>%
rename_with(~ gsub("\.x", "", .), ends_with(".x"))
# A tibble: 10 x 3
su_id Q1 Q2
<dbl> <dbl> <dbl>
1 100001 1 2
2 100002 2 4
3 100003 5 5
4 100004 4 4
5 100005 2 3
6 100006 3 5
7 100007 4 2
8 100008 2 1
9 100009 1 5
10 100010 4 5
我们还可以根据 'su_id' 列使用 'cw' 执行 left_join
,然后循环 across
'Q1'、'Q2'列,将 'var' 列值与列名 (cur_column()
) 匹配的位置替换为 coalesce
将 'newVal' 替换为列值,否则 return 列值
library(dplyr)
data %>%
left_join(cw) %>%
transmute(su_id, across(c(Q1, Q2), ~ case_when(var %in% cur_column() ~
coalesce(newVal, .), TRUE ~ .)))
-输出
su_id Q1 Q2
1 100001 1 2
2 100002 2 4
3 100003 5 5
4 100004 4 4
5 100005 2 3
6 100006 3 5
7 100007 4 2
8 100008 2 1
9 100009 1 5
10 100010 4 5
或者 data.table
的选项
library(data.table)
setDT(data)[cw, c("Q1", "Q2") := .(fcoalesce(newVal[match('Q1',
var)], Q1), fcoalesce(newVal[match("Q2", var)], Q2)),
on = .(su_id), by = .EACHI]
-输出
> data
su_id Q1 Q2
1: 100001 1 2
2: 100002 2 4
3: 100003 5 5
4: 100004 4 4
5: 100005 2 3
6: 100006 3 5
7: 100007 4 2
8: 100008 2 1
9: 100009 1 5
10: 100010 4 5
使用 merge
+ reshape
的基础 R 选项
transform(
merge(
data,
reshape(
cw,
direction = "wide",
idvar = "su_id",
timevar = "var"
),
all = TRUE
),
Q1 = replace(Q1, !is.na(newVal.Q1), na.omit(newVal.Q1)),
Q2 = replace(Q2, !is.na(newVal.Q2), na.omit(newVal.Q2))
)[names(data)]
给予
su_id Q1 Q2
1 100001 1 2
2 100002 2 4
3 100003 5 5
4 100004 4 4
5 100005 2 3
6 100006 3 5
7 100007 4 2
8 100008 2 1
9 100009 1 5
10 100010 4 5
我的调查数据需要我执行几个特定案例的重新编码,但我想避免为每个重新编码创建一行新代码(因为会有几十个)。我希望有一种方法可以使用人行横道重新编码数据,该人行横道仅根据 su_id 和需要重新编码的 Q# 重新编码所需的值。
su_id <- 100001:100010
Q1 <- c(1, 2, 5, 6, 2, 3, 4, 2, 1, 6)
Q2 <- c(2, 4, 6, 4, 3, 6, 2, 1, 6, 5)
data <- data.frame(su_id, Q1, Q2)
su_id <- c( 100004, 100010, 100003, 100006, 100009)
var <- c("Q1", "Q1", "Q2", "Q2", "Q2")
newVal <- c(4, 4, 5, 5, 5)
cw <- data.frame(su_id, var, newVal)
#data:
su_id Q1 Q2
1 100001 1 2
2 100002 2 4
3 100003 5 6
4 100004 6 4
5 100005 2 3
6 100006 3 6
7 100007 4 2
8 100008 2 1
9 100009 1 6
10 100010 6 5
#Crosswalk:
su_id var newVal
1 100004 Q1 4
2 100010 Q1 4
3 100003 Q2 5
4 100006 Q2 5
5 100009 Q2 5
我开始尝试重复这样的事情,但显然这不会成功,但希望这能让我了解我想要完成的事情。任何人都可以建议 how/if 这是可能的吗?
su_idToChange <- cw$su_id
varToChange <- cw$var
newValToChange <- cw$newVal
for(i in su_idToChange) {
data_new <- data %>%
mutate(across(all_of(varToChange), case_when(su_id %in% su_idToChange
~ coalesce(deframe(cw[cw$var == "Q1" | cw$var == "Q2", ][-1])[.], .))))
}
谢谢!
如果我对你的问题理解正确,那么你正在尝试执行如下操作:
my_change_fun <- function(data, cw) {
for (i in seq_len(nrow(cw))) {
data[data$su_id == cw[i, 1], cw[i, 2]] <- cw[i, 3]
}
data
}
my_change_fun(data, cw)
#> su_id Q1 Q2
#> 1 100001 1 2
#> 2 100002 2 4
#> 3 100003 5 5
#> 4 100004 4 4
#> 5 100005 2 3
#> 6 100006 3 5
#> 7 100007 4 2
#> 8 100008 2 1
#> 9 100009 1 5
#> 10 100010 4 5
由 reprex package (v2.0.1)
于 2021-08-30 创建这里有一个 tidyverse
您可以使用的解决方案:
library(dplyr)
library(tidyr)
cw %>%
pivot_wider(names_from = var, values_from = newVal) %>%
right_join(data, by = "su_id") %>%
mutate(across(ends_with(".x"), ~ coalesce(.x, get(gsub("\.x", "\.y", cur_column()))))) %>%
select(!ends_with(".y")) %>%
arrange(su_id) %>%
rename_with(~ gsub("\.x", "", .), ends_with(".x"))
# A tibble: 10 x 3
su_id Q1 Q2
<dbl> <dbl> <dbl>
1 100001 1 2
2 100002 2 4
3 100003 5 5
4 100004 4 4
5 100005 2 3
6 100006 3 5
7 100007 4 2
8 100008 2 1
9 100009 1 5
10 100010 4 5
我们还可以根据 'su_id' 列使用 'cw' 执行 left_join
,然后循环 across
'Q1'、'Q2'列,将 'var' 列值与列名 (cur_column()
) 匹配的位置替换为 coalesce
将 'newVal' 替换为列值,否则 return 列值
library(dplyr)
data %>%
left_join(cw) %>%
transmute(su_id, across(c(Q1, Q2), ~ case_when(var %in% cur_column() ~
coalesce(newVal, .), TRUE ~ .)))
-输出
su_id Q1 Q2
1 100001 1 2
2 100002 2 4
3 100003 5 5
4 100004 4 4
5 100005 2 3
6 100006 3 5
7 100007 4 2
8 100008 2 1
9 100009 1 5
10 100010 4 5
或者 data.table
library(data.table)
setDT(data)[cw, c("Q1", "Q2") := .(fcoalesce(newVal[match('Q1',
var)], Q1), fcoalesce(newVal[match("Q2", var)], Q2)),
on = .(su_id), by = .EACHI]
-输出
> data
su_id Q1 Q2
1: 100001 1 2
2: 100002 2 4
3: 100003 5 5
4: 100004 4 4
5: 100005 2 3
6: 100006 3 5
7: 100007 4 2
8: 100008 2 1
9: 100009 1 5
10: 100010 4 5
使用 merge
+ reshape
transform(
merge(
data,
reshape(
cw,
direction = "wide",
idvar = "su_id",
timevar = "var"
),
all = TRUE
),
Q1 = replace(Q1, !is.na(newVal.Q1), na.omit(newVal.Q1)),
Q2 = replace(Q2, !is.na(newVal.Q2), na.omit(newVal.Q2))
)[names(data)]
给予
su_id Q1 Q2
1 100001 1 2
2 100002 2 4
3 100003 5 5
4 100004 4 4
5 100005 2 3
6 100006 3 5
7 100007 4 2
8 100008 2 1
9 100009 1 5
10 100010 4 5