Найти ближайшее значение для определенного года в R - PullRequest
1 голос
/ 21 января 2020

У меня есть данные этого типа:

iso3 year    UHC         cata10
AFG 2010    0.3551409   NA
AFG 2011    0.3496452   NA
AFG 2012    0.3468012   NA
AFG 2013    0.3567721   14.631331
AFG 2014    0.3647436   NA
AFG 2015    0.3717983   NA
AFG 2016    0.3855273   4.837534
AFG 2017    0.3948606   NA
AGO 2011    0.3250651   12.379809
AGO 2012    0.3400455   NA
AGO 2013    0.3397722   NA
AGO 2014    0.3385741   NA
AGO 2015    0.3521086   16.902584
AGO 2016    0.3636765   NA
AGO 2017    0.3764945   NA

, и я хотел бы найти наиболее близкое значение к 2012 и 2017 годам (+ ou - 2 года, т.е. для 2012 года это может быть 2010, 2011, Данные 2013 или 2014) для переменной cata10. Выходные данные должны быть:

iso3year_UHC    UHC         year_cata   cata10   
AFG 2012        0.3468012   2013        14.631331
AFG 2017        0.3948606   2016        4.837534
AGO 2012        0.3400455   2011        12.379809
AGO 2017        0.3764945   2015        16.902584

РЕДАКТИРОВАТЬ: Обратите внимание, что я должен иметь NA, если нет данных за 2 года до или после отчетного года.

У меня есть пробовал тоны команд с двух дней, но не смог найти решение. Не могли бы вы посоветовать, какой тип команд попробовать?

Большое спасибо,

N.

Ответы [ 2 ]

0 голосов
/ 21 января 2020

Вот ответ только с dplyr:

library(tidyverse)

uhc_comb = read.table(header = T, text = "
iso3 year    UHC         cata10
AFG  2010    0.3551409   NA
AFG  2011    0.3496452   NA
AFG  2012    0.3468012   NA
AFG  2013    0.3567721   14.631331
AFG  2014    0.3647436   NA
AFG  2015    0.3717983   NA
AFG  2026    0.3855273   4.837534     #Year is 2026 for the example
AFG  2017    0.3948606   NA
AGO  2011    0.3250651   12.379809
AGO  2012    0.3400455   NA
AGO  2013    0.3397722   NA
AGO  2014    0.3385741   NA
AGO  2015    0.3521086   16.902584
AGO  2016    0.3636765   NA
AGO  2017    0.3764945   NA")

uhc_comb2 = uhc_comb %>% 
  pivot_longer(cols=c("UHC","cata10")) %>% #pivot UHC and cata10 to long format as columns "name" and "value"
  filter(!is.na(value)) %>% #remove missing
  group_by(iso3, name) %>% #for each iso3 and for each variable name (UHC and cata10)
  mutate(dist=pmin(abs(year-2012),abs(year-2017))) %>% #compute the distance between the year and the targets and keep only the lowest
  # filter(dist<=2) %>% #remove
  top_n(-2, dist) %>% #select the minimal distance (in each group)
  mutate(year=ifelse(dist>2, NA, year),
         value=ifelse(dist>2, NA, value)) %>% #infer NA if  distance is too high
  select(-dist) #discard the now useless variable

uhc_comb2 %>%
  pivot_wider(id_cols = iso3, values_from = c("year", "value")) %>% #pivot to wide again
  unnest #since there are several values, unnest the lists from the dataframe

Это выдаст некоторые предупреждения, но они не значимы. Я не уверен, что их можно удалить.

Если вы хотите лучше понять это, запустите каждую строку одну за другой. Поворотные столы - это трудная для ума гимнастика c в начале.

РЕДАКТИРОВАТЬ: это даст вам правильный вывод без предупреждений:

uhc_comb2 %>%
  pivot_wider(id_cols = iso3, 
              values_from = c("year", "value"), 
              values_fn = list(value = list, year = list)) %>% 
  unnest(cols = c(year_cata10, year_UHC, value_cata10, value_UHC))
0 голосов
/ 21 января 2020

Вот три подхода. Первый является наиболее понятным, поскольку он показывает, что проблема на самом деле является агрегированным и отфильтрованным самосоединением, напрямую моделирует это и автоматически обрабатывает крайний случай, упомянутый в комментариях, без дополнительного кода. Второй использует lapply l oop, чтобы получить желаемый эффект, но он требует более утомительных манипуляций, хотя и имеет преимущество нулевых зависимостей пакетов. Последний способ обойти тот факт, что dplyr не хватает сложных самообъединений, выполняя левое соединение дважды.

1) sqldf Использование DF, определенного воспроизводимым образом в примечании, в конце выполняет самостоятельное объединение таким образом, чтобы разница в годах составляла -2, -1, 1 или 2, а iso3 коды совпадают, и cata10 не является NA в соответствующем экземпляре, и среди этих строк мы используем min(...), чтобы найти строку, имеющую минимальную абсолютную разницу в году. При этом используется тот факт, что SQLite имеет функцию, которая min(...) приведет к возвращению всей строки, которая удовлетворяет условию минимизации. Наконец, возьмите только строки 2012 и 2017 годов. Возможность SQL напрямую моделировать ограничения с помощью сложного соединения позволяет нам напрямую моделировать требования в код.

