1
$\begingroup$

Is there any way to automatically rewrite units so that they display more naturally looking magnitudes? By this I mean that

{Quantity[1000,"m"],Quantity[3600,"s"]}

Could equivalently be shown as

{Quantity[1,"km"],Quantity[1,"h"]}

I would like to do this for user-facing functions, where it's much more meaningful to see Quantity[1,"day"] instead of Quantity[86400,"s"]

Note:

  • I am looking for a function automatically choosing whether to do this, and how to do it, as opposed to manually using UnitConvert (think of how your OS displays file sizes in reasonable-looking units instead of always using bytes).

  • UnitSimplify doesn't seem to be able to do this, as far as I can tell.

$\endgroup$
4
$\begingroup$

This is a difficult problem: What is the "best" unit to choose?

To get started, let's make a list of all available units (thanks Fidel I. Schaposnik):

allunits = QuantityUnits`$UnitList;

and then define a function that gives me all units that are compatible with a given unit:

allcompatibleunits[x_] := Select[allunits, CompatibleUnitQ[#, x] &]

For example, there are lots of units that are compatible with Second:

allcompatibleunits["Second"]
(*    {"AcademicBimesters", "AcademicQuarters", "AcademicQuinmesters",
      ...
       "Yottaseconds", "Zeptoseconds", "Zettaseconds"}    *)

(there are 216 of them, some very exotic).

Let's convert something to all compatible units:

allconversions[x_] := UnitConvert[x, #] & /@ allcompatibleunits[x]

For your case of 1000 meters,

allconversions[Quantity[1000, "m"]]
(*    {Quantity[720000000/1397, "AgateLines"],
       Quantity[15625/25146, "AirMiles"],
       Quantity[324.25, "Akainas"],
       ...
       Quantity[Interval[{3472.2222222222194`, 4166.66666666667}], "Zarot"],
       Quantity[1000000000000000000000000, "Zeptometers"], 
       Quantity[1/1000000000000000000, "Zettameters"]}    *)

Now we need to pick the "best" one of these. What exactly are your criteria? We could for example pick the number that is closest to unity:

bestconversion[x_] := 
  MinimalBy[allconversions[x], Abs[Log[QuantityMagnitude[#]]] &]

bestconversion[Quantity[1000, "m"]]
(*    {Quantity[1, "Kilometers"],
       Quantity[1, "LengthClicks"]}    *)

bestconversion[Quantity[3600, "s"]]
(*    {Quantity[1, "Hours"]}    *)

bestconversion[Quantity[86400, "s"]]
(*    {Quantity[1, "CivialDays"],
       Quantity[1, "Days"],
       Quantity[1, "MayanKins"],
       Quantity[1, "MesopotamianDays"],
       Quantity[1, "Nychthemerons"]}    *)

bestconversion[Quantity[86100, "s"]]
(*    {Quantity[0.9992561804, "SiderealDays"]}    *)

You'll probably have to hand-pick the allunits list to get what you want, to avoid esoteric units. For example, we can pick out metric units in order to narrow things down a bit:

metricunits = Quiet[Union[
  Cases[QuantityUnit[UnitConvert[#, "Metric"]] & /@ allunits, _String]]];
allcompatibleunits[x_] := Select[metricunits, CompatibleUnitQ[#, x] &]

bestconversion[Quantity[1000, "m"]]
(*    {Quantity[1, "Kilometers"]}    *)

bestconversion[Quantity[3600, "s"]]
(*    {Quantity[1, "Hours"]}    *)

bestconversion[Quantity[86400, "s"]]
(*    {Quantity[1, "Days"]}    *)

bestconversion[Quantity[86100, "s"]]
(*    {Quantity[287/288, "Days"]}    *)

which is a bit more sensible.

All this works only with base units, not derived units.

$\endgroup$
5
  • 1
    $\begingroup$ I agree that this is a hard problem not because of its computational aspects, but because of the difficulty in choosing the "best" unit. What do you think of "choosing the unit with the shortest name" among those that generate a number closest to unity? That might end up finding the most commonly used units, at least in the examples you provided. (+1) $\endgroup$
    – MarcoB
    Mar 8 at 17:46
  • 1
    $\begingroup$ I still think the problem shouldn't be as hard as this, since in fact most units belong to some scale and we'd probably like to stay on it: it makes sense to convert 1000 m to 1 km, or 0.3 Russian Skewed Sazhens to 1 Russian Shags, but why try to mix the two? $\endgroup$ Mar 8 at 18:18
  • 1
    $\begingroup$ On a couple of somewhat related comments, QuantityUnits`$UnitList seems simpler to get the list of all available units (at least in v12.2), and CommonUnits seems to implement some sort of heuristic to find suitable unit matches... I would have tried to exploit it for this problem, except CommonUnits@{Quantity[1000, "m"], Quantity[1, "km"]} gives {Quantity[1000, "Meters"], Quantity[1000, "Meters"]} instead of the more reasonable {Quantity[1, "Kilometers"], Quantity[1, "Kilometers"]}. Maybe mathemattica does have some concept of unit scales, deeper inside? $\endgroup$ Mar 8 at 18:24
  • $\begingroup$ @FidelI.Schaposnik For keeping Russian Shags to Russian Skewed Sazhens, I think we'll have to subdivide the unit list manually into desirable joint-blocks. I don't see a way to automate this subdivision. This is exactly what I meant when I said this was a hard problem. $\endgroup$
    – Roman
    Mar 8 at 18:44
  • $\begingroup$ @MarcoB I don't know how to pick by useful shortest name, because QuantityUnit returns strings like Meters and Seconds instead of m and s, so the simple units aren't all that short in name. In fact, minimizing the unit's name (string length) will convert all time units to cés which are centi-days, and all distances to ems and ens and others. $\endgroup$
    – Roman
    Mar 8 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.