2

The aim is to transform all Species "setosa" rows to one row "setosa":(This is a minimal example(in real more columns and more groups):

I have this dataframe:

head(iris, 2) %>%
  select(1,2,5) %>% 
  group_by(Species)

  Sepal.Length Sepal.Width Species
         <dbl>       <dbl> <fct>  
1          5.1         3.5 setosa 
2          4.9         3   setosa 

I use summarise with toString to get:

  Species Sepal.Length Sepal.Width
  <fct>   <chr>        <chr>      
1 setosa  5.1, 4.9     3.5, 3  

Expected output: I want this dataframe:

  Species Sepal.Length1 Sepal.Length2 Sepal.Width1 Sepal.Width2
  <fct>           <dbl>         <dbl>        <dbl>        <int>
1 setosa            5.1           4.9          3.5            3

I achieve this with this working code:

head(iris, 2) %>%
  select(1,2,5) %>% 
  group_by(Species) %>% 
  summarise(across(everything(), ~toString(.))) %>% 
  ungroup() %>% 
  separate(Sepal.Length, c("Sepal.Length1", "Sepal.Length2"),  sep = ", ", convert = TRUE) %>% 
  separate(Sepal.Width, c("Sepal.Width1", "Sepal.Width2"),  sep = ", ", convert = TRUE)

However I would like to be able to use separate after mutate across with anonymous function with this not working code:

head(iris, 2) %>%
  select(1,2,5) %>% 
  group_by(Species) %>% 
  summarise(across(everything(), ~toString(.))) %>% 
  ungroup() %>% 
  mutate(across(-1, ~separate(., into = paste0(., 1:2), sep = ", ", convert = TRUE)))

Error: Problem with `mutate()` input `..1`.
i `..1 = across(-1, ~separate(., into = paste0(., 1:2), sep = ", ", convert = TRUE))`.
x no applicable method for 'separate' applied to an object of class "character"

I want to learn how to apply separate function after mutate and across.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
TarJae
  • 72,363
  • 6
  • 19
  • 66
  • 1
    If your input is what you show in "I have this dataframe", then see [Transpose / reshape dataframe without "timevar" from long to wide format](https://stackoverflow.com/questions/11322801/transpose-reshape-dataframe-without-timevar-from-long-to-wide-format). E.g. with `data.table`: `dcast(setDT(d), Species ~ rowid(Species), value.var = setdiff(names(d), "Species"))`. I don't understand why you go via the `toString` step. This appears like an XY problem ;) Cheers – Henrik Oct 31 '21 at 19:47

3 Answers3

3

Main issue is that separate requires input as a data.frame. We may wrap in a tibble and then do separate if we want this within across and finally unnest the list output

library(dplyr)
library(tidyr)
library(stringr)
head(iris, 2) %>%
  select(1,2,5) %>% 
  group_by(Species) %>% 
  summarise(across(everything(), ~toString(.)), .groups = 'drop') %>%
  mutate(across(-1, ~ list(tibble(col1 = .) %>% 
        separate(col1, into = str_c(cur_column(), 1:2), sep = ",\\s+")))) %>% 
  unnest(cols = c(Sepal.Length, Sepal.Width))

-output

# A tibble: 1 × 5
  Species Sepal.Length1 Sepal.Length2 Sepal.Width1 Sepal.Width2
  <fct>   <chr>         <chr>         <chr>        <chr>       
1 setosa  5.1           4.9           3.5          3           
akrun
  • 874,273
  • 37
  • 540
  • 662
2

Another approach, pivoting long, transforming, and pivoting wide again.

library(tidyverse)
head(iris, 2) %>%
  select(1,2,5) %>% 

  pivot_longer(-Species) %>%
  group_by(name) %>% mutate(col = paste0(name, row_number())) %>% ungroup() %>%
  select(-name) %>%
  arrange(col) %>%  # for ordering columns like OP
  pivot_wider(names_from = col, values_from = value)


# A tibble: 1 x 5
  Species Sepal.Length1 Sepal.Length2 Sepal.Width1 Sepal.Width2
  <fct>           <dbl>         <dbl>        <dbl>        <dbl>
1 setosa            5.1           4.9          3.5            3
Jon Spring
  • 55,165
  • 4
  • 35
  • 53
1

Another solution:

library(tidyverse)

head(iris, 2) %>%
  select(1,2,5) %>% 
  group_by(Species) %>% 
  summarise(across(everything(), ~toString(.))) %>% 
  separate(2, into = paste0("Sepal.Length",1:2),  sep=", ") %>% 
  separate(4, into = paste0("Sepal.Width",1:2),  sep=", ")

#> # A tibble: 1 × 5
#>   Species Sepal.Length1 Sepal.Length2 Sepal.Width1 Sepal.Width2
#>   <fct>   <chr>         <chr>         <chr>        <chr>       
#> 1 setosa  5.1           4.9           3.5          3
PaulS
  • 21,159
  • 2
  • 9
  • 26