16

I have data that looks like this:

library(tidyverse)
df <- tibble(
    x = c(0, 179, 342, 467, 705, 878, 1080, 1209, 1458, 1639, 1805, 2000, 2121, 2339, 2462, 2676, 
      2857, 3049, 3227, 3403, 3583, 3651, 4009, 4034, 4151, 4194, 4512, 4523, 4679, 5789), 
    y = c(4.7005, 4.8598, 5.0876, 5.0938, 5.3891, 5.6095, 5.8777, 6.0064, 6.3063, 6.4723, 6.6053, 
          6.8145, 6.9078, 7.1701, 7.2633, 7.3865, 7.5766, 7.644, 7.8018, 7.9505, 8.0974, 8.1937, 
          8.2391, 8.294, 8.3143, 8.3452, 8.5092, 8.5172, 8.5993, 9.0275))

Is it possible to convert my dataframe/tibble object to a tribble "constructor"?

I'm looking for something like dput, but more lightweight and specifically for dataframes.

emehex
  • 9,874
  • 10
  • 54
  • 100
  • 3
    Package-heavy, but quick: `clipr::write_clip(df); datapasta::tribble_paste()` – alistaire Mar 16 '17 at 16:40
  • 2
    [This is the subject of an open issue on GitHub](https://github.com/tidyverse/tibble/issues/127) if anyone is feeling up to a PR. – alistaire Mar 16 '17 at 17:01
  • @alistaire I've put a version of my implentation up on a fork of `tibble` - see my answer below. – Nick Kennedy Mar 16 '17 at 18:24
  • 2
    oh. A tribble is a **tr**ansposed t**ibble**. https://github.com/tidyverse/tibble/issues/143 . I thought https://www.google.com/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&cad=rja&uact=8&ved=0ahUKEwjB7e7H1NvSAhWr6IMKHbafD2UQjRwIBw&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FTribble&psig=AFQjCNENJetsdn-j2-k_Sa6pTalalxXuGg&ust=1489775508756862 – Ben Bolker Mar 16 '17 at 18:32

7 Answers7

22

datapasta::dpasta() should be suitable. Output from your example:

dpasta(df)
tibble::tribble(
    ~x,     ~y,
     0, 4.7005,
   179, 4.8598,
   342, 5.0876,
   467, 5.0938,
   705, 5.3891,
   878, 5.6095,
  1080, 5.8777,
  1209, 6.0064,
  1458, 6.3063,
  1639, 6.4723,
  1805, 6.6053,
  2000, 6.8145,
  2121, 6.9078,
  2339, 7.1701,
  2462, 7.2633,
  2676, 7.3865,
  2857, 7.5766,
  3049,  7.644,
  3227, 7.8018,
  3403, 7.9505,
  3583, 8.0974,
  3651, 8.1937,
  4009, 8.2391,
  4034,  8.294,
  4151, 8.3143,
  4194, 8.3452,
  4512, 8.5092,
  4523, 8.5172,
  4679, 8.5993,
  5789, 9.0275
  )

https://cran.r-project.org/web/packages/datapasta/index.html https://github.com/MilesMcBain/datapasta

MilesMcBain
  • 1,115
  • 10
  • 12
  • This is the only one that actually worked for me for string columns that contain quotes (`"`) (e.g. json) – Stefan F Oct 13 '22 at 09:45
13

I think mc_tribble is a better name, and it looks like you can just condense it to:

mc_tribble <- function(indf, indents = 4, mdformat = TRUE) {
  name <- as.character(substitute(indf))
  name <- name[length(name)]

  meat <- capture.output(write.csv(indf, quote = TRUE, row.names = FALSE))
  meat <- paste0(
    paste(rep(" ", indents), collapse = ""),
    c(paste(sprintf("~%s", names(indf)), collapse = ", "),
      meat[-1]))

  if (mdformat) meat <- paste0("    ", meat)
  obj <- paste(name, " <- tribble(\n", paste(meat, collapse = ",\n"), ")", sep = "")
  if (mdformat) cat(paste0("    ", obj)) else cat(obj)
}

Try it out:

short_iris <- head(iris)

mc_tribble(short_iris)

Improvements:

  • Shorter code
  • Captures the name of the "tibble"
  • Has an argument for indenting
  • Has an argument for conveniently adding 4 spaces for pasting on Stack Overflow
  • Sounds more tasty

I've added this to my "SOfun" package. You can install it with:

source("http://news.mrdwab.com/install_github.R")
install_github("mrdwab/overflow-mrdwab") # for writeClip -- plus it's awesome
install_github("mrdwab/SOfun")

Usage is then simply:

library(SOfun)
mc_tribble(short_iris)

Advantages:

  • Now copies the output to your clipboard (if you have "overflow" installed)
  • Even more affordable than before!
A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
2

I've created a more extensive solution inspired by that of @A5C1D2H2I1M1N2O1R2T1. It handles most standard column types including character, factor, integer, numeric, logical and list. It also adopts the syntax of dput with respect to the second parameter, so should be able to output to a file, a connection or (by default) the console. It also adopts the standard return value of dput which is its input, invisibly.

dput_to_var <- function(x) {
  con <- textConnection("out", "w", local = TRUE)
  dput(x, con)
  close(con)
  paste(out, collapse = "")
}

dput_tribble <- function(indf, file = "") {
  stopifnot(is.data.frame(indf))
  cols <- lapply(indf, function(col) {
    switch(class(col),
           factor =, character = paste0("\"", col, "\""),
           logical =, numeric =, integer = col,
           list = lapply(col, dput_to_var)
           )
  })
  meat <- c(paste(sprintf("~%s", names(indf)), collapse = ", "),
            do.call(paste, c(cols, sep = ", ")))
  out <- paste0("tribble(\n", paste(meat, collapse = ",\n"), ")")
  if (is.character(file)) {
    if (nzchar(file)) {
      file <- file(file, "wt")
      on.exit(close(file))
    } else {
      file <- stdout()
    }
  }
  writeLines(out, file)
  invisible(indf)
}

Per the suggestion by @alistaire, I've forked the tibble package and added this. I have made a pull request.

Nick Kennedy
  • 12,510
  • 2
  • 30
  • 52
2

Per Kirill's comment here, this is now part of a package: https://github.com/krlmlr/deparse, in the function deparsec

library(deparse)
#> 
#> Attaching package: 'deparse'
#> The following object is masked from 'package:base':
#> 
#>     deparse
library(tibble)

# dataframe
df <- tribble(
  ~a, ~b, ~c,
  1L, 0.1, "a"
)

# tribble script
deparsec(df, as_tribble = TRUE)
#> tribble(
#>   ~a, ~b,  ~c, 
#>   1L, 0.1, "a"
#> )

Created on 2018-08-09 by the reprex package (v0.2.0).

shiro
  • 578
  • 3
  • 9
1

After flaming out in the previous version of this question I spent sometime trying to hack together what I am looking for:

The "mribble" function (as in make tribble):

mribble <- function(df) {

    names <- colnames(df)
    names <- sapply("~", paste, names, sep = "")
    names <- as.character(names)
    names <- paste(names, collapse = ", ")
    names <- paste(names, ",\n", sep = "")

    rows <- NULL
    for(i in seq_along(1:nrow(df))) {
        r <- as.character(df[i,])
        r <- paste(r, collapse = ", ")
        r <- paste(r, ",\n", sep = "")
        rows <- c(rows, r)
    }

    last <- rows[length(rows)]
    rows <- rows[-length(rows)]
    last <- substr(last, 1, nchar(last)-3)
    rows <- c(rows, last)

    meat <- c(names, rows)
    meat <- paste(meat, collapse = "")

    bun <- paste("df <- tribble(\n", meat, ")", sep = "")

    cat(bun)
}

mribble(df)

Which will print this to the console:

df <- tribble(
    ~x, ~y,
    0, 4.7005,
    179, 4.8598,
    342, 5.0876,
    467, 5.0938,
    705, 5.3891,
    878, 5.6095,
    1080, 5.8777,
    1209, 6.0064,
    1458, 6.3063,
    1639, 6.4723,
    1805, 6.6053,
    2000, 6.8145,
    2121, 6.9078,
    2339, 7.1701,
    2462, 7.2633,
    2676, 7.3865,
    2857, 7.5766,
    3049, 7.644,
    3227, 7.8018,
    3403, 7.9505,
    3583, 8.0974,
    3651, 8.1937,
    4009, 8.2391,
    4034, 8.294,
    4151, 8.3143,
    4194, 8.3452,
    4512, 8.5092,
    4523, 8.5172,
    4679, 8.5993,
    5789, 9.027)

My solution is super janky and doesn't work with characters. Feedback would be most appreciated.

emehex
  • 9,874
  • 10
  • 54
  • 100
0

The answer by package author Miles McBain is no longer correct since the package has been updated. Now the correct function is:

your_data <- 
  data.frame(
    x = 1:3,
    y = 4:6
  )

datapasta::tribble_paste(your_data)

This will paste a tribble at the cursor. If you want to return the string instead of pasting it, there's tribble_contruct() and if you want to copy the tribble without pasting it at the cursor, so you can choose where to put it, use tribble_format(). These functions are also pipe-friendly.

Brian
  • 7,900
  • 1
  • 27
  • 41
0

{constructive} can do it:

# remotes::install_github("cynkra/constructive")
library(constructive)
construct(df, opts_tbl_df("tribble"))
tibble::tribble(
  ~x,   ~y,
  0,    4.7005,
  179,  4.8598,
  342,  5.0876,
  467,  5.0938,
  705,  5.3891,
  878,  5.6095,
  1080, 5.8777,
  1209, 6.0064,
  1458, 6.3063,
  1639, 6.4723,
  1805, 6.6053,
  2000, 6.8145,
  2121, 6.9078,
  2339, 7.1701,
  2462, 7.2633,
  2676, 7.3865,
  2857, 7.5766,
  3049, 7.644,
  3227, 7.8018,
  3403, 7.9505,
  3583, 8.0974,
  3651, 8.1937,
  4009, 8.2391,
  4034, 8.294,
  4151, 8.3143,
  4194, 8.3452,
  4512, 8.5092,
  4523, 8.5172,
  4679, 8.5993,
  5789, 9.0275,
)

You might also use read.table(), mostly useful if you don't want a tibble but with this example:

construct(df, opts_tbl_df("next"), opts_data.frame("read.table"))
read.table(header = TRUE, text = "
x     y
0.    4.7005
179.  4.8598
342.  5.0876
467.  5.0938
705.  5.3891
878.  5.6095
1080. 5.8777
1209. 6.0064
1458. 6.3063
1639. 6.4723
1805. 6.6053
2000. 6.8145
2121. 6.9078
2339. 7.1701
2462. 7.2633
2676. 7.3865
2857. 7.5766
3049. 7.644
3227. 7.8018
3403. 7.9505
3583. 8.0974
3651. 8.1937
4009. 8.2391
4034. 8.294
4151. 8.3143
4194. 8.3452
4512. 8.5092
4523. 8.5172
4679. 8.5993
5789. 9.0275
") |>
  structure(class = c("tbl_df", "tbl", "data.frame"))

here we use "next" to fall back on the constructor for the next class, which is "data.frame", and we set the data frame constructor to "read.table", then attributes are repaired so you get a tibble in the end.

moodymudskipper
  • 46,417
  • 11
  • 121
  • 167