I want to add a row to an existing data frame (or tibble) of mixed column types that contains only characters for each 'column'. This is how I got it to work, which should be good enough but it seems that this should be easier.

df is representative of a more complicated data frame that I have previously created.

df <- read_csv("12, 34, 10\n10, 29, 14",
          col_names = c("part_1", 
                        "part_2", 
                        "part_3"))

c_name <- colnames(df)

info <- read_csv("blue, green, small\n0,0,0",
                 col_names = c_name) %>%
        filter(part_1 > 0)

results <- data.table::rbindlist(list(info, df))

Which does generate the correct output:

part_1  part_2  part_3
  blue   green   small
    12      34      10
    10      29      14

But like I said, it seems to me that there should be an easier way to do this staying within the tidyverse. When I tried this by using bind_rows I got the error :

"Error in bind_rows_(x, .id) : Can not automatically convert from character to integer in column "part_1".

Any suggestions?

upvote
  flag
bind_rows and bind_cols don't implicitly convert your column types. You are trying to bind character columns with integer ones. Use mutate_all to convert integers to character before using either: bind_rows(list(info, mutate_all(df, as.character))). – Abdou
1 upvote
  flag
If you're doing this for some report or something, a better way might be to make column names with line breaks in them: names(df) = paste(names(df), c("blue", "green", "small"), sep = "\n"). This will preserve your column classes and might print correctly (depending on your output format). – Gregor

2 Answers 11

up vote 1 down vote accepted

You can convert all columns to characters using mutate_all on both dataframes inside map:

results <- purrr::map(list(info, df), function(x){
  x %>%
    mutate_all(as.character)
}) %>%
  do.call(rbind, .)

If you really want to use bind_rows you could do this:

results <- info %>%
  mutate_all(as.character) %>%
  bind_rows(df %>%
              mutate_all(as.character))

This is assuming you want to make sure all columns in both info and df are characters, if you know for sure that info will be characters then you can drop the first mutate_all

upvote
  flag
Thanks everyone, all response do get the desired results, I like this last option with bind_rows by tbradley the best. And I learned something new, I did not know one could pipe within function call like that,cool. You are correct @crazybilly, this is just for presentation so additional calcs at this stage is in-necessary. – Austin Overman

Overall, I'd like to persuade you not to do this: by definition, in R, data frame columns are all of one type, mixing types within a column (in this case, character strings mixed with numeric values), forces R to fall back to the lowest type, see the section about Vectors May Only Have One Type on this page.

The problem is that you end up with a bunch of things that look like numbers, but are actually just text--if you need to do any more arithmetic or analysis on them, you're hosed.

That said, occasionally, I have call to do something similar, when I'm formatting a table for presentation. In that case, you'll need to coerce the values in df to character first:

df %>%
   purrr::map_df(as.character) %>% # loop through each column and apply as.charater()
   bind_rows(info)
2 upvote
  flag
To drop the purrr dependency you could do df %>% mutate_all(as.character) %>% ... – Gregor

Not the answer you're looking for? Browse other questions tagged or ask your own question.