У меня есть вопрос, который мне сложно объяснить с помощью MRE и на который легко ответить, в основном потому, что я сам не до конца понимаю, в чем проблема.Так что я сожалею о том, что являюсь неопределенной преамбулой.
У меня есть таблица со многими выборками и эталонными измерениями, для которых я хочу сделать некоторую линейную интерполяцию для каждой выборки.Я делаю это сейчас, выполняя все эталонные измерения, масштабируя их до выборочных измерений, используя approx
, а затем исправляя их обратно. Но так как я сначала убираю это, я не могу сделать это красиво по трубе group_by dplyr.прямо сейчас я делаю это с очень уродливым обходным решением, где я добавляю пустые (NA) вновь созданные столбцы в образец tibble, а затем делаю это с помощью цикла for.
Так что мой вопрос действительно: как я могу реализоватьПримерная часть внутри группы в трубу, так что я могу делать все в группах?Я экспериментировал с dplyr::do()
и наткнулся на виньетку "программирования с помощью dplyr", но поиск в основном дает мне вещи broom::augment
и lm
, которые, как мне кажется, работают по-разному ... (например, см. Использование прибл.() с группами в dplyr ).Этот поток также выглядит многообещающе: Как вы используете приблизительный () внутри mutate_at ()?
Кто-то на irc рекомендовал использовать условное изменение, с case_when
, но я неЯ полностью понимаю, где и как в этом контексте.
Я думаю, что проблема заключается в том, что я хочу отфильтровать часть данных для следующих операций изменения, но операции изменения полагаются на сгруппированные данные, которыеЯ только что отфильтровал, если в этом есть какой-то смысл.
Вот MWE:
library(tidyverse) # or just dplyr, tibble
# create fake data
data <- data.frame(
# in reality a dttm with the measurement time
timestamp = c(rep("a", 7), rep("b", 7), rep("c", 7)),
# measurement cycle, normally 40 for sample, 41 for reference
cycle = rep(c(rep(1:3, 2), 4), 3),
# wheather the measurement is a reference or a sample
isref = rep(c(rep(FALSE, 3), rep(TRUE, 4)), 3),
# measurement intensity for mass 44
r44 = c(28:26, 30:26, 36, 33, 31, 38, 34, 33, 31, 18, 16, 15, 19, 18, 17)) %>%
# measurement intensity for mass 45, normally also masses up to mass 49
mutate(r45 = r44 + rnorm(21, 20))
# of course this could be tidied up to "intensity" with a new column "mass"
# (44, 45, ...), but that would make making comparisons even harder...
# overview plot
data %>%
ggplot(aes(x = cycle, y = r44, colour = isref)) +
geom_line() +
geom_line(aes(y = r45), linetype = 2) +
geom_point() +
geom_point(aes(y = r45), shape = 1) +
facet_grid(~ timestamp)
# what I would like to do
data %>%
group_by(timestamp) %>%
do(target_cycle = approx(x = data %>% filter(isref) %>% pull(r44),
y = data %>% filter(isref) %>% pull(cycle),
xout = data %>% filter(!isref) %>% pull(r44))$y) %>%
unnest()
# immediately append this new column to the original dataframe for all the
# samples (!isref) and then apply another approx for those values.
# here's my current attempt for one of the timestamps
matchref <- function(dat) {
# split the data into sample gas and reference gas
ref <- filter(dat, isref)
smp <- filter(dat, !isref)
# calculate the "target cycle", the points at which the reference intensity
# 44 matches the sample intensity 44 with linear interpolation
target_cycle <- approx(x = ref$r44,
y = ref$cycle, xout = smp$r44)
# append the target cycle to the sample gas
smp <- smp %>%
group_by(timestamp) %>%
mutate(target = target_cycle$y)
# linearly interpolate each reference gas to the target cycle
ref <- ref %>%
group_by(timestamp) %>%
# this is needed because the reference has one more cycle
mutate(target = c(target_cycle$y, NA)) %>%
# filter out all the failed ones (no interpolation possible)
filter(!is.na(target)) %>%
# calculate interpolated value based on r44 interpolation (i.e., don't
# actually interpolate this value but shift it based on the 44
# interpolation)
mutate(r44 = approx(x = cycle, y = r44, xout = target)$y,
r45 = approx(x = cycle, y = r45, xout = target)$y) %>%
select(timestamp, target, r44:r45)
# add new reference gas intensities to the correct sample gasses by the target cycle
left_join(smp, ref, by = c("time", "target"))
}
matchref(data)
# and because now "target" must be length 3 (the group size) or one, not 9
# I have to create this ugly for-loop
# for which I create a copy of data that has the new columns to be created
mr <- data %>%
# filter the sample gasses (since we convert ref to sample)
filter(!isref) %>%
# add empty new columns
mutate(target = NA, r44 = NA, r45 = NA)
# apply matchref for each group timestamp
for (grp in unique(data$timestamp)) {
mr[mr$timestamp == grp, ] <- matchref(data %>% filter(timestamp == grp))
}