Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

function to change numeric output format #6493

Open
rominf opened this issue Apr 10, 2014 · 24 comments · May be fixed by #52543
Open

function to change numeric output format #6493

rominf opened this issue Apr 10, 2014 · 24 comments · May be fixed by #52543
Labels
domain:display and printing Aesthetics and correctness of printed representations of objects. good first issue Indicates a good issue for first-time contributors to Julia stdlib:REPL Julia's REPL (Read Eval Print Loop)

Comments

@rominf
Copy link

rominf commented Apr 10, 2014

I want change output format for numbers (stand alone floats, float matrix, BigFloats). I can do it in IPython by using %precision magic function. I didn't find any equivalent in Julia.

I know that I can use round but it actually returns different number. It's also cumbersome to convert arrays with combination map + round. One another problem: I didn't get how to change real or output precision of BigFloat: set_bigfloat_precision changes precision for all new BigFloats, copy and deepcopy don't help (they copy value irrespective of new default BigFloat precision), naive precision change via BigFloat.prec corrupts it.

I know, that I handle lot's of different topics in this question and I as a programmer understand that lot's of difficulties lays in output formats. But my friend, mathematician asked me: "Why can I just type %precision in my IPython notebook but not in Julia? Isn't output format change essential feature?".

@rominf rominf changed the title Lack of universal function to change numeric output format in Julia Lack of universal function to change numeric output format Apr 10, 2014
@pao pao added REPL labels Apr 10, 2014
@pao
Copy link
Member

pao commented Apr 10, 2014

See also MATLAB's format console commands.

@jiahao
Copy link
Member

jiahao commented Apr 10, 2014

This is more appropriate for the IJulia repository. IPython magics were last discussed in JuliaLang/IJulia.jl#12 but the issue was closed due to lack of interest.

@pao
Copy link
Member

pao commented Apr 10, 2014

I think there's a scope question--is this something we'd like to support in the native REPL, or only in IJulia?

@ivarne
Copy link
Sponsor Member

ivarne commented Apr 10, 2014

I would say there is a deeper issue that needs to be handled in the base system in order for the support to be well knitted together. IJulia can not rewrite all the printing routines in order for %precicion to work. See also: #5709 #6117

@StefanKarpinski
Copy link
Sponsor Member

I don't think this is IJulia-specific at all. This is something we need to address generally for all kinds of UIs. It's fine to have a global setting that changes the way things are printed interactively, but I think this is rather bad practice for anything non-interactive.

@rominf
Copy link
Author

rominf commented Apr 10, 2014

If the question turn into printing in general... See also: https://docs.sympy.org/dev/modules/printing.html
They have pretty impressive console output, LaTeX and html support.

@nalimilan
Copy link
Member

nalimilan commented Apr 11, 2014

I don't think this is IJulia-specific at all. This is something we need to address generally for all kinds of UIs. It's fine to have a global setting that changes the way things are printed interactively, but I think this is rather bad practice for anything non-interactive.

An exception is code producing documents automatically from a mix of text content and Julia code, like IJulia but for LaTex, ODF, etc. You likely want to specify once for all how numbers should be printed in that case. That probably can be considered as "interactive", since you immediately get to see the result.

@cdsousa
Copy link
Contributor

cdsousa commented Apr 11, 2014

Is there currently any workaround to universally change the format of numeric output?

@stevengj
Copy link
Member

Can something be done here with the new IOContext stuff?

@vtjnash
Copy link
Sponsor Member

vtjnash commented Aug 20, 2016

It is certainly technically possible, although I'm not sure that it is necessarily the right solution. At some level, this moves from printing of a value, to formatting of the value. That suggests (to me at least) that the initial output maybe should some sort of rich document rather than a string, then combine that with a style-sheet to make the final output. I think that level of detail would be more appropriate for a package however. That's a yak-shave I've considered pursuing further, but more pressing issues have been holding my attention. So I'll certainly go along with it if someone wants to add this to IOContext, for lack of any solid alternative to propose right now.

@JeffBezanson JeffBezanson added the domain:display and printing Aesthetics and correctness of printed representations of objects. label Jan 3, 2017
@JeffBezanson JeffBezanson changed the title Lack of universal function to change numeric output format function to change numeric output format Jan 3, 2017
@fbruetting
Copy link
Contributor

