frequency as "index" in Pandas dataframe and dynamic extension

Question:

I am working on an RF-project and the workflow is as follows:

  1. Define start, stop, step of the desired frequency sweep
  2. Calculate the specific line impedance Z0 and frequency dependent effective permittivity eef for every frequency
  3. Calculate the Transmission matrix ABCD for every f
  4. From ABCD calculate the scattering Parameters S (complex valued)
  5. Calculate the magnitude of the params
  6. Finally plot them

At the moment I have it implemented with lists/numpy-arrays being filled in a for-loop. This works but is ugly and labor intensive when I want to daisychain multiple ABCD’s.
The issues are a difficult debugging (loosing temporary results between the aforementioned steps.

I think a solution might be using pandas but I have difficulties to implement the following necessities:

  1. Create dataframe with the frequency as the index (a column for f might work too)
  2. expand the columns "on the fly" to store the results for every f
  3. individually set dtype= for some colums because currently dtype=np.clongdouble is necessary (otherwise I get div by zero in latter calculations)
  4. one column containing a numpy-array (the abcd-matrix)

I’ve searched around but the results didn’t clarify the needed concepts and my approach like

>>> import pandas as pd
>>> start = int(100E6)
>>> stop = int(1E9)
>>> step = int(1E6)
>>> df = pd.DataFrame(index=range(start,stop+step,step),columns=["z0","eef"])
>>> df.index
RangeIndex(start=100000000, stop=1001000000, step=1000000)
>>> 

throws errors when I try to access df.index(1000) or df[1000]

Asked By: clme

||

Answers:

In order to make it more future proof, one can create a function that helps one calculate the S-params given f_start, f_stop, f_step, and the functions Z0, and eef. Here I will consider one has already the functions Z0 and eef.

Let’s call the function calc_s (the comments make it self-explanatory)

import pandas as pd

def calc_s(f_start, f_stop, f_step, Z0, eef):

    # Create a dataframe with the frequency column
    df = pd.DataFrame({'f': pd.Series(range(f_start, f_stop, f_step))})

    # Calculate Z0 and eef and add them to the dataframe
    df['Z0'] = df['f'].apply(Z0)
    df['eef'] = df['f'].apply(eef)

    # Calculate the ABCD matrix for every frequency
    df['A'] = 1
    df['B'] = 2 * df['Z0'] * df['eef']**0.5
    df['C'] = 1
    df['D'] = -2 * df['Z0'] * df['eef']**0.5

    # Calculate the scattering parameters S
    df['S11'] = (df['A'] + df['B']) / (df['A'] - df['B'])
    df['S12'] = 2 * df['C'] / (df['A'] - df['B'])
    df['S21'] = 2 * df['C'] / (df['A'] - df['B'])
    df['S22'] = (df['A'] - df['B']) / (df['A'] + df['B'])

    # Calculate the magnitude of the scattering parameters
    df['S11_mag'] = df['S11'].apply(lambda x: x.real**2 + x.imag**2)**0.5
    df['S12_mag'] = df['S12'].apply(lambda x: x.real**2 + x.imag**2)**0.5
    df['S21_mag'] = df['S21'].apply(lambda x: x.real**2 + x.imag**2)**0.5
    df['S22_mag'] = df['S22'].apply(lambda x: x.real**2 + x.imag**2)**0.5

    return df

Note that if needed, one cal always adjust the calculations, or change it to other that one might want/need, be it removing, adding,…

Now, let’s test the function above with some dummy data

f_start = 1
f_stop = 100
f_step = 1
Z0 = lambda f: 50 # This is a dummy function that returns 50 for every f
eef = lambda f: 1 # This is a dummy function that returns 1 for every f
df = calc_s(f_start, f_stop, f_step, Z0, eef)

[Out]:
   f  Z0  eef  A      B  ...       S22   S11_mag   S12_mag   S21_mag   S22_mag
0  1  50    1  1  100.0  ... -0.980198  1.020202  0.020202  0.020202  0.980198
1  2  50    1  1  100.0  ... -0.980198  1.020202  0.020202  0.020202  0.980198
2  3  50    1  1  100.0  ... -0.980198  1.020202  0.020202  0.020202  0.980198
3  4  50    1  1  100.0  ... -0.980198  1.020202  0.020202  0.020202  0.980198
4  5  50    1  1  100.0  ... -0.980198  1.020202  0.020202  0.020202  0.980198

[5 rows x 15 columns]
Answered By: Gonçalo Peres

After messing around for a while I am fine with the following

df = pd.DataFrame(columns=['Z0', 'eef', 'A', 'B', 'C',
                      'D', 's11', 's12', 's21', 's22'], index=f, dtype=bool)
    df.index.name = 'Frequency'

with f = [*range(start, stop+step, step)]. Then I am accessing the necessary column/row by df["A"].loc(f). Even if the column is not used in further calculations I stick to this scheme because I had too much hassle to distinguish between Dataframes with(-out) specific columns. During the process the dtype= is expanded to complex but that’s working like a charm now.

Answered By: clme
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.