Andrea Mannari gave an excellent answer and mostly solved my problem, but I wanted a better explanation of why the slicing of the byte string was necessary so after some investigation, this is what I found.
Each of the elements in the DataFrame is stored as a byte string and if you print the DataFrame you clearly see that the elements are b'encoded text' denoting that they are byte strings so the following code should give the decrypted byte string but it doesn't.
f.decrypt(token2['name'][0])
"b'encrypted text'"
The problem is that when you use a pandas element this way pandas apparently applies the __str()__ function to it thereby converting the byte string into a byte string wrapped in a normal string.
token2['name'][0].__str__()
"b'encrypted text'"
The solution to this problem is to force pandas to evaluate the byte string as a byte string by using the eval() function, so the following code will produce a byte string that we can use for decoding (conversion from byte string to normal string) and decryption (encrypting and decrypting byte strings).
eval(token2['name'][0])
b'encrypted text'
I also added encryption for the column headings. Below is my version of Andrea's code.
import pandas as pd
from cryptography.fernet import Fernet
data = {'name': ["Joe", "Joe", "Joe","Jane","Jane"],
'job': ["Analyst","Manager","Director","Analyst","Manager"],
'#': [1,2,3,4,5],
'yrs_serv': [1.1, 1.2, 1.3, 1.4, 1.5]}
df = pd.DataFrame(data, columns=['name', 'job', '#', 'yrs_serv'])
print(f'orig df \n{df}\n')
# generate key
# encrypt_key = Fernet.generate_key()
encrypt_key = b'gB07ncUSR2oFkeUGD8_gM_CcBQvfWwslrXg3QZOAqII='
# Use key to encrypt data
f = Fernet(encrypt_key)
df_e = df.apply(lambda x: x.astype(str)) # preprocess
token = df_e.applymap(lambda x: f.encrypt(x.encode('utf-8')))
# Encrypt column headings
token.columns = [f.encrypt(bytes(x,'utf-8')) for x in df_e.columns]
# Save to CSV file
token.to_csv('encrypted_file.csv', index=False)
print(f'encrypted df \n{token}\n')
#Read and decoding CSV:
token2 = pd.read_csv('encrypted_file.csv')
# Decrypt column headings first
token2.columns = [f.decrypt(eval(x).decode('utf-8')).decode('utf-8') for x in token2.columns]
# Decrypt the CSV data
df_decrp = token2.applymap(lambda x: f.decrypt(eval(x).decode('utf-8')).decode('utf-8'))
print(f'decrypted df \n{df_decrp}\n', )
Decrypting the CSV data involves the following steps:
Evaluating elements to byte strings
eval(x)
Decoding the byte string to a normal string
eval(x).decode('utf-8')
Decrypting the normal string resulting in a byte string
f.decrypt(eval(x).decode('utf-8'))
Decoding the decrypted byte string to a normal string
f.decrypt(eval(x).decode('utf-8')).decode('utf-8')
See this link for decoding and encoding byte strings
Convert bytes to a string in python 3