There's NumericIO.jl which provides some of this features and maybe could help.

@ma-laforge
Copy link
Contributor

ma-laforge commented Jan 8, 2017

write vs print vs show vs display:

Sorry for doing this again, but I have gone through alot of conversations on the subject, and would like to make a little synopsis before moving forward. I realize some of what I say probably does not match the accepted picture at the moment. I am trying to fill in the gaps using my own experience with Julia, hoping it might help readers (and hopefully not hinder):

High-level differentiations:

  • write(::IO, x): Serialization of x to a binary stream - as in write(fileio, Float64(1.0)).
  • print(::IO, x): Serialization of x to a text-based stream. I believe this to be the intended behaviour of print in most languages. Though it is ok to apply minor output control here (ex: output precision to reduce output size), it is not ok to apply complex attributes like internationalization with the print function. The idea being that this output will later be read (de-serialized) by some reader program.
  • printfmt(::IO, x): Sorry for adding a new one here: There needs to be some form of print that is meant to generate a human-readable/highly-formatted output - typically targeting the REPL. Preferably, this function should be named so as to not confuse it with print. The printfmt function would make heavy use of ::IOContext arguments to allow for fine control of the output. This output is not meant to be read by some machine like the output of print. Thus, one can apply more complex formatting like internationalization here.
  • show(::IO, x): Slightly higher level than printfmt. Meant to provide the user with more detail about the object (ex: type name & other properties not normally printed).
  • display(::Display, x): Higher-level concept than show. Not necessarily tied to an IO stream (at least not directly). Meant to give the user some control over where/how to display certain output, by means of the Base.Multimedia.displays "stack".

Some basic relationships

  • write(::IO, ::MIME"text/plain", x) = print(::IO, x): When write is qualified with MIME text/plain, it basically means we are serializing to a text-based stream (i.e. print).
  • show(::IO, x) = printfmt(::IO, typeof(x), "(", x, ")"): The default behaviour.
  • show(::IO, ::MIME"text/plain", x) = show(::IO, x): The default behaviour.

printfmt() methods
One would apply internationalization of numbers at this level.

For example:
printfmt(STDOUT, 3141592.65) => "3,141,592.65" #English (US)
printfmt(STDOUT, 3141592.65) => "3 141 592,65" #French (CAN)

writemime() (deprecated)

I know in Julia v0.5, we replaced writemime() with show() instead of write(), but this is odd to me. I feel that the ex-writemime function is more of a "serializing" operation (though potentially with some information loss):

writemime(::IO, ::MIME"image/png", x) => write(::IO, ::MIME"image/png", x)

Instead, I believe the show() function should not only serialize the object to the given mime, but also describe the object (like implementations of the show() function usually do):

type MyObject
	# Contents can be rendered as image/png
end

function show(io::IO, ::MIME"text/html", x::MyObject)
	printfmt(io, typeof(x), "\n")
	write(io, MIME("image/png"), x) #Render image
end

show(), printfmt() methods & the ::IOContext object
Thus, I believe it is the show()/printfmt() functions that would make the most use of ::IOContext. For example:

function show(io::IOContext, ::MIME"text/html", x::MyObject)
	if !haskey(io.dict, :omittype)
		printfmt(io, typeof(x), "\n")
	end
	write(io, MIME("image/png"), x) #Render image
	#Maybe show other important details regarding the object here...
	#Maybe display an internal reference count, for example so one can more 
	#easily debug the code.
end

show(IOContext(fileio, compact=true, omittype=true), x) #Show the object

display() & plots

As stated before, the display mechanism applies in a more general context (not limited to IOs), and allows for some user control.

Take for example the idea of displaying a plot object created with Plots.jl:
WARNING: This is not how Plots.jl currently displays its plots - this is a conceptualization.

immutable PyPlotDisplay <: Base.Display
	backend::Symbol #:tk, :gtk, ...
end

#If we want to specify the default output for plots generated by Plots.jl,
#we could do something like the following:
pushdisplay(PyPlotDisplay(:gtk))

#... and not specifying this display would simply cause IJulia's REPL
#    to fall back to inline plots!

display to control output precision

Now this is where I address the issue itself (sorry for the long wait).

Assuming we have a reference to the Julia REPL, repl, a user could add the following to their ~/.juliarc file:

