如何在多列中循环或应用具有2个条件的ifelse语句?

时间:2019-09-05 19:52:43

标签: r loops if-statement apply

我经常需要根据其他列的条件在r中创建新列。因为我使用时间序列数据,所以列的条件是基于每个时间点的变化。当ifelse语句在每个新时间点也都发生变化时,如何循环ifelse语句以创建新变量。

我通常只是复制粘贴我的ifelse语句,但是有太多的错误余地。我宁愿只使用循环或apply语句。对于以下代码的上下文,我们在13个不同的时间点收集痰液。我们要为代表阳性痰的时间点创建13个变量。根据ifelse陈述中的两个条件,痰被认为是“阳性”。

data$smear_bl <- ifelse(data$s_concafb_sputum_specimen_1== 0 | data$s_concafb_sputum_specimen_1==5, 0, 1)

data$smear_2 <- ifelse(data$s_concafb_sputum_specimen_2 == 0 | data$s_concafb_sputum_specimen_2 == 5, 0, 1 )

data$smear_3 <- ifelse(data$s_concafb_sputum_specimen_3 == 0 | data$s_concafb_sputum_specimen_3 == 5, 0, 1 )

data$smear_4 <- ifelse(data$s_concafb_sputum_specimen_4 == 0 | data$s_concafb_sputum_specimen_4 == 5, 0, 1 )

....

data$smear_mo5 <- ifelse(data$s_concafb_sputum_specimen_13 == 0 | data$s_concafb_sputum_specimen_13 == 5, 0, 1 )

我希望通过上面的编码提供13个新变量,但是要使用更简单的语句!然后,我希望能够对这些列(它们由1和0组成)求和。

4 个答案:

答案 0 :(得分:1)

这是一个常见的用例,其中您的数据以“宽”格式记录,但最好以“长”格式进行分析。这意味着,当您当前在每个时间步都有一个单独的列,且具有相同类型的度量时,您需要两列: time =时间步,而 value =度量在那个时候。

就像哈德利·威卡姆(Hadley Wickam)所写的那样,您要使用整洁的数据,其中:

  1. 每个变量都是一列。
  2. 每次观察都是一行
  3. 每个值都是一个单元格。

以下是使用tidyr整理数据的方法。注意:如果您的数据看起来不像我构成的那样,那么请给我们提供有关数据的最小工作示例,我将更新此解决方案。收集功能需要一点时间来习惯,但是一旦到达那里,它就会非常有用。

library(tidyr) # for gather function

# Making up some data
wide.df <- data.frame(s_concafb_sputum_specimen_1 = 1,
                      s_concafb_sputum_specimen_2 = 0,
                      s_concafb_sputum_specimen_3 = 5,
                      s_concafb_sputum_specimen_4 = 2,
                      s_concafb_sputum_specimen_5 = 1)

# Converting from wide to long
long.df <- gather(wide.df, key = "time", value = "value")

# New column from condition
long.df$smear <- ifelse(long.df$value == 0 | long.df$value == 5, 0, 1)

# Result
long.df

# # A tibble: 5 x 3
#   time                        value smear
#   <chr>                       <dbl> <dbl>
# 1 s_concafb_sputum_specimen_1     1     1
# 2 s_concafb_sputum_specimen_2     0     0
# 3 s_concafb_sputum_specimen_3     5     0
# 4 s_concafb_sputum_specimen_4     2     1
# 5 s_concafb_sputum_specimen_5     1     1

您还询问了在数据为长格式时,如何对痰标本的涂片中的值求和。我将使用库summarize中的dplyr函数。由于此解决方案的原始样本数据中每个样本只有一个值,因此我们只需要复制数据框中的每一行,因此每个样本都有多个值可以求和。另外,标识样品的 time 列实际上应该是一个因子类型,因此我们将对其进行更改以简化将来的分析。

doubled.df <- bind_rows(long.df, long.df)
doubled.df$time <- as.factor(doubled.df$time)

library(dplyr)

# If you're not familiar with the pipe operator (%>%) provided by dplyr, it just takes
# the output of the thing to its left and makes it the first argument to the function
# to its right. I think it makes code more readable than nesting a bunch of functions,
# and more compact than saving stuff in a bunch of intermediate variables, which are 
# two other ways to accomplish the same thing.
doubled.df %>%
  group_by(time) %>%
  summarize(Sum = sum(smear))

# # A tibble: 5 x 2
#   time                          Sum
#   <fct>                       <dbl>
# 1 s_concafb_sputum_specimen_1     2
# 2 s_concafb_sputum_specimen_2     0
# 3 s_concafb_sputum_specimen_3     0
# 4 s_concafb_sputum_specimen_4     2
# 5 s_concafb_sputum_specimen_5     2

# Alternatively
summarize(group_by(doubled.df, time), Sum = sum(smear))

# # A tibble: 5 x 2
#   time                          Sum
#   <fct>                       <dbl>
# 1 s_concafb_sputum_specimen_1     2
# 2 s_concafb_sputum_specimen_2     0
# 3 s_concafb_sputum_specimen_3     0
# 4 s_concafb_sputum_specimen_4     2
# 5 s_concafb_sputum_specimen_5     2

答案 1 :(得分:1)

我们可以在基数R中使用lapply来创建新列。同样,我们可以跳过ifelse并将as.integer包装在逻辑值上以获得1/0。

#Columns to test
cols <- c("a", "b")
df[paste0("new_", cols)] <- lapply(df[cols], function(x) as.integer(x %in% c(0, 5)))

df
#  a b new_a new_b
#1 2 0     0     1
#2 3 1     0     0
#3 0 2     1     0
#4 5 3     1     0
#5 4 4     0     0
#6 1 5     0     1

数据

df <- data.frame(a = c(2, 3, 0, 5, 4, 1), b = 0:5)

答案 2 :(得分:1)

这是另一种基本方法:

cols <- names(DF)[-1]
new_cols <- paste('new', cols, sep = '_')

DF[, new_cols] <- !(DF[cols] == 0 | DF[cols] == 5)


  grp a b new_a new_b
1   1 2 0  TRUE FALSE
2   1 3 1  TRUE  TRUE
3   2 0 2 FALSE  TRUE
4   2 1 3  TRUE  TRUE
5   3 2 3  TRUE  TRUE
6   3 3 2  TRUE  TRUE
7   4 0 1 FALSE  TRUE
8   4 1 0  TRUE FALSE

+将布尔值强制转换为数字,而!则将逻辑比较取反。

如果要对所有内容进行求和,可以很容易地在原始逻辑语句中添加colSums

colSums(DF[new_cols])

# new_a new_b 
#   6     6

colSums(!(DF[cols] == 0 | DF[cols] == 5))

# a b 
# 6 6

数据

set.seed(123)
DF <- data.frame(grp = rep(1:4, each = 2),
                 a = c(2, 3, 0, 1, sample(4)-1),
                 b = c(0:3, 3:0))

答案 3 :(得分:0)

如果您使用索引来区分列名,则可以执行以下操作:

for (i in 1:13) { # use however many columns or variables you want
    colname <- paste0('s_concafb_sputum_specimen_', i)
    varname <- paste0('smear_', i)
    data[,varname] <- !(data[, colname] == 0 | data[, colname] == 5) # this maps to a boolean but TRUE or FALSE evaluate to 1 and 0 respectively
}