Уникальные значения строк

Я часто сталкиваюсь с данными, которые выглядят так:

#create dummy data frame
data <- as.data.frame(diag(4))
data[data==0] <- NA
data[2,2] <- NA
data

#V1 V2 V3 V4
#1  1 NA NA NA
#2 NA NA NA NA
#3 NA NA  1 NA
#4 NA NA NA  1

Строки представляют участников, а столбцы с V1 по V4 представляют условие, в котором находится участник (например, 1 под V1 означает, что этот участник находится в состоянии 1, 1 под V4 означает, что этот участник находится в состоянии 4). Sidenote: данные не являются симметричными, поэтому число участников в четырех условиях намного больше.

То, что я хочу, это вектор с условием для каждого участника:

1 NA  3  4

Я написал следующий бит, но мне было интересно, если есть более эффективный способ (то есть, используя меньше строк кода)?

#replace entries with condition numbers 
cond <- data + matrix(rep(0:3, 4), 4, byrow=TRUE) #add 0 to 1 for condition 1...

#get all unique elements (ignore NAs)
cond <- apply(cond, 1, function(x)unique(x[!is.na(x)]))

#because I ignored NAs just now, cond[2,2] is numeric(0)
#assign NA to all values that are numeric(0)
cond[sapply(cond, function(x) length(x)==0)] <- NA

cond <- unlist(cond)
cond
#[1]  1 NA  3  4

3 ответа

Решение

Мы можем использовать max.col с ties.method='first' на логической матрице не-NA элементов в "данных". Чтобы сделать строки, которые имеют только элементы NA как NA, мы умножаем max.col индекс с rowSums логической матрицы с 0 не-NA строками, преобразованными в NA (NA^).

 max.col(!is.na(data), 'first')* NA^!rowSums(!is.na(data))
 #[1]  1 NA  3  4

Или другой вариант pmax, Мы умножаем индекс столбца с данными, чтобы элементы, не являющиеся NA, заменялись индексом. Затем используйте pmax с na.rm=TRUE и получить максимальное значение для каждой строки.

 do.call(pmax, c(col(data)*data, na.rm=TRUE))
 #[1]  1 NA  3  4

Менее умный и эффективный, чем другие решения, но, возможно, более читаемый?

apply(data,
      MARGIN = 1, 
      FUN = function(x) { 
          if(all(is.na(x))) return(NA)
          return(which(!is.na(x)))
      }
)
# [1]  1 NA  3  4

С использованием reshape2 пакет:

> data$ID <- rownames(data)
> melt(data, 'ID', na.rm=TRUE)
   ID variable value
1   1       V1     1
11  3       V3     1
16  4       V4     1

ИМХО, это имеет преимущество в сохранении переменной ID вместе с фактором лечения; также, если у вас есть измерение отклика, оно также появляется в столбце значений.

РЕДАКТИРОВАТЬ:

Если вы хотите включить тему ни при каких условиях, вы можете явно восстановить эту индикаторную переменную:

data$VNA <- ifelse(apply(is.na(data), 1, all), 1, NA)
Другие вопросы по тегам