ctx = IOContext(repl)
ctx[:floatprecision] = 4
ctx[:floatoutput] = :SI #Or maybe :SCI/:ENG/:binary/:finance/...
[...]

In my opinion, this solution is consistent with the intent of both Base.Display, and IOContext.

@ma-laforge
Copy link
Contributor

As for the comment from @fbruetting:

NumericIO.jl is not a solution to the problem in general, but could be used to improve the control of Julia's numeric output:

print(formatted(STDOUT, :SI, ndigits=4), 3.14159e-8) => "31.4n"
print(formatted(STDOUT, :SCI, ndigits=4), 3.14159e-8) => "3.14×10⁻⁸"
print(formatted(STDOUT, :ENG, ndigits=4), 3.14159e-8) => "31.4×10⁻⁹"
print(formatted(STDOUT, :ENG, charset=:ASCII, ndigits=4), 3.14159e-8) => "31.4E-9"

Of course, some things would have to be modified to conform with my description above:

formatted -> IOContext
print -> printfmt
...

@stevengj
Copy link
Member

stevengj commented Aug 30, 2017

The most basic thing that is needed here is just to change the REPL display function so that it passes :compact => d.compact, where d.compact is a REPL setting that can be changed via some API. Or better yet, just passes d.defaults… (a list of an arbitrary number of default IOContext options).

To the extent that IOContext adds support for more fine-grained control of numeric output, we can add REPL settings for that too, but the basic point is that the user should be able to control what options the REPL passes to its default IOContext. (And IJulia could use the same options.)

@halars
Copy link

halars commented Dec 17, 2018

Any final conclusion about this?

@quinnj
Copy link
Member

quinnj commented Aug 7, 2019

Dusting off an old issue here; I think @stevengj, as usual, has the right idea here: some mechanism (global Refs maybe) to have some REPL defaults that the default REPL display function would pass in it's IOContext. One of those could include a :precision option that by default would be 6, but could be configurable (setNumericPrecision(x)). This would be a great "first PR" if someone wants to tackle it.

@quinnj quinnj added the good first issue Indicates a good issue for first-time contributors to Julia label Aug 7, 2019
@rfourquet
Copy link
Member

For accessing the REPL's IOContext part, see #29249.

@kubark42
Copy link

kubark42 commented Apr 23, 2020

Did this mature to the point that there is a concrete example of how to adjust the number of significant digits? I share the complaint that others have expressed previously, print() loses utility if the output cannot be tailored to a global need.

@OliverEvans96
Copy link

OliverEvans96 commented Aug 9, 2020

I'm sure the issue is much more complex than I'm aware (since I haven't read all the posts here), but I just wanted to share my quick, simplistic solution that was sufficient for my needs:

using Printf
Base.show(io::IO, x::Float64) = write(io, @sprintf("%.2f", x))

It's fairly straightforward to turn this into a macro:

macro format(ex)
   quote 
       Base.show(io::IO, x::Float64) = write(io, @sprintf($ex, x))
   end
end

which can then be called as follows:

julia> @format "%.2f"

julia> rand(3, 3)
3×3 Array{Float64,2}:
 0.64  0.68  0.98
 0.85  0.41  0.49
 0.42  0.45  0.96

@chakravala
Copy link
Contributor

That is a temporary manual solution. I need something which isn't a macro for this.

Why can't there be a regular function which dynamically accepts a format string?

@OliverEvans96
Copy link

What's wrong with a macro?

@quinnj
Copy link
Member

quinnj commented Aug 21, 2020

@chakravala, feel free to review/chime in on #32859, which allows doing Printf.format(fmt::String, args...)

@kubark42
Copy link

@OliverEvans96 the issue with a macro arises from long-term support. A macro gets the job done, but it quickly turns to a headache for future code maintainers. Couple this with the fact that macros exist in isolation and it's easy to see that they will be orphaned and bit rot on a long-enough horizon.

I think the desire is to have something officially supported so that it's a tier one feature.

@ndattani
Copy link

Why can we not just do the following?

format('long','g')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain:display and printing Aesthetics and correctness of printed representations of objects. good first issue Indicates a good issue for first-time contributors to Julia stdlib:REPL Julia's REPL (Read Eval Print Loop)
Projects
None yet
Development

Successfully merging a pull request may close this issue.