Как найти наиболее частое значение из нескольких столбцов в кадре данных - PullRequest
3 голосов
/ 19 апреля 2019

У меня есть фрейм данных следующим образом:

S A B C D E 
1 N N N N N
2 N Y Y N N
3 Y N Y N N
4 Y N Y Y Y

где мне нужно создать новый столбец F, который содержит большинство символов вхождения из нескольких столбцов A, B, C, D и E?

Вывод должен выглядеть следующим образом:

 S A B C D E F
 1 N N N N N N
 2 N Y Y N N N
 3 Y N Y N N N
 4 Y N Y Y Y Y

Ответы [ 4 ]

5 голосов
/ 19 апреля 2019

Мы можем создать функцию Mode и применить ее к строкам

df1$F <- apply(df1[-1], 1, Mode)
df1
#  S A B C D E F
#1 1 N N N N N N
#2 2 N Y Y N N N
#3 3 Y N Y N N N
#4 4 Y N Y Y Y Y

. Или другой вариант:

df1$F <- c('N', 'Y')[max.col(table(c(row(df1[-1])), unlist(df1[-1])), 'first')]

, где

Mode <- function(x) {
 ux <- unique(x)
 ux[which.max(tabulate(match(x, ux)))]
}

Или используя tidyverse

library(tidyverse)
df1 %>% 
    mutate(F = pmap_chr(.[-1], ~ Mode(c(...))))

Или другой вариант:

gather(df1, key, F, - S) %>% 
     group_by(S, F) %>% 
     summarise(n = n()) %>% 
     slice(which.max(n)) %>% 
     ungroup %>% 
     dplyr::select(F) %>% 
     bind_cols(df1, .)

Или мы транспонируем набор данных, применяя Mode для каждогостолбец, а затем связать вывод как новый столбец с исходным набором данных

t(df1[-1]) %>%
   as.data.frame %>% 
   summarise_all(Mode) %>% 
   unlist %>%
   bind_cols(df1, F = .)

или с параметром data.table

library(data.table)
setDT(df1)[,  F := names(which.max(table(unlist(.SD)))), S][]

ПРИМЕЧАНИЕ. Это общие методы, а не просто проверкав одном случае


Если нам нужен эффективный метод, без каких-либо ifelse, мы также можем сделать это с помощью

df1$F <- c("Y", "N")[(rowSums(df1[-1] == "N") > 2) + 1]
df1$F
#[1] "N" "N" "N" "Y"

или с помощью Reduce

c("Y", "N")[(Reduce(`+`, lapply(df1[-1], `==`, "N")) > 2) + 1]

Или другой подход

c("Y", "N")[(str_count(do.call(paste0, df1[-1]), "N") > 2) + 1]

data

df1 <- structure(list(S = 1:4, A = c("N", "N", "Y", "Y"), B = c("N", 
"Y", "N", "N"), C = c("N", "Y", "Y", "Y"), D = c("N", "N", "N", 
"Y"), E = c("N", "N", "N", "Y")), class = "data.frame", row.names = c(NA, 
-4L))
3 голосов
/ 19 апреля 2019

Одна dplyr возможность может быть:

df %>%
 mutate(F = ifelse(rowSums(.[2:length(.)] == "N") > 2, "N", "Y"))

  S A B C D E F
1 1 N N N N N N
2 2 N Y Y N N N
3 3 Y N Y N N N
4 4 Y N Y Y Y Y

Предполагается, что есть только значения N и Y, а количество столбцов равно 5.

Как заметил @Sotos, его можно легко переписать в base R форму:

df$F <- ifelse(rowSums(df[2:length(df)] == "N") > 2, "N", "Y")

Или без предположения о количестве столбцов (на основе @TinglTanglBob):

df %>%
 mutate(F = ifelse(rowMeans(.[2:length(.)] == "N") > 0.5, "N", "Y"))

То же самое с базой R:

df$F <- ifelse(rowMeans(df[2:length(df)] == "N") > 0.5, "N", "Y")
2 голосов
/ 19 апреля 2019

Альтернатива, немного другая:

x$F <- unlist(do.call(Map, c(function(...) names(sort(-table(c(...)), partial=1)[1]), x[,-1])))
x
#   S A B C D E F
# 1 1 N N N N N N
# 2 2 N Y Y N N N
# 3 3 Y N Y N N N
# 4 4 Y N Y Y Y Y

Возможно, я сейчас пытаюсь создать неясный код ...

Я понимаю, что это может быть более общим, чем абсолютно необходимым,Это находит наиболее частую «вещь» независимо от того, сколько разных вещей существует в строках.

sort(..., partial=1) прекращает сортировку после первого прохода.

1 голос
/ 19 апреля 2019

Или:

d <- read.table(text ="S A B C D E 
1 N N N N N
2 N Y Y N N
3 Y N Y N N
4 Y N Y Y Y", header = TRUE, row.names = 1, stringsAsFactors = FALSE)

d$F <- with(
  stack(data.frame(t(as.matrix(d)), stringsAsFactors = FALSE)),
  tapply(values, ind, function(x) names(sort(table(x), decreasing = TRUE)[1])))
d

#A B C D E F
#1 N N N N N N
#2 N Y Y N N N
#3 Y N Y N N N
#4 Y N Y Y Y Y
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...