我经常需要根据其他列的条件在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组成)求和。
答案 0 :(得分:1)
这是一个常见的用例,其中您的数据以“宽”格式记录,但最好以“长”格式进行分析。这意味着,当您当前在每个时间步都有一个单独的列,且具有相同类型的度量时,您需要两列: time =时间步,而 value =度量在那个时候。
就像哈德利·威卡姆(Hadley Wickam)所写的那样,您要使用整洁的数据,其中:
以下是使用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
}