使用相同的mapply函数创建几个新变量

时间:2017-05-17 08:27:57

标签: r function mapply

我有一个数据框(" dat"),其中每一行代表一项研究的参与者。对于每个参与者("代码")我有一个列给出了他们的性别("性别")和年龄("年龄"),以及几个包含测试结果的列(" v.1"等)。数据框看起来像这样:

> dat
   code sex age v.1 v.2
1  A1   m   8   4   9
2  B2   f   12  7   2

对于每列测试结果,我需要在数据框外的相应向量中查找值(例如" v.1.m.8"对于8岁男性参与者或&# 34; 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的解决方案。

2 个答案:

答案 0 :(得分:0)

假设您的数据如下:

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指出通过两个变量进行拆分

答案 1 :(得分:0)

使用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() - 循环更容易。

以下应该这样做(每个例子包含两个变量和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() - 循环(仅通过列表名称循环,因此ic("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",如上所示{{1}确保子列表中4个向量的顺序始终相同!在我的示例中,顺序是m8,f8,m12,f12。希望它有所帮助!