如何检查函数中多个变量中是否至少有一个等于1?

时间:2016-11-03 22:36:04

标签: r

我有多个响应数据已被拆分为单独的列,cSplit_e为这样的格式......

  ID Response IM2   IM4 ...   IM10  IM16
1  1   4,7,10  NA     1          1    NA
2  2 7,5,16,8  NA    NA         NA     1
3  3     2,10   1    NA          1    NA

我正在尝试设置一个函数,检查每一行,看一下列的子集是否包含至少一个“1”。然后它会创建一个新列,如果一行在指定的列中至少有一个“1”,则将其设置为“1”。

以前我通过为我想要创建的每个列写出for循环来完成此操作,就像这样......

parade$q9PaperAggregate <- NA
parade$q9MagazineAggregate <- NA

#Newspaper Aggregate Loop
for (i in 1:nrow(parade)) { #Starts loop setting i to each row number
    if (is.na(parade$q9PaperAds[i]) == FALSE | ##These three lines check each row is not all NA
        is.na(parade$q9PaperCircs[i]) == FALSE |
        is.na(parade$q9PaperWebAds[i]) == FALSE) {
            parade$q9PaperAggregate[i] <- 1 #Sets agg cell value to 1 if not all NA for each i
    }
}
#Magazine Aggregate Loop
for (i in 1:nrow(parade)) {
    if (is.na(parade$q9MagazineAds[i]) == FALSE |
        is.na(parade$q9MagazineWebAds[i]) == FALSE) {
            parade$q9MagazineAggregate[i] <- 1
    }
}

这有效,但显然效率低下。我想创建一个为输入执行此操作的常规函数​​。以下是我到目前为止的情况:

#df = object; n = new column name; col = vector of columns I want to check
atleastone <- function(df, n, col) {
#n = new column name (will run over list of vector - new col names with the old columns you want to agg)
    df[n] <- NA
    for (i in 1:nrow(df)) { #Starts loop setting i to each row number
    if (df[i, col] == 1) {
            (df[n])[i] <- 1 #Sets new column cell value to 1 if not all NA for each i
            }
    }
}

我的主要两个问题是:1)如果要检查的列数可以变化,如何运行for循环以检查值的多个列,以及2)如何将行和列传递给子集。目前“col”使用列的实际名称,而“i”只使用数字行值。这在我之前使用的格式中很好......

df$column[i]

...但$运算符似乎不适用于从函数传递给它的值。

知道我在这里做错了吗?有更好的方法吗?

感谢您的时间。

编辑:

我将@ SymbolixAU的回复转换为函数,如下所示:

#Aggregate Function
#takes input df = object; n = name of new column in double quotes; l = columns you want to agg
agger <- function(df, l, n) {
    #checks if the sum of the rows in the specified columns is greater than 1
    #this produces a logical value which is multiplied by 1 to change it to numeric
    df[n] <- ((rowSums(df[, l] == 1) > 0) * 1)
}

后续问题 - 我试图使用mapply将两个不同向量列的列表“x”传递给参数“l”,并将两个新列的两个名称的向量“y”传递给创建并且目标对象df = BR。该命令如下所示:

mapply(agger, l = x, n = y, MoreArgs = list(BR))

这是将我发送到调试窗口,没有关于出错的消息或信息。我的mapply是否设置错误和/或是否有更好的方法在同一数据框中的多组列上运行此函数?

谢谢。

3 个答案:

答案 0 :(得分:3)

这可以在一行中完成,没有任何循环或*apply s:

df$new_col <- ((rowSums(df[, col] == 1, na.rm=T) > 0) * 1)

