2014-09-22 4 views
4

У меня есть кадр данных в R, который выглядит следующим образомR, как объединить различные столбцы кадра данных в одном

data 
x1 x2 x3a x3b x3c x3d x4 
A 43 0 0 0 1 P 
B 23 0 1 0 0 Q 
C 11 0 0 0 0 R 
D 66 0 0 1 0 S 

Теперь я хочу, чтобы объединить столбцы x3a, x3b, x3c, x3d в один столбец. Ожидаемый одиночный столбец будет содержать номер столбца со значением 1 из x3a,x3b,x3c,x3d. Значение должно быть пронумеровано (x3a=1,x3b=2,x3c=3,x3d=4). Ожидать результат будет похож следующий

x3 
[1] 4 2 0 3 

Я попытался reshape() функцию, но не мог получить то, что я на самом деле хотел

q<-data[,3:6] 
r<-reshape(q,varying=c("x3a","x3b","x3c","x3d"),v.names="x3",direction="long",times=c("x3a","x3b","x3c","x3d")) 
final<-r[r$x3!=0,][,3] 

Но это не дало ожидаемого результата. Он пропустил значение 0 между 2 и 3:

final 
[1]4 2 3 
+1

являются те, на ряд уникальных? – Barranka

ответ

10

Это сработало:

data <- data.frame(
    x1 = c('A','B','C','D'), 
    x2 = c(43,23,11,66), 
    x3a = c(0,0,0,0), 
    x3b = c(0,1,0,0), 
    x3c = c(0,0,0,1), 
    x3d = c(1,0,0,0), 
    x4 = c('P','Q','R','S') 
) 
data$x3 <- as.matrix(data[,c('x3a','x3b','x3c','x3d')]) %*% c(1,2,3,4) 

Результат:

x1 x2 x3a x3b x3c x3d x4 x3 
1 A 43 0 0 0 1 P 4 
2 B 23 0 1 0 0 Q 2 
3 C 11 0 0 0 0 R 0 
4 D 66 0 0 1 0 S 3 

Чейз сделал комментарий, что имеет отношение: Что если x3a ... x3d отличаются от нуля или одного? Вы можете использовать ifelse() считать, что сценарий:

data$x3 <- as.matrix(ifelse(data[,c('x3a','x3b','x3c','x3d')] > 0, 1, 0)) %*% c(1,2,3,4) 
+1

умный! Учтите, что это предполагает, что значения в 'x3a - x3d' всегда будут равны 1, что в данном случае кажется безопасным. – Chase

+0

@Chase Это все, что я могу представить с данными, представленными в OP (переменные индикатора) ... Но может быть сделана простая модификация для поддержки любого состояния – Barranka

+1

Да, я согласен - в основном, просто отмечая, что для потомков/если данные OPs отличается от того, что было указано. Опять же, очень умное решение! – Chase

5

@Barrankas ответ очень умный, а также векторный, вот менее умными/Векторизованные варианты

as.numeric(apply(data[, 3:6], 1, function(x) which(x == 1))) 
## [1] 4 2 NA 3 
5

Использованием row и col индексации. Должен быть быстрым, поскольку вы назначаете только один раз.

data$new <- 0 
tmp <- data[3:6]==1 
data$new[ row(tmp)[tmp] ] <- col(tmp)[tmp] 

data 

# x1 x2 x3a x3b x3c x3d x4 new 
#1 A 43 0 0 0 1 P 4 
#2 B 23 0 1 0 0 Q 2 
#3 C 11 0 0 0 0 R 0 
#4 D 66 0 0 1 0 S 3 

tmp может быть изменен, чтобы приспособить то, что логическое сравнение требуется.

+1

Наконец, использование для 'row()' и 'col()' !! –

+0

@RichardScriven - очень недоиспользуемые функции для такого рода задач. – thelatemail

+0

Обычно я всегда даю им попробовать в матричных индексах, но я всегда в растерянности. ха-ха –

1

еще одно: Вы можете использовать match по рядам, с множеством nomatch аргумента к нулю

apply(df[-c(1,2,length(df))] == 1, 1, match, x = TRUE, nomatch = 0L) 
# [1] 4 2 0 3 
4

Даже если вопрос получил оптимальное решение, я бы просто добавить ответ, основанный на возможно не очень известная функция max.col, которая дает индекс столбца максимального элемента строки. В этом случае:

data$x3 <- max.col(data[,3:6])*as.logical(rowSums(data[,3:6])) 

Этот вызов дает 0, когда все элементы равны 0s (если требуется) и максимальный индекс в противном случае. Преимущество состоит в том, что значения могут быть любыми. Если есть галстук, max.col выбирает случайный столбец по умолчанию; вы можете установить его также как первый, так и последний максимум.

0

Другим способом было бы использовать unite из tidyr

library(dplyr) 
library(tidyr) 

dat1 <- data 

data$x3 <- unite(data[,3:6], x3, sep="") %>% #I could use starts_with("x3"), but it adds more characters 
       mutate(x3 = sub("-\\d", "0", paste(gregexpr("[^0]", x3)))) #x3 is character column 
data 
    x1 x2 x3a x3b x3c x3d x4 x3 
#1 A 43 0 0 0 1 P 4 
#2 B 23 0 1 0 0 Q 2 
#3 C 11 0 0 0 0 R 0 
#4 D 66 0 0 1 0 S 3 

Предположим, у вас есть больше, чем один матч на строку и значение является иным, чем 0

dat1$x3c[2] <- 3 
dat1$x3 <- unite(dat1[,3:6], x3, sep="") %>% 
         mutate(x3 = sub("-\\d", "0", paste(gregexpr("[^0]", x3)))) 


dat1 
# x1 x2 x3a x3b x3c x3d x4 x3 
#1 A 43 0 0 0 1 P 4 
#2 B 23 0 1 3 0 Q 2:3 
#3 C 11 0 0 0 0 R 0 
#4 D 66 0 0 1 0 S 3 
Смежные вопросы