如何在检测到特定字符串后使用过滤器和 dplyr 删除数据帧行
How to remove rows of dataframe after a particular string is detected, using filter and dplyr
我有如下例所示的数据。对于每个参与者,如果特定字符串 ("trial_end"
) 出现在 my_strings
列中,我想在它出现后删除所有行。
library(dplyr)
library(stringr)
library(tibble)
df1 <- tibble::tribble(
~participant_id, ~timestamp, ~my_strings,
1L, 1L, "other_string",
1L, 2L, "other_string",
1L, 3L, "trial_end",
1L, 4L, "other_string",
2L, 1L, "other_string",
2L, 2L, "other_string",
2L, 3L, "other_string",
2L, 4L, "other_string",
3L, 1L, "other_string",
3L, 2L, "trial_end",
3L, 3L, "other_string",
3L, 4L, "other_string"
)
我的第一次尝试是使用 str_detect
查找字符串的存在,which
提供行号,然后使用 filter
仅保留该行和所有之前的那些:
df2 <- df1 %>%
group_by(participant_id) %>%
filter(row_number() < (which(str_detect(my_strings, "trial_end"))) + 1)
当未检测到字符串时,这似乎会引发错误(例如此处示例中的参与者 2)。
我的下一次尝试是添加条件 if_else
,试图有效地说 'IF the target string is detected THEN remove all the rows after for that participant, ELSE if the string is not detected, do nothing'.
df3 <- df1 %>%
group_by(participant_id) %>%
if_else(str_detect(my_strings, "trial_end"),
filter(row_number() < (which(str_detect(my_strings, "trial_end"))) + 1),
filter(timestamp < max(timestamp)))
这也返回了一个错误:
错误:condition
必须是逻辑向量,而不是 grouped_df/tbl_df/tbl/data.frame
对象。
我最后的尝试是通过将条件 if else 放在 filter
中来利用此处已有的另一个答案,但这也产生了错误。
df4 <- df1 %>%
group_by(participant_id) %>%
filter(if(str_detect(my_strings, "trial_end") < (which(str_detect(my_strings, "trial_end")) + 1))
else < n())
谁能指出解决这个问题的最佳方法? filter
是不是做错了?
非常感谢。
为清楚起见,期望的结果如下所示:
desired_output <- tibble::tribble(
~participant_id, ~timestamp, ~my_strings,
1L, 1L, "other_string",
1L, 2L, "other_string",
1L, 3L, "trial_end",
2L, 1L, "other_string",
2L, 2L, "other_string",
2L, 3L, "other_string",
2L, 4L, "other_string",
3L, 1L, "other_string",
3L, 2L, "trial_end"
)
您可以编写一个小的辅助函数来在检测到字符串后删除行,如果未检测到该字符串,它不会删除任何内容。
library(dplyr)
drop_string_after <- function(string_vec, string) {
i <- match(string, string_vec)
if(is.na(i)) seq_along(string_vec) else seq_len(i)
}
并为每个参与者应用此功能:
df1 %>%
group_by(participant_id) %>%
slice(drop_string_after(my_strings, 'trial_end')) %>%
ungroup
# participant_id timestamp my_strings
# <int> <int> <chr>
#1 1 1 other_string
#2 1 2 other_string
#3 1 3 trial_end
#4 2 1 other_string
#5 2 2 other_string
#6 2 3 other_string
#7 2 4 other_string
#8 3 1 other_string
#9 3 2 trial_end
要使用 filter
,您需要更改函数的 return 值。
drop_string_after <- function(string_vec, string) {
i <- match(string, string_vec)
if(is.na(i)) TRUE else row_number() <= i
}
df1 %>%
group_by(participant_id) %>%
filter(drop_string_after(my_strings, 'trial_end')) %>%
ungroup
您可以计算直到前一行的匹配项的累积总和,然后过滤以仅包括每个参与者到第一个匹配项的行:
df1 %>%
group_by(participant_id) %>%
filter(lag(cumsum(my_strings == "trial_end"), default = 0) < 1) %>%
ungroup()
# A tibble: 9 x 3
participant_id timestamp my_strings
<int> <int> <chr>
1 1 1 other_string
2 1 2 other_string
3 1 3 trial_end
4 2 1 other_string
5 2 2 other_string
6 2 3 other_string
7 2 4 other_string
8 3 1 other_string
9 3 2 trial_end
一个选项可以是:
df1 %>%
group_by(participant_id) %>%
slice(if(all(my_strings != "trial_end")) 1:n() else 1:which(my_strings == "trial_end"))
participant_id timestamp my_strings
<int> <int> <chr>
1 1 1 other_string
2 1 2 other_string
3 1 3 trial_end
4 2 1 other_string
5 2 2 other_string
6 2 3 other_string
7 2 4 other_string
8 3 1 other_string
9 3 2 trial_end
critpart <- 0
for (i in 1:nrow(df1)){
if (is.na(df1$participant_id[i])) next
if (df1$my_strings[i] == "trial_end"){
critpart <- df1$participant_id[i]
next
}
if (df1$participant_id[i] == critpart){
df1[i,] <- NA
}
}
df1 <- df1 %>% filter(!is.na(participant_id))
我有如下例所示的数据。对于每个参与者,如果特定字符串 ("trial_end"
) 出现在 my_strings
列中,我想在它出现后删除所有行。
library(dplyr)
library(stringr)
library(tibble)
df1 <- tibble::tribble(
~participant_id, ~timestamp, ~my_strings,
1L, 1L, "other_string",
1L, 2L, "other_string",
1L, 3L, "trial_end",
1L, 4L, "other_string",
2L, 1L, "other_string",
2L, 2L, "other_string",
2L, 3L, "other_string",
2L, 4L, "other_string",
3L, 1L, "other_string",
3L, 2L, "trial_end",
3L, 3L, "other_string",
3L, 4L, "other_string"
)
我的第一次尝试是使用 str_detect
查找字符串的存在,which
提供行号,然后使用 filter
仅保留该行和所有之前的那些:
df2 <- df1 %>%
group_by(participant_id) %>%
filter(row_number() < (which(str_detect(my_strings, "trial_end"))) + 1)
当未检测到字符串时,这似乎会引发错误(例如此处示例中的参与者 2)。
我的下一次尝试是添加条件 if_else
,试图有效地说 'IF the target string is detected THEN remove all the rows after for that participant, ELSE if the string is not detected, do nothing'.
df3 <- df1 %>%
group_by(participant_id) %>%
if_else(str_detect(my_strings, "trial_end"),
filter(row_number() < (which(str_detect(my_strings, "trial_end"))) + 1),
filter(timestamp < max(timestamp)))
这也返回了一个错误:
错误:condition
必须是逻辑向量,而不是 grouped_df/tbl_df/tbl/data.frame
对象。
我最后的尝试是通过将条件 if else 放在 filter
中来利用此处已有的另一个答案,但这也产生了错误。
df4 <- df1 %>%
group_by(participant_id) %>%
filter(if(str_detect(my_strings, "trial_end") < (which(str_detect(my_strings, "trial_end")) + 1))
else < n())
谁能指出解决这个问题的最佳方法? filter
是不是做错了?
非常感谢。
为清楚起见,期望的结果如下所示:
desired_output <- tibble::tribble(
~participant_id, ~timestamp, ~my_strings,
1L, 1L, "other_string",
1L, 2L, "other_string",
1L, 3L, "trial_end",
2L, 1L, "other_string",
2L, 2L, "other_string",
2L, 3L, "other_string",
2L, 4L, "other_string",
3L, 1L, "other_string",
3L, 2L, "trial_end"
)
您可以编写一个小的辅助函数来在检测到字符串后删除行,如果未检测到该字符串,它不会删除任何内容。
library(dplyr)
drop_string_after <- function(string_vec, string) {
i <- match(string, string_vec)
if(is.na(i)) seq_along(string_vec) else seq_len(i)
}
并为每个参与者应用此功能:
df1 %>%
group_by(participant_id) %>%
slice(drop_string_after(my_strings, 'trial_end')) %>%
ungroup
# participant_id timestamp my_strings
# <int> <int> <chr>
#1 1 1 other_string
#2 1 2 other_string
#3 1 3 trial_end
#4 2 1 other_string
#5 2 2 other_string
#6 2 3 other_string
#7 2 4 other_string
#8 3 1 other_string
#9 3 2 trial_end
要使用 filter
,您需要更改函数的 return 值。
drop_string_after <- function(string_vec, string) {
i <- match(string, string_vec)
if(is.na(i)) TRUE else row_number() <= i
}
df1 %>%
group_by(participant_id) %>%
filter(drop_string_after(my_strings, 'trial_end')) %>%
ungroup
您可以计算直到前一行的匹配项的累积总和,然后过滤以仅包括每个参与者到第一个匹配项的行:
df1 %>%
group_by(participant_id) %>%
filter(lag(cumsum(my_strings == "trial_end"), default = 0) < 1) %>%
ungroup()
# A tibble: 9 x 3
participant_id timestamp my_strings
<int> <int> <chr>
1 1 1 other_string
2 1 2 other_string
3 1 3 trial_end
4 2 1 other_string
5 2 2 other_string
6 2 3 other_string
7 2 4 other_string
8 3 1 other_string
9 3 2 trial_end
一个选项可以是:
df1 %>%
group_by(participant_id) %>%
slice(if(all(my_strings != "trial_end")) 1:n() else 1:which(my_strings == "trial_end"))
participant_id timestamp my_strings
<int> <int> <chr>
1 1 1 other_string
2 1 2 other_string
3 1 3 trial_end
4 2 1 other_string
5 2 2 other_string
6 2 3 other_string
7 2 4 other_string
8 3 1 other_string
9 3 2 trial_end
critpart <- 0
for (i in 1:nrow(df1)){
if (is.na(df1$participant_id[i])) next
if (df1$my_strings[i] == "trial_end"){
critpart <- df1$participant_id[i]
next
}
if (df1$participant_id[i] == critpart){
df1[i,] <- NA
}
}
df1 <- df1 %>% filter(!is.na(participant_id))