Заменить значения столбцов фрейма данных пользовательской функцией в R

#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