library(sqldf)

sqldf("select 
      a.iso3year iso3year_UHC, 
      a.UHC, 
      substr(b.iso3year, 5, 8) year_cata, 
      b.cata10, 
      substr(a.iso3year, 5, 8) year, 
      min(abs(substr(a.iso3year, 5, 8) - substr(b.iso3year, 5, 8))) min_value 
    from DF a  
    left join DF b on year - year_cata in (-2, -1, 1, 2) and
      substr(a.iso3year, 1, 3) = substr(b.iso3year, 1, 3) and
      b.cata10 is not null
    group by a.iso3year
    having year in ('2012', '2017')")[1:4]

, что дает:

  iso3year_UHC       UHC year_cata    cata10
1     AFG 2012 0.3468012      2013 14.631331
2     AFG 2017 0.3948606      2016  4.837534
3     AGO 2012 0.3400455      2011 12.379809
4     AGO 2017 0.3764945      2015 16.902584

2) База R В этом решении используется только база R. Сначала мы создаем переменные year и iso, разбивая iso3year на две части. ix - это индекс в DF, в котором в строках указывается 2012 или 2017 год. Для каждой из этих строк мы находим ближайший год со значением cata10 и создаем строку выходного фрейма данных, который lapply возвращает в виде списка строк L. Наконец, мы rbind эти строки вместе. Это не так просто, как (1), но имеет преимущество, заключающееся в отсутствии зависимости от пакета.

to.year <- function(x) as.numeric(substr(x, 5, 8))
year <- to.year(DF$iso3year)
iso <- substr(DF$iso3year, 1, 3)
ix <- which(year %in% c(2012, 2017))
L <- lapply(ix, function(i) {
  DF0 <- na.omit(DF[iso[i] == iso & (year[i] - year) %in% c(-2, -1, 1, 2), ])
  if (nrow(DF0)) {
    with(DF0[which.min(abs(to.year(DF0$iso3year) - year[i])), c("iso3year", "cata10")], 
      data.frame(iso3year_UHC = DF$iso3year[i], 
               UHC = DF$UHC[i], 
               year_cata = as.numeric(substr(iso3year, 5, 8)), 
               cata10))
  } else {
      data.frame(iso3year_UHC = DF$iso3year[i], 
               UHC = DF$UHC[i], 
               year_cata = NA,
               cata10 = NA)
  }
})
do.call("rbind", L)

, что дает:

  iso3year_UHC       UHC year_cata    cata10
1     AFG 2012 0.3468012      2013 14.631331
2     AFG 2017 0.3948606      2016  4.837534
3     AGO 2012 0.3400455      2011 12.379809
4     AGO 2017 0.3764945      2015 16.902584

3) dplyr / tidyr

Сначала разделите iso3year на iso и year столбцы, получая DF2. Затем выберите строки 2012 и 2017 годов, получив DF3. Теперь оставьте соединение DF3 с DF2, используя iso, и получите те строки для cata10 в объединенном экземпляре, которые не являются NA, и абсолютная разница в годах между двумя соединенными фреймами данных равна 1 или 2. Затем используйте slice для выбора строки с наименьшим расстоянием в годах и select для выбранных столбцов, дающих DF4 Наконец, оставьте объединение DF3 с DF4, которое заполнит все строки, для которых не было совпадения.

library(dplyr)
library(tidyr)

DF2 <- DF %>%
  separate(iso3year, c("iso", "year"), remove = FALSE, convert = TRUE)

DF3 <- DF2 %>%
  filter(year %in% c(2012, 2017))

DF4 <- DF3 %>%
  left_join(DF2, "iso") %>%
  drop_na(cata10.y) %>%
  filter(abs(year.x - year.y) %in% 1:2) %>%
  group_by(iso3year.x) %>%
  slice(which.min(abs(year.x - year.y))) %>%
  ungroup %>%
  select(iso3year = iso3year.x, UHC = UHC.x, year_cata = year.y, cata10 = cata10.y)

DF3 %>% 
  select(iso3year, UHC) %>%
  left_join(DF4,  c("iso3year", "UHC"))

подача:

# A tibble: 4 x 4
  iso3year   UHC year_cata cata10
  <chr>    <dbl>     <int>  <dbl>
1 AFG 2012 0.347      2013  14.6 
2 AFG 2017 0.395      2016   4.84
3 AGO 2012 0.340      2011  12.4 
4 AGO 2017 0.376      2015  16.9 

Примечание

Lines <- "iso3year    UHC         cata10
AFG 2010    0.3551409   NA
AFG 2011    0.3496452   NA
AFG 2012    0.3468012   NA
AFG 2013    0.3567721   14.631331
AFG 2014    0.3647436   NA
AFG 2015    0.3717983   NA
AFG 2016    0.3855273   4.837534
AFG 2017    0.3948606   NA
AGO 2011    0.3250651   12.379809
AGO 2012    0.3400455   NA
AGO 2013    0.3397722   NA
AGO 2014    0.3385741   NA
AGO 2015    0.3521086   16.902584
AGO 2016    0.3636765   NA
AGO 2017    0.3764945   NA"
DF <- read.csv(text = gsub("  +", ",", Lines), as.is = TRUE)
...