Reputation: 1204
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.
Upvotes: 4
Views: 1164
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
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