# Unit scaling: human readable Quantity units

Consider some calculation yielding Quantity[x,"B"] as output ("B" interpreted as byte).

1. Is there a way to automatically convert this to the unit "GB" or "MB" or "TB" etc as the case may be. i.e. instead of checking y in x<2^(10 y), is there some inbuilt way of doing this?

Consider now the quantity Quantity[x,"s"]. ("s" interpreted as second).

Depending on x, the human readable unit may be micro seconds to days. This again has the same solution structure as the file-size case mentioned earlier. Only the base unit is different.

1. Say one implements the explicit solution mentioned in 1. If one wants to modify the function to now accommodate time based Quantitys too, one needs to know whether the input arg is of "data" category or "time" category. Are there meta categories for units in Mathematica? e.g. "KB,MB,GB,TB"->data, "s,h,year,days"->"time"

I did try CommonUnits, but it didn't work.

• Have you already seen this? Jan 8, 2021 at 9:47
• @J.M.'sennui no I hadn't, I have now....in essence its the same as what I mentioned as an explicit solution in 1. So gather there are no inbuilt functions? Jan 8, 2021 at 9:52
• Not that I recall, no. Jan 8, 2021 at 11:07
• In the Wolfram Function Repository there are resource functions BytesToQuantity, SecondsToQuantity, and MetriNumberString that might be relevant here. Jan 8, 2021 at 14:46
• Per 2: You can make UnitConvert canonicalize units for you and work from there (see the docs) which gives you the "meta-category" Jan 8, 2021 at 17:53

The relevant built-in here seems to be UnitConvert[Quantity[...],"Conventional"] at least to the extent that one's interpretation of "human-readable" is "Conventional".

For time this seems to reasonably align

Table[UnitConvert[Quantity[2^k, "Seconds"], "Conventional"], {k, 4, 24, 2}] // N


while for data "Conventional" seems to be somewhat "less human" as it insists on using bits for scaled units.

Table[UnitConvert[Quantity[2^k, "Bytes"], "Conventional"], {k, 5, 20, 5}]


• This is just an elaboration of b3m2a1's comment although "Conventional" gives "meta categories" (as opposed to a single "meta-category" - i.e. changes units according to the magnitude) and this may not have been an option when the question was asked (?). Also, I agree that it would be useful to have some more "human-readable" options/heuristics available natively. Jul 4 at 5:38
• Thanks for bringing @b3m2a1's comment to my attention (I had moved on from the problem since). Agreed, that a '-h' option should be a must with every function that deigns output to humans. Till then, UnitConvert[...,"Conventional"] is the closest Automatic solution. Also, looking from the comment's timestamp, it must have been an option even back then, though I hadn't known. Jul 4 at 12:58
• " '-h' option should be a must with every function that deigns output to humans" - the difficulty if of course that humans and contexts vary. A further complication is that argument slots are precious and usually don't merit this form of output. Further, the central design tenet in the WL is to output in the rawest form possible and leave decoration to users (much harder to undecorated-decorate). One place where these imperatives collide is in the rendering of TestReportObjects. Here I think there is a case for more native decoration since the output seems to be more likely to be read than .. Jul 4 at 23:25
• immediately processed. In our set-up there is a layer on top of this which is what bought me to this question and despite this "Conventional" setting it didn't quite do the job I was looking for -hence had to spend a few hours building a bespoke version. Apart from the bits/bytes issue, even in time sometimes those >60 seconds might be preferred in minutes, sometimes fractional units are good, sometimes not, sometimes beyond a threshold, seconds are sufficient ... so maybe we need a "-th" option - this human :) Jul 4 at 23:31

This question is very much subject to taste. Here is a few ideas that may be tailored to the users' tastes. I happened to use time units for my example.

v1=Quantity[1,"Exaseconds"]
v2=Quantity[1,"Millenia"]
v3=Quantity[0.123000456,"seconds"];
unitList={"Eons","Megayears","Millenia","Years","Months","Days","Hours","Seconds","Milliseconds","Microseconds","Nanoseconds"}
human[quantity_Quantity,units_List]:= Block[
{result},
result=UnitConvert[quantity,MixedUnit[units]];
result=Evaluate[Select[{result[[1,1]],result[[2,1]]}\[Transpose],#[[1]]!=0&]\[Transpose]];
Quantity[MixedMagnitude[result[[1]]],MixedUnit[result[[2]]]]
]
human[v1,unitList]
human[v2,unitList]
human[v3,unitList]
approximate[quantity_Quantity,units_List]:= Block[
{result},
result=UnitConvert[quantity,MixedUnit[units]];
result=Evaluate[First[Select[{result[[1,1]],result[[2,1]]}\[Transpose],#[[1]]!=0&]]\[Transpose]];
Quantity@@result
]
approximate[v1,unitList]
approximate[v2,unitList]
approximate[v3,unitList]

1Es
1millennium
{Eons,Megayears,Millenia,Years,Months,Days,Hours,Seconds,Milliseconds,Microseconds,Nanoseconds}
31\[ThinSpace]eons709\[ThinSpace]Myr791\[ThinSpace]millennia458\[ThinSpace]yr2\[ThinSpace]mo25\[ThinSpace]days17\[ThinSpace]h2800\[ThinSpace]s
1millennium
123\[ThinSpace]ms456.\[ThinSpace]ns
31eons
1millennium
123ms


Here is a potentially useful utility function:

makeUnits = Function[base, (StringJoin[#1, base] & ) /@
{"quetta", "ronna", "yotta", "zetta", "exa", "peta",
"tera", "giga", "mega", "kilo", "", "milli", "micro",
"nano", "pico", "femto", "atto", "zepto", "yocto",
"ronto", "quecto"}]


{quettajoule,ronnajoule,yottajoule,zettajoule,exajoule,petajoule,terajoule,gigajoule,megajoule,kilojoule,joule,millijoule,microjoule,nanojoule,picojoule,femtojoule,attojoule,zeptojoule,yoctojoule,rontojoule,quectojoule}

human[Quantity[1,"eV"],unitList]
160\[ThinSpace]zJ217\[ThinSpace]yJ663\[ThinSpace]rJ400\[ThinSpace]qJ
human[Quantity[1,"kWh"],unitList]
3\[ThinSpace]MJ600\[ThinSpace]kJ
human[Quantity[1,"BTU"],unitList]
1\[ThinSpace]kJ55\[ThinSpace]J55\[ThinSpace]mJ852\[ThinSpace]\[Mu]J620\[ThinSpace]nJ