У меня есть этот набор данных в R:
id = 1:5
col1 = c("12 ABC", "123", "AB", "123344567", "1345677.")
col2 = c("gggw", "12", "567", "abc 123", "p")
col3 = c("abw", "abi", "klo", "poy", "17df")
col4 = c("13 AB", "344", "Huh8", "98", "b")
my_data = data.frame(id, col1, col2, col3, col4)
id col1 col2 col3 col4
1 1 12 ABC gggw abw 13 AB
2 2 123 12 abi 344
3 3 AB 567 klo Huh8
4 4 123344567 abc 123 poy 98
5 5 1345677. p 17df b
Затем я использовал следующий код, чтобы проверить, содержит ли конкретная ячейка хотя бы одно число:
my_data$col1_check = grepl("\\d", my_data$col1)
my_data$col2_check = grepl("\\d", my_data$col2)
my_data$col3_check = grepl("\\d", my_data$col3)
my_data$col4_check = grepl("\\d", my_data$col4)
id col1 col2 col3 col4 col1_check col2_check col3_check col4_check
1 1 12 ABC gggw abw 13 AB TRUE FALSE FALSE TRUE
2 2 123 12 abi 344 TRUE TRUE FALSE TRUE
3 3 AB 567 klo Huh8 FALSE TRUE FALSE TRUE
4 4 123344567 abc 123 poy 98 TRUE TRUE FALSE TRUE
5 5 1345677. p 17df b TRUE FALSE TRUE FALSE
То, что я пытаюсь сделать, для каждой строки: я хотел бы взять все столбцы, в которых значение FALSE, и вставить (с пробелом) содержимое этих столбцов в одну ячейку.
Это будет выглядеть примерно так:
id new_col
1 1 gggw abw
2 2 abi
3 3 AB klo
4 4 poy
5 5 p b
Я пытался прочитать об «условной конкатенации» (например, условной конкатенации в R), но пока ничего из того, что я читал, не соответствует проблеме, над которой я работаю.
Может кто-нибудь предложить, что делать отсюда?
Спасибо!
Вот один из вариантов в tidyverse
- зациклить across
столбцы с col1 по col4, get
соответствующее значение из логического столбца, paste
введя _check
в имена столбцов (cur_column()
), преобразовать ИСТИННЫЕ значения в NA в case_when
и unite
эти столбцы в new_col
library(stringr)
library(dplyr)
library(tidyr)
my_data %>%
transmute(id, across(col1:col4,
~ case_when(!get(str_c(cur_column(), "_check"))~ .x))) %>%
unite(new_col, col1:col4, sep = " ", na.rm = TRUE)
-вывод
id new_col
1 1 gggw abw
2 2 abi
3 3 AB klo
4 4 poy
5 5 p b
Если мы хотим пропустить создание _check
, это будет проще, поскольку мы можем напрямую преобразовать ненужные элементы в NA
и unite
.
my_data %>%
mutate(across(col1:col4,
~ case_when(str_detect(.x, "\\d+", negate = TRUE) ~.x))) %>%
unite(new_col, col1:col4, sep = " ", na.rm = TRUE)
-вывод
id new_col
1 1 gggw abw
2 2 abi
3 3 AB klo
4 4 poy
5 5 p b
Или с помощью base R
cbind(my_data[1], new_col = gsub("\\s{2,}", " ",
trimws(do.call(paste, replace(my_data[2:5],
as.matrix(my_data[6:9]), '')))))
-вывод
id new_col
1 1 gggw abw
2 2 abi
3 3 AB klo
4 4 poy
5 5 p b
@TarJae str_c
добавит _check
к имени столбца, а get
вернет значение столбца.
@user5249203 user5249203 вы можете проверить опубликованное там решение
@akrun: большое спасибо за ответ! Можно ли пропустить шаг "col_check" и сделать все сразу?
@stats_noob уверен, это можно сделать
Начиная с my_data
вы можете использовать
library(dplyr)
library(tidyr)
library(stringr)
my_data %>%
pivot_longer(-id) %>%
filter(!str_detect(value, "\\d")) %>%
group_by(id) %>%
summarise(new_col = paste(value, collapse = " "))
Это возвращает
# A tibble: 5 × 2
id new_col
<int> <chr>
1 1 gggw abw
2 2 abi
3 3 AB klo
4 4 poy
5 5 p b
Базовый подход R
data.frame(id = my_data$id, new_col = apply(my_data[,-1], 1, function(x)
paste(x[!grepl("[[:digit:]]", x)], collapse = " ")))
id new_col
1 1 gggw abw
2 2 abi
3 3 AB klo
4 4 poy
5 5 p b
Я имел в виду точно такой же ответ! +1
Вы можете сделать это без использования какого-либо пакета. Это может выглядеть очень утомительно, но легко понять, если вы знаете функцию apply
:
data.frame(id, new_col = apply(my_data[, -1], 1, FUN = function(x) {
paste(x[!grepl("\\d", x)], collapse = " ") }))
my_data
id new_col
1 1 gggw abw
2 2 abi
3 3 AB klo
4 4 poy
5 5 p b
Обновлен улучшенный код (спасибо @Martin Gal)
library(tidyverse)
my_data %>%
transmute(across(-id, ~case_when(!str_detect(., '\\d') ~ .))) %>%
unite("New_col", col1:col4, na.rm = TRUE, sep = " ")
Еще одно: похоже на решение @akrun, но не идентично:
library(dplyr)
library(tidyr)
library(stringr)
my_data %>%
transmute(across(-id, ~case_when(!str_detect(., '\\d')== TRUE ~ .), .names = 'new_{col}')) %>%
unite(New_col, starts_with('new'), na.rm = TRUE, sep = ' ')
New_col
1 gggw abw
2 abi
3 AB klo
4 poy
5 p b
Вы можете опустить часть == TRUE
внутрь case_when
. Поскольку вы используете transmute
, вы можете даже удалить .names = 'new_{col}'
и starts_with('new')
. :)
@Мартин Гал. большое спасибо. буду обновлять! Это произошло потому, что предыдущая попытка была с мутацией!
Что вы с этим делаете
!get(str_c(cur_column(), "_check")~.x