Как заполнить NAs хронологическим списком в R?

#r #list #dplyr #na

Вопрос:

Допустим, у меня есть хронологический список доходов домашних хозяйств и база данных идентификаторов городов с доходами домашних хозяйств, но есть некоторые NAS, которые я хочу заполнить.

 HouseholdIncome_list <- c(10000, 20000, 30000,40000,50000, 60000, 70000) 
                                                 
Town_ID <- c("A", "A", "A", "A", "B", "B", "B", "B", "B")
HouseholdIncome <- c(10000, 40000, 50000, NA, 20000, 40000, NA, NA, 60000)

df <- data.frame(Town_ID, HouseholdIncome)

  Town_ID HouseholdIncome
1       A           10000
2       A           40000
3       A           50000
4       A              NA
5       B           20000
6       B           40000
7       B              NA
8       B              NA
9       B           60000
 

Как заполнить NAs во фрейме данных, чтобы отсутствующие значения были теми, которые указаны в списке. Таким образом, это выглядит как df ниже

   Town_ID HouseholdIncome
1       A           10000
2       A           40000
3       A           50000
4       A           60000
5       B           20000
6       B           40000
7       B           50000
8       B           50000
9       B           60000
 

Я потратил время на поиск какого-то варианта заполнения, но не могу найти тот, который соответствует заданному списку

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

1. Как вы HouseholdIncome_list связаны с вашим HouseholdIncome ? Первое отсутствующее значение в A заменяется на 60000, а остальные-на 50000? На данный момент это кажется мне немного случайным.

2. @Serkan, это следующее значение в списке, поэтому в случае замены A на 60000 это следующее значение в списке доходов домохозяйства после 50000. Для B 50000 заменяет NA, потому что в списке HouseholdIncome_list он находится между 40000 и 60000. Поэтому я хочу заполнить NAs следующим или предыдущим значением, как указано в списке HouseholdIncome_list

3. Хорошо, смотрите мой ответ. Это простое решение, которое должно сработать для вас.

Ответ №1:

вот еще один подход, основанный на соединениях, который также будет вменять первое значение группы в случае, если оно отсутствует:

 library(tidyverse)

rdf <- data.frame(HouseholdIncome_list = c(10000, 20000, 30000,40000,50000, 60000, 70000)) %>%
    dplyr::mutate(rn = as.double(dplyr::row_number()))
                                             
df <- data.frame(Town_ID = c("A", "A", "A", "A", "B", "B", "B", "B", "B"),
                 HouseholdIncome = c(10000, 40000, 50000, NA, 20000, 40000, NA, NA, 60000))

df %>%
    dplyr::left_join(rdf, by = c("HouseholdIncome" = "HouseholdIncome_list")) %>%
    dplyr::group_by(Town_ID) %>%
    tidyr::fill(rn, .direction = "down") %>%
    tidyr::fill(rn, .direction = "up") %>%
    dplyr::mutate(rn2 = dplyr::row_number()) %>%
    dplyr::ungroup() %>% 
    dplyr::mutate(rn = case_when(is.na(HouseholdIncome) amp; rn2 == 1 amp; rn == min(rdf$rn) ~ rn,
                                 is.na(HouseholdIncome) amp; rn2 == 1 ~ rn - 1,
                                 is.na(HouseholdIncome) amp; rn < max(rdf$rn) ~ rn   1,
                                 TRUE ~ rn)) %>%
    dplyr::left_join(rdf, by = "rn") %>%
    select(Town_ID, HouseholdIncome = HouseholdIncome_list)

# A tibble: 9 x 2
  Town_ID HouseholdIncome
  <chr>             <dbl>
1 A                 10000
2 A                 40000
3 A                 50000
4 A                 60000
5 B                 20000
6 B                 40000
7 B                 50000
8 B                 50000
9 B                 60000
 

Ответ №2:

Это ужасное решение, но оно сделает вашу работу.

 library(tidyr)
library(dplyr)

df %>% 
  group_by(grp = cumsum(!is.na(HouseholdIncome))) %>% 
  rowwise() %>%
  mutate(Income = ifelse(length(which(HouseholdIncome_list == HouseholdIncome)) > 0,
    HouseholdIncome_list[which(HouseholdIncome_list == HouseholdIncome)   1],
    NA_real_)) %>% 
  ungroup() %>% 
  fill(Income) %>% 
  mutate(HouseholdIncome = ifelse(is.na(HouseholdIncome), Income, HouseholdIncome)) %>% 
  select(Town_ID, HouseholdIncome)
 

ВОЗВРАТ

 # A tibble: 9 x 2
  Town_ID HouseholdIncome
  <chr>             <dbl>
1 A                 10000
2 A                 40000
3 A                 50000
4 A                 60000
5 B                 20000
6 B                 40000
7 B                 50000
8 B                 50000
9 B                 60000
 

Если ваш первый пункт- NA это не сработает.

Ответ №3:

Возможный вариант базового R

 transform(
    df,
    HouseholdIncome = ave(
        HouseholdIncome,
        Town_ID,
        FUN = function(x) replace(x, is.na(x), x[min(which(is.na(x))) - 1]   1e4)
    )
)
 

дает

   Town_ID HouseholdIncome
1       A           10000
2       A           40000
3       A           50000
4       A           60000
5       B           20000
6       B           40000
7       B           50000
8       B           50000
9       B           60000
 

Ответ №4:

Я бы немного «схитрил», используя tidyverse . Очевидно, что доход домохозяйства находится в интервалах 10 000, и поэтому мы можем использовать это,

 df %>% mutate(
        is_na = as.numeric(is.na(HouseholdIncome)) * 10000
) %>% fill(
        HouseholdIncome, .direction = "down"
) %>% mutate(
        HouseholdIncome =(HouseholdIncome   is_na),
        is_na = NULL
)
 

Сначала мы проверяем наличие NA , здесь is_na = 1 * 10000 если TRUE , а затем используем fill для переноса последних значений вперед.

В конце концов sum мы изменяем переменную is_na и HouseholdIncome получаем следующий HouseholdIncome интервал.

В результате получается следующее,

   Town_ID HouseholdIncome
1       A           10000
2       A           40000
3       A           50000
4       A           60000
5       B           20000
6       B           40000
7       B           50000
8       B           50000
9       B           60000