Mark Neal
Mark Neal

Reputation: 1204

Create a tibble with tribble using defaults that don't cause an error, e.g. like tibble

A recent update of packages means that this code for neatly making a tibble with tribble() (then used to make a table) no longer works.

library(dplyr)
table_data_1 <- tribble(~"Header1_A", ~"Header2_A",
                      0.19,          0.20,
                      "Header1_B",  "Header2_B",
                      0.19,          0.20)

The error message:

Error: Can't combine `Header1_1` <double> and `Header1_1` <character>.
Run `rlang::last_error()` to see where the error occurred.

And rlang::last_error() produces:

<error/vctrs_error_incompatible_type>
Can't combine `Header1_A` <double> and `Header1_A` <character>.
Backtrace:
 1. tibble::tribble(...)
 6. vctrs::vec_default_ptype2(...)
 7. vctrs::stop_incompatible_type(...)
 8. vctrs:::stop_incompatible(...)
 9. vctrs:::stop_vctrs(...)
Run `rlang::last_trace()` to see the full context.

Now that is probably one interpretation of reasonable that a recent update would make a default class for a column based on the first row of data, which in this case is then numeric. Then it is totally logical that it throws an error when I give it some text in the next row, which would be interpreted as character when it expects numeric. However, I'll note that tibble() has more sensible defaults (in my view), so I can create the desired tibble with a sensible default to character.

table_data_2 <- tibble(Header1_A = c(0.19, "Header1_B", 0.19), Header2_A = c(0.19, "Header2_B", 0.19))
table_data_2
# A tibble: 3 x 2
  Header1_A Header2_A
  <chr>     <chr>    
1 0.19      0.19     
2 Header1_B Header2_B
3 0.19      0.19  

However, it's not clear to me how I could retain the neat data entry of a tribble and override the default behaviour and specify that the columns should all be treated as character, without putting all the numbers in inverted commas - which would not give an identical tibble.

How can I get the tibble default behaviour (a sensible guess of class) with tribble, or specify the class?

EDIT Someone asked what my use case was. Basically I just wanted a neat tribble/tibble to demonstrate how you might go about making a table in rmarkdown that was a downloadable table in html output (with DT), or just neatly formatted in pdf/latex output with kableExtra). I live in hope that gt makes a lot of this stuff unnecessary!

---
title: "Untitled"
author: "me"
date: "22/05/2020"
output:
  # pdf_document:
  #   latex_engine: xelatex
  html_document: default
---

```{r setup, echo=FALSE, message=FALSE}
library(tidyverse)
table_data_2 <- tibble(Header1_A = c(0.19, "Header1_B", 0.19), Header2_A = c(0.19, "Header2_B", 0.19))
table_data_2


## Format table packages
library(kableExtra)
library(DT)

## Format for latex/PDF
my_table <- kable(table_data_2, format="latex", caption = "Dairy Farm Owner-operator Terms of Trade") %>%
  kable_styling(latex_options = c("striped", "hold_position")) %>%
  row_spec(c(0,2), bold = T) %>% 
  row_spec(0:(table_data_2 %>% 
                nrow()), extra_latex_after = "\\arrayrulecolor{gray}") %>% 
  row_spec(seq(1,table_data_2 %>% 
                 nrow(),2), background = "#daf7e1")

my_table #if latex output

## Format for html

#formatting the row stripes for datatables tables
callback <- c(
  "$('table.dataTable.display tbody tr:odd').css('background-color', '#b3ffd9');",
  "$('table.dataTable.display tbody tr:even').css('background-color', 'white');",
  "$('table.dataTable.display tbody tr:odd')",
  "  .hover(function(){",
  "    $(this).css('background-color', '#74e3ab');",
  "   }, function(){",
  "    $(this).css('background-color', '#b3ffd9');",
  "   }",
  "  );",
  "$('table.dataTable.display tbody tr:even')",
  "  .hover(function(){",
  "    $(this).css('background-color', '#d4d4d4');",
  "   }, function(){",
  "    $(this).css('background-color', 'white');",
  "   }",
  "  );"
)

formatStyle(
  datatable(table_data_2, extensions = 'Buttons',
            options=list(dom = 'Brt', buttons = c('excel','csv','print'),
                         pageLength=dim(table_data_2)[1],
                         ordering = FALSE),
            rownames = FALSE, 
            callback = JS(callback)
  ), 1, target = "row",
  fontWeight = styleEqual("Header1_B", "bold") )


```

For what its worth, image shows html on left, pdf/latex on right.

output

Upvotes: 4

Views: 1164

Answers (2)

Simon Woodward
Simon Woodward

Reputation: 2026

If your use case is easily reading a table with unknown structure into a data frame, you can do it like this.

data <- list("Header1_A", "Header2_A",
             0.19,          0.20,
             "Header1_B",  "Header2_B",
             0.19,          0.20)

df <- as.data.frame(matrix(unlist(data), ncol = 2, byrow = TRUE), stringsAsFactors = FALSE)

df 
#>          V1        V2
#> 1 Header1_A Header2_A
#> 2      0.19       0.2
#> 3 Header1_B Header2_B
#> 4      0.19       0.2

Created on 2020-05-24 by the reprex package (v0.3.0)

To preserve column types (if possible) you could send the data list to this

dribble <- function(data, ncol){
  df <- list()
  for (i in 1:ncol){
    x <- unlist(data[seq(i, length(data), ncol)])
    df[[paste0("X", i)]] <- x
  }
  as.data.frame(df, stringsAsFactors = FALSE)
}

Upvotes: 1

Simon Woodward
Simon Woodward

Reputation: 2026

You could just use list() instead of tribble; you're going to have to do a bit of manipulation in any case.

library(dplyr)
library(tidyr)

data <- list("Header1_A", "Header2_A",
                        0.19,          0.20,
                        "Header1_B",  "Header2_B",
                        0.19,          0.20)
type <- lapply(data, typeof)

df <- data.frame(
  headers = unlist(data[unlist(type) == "character"]),
  values = unlist(data[unlist(type) == "double"])
)

df %>% 
  separate(headers, into = c("header", "group"), sep = "_") %>% 
  pivot_wider(id_cols = group, names_from = header, values_from = values)
#> # A tibble: 2 x 3
#>   group Header1 Header2
#>   <chr>   <dbl>   <dbl>
#> 1 A        0.19     0.2
#> 2 B        0.19     0.2

Created on 2020-05-22 by the reprex package (v0.3.0)

Upvotes: 0

Related Questions