Emacs Calc Tricks

I've been learning and using the advanced RPN calculator included in Emacs, known as calc. The primary reason is that it is aware of units. As they say, a number without a unit is meaningless, and it now baffles me that so few programming langauges care to track them at all. It's also built-in to my favorite text editor, which makes copying data in and out of calc very easy. And along those lines, it's a useful tool with org-mode tables, turning them into mini spreadsheets.

Like Emacs itself, calc is a deep and intimidating tool. But after getting comfortable with a few basics it's easy to learn more advanced features from the built-in info documentation.

Stand-alone calc launcher

I like calc so much that I recently replaced the Galculator launcher in my desktop environment with this script:

#!/bin/sh
emacs -g 40x20-0-0 -eval '(progn (calc) (message "") (delete-other-windows))' &

This starts a small Emacs window in the bottom right corner of my screen and clears the default welcome message. I also have the following snippet in my .emacs.d/calc.el file so that the paper trail isn't displayed:

(setq calc-display-trail nil)

Mibibytes

I recently did some work defining partition table sizes, and it was useful to define these units in my calc.el:

(setq math-additional-units '(
  (GiB "1024 * MiB" "Giga Byte")
  (MiB "1024 * KiB" "Mega Byte")
  (KiB "1024 * B" "Kilo Byte")
  (B nil "Byte")
  (Gib "1024 * Mib" "Giga Bit")
  (Mib "1024 * Kib" "Mega Bit")
  (Kib "1024 * b" "Kilo Bit")
  (b "B / 8" "Bit")))
(setq math-units-table nil) ; Clear the units table cache

