Sylvia Rodriguez
Sylvia Rodriguez

Reputation: 1353

Multiply part of the data in data.frame by values in another data.frame

Someone here already kindly provided part of the following code:

 library(dplyr)
 set.seed(12345)
 df1 = data.frame(a=c(rep("a",8), rep("b",5), rep("c",7), rep("d",10)), 
      b=rnorm(30, 6, 2), 
      c=rnorm(30, 12, 3.5), 
      d=rnorm(30, 8, 3)
      )
 df2 = data.frame(b= 1.5,
      c= 13, 
      d= 0.34
      )
 df1_z <- df1 %>%
   group_by(a) %>%
   mutate(across(b:d, list(zscore = ~as.numeric(scale(.))))) %>%
   ungroup %>%
   mutate(total = rowSums(select(., ends_with('zscore'))))

This was exactly what I wanted at the time, but now I would like something slightly different. In df1_z, instead of the values in the last column called "total", I would like this value to be the sum of the multiplications of the values in the _zscore column and the corresponding values in df2, so: b_zscore x 1.5 + c_zscore x 13 + d_zscore x 0.34.

For example, the first value would be 0.6971403 x 1.5 + 0.100595417 x 13 + 0.01790090 x 0.34 = 2.359537177. Expected outcome for the new total column:

 total
 2.359537177
 16.04147765
 13.64141872
 9.146152274
 -3.380574542
 -5.55439223
 etc...

How to modify above code to get this result in the new "total" column of df1_z?

Upvotes: 0

Views: 43

Answers (2)

arg0naut91
arg0naut91

Reputation: 14774

Another option:

library(tidyverse)

df1 %>%
  group_by(a) %>%
  mutate(across(b:d, list(zscore = ~as.numeric(scale(.))))) %>%
  ungroup %>%
  mutate(total = rowSums(map2_dfc(select(., contains('zscore')), df2, `*`)))

Output:

# A tibble: 30 x 8
   a         b     c     d b_zscore c_zscore d_zscore   total
   <fct> <dbl> <dbl> <dbl>    <dbl>    <dbl>    <dbl>   <dbl>
 1 a      7.17 14.8   8.45    0.697   0.101    0.0179   2.36 
 2 a      7.42 19.7   3.97    0.841   1.17    -1.14    16.0  
 3 a      5.78 19.2   9.66   -0.108   1.05     0.332   13.6  
 4 a      5.09 17.7  12.8    -0.508   0.732    1.14     9.15 
 5 a      7.21 12.9   6.24    0.721  -0.329   -0.555   -3.38 
 6 a      2.36 13.7   2.50   -2.09   -0.146   -1.52    -5.55 
 7 a      7.26 10.9  10.7     0.749  -0.774    0.593   -8.74 
 8 a      5.45  6.18 12.8    -0.302  -1.80     1.14   -23.5  
 9 b      5.43 18.2   9.55   -0.445   1.12     1.34    14.4  
10 b      4.16 12.1   4.11   -1.06    0.0776  -1.02    -0.933
# ... with 20 more rows

Upvotes: 1

Onyambu
Onyambu

Reputation: 79348

You could use the crossprod function:

df1 %>%
     group_by(a) %>%
     mutate(across(b:d, list(zscore = ~as.numeric(scale(.))))) %>%
     ungroup %>%
     mutate(total = c(crossprod(t(select(., ends_with('zscore'))),t(df2))))
# A tibble: 30 x 8
   a         b     c     d b_zscore c_zscore d_zscore   total
   <chr> <dbl> <dbl> <dbl>    <dbl>    <dbl>    <dbl>   <dbl>
 1 a      7.17 14.8   8.45    0.697   0.101    0.0179   2.36 
 2 a      7.42 19.7   3.97    0.841   1.17    -1.14    16.0  
 3 a      5.78 19.2   9.66   -0.108   1.05     0.332   13.6  
 4 a      5.09 17.7  12.8    -0.508   0.732    1.14     9.15 
 5 a      7.21 12.9   6.24    0.721  -0.329   -0.555   -3.38 
 6 a      2.36 13.7   2.50   -2.09   -0.146   -1.52    -5.55 
 7 a      7.26 10.9  10.7     0.749  -0.774    0.593   -8.74 
 8 a      5.45  6.18 12.8    -0.302  -1.80     1.14   -23.5  
 9 b      5.43 18.2   9.55   -0.445   1.12     1.34    14.4  
10 b      4.16 12.1   4.11   -1.06    0.0776  -1.02    -0.933
# ... with 20 more rows

Upvotes: 2

Related Questions