Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dplyr use both rowwise and df-wise values in a mutate

Tags:

r

dplyr

How do you perform a rowwise operation which uses values from other rows (in dplyr/tidy style)? Let's say I have this df:

df <- data_frame(value = c(5,6,7,3,4),
                 group = c(1,2,2,3,3),
                 group.to.use = c(2,3,3,1,1))

I want to create a new variable, new.value, which is equal to each row's current value plus the maximum of value for rows whose "group" equals this row's "group.to.use." So for the first row

new.value = 5 + (max(value[group === 2])) = 5 + 7 = 12

desired output:

# A tibble: 5 x 4
  value group group.to.use new.value
  <dbl> <dbl>        <dbl>     <dbl>
1    5.    1.           2.       12.
2    6.    2.           3.       10.
3    7.    2.           3.       11.
4    3.    3.           1.        8.
5    4.    3.           1.        9.

pseudo code:

df %<>%
  mutate(new.value = value + max(value[group.to.use == <group.for.this.row>]))
like image 601
lost Avatar asked Aug 06 '18 06:08

lost


2 Answers

In rowwise operation, you can refer to the whole data.frame with . and a whole column in the data.frame with normal syntax .$colname or .[['col.name']]:

df %>%
    rowwise() %>%
    mutate(new.value = value + max(.$value[.$group == group.to.use])) %>%
    ungroup()

# # A tibble: 5 x 4
#   value group group.to.use new.value
#   <dbl> <dbl>        <dbl>    <dbl>
# 1     5     1            2       12
# 2     6     2            3       10
# 3     7     2            3       11
# 4     3     3            1        8
# 5     4     3            1        9

Alternatively, you can precompute the max for each group and then do a left-join:

df.max <- df %>% group_by(group) %>% summarise(max.value = max(value))

df %>%
    left_join(df.max, by = c('group.to.use' = 'group')) %>%
    mutate(new.value = value + max.value) %>%
    select(-max.value)
# # A tibble: 5 x 4
#   value group group.to.use new.value
#   <dbl> <dbl>        <dbl>     <dbl>
# 1     5     1            2        12
# 2     6     2            3        10
# 3     7     2            3        11
# 4     3     3            1         8
# 5     4     3            1         9
like image 155
mt1022 Avatar answered Oct 05 '22 22:10

mt1022


With base R, we can use ave, where we calculate max for each group and add them with the corresponding value matching the groups.

df$new.value <- with(df, value + 
                 ave(value, group, FUN = max)[match(group.to.use, group)])

df
#   A tibble: 5 x 4
#   value group group.to.use new.value
#  <dbl> <dbl>        <dbl>     <dbl>
#1  5.00  1.00         2.00     12.0 
#2  6.00  2.00         3.00     10.0 
#3  7.00  2.00         3.00     11.0 
#4  3.00  3.00         1.00      8.00
#5  4.00  3.00         1.00      9.00
like image 26
Ronak Shah Avatar answered Oct 05 '22 22:10

Ronak Shah