(Lifted from: https://www.reddit.com/r/emacs/comments/31xezm/common_byte_units_in_calc/cq6ef06/)

Curve fitting in calc [2020-02-05 Wed]

What really convinced me to stick with calc mode was a recent struggle with LibreOffice Calc. I wanted to do a polynomial curve fit of some data (to use as input into a circuit simulation), but after I setup a chart and plotted the fit, I was unable to copy the formula as text. If I attempted to paste it in another program it would paste as an image. This was less than helpful.

libreoffice_calc_curve_fit_plot.png

The workaround for LibreOffice Calc was to learn and use the LINEST function, which is an array function. That was a new trick for me, but it certainly wasn't intuitive to setup, and the output wasn't in a particularly useful format. There was one coefficient per cell, but no labeling for which term the coefficient applied to, and in fact the coefficients were in reverse order from the way I supplied the terms to LINEST. I was not happy with this solution.

libreoffice_calc_curve_fit_linest.png

A bit of research showed that calc could find and plot curve fits as well: https://www.gnu.org/software/emacs/manual/html_mono/calc.html#Curve-Fitting

The input points can be entered as a Nx2 matrix like below, and then transposed into the format calc expects with v t ("vector, transpose").

[
[ 1000,lufquant(-193.5dB,1)],
[ 2000,lufquant(-191.0dB,1)],
[ 2500,lufquant(-190.0dB,1)],
[ 3000,lufquant(-190.5dB,1)],
[ 4000,lufquant(-190.5dB,1)],
[ 5000,lufquant(-189.5dB,1)],
[ 6000,lufquant(-188.5dB,1)],
[ 7000,lufquant(-187.5dB,1)],
[ 8000,lufquant(-183.0dB,1)],
[ 9000,lufquant(-182.5dB,1)],
[10000,lufquant(-181.5dB,1)],
[11000,lufquant(-182.0dB,1)],
[12000,lufquant(-183.0dB,1)],
[13000,lufquant(-190.0dB,1)],

[15000,lufquant(-194.0dB,1)],

[18000,lufquant(-200.0dB,1)],

[20000,lufquant(-250.0dB,1)]
]

The lufquant is a calc function that will convert a decibel value (\(20\log_{10}(x)\)) into the corresponding field quantity, with default reference and units set by the calc-lu-field-reference variable in Emacs. But here I'm passing in a unitless `1` to do the ratio calculation without units.

The a F command will take a 2D matrix of data (2xN), then ask what type of fit you would like to perform. I entered "P" to request a graphical plot using gnuplot, then "5" for a 5th order-polynomial fit, and finally entered "F" to be used as the variable of the polynomial, and calc output a polynomial in a convenient text format. Minimal edits allowed that to be understood by my circuit simulation program, and I now had a voltage source whose amplitude was a function of the simulation frequency.

3.64573332806e-31 F^5 + 1.20125808335e-25 F^4 - 5.36854825319e-21 F^3 
  + 6.69611214816e-17 F^2 - 2.32309953748e-13 F + 4.68451430373e-10

And it even generates a plot of the input data and the fit at the same time. Very handy.

calc_curve_fit.png

Update [2020-08-03 Mon]

This gets even better with WebPlotDigitizer, which I just read about in XKCD #2341. WebPlotDigitizer lets you upload a graph, click two points on each axis (X and Y), specify if they are logarithmic or not, and then automatically trace a plot based on it's color.

Using the following data from WebPlotDigitizer, I get this graph for an 8th degree polynomial fit:

calc_wpd_curve_fit.png

I think it did a better job than I did…

Here's the data, after trimming some of the lower frequency points and getting it ready for calc (v t a F P 8 F <enter>):

[
[1206.658901024318, lufquant(-190.46560392572664dB, 1)],
[1417.471187673314, lufquant(-190.3630806820991dB, 1)],
[1665.1139490856867, lufquant(-189.72683349370467dB, 1)],
[1956.0217431937924, lufquant(-189.4222791523405dB, 1)],
[2297.7532930690713, lufquant(-189.16597104327167dB, 1)],
[2699.1878869347897, lufquant(-188.56289313958027dB, 1)],
[3170.756090722092, lufquant(-188.14375399651473dB, 1)],
[3724.7107678258976, lufquant(-187.809045759966dB, 1)],
[4311.86372182869, lufquant(-187.11148565136295dB, 1)],
[4847.5578865851, lufquant(-186.30436639025598dB, 1)],
[5331.44717626, lufquant(-185.54147284208634dB, 1)],
[5778.431510723953, lufquant(-184.8979887188476dB, 1)],
[6217.219580237294, lufquant(-184.06875660127196dB, 1)],
[6640.546443020515, lufquant(-183.3440077305108dB, 1)],
[7411.108515269597, lufquant(-182.57432955592466dB, 1)],
[8454.70379670961, lufquant(-182.21980590254034dB, 1)],
[9645.252952755549, lufquant(-181.46768160265094dB, 1)],
[11165.703563910034, lufquant(-181.78228724240995dB, 1)],
[12370.48776254165, lufquant(-182.60930807433877dB, 1)],
[13309.846900776742, lufquant(-183.34566619474597dB, 1)],
[14216.106629891186, lufquant(-184.0571473516259dB, 1)],
[14963.42590478183, lufquant(-184.67907143980764dB, 1)],
[15750.030626325619, lufquant(-185.50830355738333dB, 1)],
[16577.985971175312, lufquant(-186.2380278208499dB, 1)],
[17449.4656792043, lufquant(-187.01750601137104dB, 1)],
[18501.67814519684, lufquant(-187.92966134070429dB, 1)],
[19617.33960693196, lufquant(-188.7340164947527dB, 1)],
[20953.072843434187, lufquant(-189.4156452953999dB, 1)],
[22379.755378610287, lufquant(-190.2100496640374dB, 1)],
[23729.26705009381, lufquant(-191.10562035101913dB, 1)],
[24976.67881389389, lufquant(-192.0315962156453dB, 1)],
[26097.952158352404, lufquant(-192.84977190498662dB, 1)],
[27269.562616178202, lufquant(-193.701116879031dB, 1)],
[28493.769961934435, lufquant(-194.49717971190364dB, 1)],
[29991.64445466525, lufquant(-195.32641182947933dB, 1)],
[31800.15726250572, lufquant(-196.11418234117622dB, 1)],
[33717.724396495825, lufquant(-196.84390660464283dB, 1)],
[36013.54462686531, lufquant(-197.7477696128003dB, 1)],
[38748.25104223567, lufquant(-198.52393087485112dB, 1)],
[41386.596904136204, lufquant(-199.42281849030317dB, 1)]
]
© Copyright 2020, Remington Furman