24

I am running pivot_longer on multiple columns (i.e. two character columns and one numeric). I am encountering an error related to the class mismatch.

I have investigated the documentation for any "force" options and did not see any arguments within pivot_longer to specify the class to use -- or to allow the function auto-detect the most general class.

Are there any parameters within pivot_longer to avoid this error? Or do you need to convert the columns to a single class before running pivot_longer?

library(dplyr)
library(tidyr)
library(ggplot2) # Just for `diamonds` dataset

small_diamonds <- diamonds %>% 
  # Select a few columns (two character, one numeric, specifically integers)
  select(cut, color, price) %>% 
  # Create a row_id
  mutate(row_num = row_number()) 

# This works with `gather`
small_diamonds %>% 
  gather(key, val, - row_num)

# This fails due to class error:
small_diamonds %>% 
  # Pivot data
  pivot_longer( - row_num, 
                names_to = "key",
                values_to = "val")

# Output
# Error: No common type for `cut` <ordered<4bd7e>> and `price` <integer>.
# Call `rlang::last_error()` to see a backtrace

# Convert columns to a single class (character) and then use `pivot_longer`. 
# Runs successfully
small_diamonds %>% 
  mutate_all(as.character) %>% 
  # Pivot data
  pivot_longer( - row_num, 
                names_to = "key",
                values_to = "val")

Ryan Baxter-King
  • 339
  • 1
  • 2
  • 11

3 Answers3

29

The error is now there again in a different guise when the values_ptypes argument is used.

library(tidyverse)

small_diamonds <- diamonds %>% 
  select(cut, color, price) %>% 
  mutate(row_num = row_number())

small_diamonds %>%  
  pivot_longer( - row_num, 
                names_to = "key",
                values_to = "val", 
                values_ptypes = list(val = 'character'))
#> Error: Can't convert <integer> to <character>.

Therefore I need to use the values_transform argument to get the desired result.

library(tidyverse)

  small_diamonds <- diamonds %>% 
    select(cut, color, price) %>% 
    mutate(row_num = row_number())
  
  small_diamonds %>%  
    pivot_longer( - row_num, 
                  names_to = "key",
                  values_to = "val", 
                  values_transform = list(val = as.character))
#> # A tibble: 161,820 x 3
#>    row_num key   val    
#>      <int> <chr> <chr>  
#>  1       1 cut   Ideal  
#>  2       1 color E      
#>  3       1 price 326    
#>  4       2 cut   Premium
#>  5       2 color E      
#>  6       2 price 326    
#>  7       3 cut   Good   
#>  8       3 color E      
#>  9       3 price 327    
#> 10       4 cut   Premium
#> # ... with 161,810 more rows

Created on 2020-08-25 by the reprex package (v0.3.0)

Koray
  • 461
  • 6
  • 10
  • I think the reason for this error is that the tidyverse crew wants a data type transformation to be explicit during pivoting. – Koray Aug 28 '20 at 13:04
  • Why this does not work with the relig_income dataset? relig_income %>% pivot_longer(1:11, names_to = "a", values_to = "b", values_transform = list(val = as.character)) – Dario Lacan Nov 28 '21 at 15:49
  • 1
    @DarioLacan It works when you specify the value column name correctly. In your code the column name for the value column is specified as `val`, but your value column is named as `b`, when I replace `val` with `b` the transformation works on my machine. – Koray Nov 29 '21 at 16:33
18

We can specify the values_ptype in this case (as the value columns differ in types)

library(ggplot2)
library(tidyr)
library(dplyr)
small_diamonds %>%  
   pivot_longer( - row_num, 
             names_to = "key",
             values_to = "val", values_ptypes = list(val = 'character'))
# A tibble: 161,820 x 3
#   row_num key   val    
#     <int> <chr> <chr>  
# 1       1 cut   Ideal  
# 2       1 color E      
# 3       1 price 326    
# 4       2 cut   Premium
# 5       2 color E      
# 6       2 price 326    
# 7       3 cut   Good   
# 8       3 color E      
# 9       3 price 327    
#10       4 cut   Premium
# … with 161,810 more rows
akrun
  • 874,273
  • 37
  • 540
  • 662
8

Using your example, you can see with str() that you have two vectors encoded as factors, and two as integers. pivot_longer demands that all vectors are of the same type, and throws the error you have reported.

    library(tidyverse)
    small_diamonds <- diamonds %>%
      select(cut, color, price) %>%
      mutate(row_num = row_number())

    str(small_diamonds)

One solution is to convert all vector to characters with mutate.if, and then pass the pivot_longer command.

    small_diamonds %>% 
      mutate_if(is.numeric,as.character, is.factor, as.character) %>% 
      pivot_longer( - row_num, 
            names_to = "key",
            values_to = "val") 
BMLopes
  • 546
  • 6
  • 10
  • 1
    I believe that converting the columns before running pivot_longer produces a cleaner and more undestandable code. It is very obvious you are performing a conversion when you use the command "mutate". It also adheres to the Unix "kiss" principle: use one tool to convert data, and other to "pivot" the data. – BMLopes Mar 02 '20 at 15:08