Pandas: Generate new columns by old one

I have a dataframe that consists of one column being FEN notations from chess and I would like to split this column, which is in the format

2kr2nr/pp5p/3bpppP/1b1p4/8/1BB1P3/PP3PP1/R3K2R

into 64 columns a1 to h8 containing the letter of the piece being in it, or - if it is empty.

The function is a little bit more complex and should not be the matter of the question, but what I ask myself is:

What is the best way of generating 64 columns out of one in pandas dataframes?

I know things like the apply-function which can run a function, but I have only seen that for only one column. Is there a smart way to do this for all 64 columns at once?


For those of you who want to know the exact thing I do:

FEN consists of eight substrings separated by / which are the rows 8 to 1 (yes in reverse order). Each part says how the eight fields in that row look like. If there is a number, it tells how many empty fields are coming (from left), if its a letter, it says which piece is followed.

So in my example above, a8 and b8 are empty (because of the 2), then there is a "k", "r", two more empty on e8 and h8, followed by the pieces "n" and "r". An 8 in one row means that the row is empty.

So an example would be: Input:

2kr2nr/pp5p/3bpppP/1b1p4/8/1BB1P3/PP3PP1/R3K2R

Output:

a8 -
b8 -
c8 k
d8 r
e8 -
f8 -
g8 n
h8 r
a7 p
b7 p
c7 -
d7 -
e7 -
f7 -
g7 -
h7 p
...
a1 R
b2 -
c1 -
d1 -
e1 K
f1 -
g1 -
h1 R

You could use a custom function. For instance:

def fen2list(s):
    from itertools import chain, product
    l = list(chain.from_iterable('-'*int(c)
                                 if c.isdigit() else c
                                 for c in s if c != '/'))
    
    cols = list(map(''.join, product('abcdefgh', '87654321')))
    
    return pd.Series(l, index=cols)

Example:

fen2list('2kr2nr/pp5p/3bpppP/1b1p4/8/1BB1P3/PP3PP1/R3K2R')

Output:

a8    -
a7    -
a6    k
a5    r
a4    -
     ..
h5    -
h4    K
h3    -
h2    -
h1    R

If you have a DataFrame with a column of FEN strings you can use apply with your custom function:

df['FEN'].apply(fen2list)

Suppose the following dataframe:

import pandas as pd

# https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation#Examples
data = ['rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',
        'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR',
        'rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR',
        'rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R']
df = pd.DataFrame({'FEN': data})
print(df)

# Output
                                                 FEN
0        rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
1      rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR
2    rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR
3  rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R

Create a decode function (adapted from @TimRoberts answer)

from itertools import product

cells = [f"{l}{n}" for n, l in product('87654321', 'ABCDEFGH')]

def decode_fen(fen):
    board = []
    for c in fen:
        if c == '/':
            continue
        elif c.isdigit():
            board.extend('-' * int(c))
        elif c.isalpha():
            board.append(c)
        else:
            board.append('-')
    return pd.Series(board, index=cells)

Create your columns:

fen = df['FEN'].apply(decode_fen)

df = df.join(fen)

Output:

>>> df
                                                 FEN A8 B8 C8 D8 E8 F8 G8 H8 A7 B7 C7 D7 E7 F7 G7 H7 A6 B6 C6 D6 E6 F6 G6 H6 A5 B5 C5 D5 E5 F5 G5 H5 A4 B4 C4 D4 E4 F4 G4 H4 A3 B3 C3 D3 E3 F3 G3 H3 A2 B2 C2 D2 E2 F2 G2 H2 A1 B1 C1 D1 E1 F1 G1 H1
0        rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR  r  n  b  q  k  b  n  r  p  p  p  p  p  p  p  p  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  P  P  P  P  P  P  P  P  R  N  B  Q  K  B  N  R
1      rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR  r  n  b  q  k  b  n  r  p  p  p  p  p  p  p  p  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  P  -  -  -  -  -  -  -  -  -  -  -  P  P  P  P  -  P  P  P  R  N  B  Q  K  B  N  R
2    rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR  r  n  b  q  k  b  n  r  p  p  -  p  p  p  p  p  -  -  -  -  -  -  -  -  -  -  p  -  -  -  -  -  -  -  -  -  P  -  -  -  -  -  -  -  -  -  -  -  P  P  P  P  -  P  P  P  R  N  B  Q  K  B  N  R
3  rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R  r  n  b  q  k  b  n  r  p  p  -  p  p  p  p  p  -  -  -  -  -  -  -  -  -  -  p  -  -  -  -  -  -  -  -  -  P  -  -  -  -  -  -  -  -  N  -  -  P  P  P  P  -  P  P  P  R  N  B  Q  K  B  -  R