Как мне удалить строку, содержащую NA, если NAS разрешены до того, как пользователь введет выборку?

#r #dataframe #subset

#r #фрейм данных #подмножество

Вопрос:

Я работаю с набором данных панели, содержащим годы 1-12. Если пользователь введет в year 4, его значения для Weight будут NA до year 4. Я хочу удалить эту строку (person) только в том случае, если после 4 года появятся какие-либо NA значения.

 data : Weight_Y1   W_Y2    W_Y3    W_Y4    W_Y5    W_Y6    W_Y7    W_8     W_9 ...
Ind_1    NA        NA       NA     82kg   81       83      84      NA      86
  

Этот пользователь должен быть удален. Итак, как только наблюдение зарегистрировано как активное в опросе, NA оно не разрешено для следующих наблюдений. Кроме того, Weight у меня также есть другие переменные (столбцы), поэтому мне нужно сделать это и для определенных столбцов.

Комментарии:

1. допустим, x <- data.frame(x = 1:10) теперь у вас есть dataframe, чтобы поделиться им с другими, сделайте это dput(x) в вашей консоли R и нажмите enter. Скопируйте выходные данные и используйте их, чтобы поделиться ими с другими. Итак, для этого примера вы получаете structure(list(x = 1:10), class = "data.frame", row.names = c(NA, -10L)) . Итак, теперь мы копируем это и выполняем x <- structure(list(x = 1:10), class = "data.frame", row.names = c(NA, -10L)) в нашей консоли, и у нас есть те же данные, x что и у вас. Это лучший способ обмена данными.

2. аааа, хорошо, мы проходим через R. Я подумал, может быть, Stack Overflow может перевести это напрямую. Кстати, я скоро задам вопрос о melt команде из reshape2 пакета. Мне нужно добавить переменную времени в мой набор данных и изменить его формат из широкого в длинный.

Ответ №1:

Вариант с apply использованием MARGIN = 1 по строкам

 #Select columns based on pattern in the weight column
cols <- grep("^W", names(df))

#Select rows only if there is no NA after the first non-NA is encountered.
df[!apply(df[cols], 1, function(x) any(which(is.na(x)) > which.max(!is.na(x)))), ]

#   data W_Y1 W_Y2 W_Y3 W_Y4 W_Y5 W_Y6 W_Y7 W_8 W_9
#2 Ind_2   NA   NA   NA   82   81   83   84  65  86
  

Используя аналогичную логику, но с mapply и max.col

 df[mapply(function(x, y) !any(which(is.na(df[x, cols])) > y),1:nrow(df),
       max.col(!is.na(df[cols]), ties.method = "first")), ]
  

Используя max.col , мы находим индекс первого значения, отличного от NA, в cols , а затем проверяем, есть ли в этой строке какое-либо значение, которое имеет NA после этого индекса.

данные

Я добавил несколько строк, чтобы сделать пример лучше

 df <- structure(list(data = structure(1:4, .Label = c("Ind_1", "Ind_2", 
"Ind_3", "Ind_4"), class = "factor"), W_Y1 = c(NA, NA, NA, NA
), W_Y2 = c(NA, NA, NA, 23L), W_Y3 = c(NA, NA, NA, NA), W_Y4 = c(82L, 
82L, 82L, 82L), W_Y5 = c(81L, 81L, 81L, 81L), W_Y6 = c(83L, 83L, 
83L, 83L), W_Y7 = c(84L, 84L, NA, 84L), W_8 = c(NA, 65L, NA, 
12L), W_9 = c(86L, 86L, 86L, 86L)), class = "data.frame", row.names = c(NA, 
-4L))

df
#   data W_Y1 W_Y2 W_Y3 W_Y4 W_Y5 W_Y6 W_Y7 W_8 W_9
#1 Ind_1   NA   NA   NA   82   81   83   84  NA  86
#2 Ind_2   NA   NA   NA   82   81   83   84  65  86
#3 Ind_3   NA   NA   NA   82   81   83   NA  NA  86
#4 Ind_4   NA   23   NA   82   81   83   84  12  86
  

Комментарии:

1. У меня есть два вопроса. Что вы имеете в виду под MARGIN = 1 ? Вы используете эту команду? Я не понимаю, меня также смущает использование первой строки grep : что означает » ^ » перед именем переменной? Моими переменными являются fx r1srhlt - r12rsrhlt , и r1diabets - r12diabetes . Стал бы я тогда писать: colsdiabetes <--grep("^diab", names(df))

2. @EmilKrabbe «^» означает, что начинается с, поэтому, если все столбцы с весом начинаются с r, выполните cols <- grep("^r", names(df))

3. хотя все мои переменные называются «r1 — r12». Итак, например, у меня есть r1height — r12height, но также r1weight-r12weight и r1bmi — r12 bmi. что я могу сделать тогда? Есть ли команда ends with ? все мои окончания относятся к переменным

4. итак, какую переменную вы хотите выбрать в качестве веса? В моем примере у меня есть W_Y1 , W_Y2 в качестве весов. Какой из них вы хотите рассмотреть? r1height , r2height единицы? или строку с весом, или строку с ИМТ, или все они??

5. @EmilKrabbe итак, сначала для роста сделайте cols <- grep("height$", names(df)) , а затем сделайте df[!apply(df[cols], 1, function(x) any(which(is.na(x)) > which.max(!is.na(x)))), ] , а затем для веса просто измените cols на cols <- grep("weight$", names(df)) и используйте ту же apply функцию.

Ответ №2:

tidyverse решение:

 library(tidyverse)

df %>%
  gather(year, weight, W_Y1:W_Y12) %>%
  group_by(data) %>%
  mutate(
    cond = max(which(is.na(weight))) < min(which(!is.na(weight))),
    year = year %>% reorder(str_extract(year, '\d ') %>% as.numeric()) # just to keep right order of columns after 'spread()'
  ) %>%
  spread(year, weight) %>%
  filter(cond)
  

Ответ №3:

Мы можем проверить наличие NAs, используя base::rle затем фильтровать, когда NAs произошел на один или меньше

 flag<-apply(df[,grepl('^W',names(df))],1, function(x) sum(rle(is.na(x))$values))
df[df$flag<=1,]