[英]Use the same mapply function to create several new variables
我有一個數據框(“dat”),其中每行代表一個研究的參與者。 對於每個參與者(“代碼”),我有一個列給出了他們的性別(“性別”)和年齡(“年齡”),以及幾個帶有測試結果的列(“v.1”等)。 數據框看起來像這樣:
> dat
code sex age v.1 v.2
1 A1 m 8 4 9
2 B2 f 12 7 2
對於每列測試結果,我需要在數據框外的相應向量中查找值(例如,對於8歲男性參與者的“v.1.m.8”或對於“v.1.f.12”, 12歲的女性參與者)並將該向量中的值插入數據框中的新列(“v.1.t”)。 男性和女性參與者以及不同年齡組有不同的載體。 向量看起來像這樣:
v.1.m.8 <- c(4, 5, 2, 8, 2, ...)
v.2.m.8 <- c(3, 2, 2, 1, 8, ...)
v.1.m.12 <- c(...)
v.2.m.12 <- c(...)
v.1.f.8 <- c(...)
v.2.f.8 <- c(...)
v.1.f.12 <- c(...)
v.2.f.12 <- c(...)
對我來說,在向量中查找值的最邏輯直接的方法是使用nestes if-statemenst的for循環。 排序或喜歡這個:
for (i in nrow(dat)) {
if (dat[i, ]$age < 8 | dat[i, ]$age > 18) {
dat[i, ]$v.1.t <- NA
dat[i, ]$v.2.t <- NA
} else if (dat[i, ]$age < 12) {
if (dat[i, ]$dat.sex == "m") {
dat[i, ]$v.1.t <- v.1.m.8[dat[i, ]$v.1]
dat[i, ]$v.2.t <- v.2.m.8[dat[i, ]$v.2]
} else {
dat[i, ]$v.1.t <- v.1.f.8[dat[i, ]$v.1]
dat[i, ]$v.2.t <- v.2.f.8[dat[i, ]$v.2]
}
} else {
if (dat[i, ]$dat.sex == "m") {
dat[i, ]$v.1.t <- v.1.m.12[dat[i, ]$v.1]
dat[i, ]$v.2.t <- v.2.m.12[dat[i, ]$v.2]
} else {
dat[i, ]$v.1.t <- v.1.f.12[dat[i, ]$v.1]
dat[i, ]$v.2.t <- v.2.f.12[dat[i, ]$v.2]
}
}
}
為了避免循環,我可能會像這樣使用mapply():
dat$v.1.t <- mapply(
function(a, b, c) {
if (a < 8 | a > 18) {
NA
} else if (a < 12) {
if (b == "m") {
v.1.m.8[c]
} else {
v.1.f.8[c]
}
} else {
if (b == "m") {
v.1.m.12[c]
} else {
v.1.f.12[c]
}
}
},
dat$age,
dat$dat.sex,
dat$v.1
)
dat$v.2.t <- mapply(
function(a, b, c) {
if (a < 8 | a > 18) {
NA
} else if (a < 12) {
if (b == "m") {
v.2.m.8[c]
} else {
v.2.f.8[c]
}
} else {
if (b == "m") {
v.2.m.12[c]
} else {
v.2.f.12[c]
}
}
},
dat$age,
dat$dat.sex,
dat$v.2
)
第二個解決方案的問題是我必須為我想要分配的每個變量重復整個代碼。
有更好的解決方案嗎?
在我的真實代碼中,我必須在44個向量中查找11個列以創建11個新列。
我更喜歡基礎R的解決方案。
假設您的數據如下所示:
dat <- data.frame(code = paste0(LETTERS[1:24], 1:24), sex=c("m", "f"), age=c(8,12, 12, 8), v.1 = sample(1:10, 24, replace=T), v.2 = sample(1:10, 24, replace=T))
根據性別和年齡的組合進行拆分,並為每個拆分調出v.1值:
lapply(split(dat, list(dat$sex, dat$age)), '[[', "v.1")
$f.12
[1] 1 9 2 3 3 10
$f.8
[1] 8 3 7 7 3 8
$m.12
[1] 10 3 2 2 4 1
$m.8
[1] 8 10 1 9 5 7
根據性別和年齡的組合進行拆分,並為每個拆分調出v.2值:
lapply(split(dat, list(dat$sex, dat$age)), '[[', "v.2")
$f.12
[1] 10 3 5 8 9 2
$f.8
[1] 2 3 4 8 2 5
$m.12
[1] 9 7 1 1 1 2
$m.8
[1] 5 2 1 5 9 10
編輯:感謝@Sotos指出兩個變量分裂
ifelse()
應該很簡單。
以下示例僅適用於一個新變量:
數據示例(感謝@Adam Quek):
dat <- data.frame(code = paste0(LETTERS[1:24], 1:24), sex=c("m", "f"),
age=c(8,12, 12, 8), v.1 = sample(1:10, 24, replace=T),
v.2 = sample(1:10, 24, replace=T))
矢量示例:
v.1.m.8 <- c(21:30)
v.1.f.8 <- c(31:40)
v.1.m.12 <- c(41:50)
v.1.f.12 <- c(51:60)
新變量v.1.t
代碼:
dat$v.1.t <- with(dat, ifelse(!(age %in% c(8,12)), NA,
ifelse(age == 8 & sex == "m", v.1.m.8[v.1],
ifelse(age == 8 & sex == "f", v.1.f.8[v.1],
ifelse(age == 12 & sex == "m", v.1.m.12[v.1],
v.1.f.12[v.1])))))
可以輕松編輯年齡限制以包括更多類別並分出可能的向量。
輸出:
code sex age v.1 v.2 v.1.t
1 A1 m 8 10 1 30
2 B2 f 12 6 5 56
3 C3 m 12 10 3 50
4 D4 f 8 7 10 37
5 E5 m 8 5 4 25
6 F6 f 12 6 9 56
7 G7 m 12 2 9 42
8 H8 f 8 2 3 32
9 I9 m 8 4 1 24
10 J10 f 12 7 4 57
11 K11 m 12 7 4 47
12 L12 f 8 9 10 39
13 M13 m 8 9 2 29
14 N14 f 12 5 8 55
15 O15 m 12 1 10 41
16 P16 f 8 8 4 38
17 Q17 m 8 6 7 26
18 R18 f 12 4 10 54
19 S19 m 12 10 1 50
20 T20 f 8 9 6 39
21 U21 m 8 9 8 29
22 V22 f 12 10 2 60
23 W23 m 12 6 6 46
24 X24 f 8 6 7 36
如果你不想為11個變量中的每個變量寫ifelse()
,請將向量放入一個包含兩個圖層的列表(11個列表的列表,每個列表包含4個向量),並在變量和矢量列表上使用mapply()
名單。
編輯:
我想到了mapply()
的實現,我認為簡單的for()
loop更容易。
以下應該這樣做(例如,每個變量有兩個變量和4個向量(m8,f8,m12,f12)):
向量:
v.1.m.8 <- c(21:30)
v.1.f.8 <- c(31:40)
v.1.m.12 <- c(41:50)
v.1.f.12 <- c(51:60)
v.2.m.8 <- c(61:70)
v.2.f.8 <- c(71:80)
v.2.m.12 <- c(81:90)
v.2.f.12 <- c(91:100)
矢量列表:
myvectors <- list("v.1" = list(v.1.m.8, v.1.f.8, v.1.m.12, v.1.f.12),
"v.2" = list(v.2.m.8, v.2.f.8, v.2.m.12, v.2.f.12))
for()
loop(僅通過列表的名稱循環,所以i
是c("v.1", "v.2"))
:
for(i in names(myvectors)){
dat[, paste(i, "t", sep = ".")] <- with(dat, ifelse(!(age %in% c(8,12)), NA,
ifelse(age == 8 & sex == "m", myvectors[[i]][[1]][eval(parse(text = i))],
ifelse(age == 8 & sex == "f", myvectors[[i]][[2]][eval(parse(text = i))],
ifelse(age == 12 & sex == "m", myvectors[[i]][[3]][eval(parse(text = i))],
myvectors[[i]][[4]][eval(parse(text = i))])))))
}
輸出:
code sex age v.1 v.2 v.1.t v.2.t
1 A1 m 8 3 2 23 62
2 B2 f 12 7 10 57 100
3 C3 m 12 2 3 42 83
4 D4 f 8 7 6 37 76
5 E5 m 8 2 10 22 70
6 F6 f 12 1 9 51 99
7 G7 m 12 10 6 50 86
8 H8 f 8 4 6 34 76
9 I9 m 8 3 1 23 61
10 J10 f 12 5 4 55 94
11 K11 m 12 5 5 45 85
12 L12 f 8 3 8 33 78
13 M13 m 8 10 9 30 69
14 N14 f 12 3 4 53 94
15 O15 m 12 6 2 46 82
16 P16 f 8 8 3 38 73
17 Q17 m 8 9 5 29 65
18 R18 f 12 5 6 55 96
19 S19 m 12 6 4 46 84
20 T20 f 8 2 9 32 79
21 U21 m 8 5 1 25 61
22 V22 f 12 2 1 52 91
23 W23 m 12 3 10 43 90
24 X24 f 8 2 9 32 79
有了這個,你需要准備的唯一事情就是在第一級有正確命名的子列表的向量列表列表(所以"v.1"
到"v.11"
如上所示,帶有"v.1"
和"v.2"
。確保子列表中4個向量的順序始終相同!在我的例子中,順序是m8,f8,m12,f12。希望它有所幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.