Drawing on the Logarithmic Scale

1
A B C D E F

The musical scale is logarithmic – I am going to use this concrete example to show my way of doing mathematical plots.

_

2

The curve above is drawn using the log2. To show this specific plot, I had to make some choice for the scale factors. – This notebook is online at the following address

https://kloimhardt.github.io/site/log.html

Its appendix links a Jupyter notebook for the ensuing code snippets. But I have jumped ahead. My story starts with the Python language. And plotting just the plain log2. And that already hits upon the main message of my presentation.

_

3
plot(log2, [1, 2, 4, 8])

The message is this. – It is the log2 that you plot. This is so obvious that I’d like to state what log2 is not. It is not the log2 of x or y or z. Also, it is not some text that contains the string log2. Rather, log2 is a mathematical object. Only objects like this are suitable for plotting within the paradigm of my presentation.

_

4
A B

I am aware that you need some time for digesting my self-imposed plotting restrictions. So I move more towards the practical goal. -B- I need to get more to something like this. For that, I need to get back to the formula I showed at the start.

_

5
A C D E

The formula I showed at the start is not something you can plot. -B- This is the main theme again. The theme is that I need a proper mathematical target to plot. To arrive at such a target, it is necessary to bind the formula to a procedure. -C- The procedure itself I gave the name Poe. It takes three arguments. Two parameters and the frequency x. -D- The procedure does the wanted computation. – It returns the value given by the formula. -E- In its code, you have to replace the numbers with the names of the two parameters. – I provide the procedure as Python code. -F-

_

B
plot("log2(x / 220) * 7", [1, 2, 4, 8])
"That's not something you can plot"
F
def Poe(intersect, steps, x):
    return log2(x/intersect) * steps

Poe(220, 7, 440)

\(\displaystyle 7.0\)

6
A B D F
I can plot log2 because it is a mathematical object. -B- But still I cannot plot the procedure Poe. -C- I need an operation called “partial” to transform Poe into the proper target to plot. -D- The procedure named “partial” takes “Poe” as an argument. – Then I can perform the plotting. -E- Here is the main theme in spades: “partial” taking a procedure to deliver the target to plot. -F- I turn all this into yet another procedure named log2b – That is very convenient for plotting. -G-
_
C
plot(Poe(220, 7, 440), [1, 2, 4, 8])
"That's not something you can plot"
E
plot(partial(Poe, 220, 7), [1, 2, 4, 8])

G
plot(log2b(220, 7), [1, 2, 4, 8])

7

A B D F G

Not only can log2b be plotted. -B- Log2b can also be called. -C- Not only can log2b be called with data of type number. Log2b can also be called with a symbol. -DE- Using symbols along with numbers is possible thanks to a package named Sympy. Here you see that for the name of the symbol I chose x-naught. But x-naught is only the default of possible choices. As a symbol, for example, I can also choose x1. -F- For log2b, the x1 is just yet another data that happens to be of type symbol. This idea of “symbols as data” is the source for my freedom of choice. Before being able to process some data, log2b needs to be initialized with two parameters. -G- But these do not need to be of type integer. The float 0.7 does nicely as well. -a-

_
C
call(log2b(220, 7), 8)

\(\displaystyle -33.4695179946726\)

E
call(log2b(220, 7), x0)

\(\displaystyle 7 \operatorname{log}_{2}{\left(\frac{x_{0}}{220} \right)}\)

a
call(log2b(1, 0.7), x1)

\(\displaystyle 0.7 \operatorname{log}_{2}{\left(x_{1} \right)}\)

8
A B C D E F G

I would not have bothered you with my O point seven invention, hadn’t I discovered it to be a segue to the natural logarithm. -B- In general, you can fit a series of triangles onto any curve. -C- Moving those triangles to the ground level reveals -D- that a new curve “one over x” fits their heights quite agreeably. −E− You can also try the other way around -F- That does not work quite as well. -G- The trick is to shrink the area while retaining the angles.

_

9
A D

I hope my triangles just formed an agreeable entertainment. For me they were (while making them). In conclusion, I assume you caught on to the topic I am aiming at: -D- differentiation. – Taking the derivative of log2b is simple and easy.

_

10
call(D(log2b(1, 0.7)), x0).evalf()

\(\displaystyle \frac{1.00988652862227}{x_{0}}\)

call(D(ln), x0)

\(\displaystyle \frac{1}{x_{0}}\)

With the log2b initialized at O point seven, I calculate its derivative. As a result I almost get one over x. To become exact, I need to switch to “ln”, the natural logarithm. The pictures above can, within this presentation, be called its pinnacle. As a kind of confirmation, I’d like to drop some names. In math, the procedure named “call” is a functional while D is an operator. In computer science, both is called functions of higher order. The natural logarithm “ln” of course is a function in any field.

_

\[\Large \left. \frac{d \ln(x)}{dx} \right|_{x=x_0}\]

I’d like to wind down with a note on notation. When I differentiate a function like “ln” and then evaluate that derivative at x-naught, the common notation is the vertical bar. This bar is also called “pipe”.

_

A B

In taking this inspiration from math, I use pipe in my pictures as well. -B- Note: it’s not the D operator which is replaced by the pipe, but it’s the “call” that is dropped. – Not only can “pipe” be used in pictures. The pipe can also be used within Python code. -C- You might think that my notation for pipe is hard to achieve. This is not so. My custom pipe is a few lines of Python only. – The pipe symbol is an invented alternative notation. In the following finale, I’d like to discover its explanatory virtues.

_
C
(D(ln) | x0)

\(\displaystyle \frac{1}{x_{0}}\)

class PipeAsCall(object):
    def __init__(self, func):
        self.func = func
    def __or__(self, other):
        return self.func(other)
    def __call__(self, x):
        return self.func(x)

def partial(*args):
    return PipeAsCall(
        functools.partial(*args))
11
A B C E G b

The pipe notation is very versatile. I can also use it when plotting something. -B- In my dataset, the highest frequency is eight kilohertz. -CD- I’d like to cut that down to 900 cycles per second. -E- So I apply a filter – and plot. -F- I want to move the filtering to the left. Or in a more picturesque manner: I want the filtering to be ahead of the pipes. For this, I use function composition. -G- This composition is completely decoupled from input sources and output formats. This leads to a method commonly known as “transducing” – Transducers are ahead of the pipe. -a- For sure you can insert differentiation into the composition as easily as ever. -bc-

_

D
(plotting(ln) | frequencies)

F
(plotting(ln) |
  (filtering(smaller(900)) |
    frequencies))

a
(plotting
  (compose
    (mapping(ln), 
     filtering(smaller(900)))) |
  frequencies)

c
(plotting
  (compose
    (mapping(D(ln)), 
     filtering(smaller(900)))) |
  frequencies)

Appendix

Try the Jupyter notebook:

  • go to the Jupyter-lite web page https://jupyter.org/try-jupyter/lab/
  • click File -> Open from URL
  • paste the following URL: https://raw.githubusercontent.com/kloimhardt/site/main/mysite/log.ipynb
  • select the “Pyodide” Kernel and click Run -> Run All Cells

This notebook was generated using Quarto.

The idea for this Python notebook is based on an older interactive workspace: