R replace_na значения условно по столбцу с несколькими условиями - PullRequest
1 голос
/ 14 октября 2019

Мой вопрос аналогичен другим сообщениям replace_na, но я не могу найти правильную комбинацию ответов.

У меня есть дата-кадр с показателями инфляции для всех стран за 8 лет (в широком формате - страны в виде строк игоды как колонны).

  1. В некоторых странах есть NA на все 8 лет (столбцы 3:10), и в этом случае я хочу заменить все NA на среднее значение столбца
library(tidyverse)

sample %>% 
  mutate_if((rowSums(is.na[,3:10]))!=8, replace_na = colMeans(na.rm=T)) 

Это близко, но что-то не так.

В других странах есть только NA в некоторых столбцах, и в этом случае я хочу заменить NA на значение предыдущего года
library(zoo)
sample %>% 
  mutate_if((rowSums(is.na[,3:10]))!=8, replace_na = colMeans(na.rm=T)),
         is.na[,4:10], na.locf(fromLast = TRUE))) 

Пробовал использовать na.locf из пакета zoo, но не могусделать все правильно с другими условиями

Последнее условие заключается в том, что, если НС будет в первом году (2007), я хочу заменить его на среднее значение по столбцу 2007 года вместо следующего года (2008 год был финансовым кризисом, поэтому все уровни инфляции сошли с ума),
  mutate_if((rowSums(is.na[,3:10]))!=8, replace_na = colMeans(na.rm=T)),
         is.na[,4:10], na.locf(fromLast = TRUE)),
          is.na("2007"), replace = colMeans("2007", na.rm = TRUE)) 

Но это полно ошибок, и я застрял, пытаясь связать все эти условия вместе - довольно плохо знакомый с утверждениями ifelse. Я пытаюсь найти решение dplyr, так как это синтаксис, с которым я больше всего знаком, но, может быть, это проще для base R или data.table

под управлением R 3.6.1

sample <- structure(list(`Country Name` = c("Aruba", "Afghanistan", "Angola", 
"Albania", "Andorra", "Arab World", "United Arab Emirates", "Argentina", 
"Armenia", "American Samoa", "Antigua and Barbuda", "Australia"
), `Country Code` = c("ABW", "AFG", "AGO", "ALB", "AND", "ARB", 
"ARE", "ARG", "ARM", "ASM", "ATG", "AUS"), `2007` = c(5.39162036843645, 
8.68057078513406, 12.2514974459487, 2.93268248162318, NA, 4.74356585295154, 
NA, NA, 4.40736089644519, NA, 1.41605259409743, 2.32761128891476
), `2008` = c(8.95722105296535, 26.4186641547444, 12.4758291326398, 
3.36313757366391, NA, 11.2706652380848, 12.2504202448139, NA, 
8.94995335353386, NA, 5.33380639820232, 4.35029854990047), `2009` = c(-2.13630037272305, 
-6.81116108898995, 13.7302839288409, 2.23139683475865, NA, 2.92089711805365, 
1.55980098148558, NA, 3.40676682683799, NA, -0.550159995508869, 
1.77111716621252), `2010` = c(2.07773902027782, 2.1785375238942, 
14.4696564932574, 3.61538461538463, NA, 3.91106195534027, 0.879216764156813, 
NA, 8.17636138473956, NA, 3.3700254022015, 2.91834002677376), 
    `2011` = c(4.31633194082721, 11.8041858089129, 13.4824679218511, 
    3.44283593170005, NA, 4.75316388885632, 0.877346595685083, 
    NA, 7.6500080785929, NA, 3.45674967234599, 3.30385015608744
    ), `2012` = c(0.627927921638161, 6.44121280934118, 10.2779049218839, 
    2.03642235579081, NA, 4.61184432206646, 0.662268900269082, 
    NA, 2.55802007757907, NA, 3.37688044338879, 1.76278015613193
    ), `2013` = c(-2.37226328015073, 7.38577178397857, 8.77781429332619, 
    1.92544399507649, NA, 3.23423783752364, 1.10111836375706, 
    NA, 5.78966778544654, NA, 1.05949782356168, 2.44988864142539
    ), `2014` = c(0.421637771012246, 4.67399603536339, 7.28038730361125, 
    1.61304235314414, NA, 2.77261158414198, 2.34626865671643, 
    NA, 2.98130868933673, NA, 1.08944157435363, 2.48792270531403
    )), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-12L))

Ответы [ 2 ]

1 голос
/ 14 октября 2019

Сначала вычислите логический вектор, all.na имеющий по одному компоненту на строку, который ИСТИНА, если числовые данные этой строки все NA, и ЛОЖЬ в противном случае. Затем используйте na.aggregate, чтобы заполнить строки всех NA. Также используйте na.aggregate на 2007. Затем преобразуйте в длинную форму и примените na.locf0 по стране и верните обратно в широкую форму.

library(dplyr)
library(tidyr)
library(zoo)

all.na <- sample %>%
  select_if(is.numeric) %>%
  { rowSums(is.na(.)) == ncol(.) }

sample %>% 
  mutate_at(-(1:3), ~ if_else(all.na, na.aggregate(.x), .x)) %>%
  mutate(`2007` = na.aggregate(`2007`)) %>%
  gather(key, value, -`Country Name`, -`Country Code`) %>%
  group_by(`Country Name`, `Country Code`) %>%
  mutate(value = na.locf0(value)) %>%
  ungroup %>%
  spread(key, value)

или используя только зоопарк:

library(zoo)

all.na <- apply(is.na(sample[grep("^2", names(sample))]), 1, all)

ix <- -(1:3)
sample.out <- sample
Fill <- function(x) ifelse(all.na, na.aggregate(x), x)
sample.out[ix] <- lapply(sample[ix], Fill)
sample.out$"2007" <- na.aggregate(sample.out$"2007")
sample.out[ix] <- t(apply(sample.out[ix], 1, na.locf0))
0 голосов
/ 17 октября 2019

Пытался сделать то же самое, используя только tidyverse (dplyr и tidyr), вот что я получил:

Создание замены в соответствии с условием 1. Значения столбца.

# Getting the Column Means to Replace according to Condition 1 and 3. 
replacement <- sample %>%
    select_if(is.numeric) %>%
    summarize_all( ~ mean(., na.rm = TRUE)) %>%
    #Transformed to List since it is a requirement for tidyr::replace_na()
    as.list()

Затем я создал все в один конвейер.

sample %>%
  pivot_longer(`2007`:`2014`, names_to = "year", values_to = "int_rate") %>%
  group_by(`Country Name`) %>%
  summarize(na_num = is.na(int_rate) %>% sum) %>%
  #Joining the number of NAs na_num as a new column
  left_join(sample, by = "Country Name") %>%
  #Replacing 2007 missing as a first value. Condition 3.
  mutate(`2007` = if_else(between(na_num, 1, 7) &
                            is.na(`2007`), replacement[[1]] , `2007`)) %>%
  #Making dataset wider 
  pivot_longer(`2007`:`2014`, names_to = "year", values_to = "int_rate") %>%
  group_by(`Country Name`) %>%
  #Using fill to impute NAs with the previous one. Condition 2.
  fill(int_rate) %>%
  pivot_wider(names_from = year, values_from = int_rate) %>%
  #Replacing Values when all values are missing. Condition 1.
  replace_na(replace = replacement)
...