简体   繁体   中英

How to mutate multiple columns into new multiple columns with dplyr

I am trying to mutate 3 columns into 3 new columns in a larger table (with more columns). 3 of the new columns depend on first existing 3 columns. 4th new column depends only on the 4th existing columns. Basically, I want to rotate coordinates of points (in columns x, y, z) in 3D space and store in a new columns (x_rot, y_rot, z_rot).

I could mutate 3 columns into 1 new column and repeat this for each dimension, which seems to be wasteful. It would be simpler, if my custom function would get 3 coordinates and return 3 rotated coordinates.

Here if I do this with a for loop:

df = data.frame(x = rnorm(5), y = rnorm(5), z = rnorm(5))
for(i in nrow(df){
    r = sqrt(df$x[i] ^ 2 + df$y[i] ^ 2 + df$z[i] ^ 2)
    phi = atan2(y = df$y[i], x = df$x[i])
    phi = phi + rotationAngle1
    theta = acos(df$z[i] / r)
    theta = theta + rotationAngle2
    df$x_ROT[i] = r * cos(phi) * sin(theta)
    df$y_ROT[i] = r * sin(phi) * sin(theta)
    df$z_ROT[i] = r * cos(theta)
}

or with mutate_at and function rotate :

rotate = function(x,y,z){
    r = sqrt(x ^ 2 + y ^ 2 + z ^ 2)
    phi = atan2(y = y, x = x)
    phi = phi + rotationAngle1
    theta = acos(z / r)
    theta = theta + rotationAngle2
    return(c(r * cos(phi) * sin(theta), r * sin(phi) * sin(theta),r * cos(theta))
    # OR
    return(list(x = r * cos(phi) * sin(theta), y = r * sin(phi) * sin(theta), z = r * cos(theta))
}

How, can I do this with dplyr? How can I formulate df %>% group_by(group) %>% mutate_at(???)

With dplyr , we can use mutate_at where f1 , f2 are the functions used for rotation

library(dplyr)
df2 <- df1 %>%
         mutate_at(vars(x, y, z),   list(rot =  f1)) %>%
         mutate(col4_rot = f2(col4))

In the devel version, use mutate with across

df2 <- df1 %>%
           mutate(across(vars(x, y, z),  f1, names = "{col}_rot"), 
                   col4_rot = f2(col4))

Update

Based on the updated function, we can use pmap

library(purrr)
library(stringr)

pmap_dfr(df, rotate) %>% 
     rename_all(~ str_c(., '_rot')) %>% 
     bind_cols(df, .)
# A tibble: 5 x 6
#        x       y      z   x_rot    y_rot   z_rot
#    <dbl>   <dbl>  <dbl>   <dbl>    <dbl>   <dbl>
#1 -0.303   1.20   -0.503 -0.0457  0.00799 -1.34  
#2 -0.0662 -0.599   1.45   1.35   -0.793    0.0405
#3  0.239   0.953   1.49  -1.39    1.09    -0.288 
#4 -0.490   0.0106 -0.622  0.157   0.333   -0.701 
#5  0.554   1.08    0.761 -0.748   0.928   -0.802 

where

rotationAngle2 <- 20
rotate <- function(x,y,z){
     r = sqrt(x ^ 2 + y ^ 2 + z ^ 2)
     phi = atan2(y = y, x = x)
     phi = phi + rotationAngle2
     theta = acos(z / r)
     theta = theta + rotationAngle2   
     return(list(x = r * cos(phi) * sin(theta), 
                 y = r * sin(phi) * sin(theta),
                 z = r * cos(theta)))
 }

It is also possible with mutate

library(tidyr)
df %>%
  rowwise %>% 
  mutate(out = list(rotate(x, y, z))) %>% 
  unnest_wider(c(out))
# A tibble: 5 x 6
#        x       y      z   x_rot    y_rot   z_rot
#    <dbl>   <dbl>  <dbl>   <dbl>    <dbl>   <dbl>
#1 -0.303   1.20   -0.503 -0.0457  0.00799 -1.34  
#2 -0.0662 -0.599   1.45   1.35   -0.793    0.0405
#3  0.239   0.953   1.49  -1.39    1.09    -0.288 
#4 -0.490   0.0106 -0.622  0.157   0.333   -0.701 
#5  0.554   1.08    0.761 -0.748   0.928   -0.802 

Or another option is to return the list in summarise and then do the unnest_wider and unnest

df %>%
    summarise(out = list(rotate(x, y, z))) %>% 
    unnest_wider(c(out)) %>% 
    unnest(cols = everything()) %>%
    bind_cols(df, .)
# A tibble: 5 x 6
#        x       y      z   x_rot    y_rot   z_rot
#    <dbl>   <dbl>  <dbl>   <dbl>    <dbl>   <dbl>
#1 -0.303   1.20   -0.503 -0.0457  0.00799 -1.34  
#2 -0.0662 -0.599   1.45   1.35   -0.793    0.0405
#3  0.239   0.953   1.49  -1.39    1.09    -0.288 
#4 -0.490   0.0106 -0.622  0.157   0.333   -0.701 
#5  0.554   1.08    0.761 -0.748   0.928   -0.802 

where

rotate <- function(x,y,z){
     r = sqrt(x ^ 2 + y ^ 2 + z ^ 2)
     phi = atan2(y = y, x = x)
     phi = phi + rotationAngle2
     theta = acos(z / r)
     theta = theta + rotationAngle2   
     return(list(x_rot = r * cos(phi) * sin(theta), 
                 y_rot = r * sin(phi) * sin(theta), 
                 z_rot = r * cos(theta)))
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM