14
$\begingroup$

Is there a function that automatically adds the appropriate prefix to a Quantity object?

For example, I would like a function

quantityWithAppropriatePrefix[Quantity[0.000001,"Meter"]]

that would yield

Quantity[1,"Micrometers"]

For some units this hack works:

UnitConvert[UnitConvert[0.00001,"Meter"],"Imperial"],"SI"]
Quantity[10., "Micrometers"]
$\endgroup$

4 Answers 4

8
$\begingroup$

EDITED to include Mr.Wizard's replacement for Switch EDITED to cover additional cases

Roll your own:

quantityWithAppropriatePrefix[quant_Quantity] := 
  Fold[UnitConvert[#1, #2] &, quant,
   {"Imperial", "SI"}];

EDIT: As Kuba pointed out in a comment, this can be written more compactly as

quantityWithAppropriatePrefix[quant_Quantity] := 
  Fold[UnitConvert, quant, {"Imperial", "SI"}];

quantityWithAppropriatePrefix /@
 {Quantity[0.0000011, "Meter"],
  Quantity[0.0000033, "Feet"]}

{Quantity[1.1, "Micrometers"], Quantity[1.00584, "Micrometers"]}

A brute force approach:

quantityWithAppropriatePrefix[quant_Quantity] :=
 Module[{si = UnitConvert[quant, "SI"], mag,
   log10Mag, multiplier},
  mag = QuantityMagnitude[si];
  log10Mag = Round[Log10[mag], 3];
  multiplier = {"Yocto", "Zepto", "Atto", "Femto",
     "Pico", "Nano", "Micro", "Milli", "", "kilo",
     "Mega", "Giga", "Tera", "Peta", "Exa", "Zetta",
     "Yatta"}[[log10Mag/3 + 9]];
  Quantity[mag 10^-log10Mag, multiplier <> QuantityUnit[si]]];

  quantityWithAppropriatePrefix /@ {Quantity[0.0000011, "Meter"], 
      Quantity[0.0000033, "Feet"]}

{Quantity[1.1, "Micrometers"], Quantity[1.00584, "Micrometers"]}

Partition[Table[quantityWithAppropriatePrefix[
    Quantity[1.23*10^n, "Volt"]], {n, -25, 25}],
  6] // Grid

enter image description here

$\endgroup$
6
  • $\begingroup$ You didn't leave a lot of room for improvement but I'll try anyway. If this is used a lot Switch is not the best choice as it is slower than other options such as a Dispatch table or Association, or a simple list extraction using Part. May I edit your answer or shall I post it as my own? $\endgroup$
    – Mr.Wizard
    Commented Aug 22, 2014 at 1:02
  • $\begingroup$ @Mr.Wizard - Fell free to post your solution. $\endgroup$
    – Bob Hanlon
    Commented Aug 22, 2014 at 1:05
  • 1
    $\begingroup$ I found that the difference in timing is completely swamped by other factors so it is not worth posting separately. My proposal is simply: multiplier = {"Yocto", "Zepto", "Atto", "Femto", "Pico", "Nano", "Micro", "Milli", "", "kilo", "Mega", "Giga", "Tera", "Peta", "Exa", "Zetta", "Yatta"}[[log10Mag/3 + 9]]. $\endgroup$
    – Mr.Wizard
    Commented Aug 22, 2014 at 1:15
  • 1
    $\begingroup$ UnitConvert is enough, no need to UnitConvert[ #, #2]&. $\endgroup$
    – Kuba
    Commented May 11, 2016 at 14:03
  • $\begingroup$ @Kuba - Thanks, included above. $\endgroup$
    – Bob Hanlon
    Commented May 11, 2016 at 16:00
5
$\begingroup$

Just a variant:

qf[u_] := Module[{rng = Range[-24, 24, 3],
   multiplier = {"Yocto", "Zepto", "Atto", "Femto", "Pico", "Nano", 
     "Micro", "Milli", "", "Kilo", "Mega", "Giga", "Tera", "Peta", 
     "Exa", "Zetta", "Yatta"}, v, p}, 
  v = QuantityMagnitude[u];
  p = First@Nearest[rng, Log10[v]];
  Quantity[
   v 10^-p, (p /. Thread[rng -> multiplier]) <> QuantityUnit[u]]
  ]
$\endgroup$
2
$\begingroup$

This is a brute force solution, but seems to be more robust and does not require an internet connection, that I put together based on the other posts in this thread.

AutoSIPrefix[quant_Quantity] := (Module[
    {prefixlist, prefixrules, siunitstring, pfloc, baseunit, 
     basequantity, mag, log10Mag, prefix, newunit},
    prefixlist = 
     "kilo" | "mega" | "giga" | "tera" | "peta" | "exa" | "zetta" | 
      "yotta" | "deci" | "centi" | "milli" | "micro" | "nano" | 
      "pico" | "femto" | "atto" | "zepto" | "yocto";
    prefixrules = {3 -> "Kilo", 6 -> "Mega", 9 -> "Giga", 
      12 -> "Tera", 15 -> "Peta", 18 -> "Exa", 21 -> "Zetta", 
      24 -> "Yotta", -1 -> "Deci", -2 -> "Centi", -3 -> "Milli", -6 ->
        "Micro", -9 -> "Nano", -12 -> "Pico", -15 -> 
       "Femto" - 18 -> "Atto", -21 -> "Zepto", -24 -> "Yocto", 
      0 -> ""};
    siunitstring = QuantityUnit[UnitConvert[quant, "SI"]];
    pfloc = 
     StringPosition[siunitstring, prefixlist, IgnoreCase -> True];
    baseunit = 
     If[Length[pfloc] == 1, 
      Capitalize[StringDrop[siunitstring, pfloc[[1]]]], 
      Capitalize[siunitstring]];
    basequantity = 
     UnitConvert[quant, 
      Capitalize[StringDrop[siunitstring, pfloc[[1]]]]];
    mag = QuantityMagnitude[basequantity];
    log10Mag = Round[Log10[mag], 3];
    prefix = log10Mag /. prefixrules;
    newunit = Capitalize[ prefix <> ToLowerCase[baseunit]];
    UnitConvert[quant, newunit]]);
$\endgroup$
0
$\begingroup$

The answer by @ubpdqn, with some small modifications to accept units with prefixes and avoid magnitudes smaller than 1.

NicePrefix[input_Quantity] := Module[
  {
   rng = Range[-24, 24, 3], 
   multiplier = {"Yocto", "Zepto", "Atto", "Femto", "Pico", "Nano", 
     "Micro", "Milli", "", "Kilo", "Mega", "Giga", "Tera", "Peta", 
     "Exa", "Zetta", "Yatta"},
   value, power, baseunit
   },
  baseunit = StringDelete[QuantityUnit[input], multiplier];
  value = QuantityMagnitude[input, Capitalize@baseunit];
  power = Clip[Floor[Log10[value], 3], {-24, 24}];
  Quantity[
   value Power[10, -power], (power /. Thread[rng -> multiplier]) <> baseunit]
  ]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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