我有一堆列,它们都以前缀wtp_
开头,出现在一个宽数据帧的中间(在wtp_
列之前和之后有几个列(。小型示例:
df <- tribble(~id, ~complete, ~wtp_20,~wtp_40,~wtp_60,~wtp_80,~wtp_100, ~sex,
1, 1, 0,0,1,1,1, "F",
2, 0, 0,0,0,1,1, "F",
3, 0, 0,0,0,0,1, "M",
4, 1, 1,1,1,1,1, "M",
5, 1, 0,0,0,0,0, "M",
6, 0, 0,1,1,1,1, "F"); df
我要找的是:我需要创建一个新变量(min_wtp
(,该变量在其中一个wtp_
列从0切换到1的第一次时返回列的名称。换句话说,我需要一个解决方案来创建以下内容:
df_needed <- tribble(~id, ~complete, ~wtp_20,~wtp_40,~wtp_60,~wtp_80,~wtp_100, ~sex, ~min_wtp,
1, 1, 0,0,1,1,1, "F", "wtp_60",
2, 0, 0,0,0,1,1, "F", "wtp_80",
3, 0, 0,0,0,0,1, "M", "wtp_100",
4, 1, 1,1,1,1,1, "M", "wtp_20",
5, 1, 0,0,0,0,0, "M", "NA",
6, 0, 0,1,1,1,1, "F", "wtp_40"); df_needed
请注意以下并发症:
-有些人(如id=5(从不更改为1,而另一些人(如id=4(则一直为1
-在wtp_
列之前出现了一些不相关的列,这些列中有0和1,在构造min_wtp
时应忽略这些列
-列(包括wtp_
列(比我上面包含的最小示例多得多。
我尝试过将which
和colnames
功能与select(starts_with("wtp_"))
结合使用,但都没有成功。
如果有人有dplyr解决方案,那将是首选。
我们可以使用apply
为每一行获取满足您的条件的第一列的数量。然后我们使用这个数字作为索引来获得列名。
df$min_wtp = apply(df[ , grepl("wtp", names(df))], 1, function(x) {
names(x)[min(which(x > 0))]
})
df
id complete wtp_20 wtp_40 wtp_60 wtp_80 wtp_100 sex min_wtp <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr> 1 1 1 0 0 1 1 1 F wtp_60 2 2 0 0 0 0 1 1 F wtp_80 3 3 0 0 0 0 0 1 M wtp_100 4 4 1 1 1 1 1 1 M wtp_20 5 5 1 0 0 0 0 0 M NA 6 6 0 0 1 1 1 1 F wtp_40
如果您获得长格式的数据,会容易得多:
library(dplyr)
df %>%
tidyr::pivot_longer(cols = starts_with('wtp')) %>%
group_by(id) %>%
summarise(min_wtp = name[which(value == 1 &
lag(value, default = 0) == 0)[1]]) %>%
left_join(df, by = 'id')
# A tibble: 6 x 9
# id min_wtp complete wtp_20 wtp_40 wtp_60 wtp_80 wtp_100 sex
# <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
#1 1 wtp_60 1 0 0 1 1 1 F
#2 2 wtp_80 0 0 0 0 1 1 F
#3 3 wtp_100 0 0 0 0 0 1 M
#4 4 wtp_20 1 1 1 1 1 1 M
#5 5 NA 1 0 0 0 0 0 M
#6 6 wtp_40 0 0 1 1 1 1 F
在不重塑数据的情况下,您可以将rowwise
与c_across
:一起使用
apply_fun <- function(x) {
which(x == 1 & lag(x, default = 0) == 0)[1]
}
cols <- grep('^wtp', names(df), value = TRUE)
df %>%
rowwise() %>%
mutate(min_wtp = cols[apply_fun(c_across(cols))])
如果它从不从1倒退到0,那么你可以用一些基本的和很快找到变化点:
sw <- startsWith(names(df), "wtp_")
names(df[sw])[sum(sw) - rowSums(df[sw]) + 1]
#[1] "wtp_60" "wtp_80" "wtp_100" "wtp_20" NA "wtp_40"