У меня есть data.frame dt_info, который выглядит следующим образом:

date       col_1     col_2    col_3  ... col_100
d/m/y       info1     n/a      n/a   ...  n/a
d/m/y       n/a       n/a     info2  ...  n/a
d/m/y       n/a       n/a       n/a  ...  infon

Результат, который я ищу, должен быть таким:

date       col_1     col_2    col_3  ... col_100    new_col
d/m/y       info1     n/a      n/a   ...  n/a         info1
d/m/y       n/a       n/a     info2  ...  n/a         info2
d/m/y       n/a       n/a       n/a  ...  infon       infon

Я использую эту строку кода

dt_info$new_col <- apply(dt_info[2],1, function(x){
ifelse(x != "n/a", x, "")})

Я делаю то, что хочу, но у меня есть две проблемы Я не могу сделать это для каждого столбца, используя [1:n] Если я вручную изменяю индекс [] и запускаю код, весь столбец заменяется.

Чего мне не хватает в моем сценарии? Есть ли другой способ добиться того, что я пытаюсь сделать?

1
Alejo Vlsqz 31 Май 2020 в 09:00

5 ответов

Лучший ответ

Если значение n/a действительно является значением NA для R, то вы можете использовать coalesce здесь из библиотеки dplyr

library(dplyr_
dt_info$new_col <- coalesce(dt_info$col_1, dt_info$col_2, ..., dt_info$col_100)

Если значение n/a находится буквально там, то вы можете сначала преобразовать его в NA, а затем использовать coalesce, как указано выше:

dt_info[dt_info == "n/a"] = NA
dt_info$new_col <- coalesce(dt_info$col_1, dt_info$col_2, ..., dt_info$col_100)
1
Tim Biegeleisen 31 Май 2020 в 06:12

Вы можете легко подстроить свои данные построчно для элементов, которые не являются (!) %in% "n/a".

dt_info$new_col <- apply(dt_info[-1], 1, function(x) x[!x %in% "n/a"])
dt_info
#    date col_1 col_2 col_3 col_100 new_col
# 1 d/m/y info1   n/a   n/a     n/a   info1
# 2 d/m/y   n/a   n/a info2     n/a   info2
# 3 d/m/y   n/a   n/a   n/a   infon   infon

Однако строки "n/a" немного странные; вы могли бы сделать их "истинными" NA,

dt_info2 <- as.data.frame(lapply(dt_info, function(x) {x[x %in% "n/a"] <- NA;x}))

И используйте na.omit тогда, что гораздо эффективнее.

dt_info2$new_col <- apply(dt_info2[-1], 1, na.omit)
#    date col_1 col_2 col_3 col_100 new_col
# 1 d/m/y info1  <NA>  <NA>    <NA>   info1
# 2 d/m/y  <NA>  <NA> info2    <NA>   info2
# 3 d/m/y  <NA>  <NA>  <NA>   infon   infon

< EM> Данные:

dt_info <- read.table(header=TRUE, text="date       col_1     col_2    col_3   col_100
'd/m/y'       info1     'n/a'      'n/a'     'n/a'
'd/m/y'       'n/a'       'n/a'     info2    'n/a'
'd/m/y'       'n/a'       'n/a'       'n/a'    infon")
0
jay.sf 31 Май 2020 в 06:57

Вы были близки, функция apply принимает аргумент MARGIN =, когда вы устанавливаете его в MARGIN = 1, вы можете применять свои функции к строкам, поэтому вам не нужно передавать свои индексы. Когда вы используете ifelse, происходит то, что вы получаете вектор length = ncol(dt_info), который вы пытаетесь присвоить записи length = 1. Чтобы избежать этого, вам нужно объединить его в одно значение - name или "":

    dt_info$new_col <- apply(dt_info,1, function(x){
                                                    if(sum(x != "n/a")){
                                                        x[x!="n/a"]
                                                    }else{
                                                     ""
                                                    }})
0
dvd280 31 Май 2020 в 06:17

Вот решение с использованием tidyverse и gsub

library(dplyr)
library(tidyr)
df1<- df %>% 
  dplyr::select(-date) %>%
  tidyr::unite(newCol,everything(),remove = FALSE) %>%  # concat all columsn except date
  dplyr::mutate(newCol=gsub("n/a","",newCol), # removing n/a as text
                newCol=gsub("\\_","\\1",newCol)) %>% # removing all '_' cretaed during concat 
  dplyr::bind_cols(date=df$date) # merging date column back

df1

   newCol col_1 col_2 col_3 col_100  date
1:  info1 info1   n/a   n/a     n/a d/m/y
2:  info2   n/a   n/a info2     n/a d/m/y
3:  infon   n/a   n/a   n/a   infon d/m/y
0
rj-nirbhay 31 Май 2020 в 06:28

Векторизованный способ с использованием max.col:

cols <- grep('col', names(dt_info))
dt_info$new_col <- dt_info[cols][cbind(1:nrow(dt_info), 
                       max.col(dt_info[cols] != 'n/a', ties.method = 'first'))]

Предполагая, что у вас всегда будет не - 'n/a' значение в строке, будет возвращено первое из них.

0
Ronak Shah 31 Май 2020 в 06:53