Вот один из способов сделать это с базой R:
df$calc <- unlist(tapply(df$value, df$name, function(x) rep(if(x[1]==0) x[x!=0][1] else -1, length(x))))
... и лучший способ:
df$calc <- ave(df$value, df$name, FUN = function(x) if(x[1]==0) x[x!=0][1] else -1)
Это лучше понять в двух частях:
Сначала напишите функцию, которая соответствует вашим желаемым условиям.
doit <- function(x) if(x[1]==0) x[x!=0][1] else -1
Во-вторых, используйте его в ave
:
ave(df$value, df$name, FUN=doit)
| edit |
Как следует изменить функцию, если я хочуприсвоить столбцу «calc» значение из другого столбца, например, «value2», соответствующее первому ненулевому «значению»?
Здесь ave
вам больше не поможет, вам потребуется split
фрейм данных и присоединиться к нему.
df$value2 <- 101:108
do.call(rbind, lapply(split(df, df$name), function(x) {
x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
x
}))
Обратите внимание на второй рядв function(x)
... это для , возвращающего всего x
вместо только $calc
компонента.Логический порядок такой: split -> lapply -> do.call, но он выглядит наоборот, из-за того, как работают скобки.Можно переписать это, используя каналы из magrittr, чтобы сохранить логический порядок (канал LHS %>% RHS
направляет LHS в качестве первого аргумента RHS, поэтому необходим трюк с do.call
, где мы хотим, чтобы он был вторым аргументом.).
library(magrittr)
split(df, df$name) %>%
lapply(function(x) {
x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
x
}) %>% {do.call(rbind, .)}