#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
: что означает »^
» перед именем переменной? Моими переменными являются fxr1srhlt - 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,]