Возьмите первое не 0 или последнее 0 значение, если это все, что есть - PullRequest
0 голосов
/ 09 октября 2018

Ciao,

Вот мой реплицирующий пример.

HAVE <- data.frame(ID=c(1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6),
                   ABSENCE=c(NA,NA,NA,0,0,0,0,0,1,NA,0,NA,0,1,2,0,0,0),
                   TIME=c(1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3))


WANT <- data.frame(ID=c(1,2,3,4,5,6),
                   ABSENCE=c(NA,0,1,0,1,0),
                   TIME=c(NA,3,3,2,2,3))

Файл с данными HAVE необходим для преобразования в WANT.Таким образом, по существу для каждого идентификатора мне нужно идентифицировать первое ненулевое значение, и это значение попадает в файл данных ХОЧУ.Если все значения отсутствия равны NA, тогда TIME равно NA.Если все значения ABSENCE равны 0, тогда я сообщаю о последней возможной строке в WANT (как отражено в переменной TIME)

Это моя попытка:

WANT <- group_by(HAVE,ID) %>% slice(seq_len(min(which(ABSENCE > 0), n())))

, но я не знаю, каквзять последнюю из 0 строк, если есть только 0.

Ответы [ 3 ]

0 голосов
/ 09 октября 2018

Также с использованием data.table на основе установки счетчика строк .I:

WANT <- HAVE[
  HAVE[,
    if(all(is.na(ABSENCE))) .I[1] else
    if(!any(ABSENCE > 0, na.rm=TRUE)) max(.I[ABSENCE==0], na.rm=TRUE) else
    min(.I[ABSENCE > 0], na.rm=TRUE),
    by=ID
  ]$V1,
]
WANT[is.na(ABSENCE), TIME := NA_integer_]

#   ID ABSENCE TIME
#1:  1      NA   NA
#2:  2       0    3
#3:  3       1    3
#4:  4       0    2
#5:  5       1    2
#6:  6       0    3
0 голосов
/ 09 октября 2018

Вот два подхода с использованием dplyr и пользовательских функций.И те, и другие полагаются на данные, сортируемые по TIME.

Подход фильтра

# We'll use this function inside filter() to keep only the desired rows
flag_wanted <- function(absence){

  flags <- rep(FALSE, length(absence))

  if (any(absence > 0, na.rm = TRUE)) {
  # There's a nonzero value somewhere in x; we want the first one.

    flags[which.max(absence > 0)] <- TRUE

  } else if (any(absence == 0, na.rm = TRUE)) {
  # There's a zero value somewhere in x; we want the last one.

    flags[max(which(absence == 0))] <- TRUE

  } else {
  # All values are NA; we want the last row

    flags[length(absence)] <- TRUE

  }
  return(flags) 
}

# After filtering, we have to flip TIME to NA if ABSENCE is NA
HAVE %>%
  arrange(ID, TIME) %>%
  group_by(ID) %>%
  filter(flag_wanted(ABSENCE)) %>%
  mutate(TIME = ifelse(is.na(ABSENCE), NA, TIME)) %>%
  ungroup()

# A tibble: 6 x 3
     ID ABSENCE  TIME
  <dbl>   <dbl> <dbl>
1    1.     NA    NA 
2    2.      0.    3.
3    3.      1.    3.
4    4.      0.    2.
5    5.      1.    2.
6    6.      0.    3.

Шаг filter() сокращает фрейм данных до нужных вам строк.Поскольку он не изменяет значения TIME, нам также необходимо mutate().

Подход суммирования

# This function captures the general logic of getting the value of one variable
# based on the value of another
get_wanted <- function(of_this, by_this){

  # If there are any positive values of `by_this`, use the first
  if (any(by_this > 0, na.rm = TRUE)) {

    return( of_this[ which.max(by_this > 0) ] )

  }

  # If there are any zero values of `by_this`, use the last
  if (any(by_this == 0, na.rm = TRUE)) {

    return( of_this[ max(which(by_this == 0)) ] )

  }  
  # Otherwise, use NA
  return(NA)     
}

HAVE %>%
  arrange(ID, TIME) %>%
  group_by(ID) %>%
  summarize(TIME = get_first_nz(of_this = TIME, by_this = ABSENCE),
            ABSENCE = get_first_nz(of_this = ABSENCE, by_this = ABSENCE))

# A tibble: 6 x 3
     ID  TIME ABSENCE
  <dbl> <dbl>   <dbl>
1    1.   NA      NA 
2    2.    3.      0.
3    3.    3.      1.
4    4.    2.      0.
5    5.    2.      1.
6    6.    3.      0.

Порядок суммирования имеет значение, поскольку мы перезаписываем переменные, поэтомуподход опасен.Он выдает только WANT, если вы суммируете TIME, а затем ABSENCE.

0 голосов
/ 09 октября 2018
library(data.table)
setDT(HAVE)

res = unique(HAVE[, .(ID)])

# look up first ABSENCE > 0
res[, c("ABSENCE", "TIME") := unique(HAVE[ABSENCE > 0], by="ID")[.SD, on=.(ID), .(ABSENCE, TIME)]]

# if nothing was found, look up last ABSENCE == 0
res[is.na(ABSENCE), c("ABSENCE", "TIME") := unique(HAVE[ABSENCE == 0], by="ID", fromLast=TRUE)[.SD, on=.(ID), .(ABSENCE, TIME)]]

# check
all.equal(as.data.frame(res), WANT)
# [1] TRUE

   ID ABSENCE TIME
1:  1      NA   NA
2:  2       0    3
3:  3       1    3
4:  4       0    2
5:  5       1    2
6:  6       0    3

Я использую data.table, поскольку tidyverse не и никогда не будет поддерживать под-присваивание / изменение только строк, выбранных условием (например, is.na(ABSENCE) здесь).

Если есть два правила, которые можно сделать более согласованными друг с другом, это должно быть выполнимо при левом соединении или отдельном срезе group_by + при попытке OP, однако.Хорошо, вот один из способов, хотя отладка выглядит невозможной:

HAVE %>% 
  arrange(ID, -(ABSENCE > 0), TIME*(ABSENCE > 0), -TIME) %>% 
  distinct(ID, .keep_all = TRUE)

  ID ABSENCE TIME
1  1      NA    3
2  2       0    3
3  3       1    3
4  4       0    2
5  5       1    2
6  6       0    3
...