Write fixed-width table

Hi! I cannot find a simple way to write a dataframe/array of tuples/… to file with fixed-width columns. CSV package does not support fixed width or formatting in general; manually looping over rows and calling @sprintf makes it really verbose and error-prone to handle missings - not even talking about manually changing the code each time a column is added/removed.

Am I missing something here?

Have you tried this package? https://github.com/ronisbr/PrettyTables.jl

1 Like

Thanks for suggestion, it mostly works for simple scenarios!
However, anything even slightly more complicated requires much more code, and becomes more and more verbose with repetitions:

data = [[ rand() < 0.8 ? (col == "C" ? "Abc" : rand()) : missing for a = 0:10] for col = ["A", "B", "C"]];
df = DataFrame(data)


my_format(x::Float64, _) = @sprintf("%.2f", x)
my_format(x::String, _) = x
my_format(::Missing, _) = ""

pretty_table(df, borderless; formatter=Dict(0 => my_format, 2 => (x, _) -> ismissing(x) ? "" : @sprintf("%.5f", x)))

Repetitions are a sign that you might write your own macros :wink:

Can you clarify where? I find the above code quite compact and readable. You can always introduce a function that wraps a function to ignore the second argument to get rid of the _s, but here the gain is minor.

Compare

my_format(x::Float64, _) = @sprintf("%.2f", x)
my_format(x::String, _) = x
my_format(::Missing, _) = ""

pretty_table(df, borderless; formatter=Dict(
    0 => my_format,
    2 => (x, _) -> ismissing(x) ? "" : @sprintf("%.5f", x),
    3 => (x, _) -> ismissing(x) ? "" : @sprintf("%.4f", x),
    7 => (x, _) -> ismissing(x) ? "" : @sprintf("%.3f", x)))

to this (hypothetical) example:

pretty_table(df, borderless; format=Dict(
    Missing => "", Real => "%.2f", String => "%s",
    :x2 => "%.5f", :x3 => "%.4f", :x7 => "%.4f"))

or something along these lines.

There are two (orthogonal) improvements that could be made here:

  1. As @tamasgal suggested, define a macro that transforms @myformatter "%2f" to (x, _) -> ismissing(x) ? "" : @sprintf("%.5f", x) etc. This is quite easy to do. If you use something like Formatting.sprintf1, it does not even have to be a macro.

  2. Open an issue in PrettyTables, asking for lookup by column.

1 Like