Вот три подхода. Первый является наиболее понятным, поскольку он показывает, что проблема на самом деле является агрегированным и отфильтрованным самосоединением, напрямую моделирует это и автоматически обрабатывает крайний случай, упомянутый в комментариях, без дополнительного кода. Второй использует 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)