#r #dataframe #dplyr #user-defined-functions
#r #фрейм данных #dplyr #определяемые пользователем функции
Вопрос:
У меня есть сгруппированный набор значений в столбце, который я пытаюсь заменить значением since
col1
a
a;a;b;c
c;b;a
NA
b;b;b
Я хочу заменить все значения на одно mixed
или единственное текущее значение, если, например a;a;a;a
, становится a
Ожидаемый результат
col1
a
Mixed
Mixed
NA
b
Код
grouping = function(x){
y = as.list(strsplit(x, ";")[[1]])
#select first element, and test if each is the same element.
z = ""
for (i in 1:length(y)){
if (as.character(y[1]) != as.character(y[i])) {
z = 'mixed'
break
} else {
z = as.character(y[1])
}
}
return(z)
}
db %>%
select(col1) %>%
mutate(
test = grouping(col1)
)
Я пробовал это несколькими разными способами и либо в итоге он вообще не работает, либо выдает значение a
для всего
Комментарии:
1. Хороший вызов, обновлен
Ответ №1:
Базовый параметр R через определение пользовательской функции f
f <- function(x) ifelse(length(u <- unique(unlist((strsplit(x, ";"))))) > 1, "Mixed", u)
такой, что
> transform(df, col1 = Vectorize(f)(col1))
col1
1 a
2 Mixed
3 Mixed
4 <NA>
5 b
Ответ №2:
Вы также можете рассмотреть это для своей функции и использовать base R
:
#Function
myfun <- function(x)
{
y <- unlist(strsplit(x, ";"))
if(length(unique(y))==1)
{
z <- unique(y)
} else
{
z <- 'Mixed'
}
}
#Apply
df$New <- apply(df,1,myfun)
Вывод:
df
col1 New
1 a a
2 a;a;b;c Mixed
3 c;b;a Mixed
4 <NA> <NA>
5 b;b;b b
Некоторые используемые данные:
#Data
df <- structure(list(col1 = c("a", "a;a;b;c", "c;b;a", NA, "b;b;b")), class = "data.frame", row.names = c(NA,
-5L))
Ответ №3:
Мы можем извлечь подстроку из ‘col1’, которая представляет собой буквы, проверить количество отдельных элементов n_distinct
, использовать case_when
для изменения тех, которые имеют более одного уникального элемента, на ‘Mixed’
library(dplyr)
library(stringr)
library(purrr)
df1 %>%
mutate(col1 = case_when(map_dbl(str_extract_all(col1,
"[a-z]"), n_distinct) >1 ~ "Mixed",
is.na(col) ~ NA_character_,
TRUE ~ substr(col1, 1, 1)))
-вывод
# col1
#1 a
#2 Mixed
#3 Mixed
#4 <NA>
#5 b
Или другой вариант — разделить столбец по разделителю с separate_rows
помощью и выполнить группировку по row_number
summarise
элементам, имеющим более одной строки (после distinct
), которые будут «смешаны»
library(tidyr)
df1 %>%
mutate(rn = row_number()) %>%
separate_rows(col1) %>%
distinct() %>%
group_by(rn) %>%
summarise(col1 = case_when(n() > 1 ~ 'Mixed', TRUE ~ first(col1)),
.groups = 'drop') %>%
select(-rn)
-вывод
# A tibble: 5 x 1
# col1
# <chr>
#1 a
#2 Mixed
#3 Mixed
#4 <NA>
#5 b
Или base R
с помощью опции compact
v1 <- gsub("([a-z])\1 ", "\1", gsub(";", "", df1$col1))
replace(v1, nchar(v1) > 1, "Mixed")
#[1] "a" "Mixed" "Mixed" NA "b"
Проблема в функции OP заключается в том, что она извлекает только первый [[1]]
list
элемент
as.list(strsplit(x, ";")[[1]])
as strsplit
возвращает значение a list
, length
равное количеству строк исходных данных. Итак, в основном, выбирая только первое, оно перерабатывается
данные
df1 <- structure(list(col1 = c("a", "a;a;b;c", "c;b;a", NA, "b;b;b")),
class = "data.frame", row.names = c(NA,
-5L))
Ответ №4:
Вы можете записать grouping
функцию как :
grouping <- function(x) {
sapply(strsplit(x, ';'), function(x)
if(length(unique(x)) == 1) unique(x) else 'Mixed')
}
db$test <- grouping(db$col1)
db
# col1 test
#1 a a
#2 a;a;b;c Mixed
#3 c;b;a Mixed
#4 <NA> <NA>
#5 b;b;b b