2017-02-22 56 views
0

Это может быть базовое, но я пытался понять это в течение нескольких дней и не нашел ответа.Функция, определенная пользователем, основанная на нескольких столбцах, сгруппированных по категориям

Я пытаюсь вычислить новое количество, основанное на концентрации двух столбцов и области, сгруппированных по «водосборной». Я написал функцию для расчета разницы в концентрации для каждой строки и строки с наибольшей площадью, нормированной по доле площади в этом водосборном бассейне, но она не будет работать с dplyr или aggregate (она отлично работает, но затем возвращает список

в идеале, я хочу, чтобы добавить столбец на dataframe или полностью заменить колонку концентрации Вот dataframe «лев»:..

area catchment concentration 
1 1  Yup  2.00000 
2 10  Yup  40.50000 
3 25  Yup  50.82031 
4 35  Yup  50.00000 
5 1  Nope  1.00000 
6 10  Nope  5.00000 
7 25  Nope  40.08333 
8 35  Nope  38.00000 

Здесь функция:

lever <- function(data=lev, x=data[,"concentration"], y=data[,"area"]){ 
N= which.max(y) 
L = (x - x[N]) * y/max(y) 
return(L)} 

И здесь это желаемый результат:

area catchment concentration leverage 
1 1  Yup  2.00000 -1.3714286 
2 10  Yup  40.50000 -2.7142857 
3 25  Yup  50.82031 0.5859375 
4 35  Yup  50.00000 0.0000000 
5 1  Nope  1.00000 -1.0571429 
6 10  Nope  5.00000 -9.4285714 
7 25  Nope  40.08333 1.4880952 
8 35  Nope  38.00000 0.0000000 

Используя by, я могу получить два списка с результатами для каждого водосбора:

by(lev, lev$catchment, lever) 

, но я хочу использовать функцию на несколько столбцов, классифицированных несколькими факторами (например, дата в дополнение к водосборной площади), и я получаю

'неверное количество измерений'

ошибок с doBy и dplyr.

+0

Мы можем дать лучшие ответы, если вы предоставите [воспроизводимый пример] (http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example/5965451#5965451). –

+0

Благодарим за редактирование, чтобы сделать его воспроизводимым. Я буду делать в следующий раз :) – benjabiker

ответ

1

Загрузка ваши данные:

lev <- read.table(text = "area catchment concentration 
    1  Yup  2.00000 
    10  Yup  40.50000 
    25  Yup  50.82031 
    35  Yup  50.00000 
    1  Nope  1.00000 
    10  Nope  5.00000 
    25  Nope  40.08333 
    35  Nope  38.00000", 
    header=TRUE) 

Группировать по водосбора

library(dplyr) 
lev %>% 
    group_by(catchment) %>% 
    mutate(N = which.max(area), 
      L = (concentration - concentration[N]) * area/max(area)) 

# 
# area catchment concentration  N   L 
# <int> <fctr>   <dbl> <int>  <dbl> 
# 1  1  Yup  2.00000  4 -1.3714286 
# 2 10  Yup  40.50000  4 -2.7142857 
# 3 25  Yup  50.82031  4 0.5859357 
# 4 35  Yup  50.00000  4 0.0000000 
# 5  1  Nope  1.00000  4 -1.0571429 
# 6 10  Nope  5.00000  4 -9.4285714 
# 7 25  Nope  40.08333  4 1.4880929 
# 8 35  Nope  38.00000  4 0.0000000 

Использование функции

Я модифицировать функцию таким образом, что она возвращает фрейм данных.

lever2 <- function(data, 
        x = data[,"concentration"][[1]], 
        y = data[,"area"][[1]]){ 
    # Use [[1]] to extract the vector only 
    N <- which.max(y) 
    L <- (x - x[N]) * y/max(y) 
    # Put L back into the data frame 
    # so that we keep the concentration and area in the result 
    data$L <- L 
    return(data) 
    } 

Funtion может быть использован с dplyr::group_by %>% do

lev %>% 
    group_by(catchment) %>% 
    do(lever2(.)) 
+0

Да, я писал одновременно на своем ноутбуке, но я медленнее, чем вы. Я хотел бы добавить пример, используя функцию OP 'рычаг' и механизм' group_by'%>% 'do', но каким-то образом это возвращает объект' (list), который не может быть принужден к типу «double». Мне еще нужно выяснить как заставить эту работу работать. –

+0

Работает отлично! Если у меня есть несколько столбцов (например, концентрация1, концентрация2), как я могу добавить L к кадру данных для каждого из них? – benjabiker

+0

Редактирование команды 'mutate'' L = (концентрация2 - концентрация2 [N]) * area/max (area) '. Но если у вас широкая структура данных, вы можете подумать о том, чтобы переформатировать фрейм данных в длинный формат с помощью [tidyr :: gather] (ftp://cran.r-project.org/pub/R/web/packages/tidyr/ vignettes/tidy-data.html) перед выполнением 'mutate'. –

1

Мы можем использовать tidyverse

library(tidyverse) 
df1 %>% 
    group_by(catchment) %>% 
    mutate(leverage = (concentration- concentration[which.max(area)]) * area/max(area)) 

На основе описания, если существует несколько столбцов, как группировка переменной, поместить те в group_by, и расчет также может быть применен к нескольким столбцам с mutate_each

1

Вы также можете использовать data.table для вычисления этого значения: данные

library(data.table) 
# convert to data.table 
setDT(df) 

df[, leverage := (concentration - concentration[which.max(area)]) * (area/max(area)), 
    by=catchment] 
df 
    area catchment concentration leverage 
1: 1  Yup  2.00000 -1.3714286 
2: 10  Yup  40.50000 -2.7142857 
3: 25  Yup  50.82031 0.5859357 
4: 35  Yup  50.00000 0.0000000 
5: 1  Nope  1.00000 -1.0571429 
6: 10  Nope  5.00000 -9.4285714 
7: 25  Nope  40.08333 1.4880929 
8: 35  Nope  38.00000 0.0000000 

df <- 
structure(list(area = c(1L, 10L, 25L, 35L, 1L, 10L, 25L, 35L), 
    catchment = structure(c(2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L), .Label = c("Nope", 
    "Yup"), class = "factor"), concentration = c(2, 40.5, 50.82031, 
    50, 1, 5, 40.08333, 38)), .Names = c("area", "catchment", 
"concentration"), class = "data.frame", row.names = c("1", "2", 
"3", "4", "5", "6", "7", "8"))