解释

  1. 您可以使用以下内容检查列子集中的值是否等于1 df[, col] == 1

  2. 然后,您可以检查每行中有多少个值 使用rowSums()

  3. 然后,如果有任何rowSums结果的值大于0,您就知道了 该行中的一列中至少有一个

  4. > 0检查返回逻辑,因此将其乘以1以将其转换 到数字

  5. 实施例

    ## taking the data you've provide
    df <- read.table(text = "ID Response IM2   IM4  IM10  IM16
    1   4,7,10  NA     1          1    NA
    2 7,5,16,8  NA    NA         NA     1
    3     2,10   1    NA          1    NA", header = T)
    
    
    df
    #   ID Response IM2 IM4 IM10 IM16
    # 1  1   4,7,10  NA   1    1   NA
    # 2  2 7,5,16,8  NA  NA   NA    1
    # 3  3     2,10   1  NA    1   NA
    
    ## specify the columns of interest
    col <- c("IM4", "IM10")
    
    ## assign the new column
    df$new_col <- ((rowSums(df[, col] == 1, na.rm=T) > 0) * 1)
    df
    #    ID Response IM2 IM4 IM10 IM16 new_col
    # 1  1   4,7,10  NA   1    1   NA       1
    # 2  2 7,5,16,8  NA  NA   NA    1       0
    # 3  3     2,10   1  NA    1   NA       1
    

    这也适用于@Barker提供的数据

    set.seed(100)
    df <- data.frame(ID = 1:20)
    df[paste0("IM", 1:7)] <- replicate(7,sample(c(rep(NA,5),1,1),20, replace = TRUE))
    
    col <- paste0("IM", 1:7)
    df$new_col <- ((rowSums(df[, col] == 1, na.rm=T) > 0) * 1)
    

    更新

    我不完全确定你在评论中提出的问题,但你可以做点什么

    ## using @Barker 's data gain, create a list of groups of columns
    col_groups <- list(grp1 = c("IM1","IM2"),
                       grp2 = c("IM2","IM7"),
                       grp3 = c("IM5","IM7"))
    
    ## use lapply to do the calculation for each group of columns
    df2 <- lapply(col_groups, function(x){
      df['new_col'] <- ((rowSums(df[, x] == 1, na.rm=T) > 0) * 1)
      return(df)
    })
    

    这显然会返回data.frame个列表,每个列表都根据grp值命名,每个都附加了不同的new_col

答案 1 :(得分:1)

使用apply函数实际上可以很简单地完成。

让我们首先制作一个示例数据集来使用:

set.seed(100)
df <- data.frame(ID = 1:20)
df[paste0("IM", 1:7)] <- replicate(7,sample(c(rep(NA,5),1,1),20, replace = TRUE))

现在我们有了数据,我们实际上可以在一行中完成任务:

df[["newName"]] <- apply(df[grep("^IM", names(df))] == 1, 1, any, na.rm = TRUE)

让我们回顾一下这里发生的事情。首先,我们使用df[grep("^IM", names(df))]df中以IM开头的列进行子集化。接下来,我们使用==来确定哪些值等于1apply函数接受新的逻辑数据帧,并且对于每一行(因为第二个参数等于1,columnes将为2,每个元素将为c(1, 2)any函数。如果至少有一个输入为TRUE,则返回TRUE,但是,如果没有为TRUE且其中一个值为NA,则它将返回NA,因为它不会我知道NATRUE还是FALSE。通过在末尾添加na.rm = TRUE,我们告诉any函数忽略NA值。如果您希望这些值包含NA而不是FALSE,只需删除na.rm = TRUE语句。

答案 2 :(得分:1)

此代码

parade$q9PaperAggregate <- NA

#Newspaper Aggregate Loop
for (i in 1:nrow(parade)) { #Starts loop setting i to each row number
    if (is.na(parade$q9PaperAds[i]) == FALSE | ##These three lines check each row is not all NA
        is.na(parade$q9PaperCircs[i]) == FALSE |
        is.na(parade$q9PaperWebAds[i]) == FALSE) {
            parade$q9PaperAggregate[i] <- 1 #Sets agg cell value to 1 if not all NA for each i
    }
}

可以矢量化(运行得更快):

parade$q9PaperAggregate <- ifelse(is.na(parade$q9PaperAds) &
                                  is.na(parade$q9PaperCircs) &
                                  is.na(parade$q9PaperWebAds), NA, 1)