vincentarelbundock / tinytable

Simple and Customizable Tables in `R`
https://vincentarelbundock.github.io/tinytable
GNU General Public License v3.0
196 stars 16 forks source link

Examples in tutorial for changing column widths? #214

Closed andrewheiss closed 6 months ago

andrewheiss commented 6 months ago

I've found that lots of my students and collaborators want to be able to control column widths in both LaTeX and HTML. Currently tt() does a good job with automatic widths:

x <- data.frame(
  short = c("H1", "H2", "H3"),
  hypothesis = c(
    "Lorem ipsum dolor sit amet, consectetur adipisicing elit",
    "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
    "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi"
  )
)

tt(x)
HTML PDF
image image

Based on #172 it seems possible to control HTML widths with style_tt(width = X), but that uses hard-coded em values. I was trying to use percents, like gt::pct(), but couldn't find a way until I stumbled on this in #112:

it is easy to achieve with CSS

And it is indeed!

tt(x) |> 
  style_tt(j = 1, bootstrap_css = "width: 30%;") |> 
  style_tt(j = 2, bootstrap_css = "width: 70%;")
image

At least for HTML.

I'm still trying to figure out how to control PDF column widths—I'm assuming it involves some settings passed to the tabularray_inner argument in style_tt(), but I haven't taught myself enough tabularray to figure it out yet.

In any case, I think that setting column widths is a common enough use case (see {gt} and {kableExtra} and {huxtable} and {flextable}) to warrant an example in the tutorial.

vincentarelbundock commented 6 months ago

Yes, I agree this is a very common use case, and it should be easier. People should not have to write CSS for this.

I'll try to think of a solution. In the meantime, I believe that tabularray_inner accepts a wd setting, but I'm not sure what unit it requires or if it does percentages.

andrewheiss commented 6 months ago

I figured out one way (based on §2.6.1 in the tabularray manual, but it doesn't use the wd setting and instead sets column widths for the whole table.

tt(x) |> 
  style_tt(tabularray_inner = "colspec={X[0.3,l] X[0.7,l]}")
image

You can also optionally set column alignment there too, which probably conflicts with style_tt(..., align = "blah").

Surely the wd argument is better, but it's hard to search for in the documentation :)

andrewheiss commented 6 months ago

Ah, wd is easier than I thought. It takes any of the standard LaTeX units like pt, mm, cm, in, ex, and em:

tt(x) |> 
  style_tt(j = 1, tabularray_inner = "column{1}={wd=1in}") |> 
  style_tt(j = 2, tabularray_inner = "column{2}={wd=5in}")

You can use percentages too by multiplying stuff with \linewidth:

tt(x) |> 
  style_tt(j = 1, tabularray_inner = "column{1}={wd=0.3\\linewidth}") |> 
  style_tt(j = 2, tabularray_inner = "column{2}={wd=0.7\\linewidth}")

So it seems there are a bunch of ways to control widths (in exact measurements and in percentages) across different formats:

# With colspec and percentages (or exact measurements too)
tt(x) |> 
  style_tt(tabularray_inner = "colspec={X[0.3,l] X[0.7,l]}")

# With wd and inches (or cm or pt or mm or ex or em)
tt(x) |> 
  style_tt(j = 1, tabularray_inner = "column{1}={wd=1in}") |> 
  style_tt(j = 2, tabularray_inner = "column{2}={wd=5in}")

# With wd and percentages
tt(x) |> 
  style_tt(j = 1, tabularray_inner = "column{1}={wd=0.3\\linewidth}") |> 
  style_tt(j = 2, tabularray_inner = "column{2}={wd=0.7\\linewidth}")

# With CSS for HTML
tt(x) |> 
  style_tt(j = 1, bootstrap_css = "width: 30%;") |>
  style_tt(j = 2, bootstrap_css = "width: 70%;")

I have no idea what the best approach for making this all more general and cross-format would be, though. Maybe a function similar to gt::pct() that builds the right percent based on output?

tinytable::pct(30, format = "latex")
#> "column{BLAH}={wd=0.3\\linewidth}"

tinytable::pct(30, format = "html")
#> "width: 30%;"

gt(x) |>
  style_tt(j = 1, width = pct(30)) |>
  style_tt(j = 2, width = pct(70))

That seems complicated, though—idk.

vincentarelbundock commented 6 months ago

Ah thanks for the investigation. That's very useful.

I'm thinking maybe we should just allow a vector in the width argument of tt(), and then document the other options better for those who want more control.

andrewheiss commented 6 months ago

Cool, yeah, a vector in tt() would be easy and straightforward. It would just need to use some sensible default unit for each output

Like tt(x, width = c(3, 7)) could use ems in HTML, and maybe also in LaTeX?

vincentarelbundock commented 6 months ago

I was thinking: elements of the vector are proportions of line width. So you can control both total and column width together. Width a useful error message if the sum of the vector exceeds 1.

andrewheiss commented 6 months ago

Oooh I like combining total and column widths. That fixes an issue I thought of after posting that last comment—if widths is a vector, there's no way to control the overall table width, so you'd need something like tt(x, table_width = 0.8, col_widths = c(0.3, 0.7), where the columns are 30% and 70% of the 80%-wide table

So something like tt(x, width = c(0.24, 0.56)) would get the same thing—30 and 70% of the 80%-wide table

You could feed it math to simplify things: tt(x, width = c(0.3, 0.7) * 0.8)

andrewheiss commented 6 months ago

(↑ that gets trickier with HTML, where CSS would be defined as 30% and 70% within the parent 80% element, though)

vincentarelbundock commented 6 months ago

this feature is merged on Github and there's an example in the tutorial. I just released to CRAN, so have to wait over a month before this makes it there.

Thanks for a great suggestion.

vincentarelbundock commented 6 months ago

Minor improvement merged:

When the sum of the width vector exceeds 1, it is automatically normalized to full-width. This is convenient when we only want to specify column width in relative terms:

tt(x, width = c(1, 1, 1, 4)) |> 
    style_tt(0:4, line = "tblr", align = "c")

Screenshot_2024-03-30_00-43-21

andrewheiss commented 5 months ago

Finally getting around to playing with these changes and it's sooo nice!

This monstrosity (for simultaneous HTML and PDF output)…

tt(x) |> 
  style_tt(tabularray_inner = "width={1\\linewidth},colspec={X[0.3]X[0.7]}") |> 
  style_tt(j = 1, bootstrap_css = "width: 30%;") |>
  style_tt(j = 2, bootstrap_css = "width: 70%;")

…gets simplified down to just this:

tt(x, width = c(0.3, 0.7))
vincentarelbundock commented 5 months ago

Nice!

You can also pick 1 and 2.5 if you want to use relative widths. When the sum is greater than 1, entries get normalized.